From 3aefbafb9fe26127e26bf80a7061473e35fa7e87 Mon Sep 17 00:00:00 2001 From: Kartheek Palla Date: Wed, 14 Jul 2021 14:20:33 +0530 Subject: [PATCH 001/490] Issue #SB-25698 fix: compatibilityLevel default value updated --- schemas/content/1.0/schema.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/schemas/content/1.0/schema.json b/schemas/content/1.0/schema.json index 3f934d69d..731fe001a 100644 --- a/schemas/content/1.0/schema.json +++ b/schemas/content/1.0/schema.json @@ -147,7 +147,7 @@ }, "compatibilityLevel": { "type": "number", - "default": 1 + "default": 2 }, "minGenieVersion": { "type": "string" @@ -1372,4 +1372,4 @@ } } } -} \ No newline at end of file +} From 1dba12b88d97ce466d363c19b1df4b36a857e097 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Wed, 2 Feb 2022 11:54:52 +0530 Subject: [PATCH 002/490] Issue #SB-28558 fix: key not found error --- .../url/mgr/impl/GoogleDriveURLManagerImplTest.java | 6 +++--- .../java/org/sunbird/url/util/GoogleDriveUtilTest.java | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/platform-modules/url-manager/src/test/java/org/sunbird/url/mgr/impl/GoogleDriveURLManagerImplTest.java b/platform-modules/url-manager/src/test/java/org/sunbird/url/mgr/impl/GoogleDriveURLManagerImplTest.java index bcc58b89a..ee7998fc5 100644 --- a/platform-modules/url-manager/src/test/java/org/sunbird/url/mgr/impl/GoogleDriveURLManagerImplTest.java +++ b/platform-modules/url-manager/src/test/java/org/sunbird/url/mgr/impl/GoogleDriveURLManagerImplTest.java @@ -27,7 +27,7 @@ public class GoogleDriveURLManagerImplTest { @Test public void testValidateUrlWithValidUrlValidCriteria() { - String driveUrl = "https://drive.google.com/file/d/1az_AFAoRwu9cXlr1R5pO9fNhHexzJKXo/view?usp=sharing"; + String driveUrl = "https://drive.google.com/file/d/1ZUSXrODwNK52pzDJZ_fuNKK9lXBzxCsS/view?usp=sharing"; Map result = gdMgr.validateURL(driveUrl, "size"); assertTrue(MapUtils.isNotEmpty(result)); assertTrue(result.size() == 2); @@ -41,7 +41,7 @@ public void testValidateUrlWithValidUrlValidCriteria() { public void testValidateUrlWithValidUrlInvalidCriteria() { exception.expect(ClientException.class); exception.expectMessage("Please Provide Valid Criteria For Validation. Supported Criteria : [size]"); - String driveUrl = "https://drive.google.com/file/d/1az_AFAoRwu9cXlr1R5pO9fNhHexzJKXo/view?usp=sharing"; + String driveUrl = "https://drive.google.com/file/d/1ZUSXrODwNK52pzDJZ_fuNKK9lXBzxCsS/view?usp=sharing"; Map result = gdMgr.validateURL(driveUrl, "name"); } @@ -56,7 +56,7 @@ public void testValidateUrlWithInvalidUrlValidCriteria() { @Test public void testReadMetadataWithValidUrl() { - String driveUrl = "https://drive.google.com/file/d/1az_AFAoRwu9cXlr1R5pO9fNhHexzJKXo/view?usp=sharing"; + String driveUrl = "https://drive.google.com/file/d/1ZUSXrODwNK52pzDJZ_fuNKK9lXBzxCsS/view?usp=sharing"; Map result = gdMgr.readMetadata(driveUrl); assertTrue(MapUtils.isNotEmpty(result)); assertTrue(result.size() == 3); diff --git a/platform-modules/url-manager/src/test/java/org/sunbird/url/util/GoogleDriveUtilTest.java b/platform-modules/url-manager/src/test/java/org/sunbird/url/util/GoogleDriveUtilTest.java index 55dd2cd82..17a5b8723 100644 --- a/platform-modules/url-manager/src/test/java/org/sunbird/url/util/GoogleDriveUtilTest.java +++ b/platform-modules/url-manager/src/test/java/org/sunbird/url/util/GoogleDriveUtilTest.java @@ -29,9 +29,9 @@ public class GoogleDriveUtilTest { @Test public void testGetDriveUrlWithValidUrl() { - String driveUrl = "https://drive.google.com/file/d/1az_AFAoRwu9cXlr1R5pO9fNhHexzJKXo/view?usp=sharing"; + String driveUrl = "https://drive.google.com/file/d/1ZUSXrODwNK52pzDJZ_fuNKK9lXBzxCsS/view?usp=sharing"; String output = GoogleDriveUrlUtil.getDriveFileId(driveUrl); - assertEquals("1az_AFAoRwu9cXlr1R5pO9fNhHexzJKXo", output); + assertEquals("1ZUSXrODwNK52pzDJZ_fuNKK9lXBzxCsS", output); } @Test @@ -43,7 +43,7 @@ public void testGetDriveUrlWithInvalidUrl() { @Test public void testGetDriveFileWithValidUrl() { - String driveUrl = "https://drive.google.com/file/d/1az_AFAoRwu9cXlr1R5pO9fNhHexzJKXo/view?usp=sharing"; + String driveUrl = "https://drive.google.com/file/d/1ZUSXrODwNK52pzDJZ_fuNKK9lXBzxCsS/view?usp=sharing"; String fileId = GoogleDriveUrlUtil.getDriveFileId(driveUrl); File driveFile = GoogleDriveUrlUtil.getDriveFile(fileId); assertNotNull(driveFile); @@ -70,7 +70,7 @@ public void testGetDriveFileWithInvalidUrl() { @Test public void testGetMetadataWithValidUrl() { - String driveUrl = "https://drive.google.com/file/d/1az_AFAoRwu9cXlr1R5pO9fNhHexzJKXo/view?usp=sharing"; + String driveUrl = "https://drive.google.com/file/d/1ZUSXrODwNK52pzDJZ_fuNKK9lXBzxCsS/view?usp=sharing"; Map result = GoogleDriveUrlUtil.getMetadata(driveUrl); assertTrue(MapUtils.isNotEmpty(result)); assertTrue(result.size() == 3); @@ -92,7 +92,7 @@ public void testGetMetadataWithInvalidUrl() { @Test public void testGetSizeWithValidUrl() { - String driveUrl = "https://drive.google.com/file/d/1az_AFAoRwu9cXlr1R5pO9fNhHexzJKXo/view?usp=sharing"; + String driveUrl = "https://drive.google.com/file/d/1ZUSXrODwNK52pzDJZ_fuNKK9lXBzxCsS/view?usp=sharing"; Long result = GoogleDriveUrlUtil.getSize(driveUrl); assertTrue(result > 0); } From c02c25daed9eb0d99a3da20044b4ba9d5cc0bf45 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Thu, 24 Feb 2022 16:47:56 +0530 Subject: [PATCH 003/490] Issue #SB-28931 debug: Shallow copy flow throwing nullpointer exception --- .../main/scala/org/sunbird/managers/HierarchyManager.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content-api/hierarchy-manager/src/main/scala/org/sunbird/managers/HierarchyManager.scala b/content-api/hierarchy-manager/src/main/scala/org/sunbird/managers/HierarchyManager.scala index 186232d39..039c251fa 100644 --- a/content-api/hierarchy-manager/src/main/scala/org/sunbird/managers/HierarchyManager.scala +++ b/content-api/hierarchy-manager/src/main/scala/org/sunbird/managers/HierarchyManager.scala @@ -453,7 +453,7 @@ object HierarchyManager { responseFuture.map(response => { if (!ResponseHandler.checkError(response)) { val relationalMetadataString = response.getResult.toMap.getOrDefault("relational_metadata", "").asInstanceOf[String] - if (StringUtils.isNotEmpty(relationalMetadataString)) { + if (StringUtils.isNotEmpty(relationalMetadataString) && !relationalMetadataString.trim.isBlank) { Future(JsonUtils.deserialize(relationalMetadataString, classOf[java.util.Map[String, AnyRef]]).toMap) } else Future(Map[String, AnyRef]()) @@ -464,7 +464,7 @@ object HierarchyManager { responseFuture.map(response => { if (!ResponseHandler.checkError(response)) { val relationalMetadataString = response.getResult.toMap.getOrDefault("relational_metadata", "").asInstanceOf[String] - if (StringUtils.isNotEmpty(relationalMetadataString)) { + if (StringUtils.isNotEmpty(relationalMetadataString) && !relationalMetadataString.trim.isBlank) { Future(JsonUtils.deserialize(relationalMetadataString, classOf[java.util.Map[String, AnyRef]]).toMap) } else Future(Map[String, AnyRef]()) From 9cd8a532c2f3861f7d81b6beb2a3cf23b577735e Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Thu, 24 Feb 2022 19:31:34 +0530 Subject: [PATCH 004/490] Issue #SB-28931 debug: Shallow copy flow throwing nullpointer exception --- .../src/main/scala/org/sunbird/managers/HierarchyManager.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/content-api/hierarchy-manager/src/main/scala/org/sunbird/managers/HierarchyManager.scala b/content-api/hierarchy-manager/src/main/scala/org/sunbird/managers/HierarchyManager.scala index 039c251fa..1640d3680 100644 --- a/content-api/hierarchy-manager/src/main/scala/org/sunbird/managers/HierarchyManager.scala +++ b/content-api/hierarchy-manager/src/main/scala/org/sunbird/managers/HierarchyManager.scala @@ -454,7 +454,8 @@ object HierarchyManager { if (!ResponseHandler.checkError(response)) { val relationalMetadataString = response.getResult.toMap.getOrDefault("relational_metadata", "").asInstanceOf[String] if (StringUtils.isNotEmpty(relationalMetadataString) && !relationalMetadataString.trim.isBlank) { - Future(JsonUtils.deserialize(relationalMetadataString, classOf[java.util.Map[String, AnyRef]]).toMap) + val relMetadataJavaMap = JsonUtils.deserialize(relationalMetadataString, classOf[java.util.Map[String, AnyRef]]) + if(relMetadataJavaMap != null && relMetadataJavaMap.size()>0) Future(relMetadataJavaMap.toMap) else Future(Map[String, AnyRef]()) } else Future(Map[String, AnyRef]()) } else { From bbf73e55c7ef2415ab270021a908fcd1ff0fae4c Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Thu, 24 Mar 2022 14:39:34 +0530 Subject: [PATCH 005/490] Issue #SB-29319 fix: Name getting stored without trim. --- .../collectioncsv/manager/CollectionCSVManager.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/content-api/collection-csv-actors/src/main/scala/org.sunbird/collectioncsv/manager/CollectionCSVManager.scala b/content-api/collection-csv-actors/src/main/scala/org.sunbird/collectioncsv/manager/CollectionCSVManager.scala index 0ea1eb3e5..61c4fd97a 100644 --- a/content-api/collection-csv-actors/src/main/scala/org.sunbird/collectioncsv/manager/CollectionCSVManager.scala +++ b/content-api/collection-csv-actors/src/main/scala/org.sunbird/collectioncsv/manager/CollectionCSVManager.scala @@ -318,12 +318,12 @@ object CollectionCSVManager extends CollectionInputFileReader { val nodeInfo = record._2.asInstanceOf[scala.collection.mutable.Map[String, AnyRef]] if(mode.equals(CollectionTOCConstants.CREATE)) s""""${record._1}": {"isNew": true,"root": false, "metadata": {"mimeType": "application/vnd.ekstep.content-collection","contentType": "$collectionUnitType", - |"name": ${JsonUtils.serialize(nodeInfo("name").toString)}, "description": ${if(nodeInfo.contains(CollectionTOCConstants.DESCRIPTION)) JsonUtils.serialize(nodeInfo(CollectionTOCConstants.DESCRIPTION).toString) else JsonUtils.serialize("")}, + |"name": ${JsonUtils.serialize(nodeInfo("name").toString.trim)}, "description": ${if(nodeInfo.contains(CollectionTOCConstants.DESCRIPTION)) JsonUtils.serialize(nodeInfo(CollectionTOCConstants.DESCRIPTION).toString) else JsonUtils.serialize("")}, |"dialcodeRequired": "No","code": "nodeID","framework": "$frameworkID" }}""".stripMargin else try { s""""${nodeInfo(CollectionTOCConstants.IDENTIFIER).toString}": {"isNew": false,"root": false, "metadata": {"mimeType": "application/vnd.ekstep.content-collection", - |"contentType": "$collectionUnitType","name": ${JsonUtils.serialize(nodeInfo("name").toString)}, + |"contentType": "$collectionUnitType","name": ${JsonUtils.serialize(nodeInfo("name").toString.trim)}, |"description": ${if(nodeInfo.contains(CollectionTOCConstants.DESCRIPTION)) JsonUtils.serialize(nodeInfo(CollectionTOCConstants.DESCRIPTION).toString) else JsonUtils.serialize("")}, |"dialcodeRequired": "${nodeInfo(CollectionTOCConstants.DIAL_CODE_REQUIRED).toString}","dialcodes": "${nodeInfo(CollectionTOCConstants.DIAL_CODES).toString}", |"code": "${nodeInfo(CollectionTOCConstants.IDENTIFIER).toString}","framework": "$frameworkID", @@ -368,7 +368,7 @@ object CollectionCSVManager extends CollectionInputFileReader { val hierarchyChildNodesMetadata = if(mode.equals(CollectionTOCConstants.CREATE)) { folderInfoMap.map(record => { val nodeInfo = record._2.asInstanceOf[scala.collection.mutable.Map[String, AnyRef]] - s""""${record._1}": {"name": ${JsonUtils.serialize(nodeInfo("name").toString)},"root": false,"contentType": "$collectionUnitType", "children": ${if (nodeInfo.contains(CollectionTOCConstants.CHILDREN)) nodeInfo(CollectionTOCConstants.CHILDREN).asInstanceOf[Seq[String]].mkString("[\"", "\",\"", "\"]") else "[]"}}""" + s""""${record._1}": {"name": ${JsonUtils.serialize(nodeInfo("name").toString.trim)},"root": false,"contentType": "$collectionUnitType", "children": ${if (nodeInfo.contains(CollectionTOCConstants.CHILDREN)) nodeInfo(CollectionTOCConstants.CHILDREN).asInstanceOf[Seq[String]].mkString("[\"", "\",\"", "\"]") else "[]"}}""" }).mkString(",") } else { val linkedContentsInfoMap: Map[String, Map[String, String]] = if(linkedContentsDetails.nonEmpty) { @@ -421,7 +421,7 @@ object CollectionCSVManager extends CollectionInputFileReader { nodeInfo(CollectionTOCConstants.LINKED_CONTENT).asInstanceOf[Seq[String]].toSet.mkString("[\"","\",\"","\"]") else "[]" } - val folderNodeHierarchy = s""""${record._1}": {"name": "${nodeInfo("name").toString}","root": false,"contentType": "$collectionUnitType", "children": $childrenFolders}""" + val folderNodeHierarchy = s""""${record._1}": {"name": "${nodeInfo("name").toString.trim}","root": false,"contentType": "$collectionUnitType", "children": $childrenFolders}""" val contentsNode = if(nodeInfo.contains(CollectionTOCConstants.LINKED_CONTENT) && nodeInfo(CollectionTOCConstants.LINKED_CONTENT).asInstanceOf[Seq[String]].nonEmpty && linkedContentsInfoMap.nonEmpty) { From aca259ea1d699c88ad7fabcfb9ce0fc7d2b77c08 Mon Sep 17 00:00:00 2001 From: joffinjoy <35325730+joffinjoy@users.noreply.github.com> Date: Sat, 26 Mar 2022 10:22:22 +0530 Subject: [PATCH 006/490] Issue #SB-29145 feat: Added Question & QuestionSet Copy APIs & Test-Cases --- .../org/sunbird/actors/QuestionActor.scala | 8 +- .../org/sunbird/actors/QuestionSetActor.scala | 8 +- .../org/sunbird/managers/CopyManager.scala | 293 ++++++++++++++++++ .../sunbird/utils/AssessmentContants.scala | 39 +++ .../src/test/resources/application.conf | 10 + .../sunbird/actors/QuestionActorTest.scala | 41 ++- .../sunbird/actors/QuestionSetActorTest.scala | 58 +++- .../scala/org/sunbird/actors/copyTrait.scala | 264 ++++++++++++++++ .../controllers/v4/QuestionController.scala | 13 + .../v4/QuestionSetController.scala | 11 + .../assessment-service/app/utils/ApiId.scala | 3 +- .../app/utils/QuestionOperations.scala | 2 +- .../app/utils/QuestionSetOperations.scala | 6 +- .../assessment-service/conf/application.conf | 12 +- assessment-api/assessment-service/conf/routes | 8 +- schemas/question/1.0/schema.json | 6 + schemas/questionset/1.0/schema.json | 6 + 17 files changed, 774 insertions(+), 14 deletions(-) create mode 100644 assessment-api/assessment-actors/src/main/scala/org/sunbird/managers/CopyManager.scala create mode 100644 assessment-api/assessment-actors/src/main/scala/org/sunbird/utils/AssessmentContants.scala create mode 100644 assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/copyTrait.scala diff --git a/assessment-api/assessment-actors/src/main/scala/org/sunbird/actors/QuestionActor.scala b/assessment-api/assessment-actors/src/main/scala/org/sunbird/actors/QuestionActor.scala index 8f429f13a..718dc83d6 100644 --- a/assessment-api/assessment-actors/src/main/scala/org/sunbird/actors/QuestionActor.scala +++ b/assessment-api/assessment-actors/src/main/scala/org/sunbird/actors/QuestionActor.scala @@ -8,7 +8,7 @@ import org.sunbird.common.{DateUtils, Platform} import org.sunbird.graph.OntologyEngineContext import org.sunbird.graph.nodes.DataNode import org.sunbird.graph.utils.NodeUtil -import org.sunbird.managers.AssessmentManager +import org.sunbird.managers.{AssessmentManager, CopyManager} import org.sunbird.utils.RequestUtil import java.util @@ -36,6 +36,7 @@ class QuestionActor @Inject()(implicit oec: OntologyEngineContext) extends BaseA case "systemUpdateQuestion" => systemUpdate(request) case "listQuestions" => listQuestions(request) case "rejectQuestion" => reject(request) + case "copyQuestion" => copy(request) case _ => ERROR(request.getOperation) } @@ -127,4 +128,9 @@ class QuestionActor @Inject()(implicit oec: OntologyEngineContext) extends BaseA AssessmentManager.updateNode(updateRequest) }) } + + def copy(request: Request): Future[Response] ={ + RequestUtil.restrictProperties(request) + CopyManager.copy(request) + } } diff --git a/assessment-api/assessment-actors/src/main/scala/org/sunbird/actors/QuestionSetActor.scala b/assessment-api/assessment-actors/src/main/scala/org/sunbird/actors/QuestionSetActor.scala index f5b5742d9..ea022c3fa 100644 --- a/assessment-api/assessment-actors/src/main/scala/org/sunbird/actors/QuestionSetActor.scala +++ b/assessment-api/assessment-actors/src/main/scala/org/sunbird/actors/QuestionSetActor.scala @@ -1,7 +1,6 @@ package org.sunbird.actors import java.util - import javax.inject.Inject import org.apache.commons.collections4.CollectionUtils import org.sunbird.`object`.importer.{ImportConfig, ImportManager} @@ -13,7 +12,7 @@ import org.sunbird.graph.OntologyEngineContext import org.sunbird.graph.nodes.DataNode import org.sunbird.graph.dac.model.Node import org.sunbird.managers.HierarchyManager.hierarchyPrefix -import org.sunbird.managers.{AssessmentManager, HierarchyManager, UpdateHierarchyManager} +import org.sunbird.managers.{AssessmentManager, CopyManager, HierarchyManager, UpdateHierarchyManager} import org.sunbird.utils.RequestUtil import scala.collection.JavaConverters._ @@ -40,6 +39,7 @@ class QuestionSetActor @Inject()(implicit oec: OntologyEngineContext) extends Ba case "rejectQuestionSet" => reject(request) case "importQuestionSet" => importQuestionSet(request) case "systemUpdateQuestionSet" => systemUpdate(request) + case "copyQuestionSet" => copy(request) case _ => ERROR(request.getOperation) } @@ -156,4 +156,8 @@ class QuestionSetActor @Inject()(implicit oec: OntologyEngineContext) extends Ba }).map(node => ResponseHandler.OK.put("identifier", identifier).put("status", "success")) } + def copy(request: Request): Future[Response] ={ + RequestUtil.restrictProperties(request) + CopyManager.copy(request) + } } diff --git a/assessment-api/assessment-actors/src/main/scala/org/sunbird/managers/CopyManager.scala b/assessment-api/assessment-actors/src/main/scala/org/sunbird/managers/CopyManager.scala new file mode 100644 index 000000000..5c5a5fa73 --- /dev/null +++ b/assessment-api/assessment-actors/src/main/scala/org/sunbird/managers/CopyManager.scala @@ -0,0 +1,293 @@ +package org.sunbird.managers + +import org.apache.commons.collections.CollectionUtils +import org.apache.commons.collections4.MapUtils +import org.apache.commons.lang.StringUtils +import org.sunbird.common.Platform +import org.sunbird.common.dto.{Request, Response, ResponseHandler} +import org.sunbird.common.exception.{ClientException, ServerException} +import org.sunbird.graph.OntologyEngineContext +import org.sunbird.graph.common.Identifier +import org.sunbird.graph.dac.model.Node +import org.sunbird.graph.nodes.DataNode +import org.sunbird.graph.schema.DefinitionNode +import org.sunbird.graph.utils.{NodeUtil, ScalaJsonUtils} +import org.sunbird.telemetry.logger.TelemetryManager +import org.sunbird.utils.AssessmentConstants + +import java.util +import java.util.concurrent.{CompletionException, TimeUnit} +import java.util.{Optional, UUID} +import scala.collection.JavaConverters._ +import scala.concurrent.duration.{Duration, FiniteDuration} +import scala.concurrent.{Await, ExecutionContext, Future} + +object CopyManager { + + private val originMetadataKeys: util.List[String] = Platform.getStringList("assessment.copy.origin_data", new util.ArrayList[String]()) + private val internalHierarchyProps = List("identifier", "parent", "index", "depth") + private val metadataNotTobeCopied = Platform.config.getStringList("assessment.copy.props_to_remove") + + def copy(request: Request)(implicit ec: ExecutionContext, oec: OntologyEngineContext): Future[Response] = { + validateRequest(request) + DataNode.read(request).map(node => { + validateExistingNode(request, node) + val copiedNodeFuture: Future[Node] = node.getMetadata.get(AssessmentConstants.MIME_TYPE) match { + case AssessmentConstants.QUESTIONSET_MIME_TYPE => + node.setInRelations(null) + node.setOutRelations(null) + validateShallowCopyReq(node, request) + copyQuestionSet(node, request) + case AssessmentConstants.QUESTION_MIME_TYPE => + node.setInRelations(null) + validateCopyQuestionReq(request, node) //Check if the question has got "Default" visibility. + copyNode(node, request) + } + copiedNodeFuture.map(copiedNode => { + val response = ResponseHandler.OK() + response.put("node_id", new util.HashMap[String, AnyRef]() { + { + put(node.getIdentifier, copiedNode.getIdentifier) + } + }) + response.put(AssessmentConstants.VERSION_KEY, copiedNode.getMetadata.get(AssessmentConstants.VERSION_KEY)) + response + }) + }).flatMap(f => f) recoverWith { case e: CompletionException => throw e.getCause } + } + + def validateCopyQuestionReq(request: Request, node: Node) = { + val visibility = node.getMetadata.getOrDefault(AssessmentConstants.VISIBILITY, AssessmentConstants.VISIBILITY_PARENT).asInstanceOf[String] + if (StringUtils.equalsIgnoreCase(visibility, AssessmentConstants.VISIBILITY_PARENT)) { + throw new ClientException(AssessmentConstants.ERR_INVALID_REQUEST, "Question With Visibility Parent Cannot Be Copied Individually!") + } + } + + def validateExistingNode(request: Request, node: Node) = { + val requestObjectType = request.getObjectType + val nodeObjectType = node.getObjectType + if (!StringUtils.equalsIgnoreCase(requestObjectType, nodeObjectType)) { + throw new ClientException(AssessmentConstants.ERR_INVALID_OBJECT_TYPE, "Invalid Object Type: " + requestObjectType) + } + } + + def copyQuestionSet(originNode: Node, request: Request)(implicit ex: ExecutionContext, oec: OntologyEngineContext): Future[Node] = { + val copyType = request.getRequest.get(AssessmentConstants.COPY_TYPE).asInstanceOf[String] + copyNode(originNode, request).map(node => { + val req = new Request(request) + req.put(AssessmentConstants.ROOT_ID, request.get(AssessmentConstants.IDENTIFIER)) + req.put(AssessmentConstants.MODE, request.get(AssessmentConstants.MODE)) + HierarchyManager.getHierarchy(req).map(response => { + val originHierarchy = response.getResult.getOrDefault(AssessmentConstants.QUESTIONSET, new util.HashMap[String, AnyRef]()).asInstanceOf[java.util.Map[String, AnyRef]] + copyType match { + case AssessmentConstants.COPY_TYPE_SHALLOW => updateShallowHierarchy(request, node, originNode, originHierarchy) + case _ => updateHierarchy(request, node, originNode, originHierarchy, copyType) + } + }).flatMap(f => f) + }).flatMap(f => f) recoverWith { case e: CompletionException => throw e.getCause } + } + + + def copyNode(node: Node, request: Request)(implicit ec: ExecutionContext, oec: OntologyEngineContext): Future[Node] = { + val copyCreateReq: Future[Request] = getCopyRequest(node, request) + copyCreateReq.map(req => { + DataNode.create(req).map(copiedNode => { + Future(copiedNode) + }).flatMap(f => f) + }).flatMap(f => f) + } + + def updateHierarchy(request: Request, node: Node, originNode: Node, originHierarchy: util.Map[String, AnyRef], copyType: String)(implicit ec: ExecutionContext, oec: OntologyEngineContext): Future[Node] = { + prepareHierarchyRequest(originHierarchy, originNode, node, copyType, request).map(req => { + val hierarchyRequest = new Request(request) + hierarchyRequest.putAll(req) + hierarchyRequest.getContext.put(AssessmentConstants.SCHEMA_NAME, AssessmentConstants.QUESTIONSET_SCHEMA_NAME) + hierarchyRequest.getContext.put(AssessmentConstants.VERSION, AssessmentConstants.SCHEMA_VERSION) + UpdateHierarchyManager.updateHierarchy(hierarchyRequest).map(response => { + if (!ResponseHandler.checkError(response)) node else { + TelemetryManager.info(s"Update Hierarchy Failed For Copy Question Set Having Identifier: ${node.getIdentifier} | Response is : " + response) + throw new ServerException("ERR_QUESTIONSET_COPY", "Something Went Wrong, Please Try Again") + } + }) + }).flatMap(f => f) + } + + def prepareHierarchyRequest(originHierarchy: util.Map[String, AnyRef], originNode: Node, node: Node, copyType: String, request: Request)(implicit ec: ExecutionContext, oec: OntologyEngineContext): Future[util.Map[String, AnyRef]] = { + val children: util.List[util.Map[String, AnyRef]] = originHierarchy.get("children").asInstanceOf[util.List[util.Map[String, AnyRef]]] + if (null != children && !children.isEmpty) { + val nodesModified = new util.HashMap[String, AnyRef]() + val hierarchy = new util.HashMap[String, AnyRef]() + val idMap = new util.HashMap[String, String]() + hierarchy.put(node.getIdentifier, new util.HashMap[String, AnyRef]() { + { + put(AssessmentConstants.CHILDREN, new util.ArrayList[String]()) + put(AssessmentConstants.ROOT, true.asInstanceOf[AnyRef]) + put(AssessmentConstants.PRIMARY_CATEGORY, node.getMetadata.get(AssessmentConstants.PRIMARY_CATEGORY)) + } + }) + populateHierarchyRequest(children, nodesModified, hierarchy, node.getIdentifier, copyType, request, idMap) + getExternalData(idMap.keySet().asScala.toList, request).map(exData => { + idMap.asScala.toMap.foreach(entry => { + nodesModified.get(entry._2).asInstanceOf[java.util.Map[String, AnyRef]].get("metadata").asInstanceOf[util.Map[String, AnyRef]].putAll(exData.getOrDefault(entry._1, new util.HashMap[String, AnyRef]()).asInstanceOf[util.Map[String, AnyRef]]) + }) + new util.HashMap[String, AnyRef]() { + { + put(AssessmentConstants.NODES_MODIFIED, nodesModified) + put(AssessmentConstants.HIERARCHY, hierarchy) + } + } + }) + + } else Future(new util.HashMap[String, AnyRef]()) + } + + def getExternalData(identifiers: List[String], request: Request)(implicit ec: ExecutionContext, oec: OntologyEngineContext): Future[util.Map[String, AnyRef]] = { + val req = new Request(request) + req.getContext().putAll(Map("objectType" -> "Question", "schemaName" -> "question").asJava) + req.put("identifiers", identifiers) + val result = new util.HashMap[String, AnyRef]() + val externalProps = DefinitionNode.getExternalProps(req.getContext.getOrDefault("graph_id", "domain").asInstanceOf[String], req.getContext.getOrDefault("version", "1.0").asInstanceOf[String], req.getContext.getOrDefault("schemaName", "question").asInstanceOf[String]) + val externalPropsResponse = oec.graphService.readExternalProps(req, externalProps) + externalPropsResponse.map(response => { + identifiers.map(id => { + val externalData = Optional.ofNullable(response.get(id).asInstanceOf[util.Map[String, AnyRef]]).orElse(new util.HashMap[String, AnyRef]()) + result.put(id, externalData) + }) + result + }) + } + + def populateHierarchyRequest(children: util.List[util.Map[String, AnyRef]], nodesModified: util.HashMap[String, AnyRef], hierarchy: util.HashMap[String, AnyRef], parentId: String, copyType: String, request: Request, idMap: java.util.Map[String, String])(implicit ec: ExecutionContext, oec: OntologyEngineContext): Unit = { + if (null != children && !children.isEmpty) { + children.asScala.toList.foreach(child => { + val id = if ("Parent".equalsIgnoreCase(child.get(AssessmentConstants.VISIBILITY).asInstanceOf[String])) { + val identifier = UUID.randomUUID().toString + nodesModified.put(identifier, new util.HashMap[String, AnyRef]() { + { + put(AssessmentConstants.METADATA, cleanUpCopiedData(new util.HashMap[String, AnyRef]() { + { + putAll(child) + put(AssessmentConstants.CHILDREN, new util.ArrayList()) + internalHierarchyProps.map(key => remove(key)) + } + }, copyType)) + put(AssessmentConstants.ROOT, false.asInstanceOf[AnyRef]) + put(AssessmentConstants.OBJECT_TYPE, child.getOrDefault(AssessmentConstants.OBJECT_TYPE, "")) + put("isNew", true.asInstanceOf[AnyRef]) + put("setDefaultValue", false.asInstanceOf[AnyRef]) + } + }) + if (StringUtils.equalsIgnoreCase(AssessmentConstants.QUESTION_MIME_TYPE, child.getOrDefault("mimeType", "").asInstanceOf[String])) + idMap.put(child.getOrDefault("identifier", "").asInstanceOf[String], identifier) + identifier + } else + child.get(AssessmentConstants.IDENTIFIER).asInstanceOf[String] + if (StringUtils.equalsIgnoreCase(child.getOrDefault(AssessmentConstants.MIME_TYPE, "").asInstanceOf[String], AssessmentConstants.QUESTIONSET_MIME_TYPE)) + hierarchy.put(id, new util.HashMap[String, AnyRef]() { + { + put(AssessmentConstants.CHILDREN, new util.ArrayList[String]()) + put(AssessmentConstants.ROOT, false.asInstanceOf[AnyRef]) + put(AssessmentConstants.PRIMARY_CATEGORY, child.get(AssessmentConstants.PRIMARY_CATEGORY)) + } + }) + hierarchy.get(parentId).asInstanceOf[util.Map[String, AnyRef]].get(AssessmentConstants.CHILDREN).asInstanceOf[util.List[String]].add(id) + populateHierarchyRequest(child.get(AssessmentConstants.CHILDREN).asInstanceOf[util.List[util.Map[String, AnyRef]]], nodesModified, hierarchy, id, copyType, request, idMap) + }) + } + } + + def updateShallowHierarchy(request: Request, node: Node, originNode: Node, originHierarchy: util.Map[String, AnyRef])(implicit ec: ExecutionContext, oec: OntologyEngineContext): Future[Node] = { + val childrenHierarchy = originHierarchy.get("children").asInstanceOf[util.List[util.Map[String, AnyRef]]] + val req = new Request(request) + req.getContext.put(AssessmentConstants.SCHEMA_NAME, AssessmentConstants.QUESTIONSET_SCHEMA_NAME) + req.getContext.put(AssessmentConstants.VERSION, AssessmentConstants.SCHEMA_VERSION) + req.getContext.put(AssessmentConstants.IDENTIFIER, node.getIdentifier) + req.put(AssessmentConstants.HIERARCHY, ScalaJsonUtils.serialize(new java.util.HashMap[String, AnyRef]() { + { + put(AssessmentConstants.IDENTIFIER, node.getIdentifier) + put(AssessmentConstants.CHILDREN, childrenHierarchy) + } + })) + DataNode.update(req).map(node => node) + } + + def getCopyRequest(node: Node, request: Request)(implicit ec: ExecutionContext, oec: OntologyEngineContext): Future[Request] = { + val metadata: util.Map[String, AnyRef] = NodeUtil.serialize(node, new util.ArrayList(), node.getObjectType.toLowerCase.replace("image", ""), AssessmentConstants.SCHEMA_VERSION) + val requestMap = request.getRequest + requestMap.remove(AssessmentConstants.MODE) + requestMap.remove(AssessmentConstants.COPY_SCHEME).asInstanceOf[String] + val copyType = requestMap.remove(AssessmentConstants.COPY_TYPE).asInstanceOf[String] + val originData: java.util.Map[String, AnyRef] = getOriginData(metadata, copyType) + cleanUpCopiedData(metadata, copyType) + metadata.putAll(requestMap) + metadata.put(AssessmentConstants.STATUS, "Draft") + metadata.put(AssessmentConstants.ORIGIN, node.getIdentifier) + metadata.put(AssessmentConstants.IDENTIFIER, Identifier.getIdentifier(request.getContext.get("graph_id").asInstanceOf[String], Identifier.getUniqueIdFromTimestamp)) + if (MapUtils.isNotEmpty(originData)) + metadata.put(AssessmentConstants.ORIGIN_DATA, originData) + request.getContext().put(AssessmentConstants.SCHEMA_NAME, node.getObjectType.toLowerCase.replace("image", "")) + val req = new Request(request) + req.setRequest(metadata) + + + val graphId = request.getContext.getOrDefault("graph_id", "").asInstanceOf[String] + val version = request.getContext.getOrDefault("version", "").asInstanceOf[String] + val externalProps = if (StringUtils.equalsIgnoreCase(AssessmentConstants.QUESTIONSET_MIME_TYPE, node.getMetadata.getOrDefault("mimeType", "").asInstanceOf[String])) { + DefinitionNode.getExternalProps(graphId, version, AssessmentConstants.QUESTIONSET_SCHEMA_NAME).diff(List("hierarchy")) + } else { + DefinitionNode.getExternalProps(graphId, version, AssessmentConstants.QUESTION_SCHEMA_NAME) + } + val readReq = new Request() + readReq.setContext(request.getContext) + readReq.put("identifier", node.getIdentifier) + readReq.put("fields", externalProps.asJava) + val maxWaitTime: FiniteDuration = Duration(5, TimeUnit.SECONDS) + Await.result( + DataNode.read(readReq).map(node => { + val metadata: util.Map[String, AnyRef] = NodeUtil.serialize(node, externalProps.asJava, node.getObjectType.toLowerCase.replace("image", ""), request.getContext.get("version").asInstanceOf[String]) + externalProps.foreach(prop => { + val propValue = metadata.get(prop) + if (metadata.containsKey(prop) && propValue != null) { + req.put(prop, propValue) + } + }) + }), maxWaitTime) + Future(req) + } + + def getOriginData(metadata: util.Map[String, AnyRef], copyType: String): java.util.Map[String, AnyRef] = { + new java.util.HashMap[String, AnyRef]() { + { + putAll(originMetadataKeys.asScala.filter(key => metadata.containsKey(key)).map(key => key -> metadata.get(key)).toMap.asJava) + put(AssessmentConstants.COPY_TYPE, copyType) + } + } + } + + def validateRequest(request: Request)(implicit ec: ExecutionContext, oec: OntologyEngineContext): Unit = { + val keysNotPresent = AssessmentConstants.REQUIRED_KEYS.filter(key => emptyCheckFilter(request.getRequest.getOrDefault(key, ""))) + if (keysNotPresent.nonEmpty) + throw new ClientException(AssessmentConstants.ERR_INVALID_REQUEST, "Please provide valid value for " + keysNotPresent) + } + + def validateShallowCopyReq(node: Node, request: Request) = { + val copyType: String = request.getRequest.get("copyType").asInstanceOf[String] + if (StringUtils.equalsIgnoreCase("shallow", copyType) && !StringUtils.equalsIgnoreCase("Live", node.getMetadata.get("status").asInstanceOf[String])) + throw new ClientException(AssessmentConstants.ERR_INVALID_REQUEST, "QuestionSet with status " + node.getMetadata.get(AssessmentConstants.STATUS).asInstanceOf[String].toLowerCase + " cannot be partially (shallow) copied.") + //TODO: check if need to throw client exception for combination of copyType=shallow and mode=edit + } + + def emptyCheckFilter(key: AnyRef): Boolean = key match { + case k: String => k.asInstanceOf[String].isEmpty + case k: util.Map[String, AnyRef] => MapUtils.isEmpty(k.asInstanceOf[util.Map[String, AnyRef]]) + case k: util.List[String] => CollectionUtils.isEmpty(k.asInstanceOf[util.List[String]]) + case _ => true + } + + def cleanUpCopiedData(metadata: util.Map[String, AnyRef], copyType: String): util.Map[String, AnyRef] = { + if (StringUtils.equalsIgnoreCase(AssessmentConstants.COPY_TYPE_SHALLOW, copyType)) { + metadata.keySet().removeAll(metadataNotTobeCopied.asScala.toList.filter(str => !str.contains("dial")).asJava) + } else metadata.keySet().removeAll(metadataNotTobeCopied) + metadata + } +} diff --git a/assessment-api/assessment-actors/src/main/scala/org/sunbird/utils/AssessmentContants.scala b/assessment-api/assessment-actors/src/main/scala/org/sunbird/utils/AssessmentContants.scala new file mode 100644 index 000000000..c422c3256 --- /dev/null +++ b/assessment-api/assessment-actors/src/main/scala/org/sunbird/utils/AssessmentContants.scala @@ -0,0 +1,39 @@ +package org.sunbird.utils + +object AssessmentConstants { + val REQUIRED_KEYS: List[String] = List("createdBy", "createdFor") + val ERR_INVALID_REQUEST: String = "ERR_INVALID_REQUEST" + val ERR_INVALID_OBJECT_TYPE: String = "ERR_INVALID_OBJECT_TYPE" + val COPY_SCHEME: String = "copyScheme" + val MIME_TYPE: String = "mimeType" + val QUESTIONSET_MIME_TYPE: String = "application/vnd.sunbird.questionset" + val STATUS: String = "status" + val COPY_TYPE: String = "copyType" + val SCHEMA_NAME: String = "schemaName" + val VERSION: String = "version" + val ROOT_ID: String = "rootId" + val MODE: String = "mode" + val COPY_TYPE_SHALLOW: String = "shallow" + val QUESTIONSET_SCHEMA_NAME: String = "questionset" + val SCHEMA_VERSION: String = "1.0" + val IDENTIFIER: String = "identifier" + val QUESTION: String = "question" + val HIERARCHY: String = "hierarchy" + val CHILDREN: String = "children" + val ORIGIN: String = "origin" + val ORIGIN_DATA: String = "originData" + val CONTENT_TYPE: String = "contentType" + val ROOT: String = "root" + val NODES_MODIFIED: String = "nodesModified" + val VISIBILITY: String = "visibility" + val METADATA: String = "metadata" + val VERSION_KEY: String = "versionKey" + val PRIMARY_CATEGORY : String = "primaryCategory" + val QUESTIONSET : String = "questionSet" + val OBJECT_TYPE : String = "objectType" + val COPY_TYPE_DEEP: String = "deep" + val QUESTION_MIME_TYPE: String = "application/vnd.sunbird.question" + val QUESTION_SCHEMA_NAME: String = "question" + val VISIBILITY_PARENT: String = "Parent" + val VISIBILITY_DEFAULT: String = "Default" +} diff --git a/assessment-api/assessment-actors/src/test/resources/application.conf b/assessment-api/assessment-actors/src/test/resources/application.conf index 24d3a74f2..b9ad82663 100644 --- a/assessment-api/assessment-actors/src/test/resources/application.conf +++ b/assessment-api/assessment-actors/src/test/resources/application.conf @@ -412,5 +412,15 @@ import { } } +assessment.copy.origin_data=["name", "author", "license", "organisation"] +assessment.copy.props_to_remove=["downloadUrl", "artifactUrl", "variants", + "createdOn", "collections", "children", "lastUpdatedOn", "SYS_INTERNAL_LAST_UPDATED_ON", + "versionKey", "s3Key", "status", "pkgVersion", "toc_url", "mimeTypesCount", + "contentTypesCount", "leafNodesCount", "childNodes", "prevState", "lastPublishedOn", + "flagReasons", "compatibilityLevel", "size", "publishChecklist", "publishComment", + "LastPublishedBy", "rejectReasons", "rejectComment", "gradeLevel", "subject", + "medium", "board", "topic", "purpose", "subtopic", "contentCredits", + "owner", "collaborators", "creators", "contributors", "badgeAssertions", "dialcodes", + "concepts", "keywords", "reservedDialcodes", "dialcodeRequired", "leafNodes", "sYS_INTERNAL_LAST_UPDATED_ON", "prevStatus", "lastPublishedBy", "streamingUrl"] diff --git a/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/QuestionActorTest.scala b/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/QuestionActorTest.scala index 3adc82110..11cc17f53 100644 --- a/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/QuestionActorTest.scala +++ b/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/QuestionActorTest.scala @@ -16,7 +16,7 @@ import scala.collection.JavaConverters._ import scala.concurrent.Future import scala.concurrent.ExecutionContext.Implicits.global -class QuestionActorTest extends BaseSpec with MockFactory { +class QuestionActorTest extends BaseSpec with MockFactory with copyTrait { "questionActor" should "return failed response for 'unknown' operation" in { implicit val oec: OntologyEngineContext = new OntologyEngineContext @@ -314,6 +314,45 @@ class QuestionActorTest extends BaseSpec with MockFactory { assert("successful".equals(response.getParams.getStatus)) } + it should "return success response for 'copyQuestion'" in { + implicit val oec: OntologyEngineContext = mock[OntologyEngineContext] + val graphDB = mock[GraphService] + (oec.graphService _).expects().returns(graphDB).anyNumberOfTimes() + val nodes: util.List[Node] = getCategoryNode() + (graphDB.getNodeByUniqueIds(_: String, _: SearchCriteria)).expects(*, *).returns(Future(nodes)).anyNumberOfTimes() + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects("domain", "do_1234", false, *).returns(Future(getExistingQuestionNode())).anyNumberOfTimes() + (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, List("objectMetadata")).returns(Future(getSuccessfulResponse())).anyNumberOfTimes() + (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, *).returns(Future(getReadPropsResponseForQuestion())).anyNumberOfTimes() + (graphDB.addNode(_: String, _: Node)).expects(*, *).returns(Future(getNewQuestionNode())) + (graphDB.saveExternalProps(_: Request)).expects(*).returns(Future(getSuccessfulResponse())).anyNumberOfTimes + val request = getQuestionCopyRequest() + request.putAll(mapAsJavaMap(Map("identifier" -> "do_1234", "mode" -> "", "copyType"-> "deep"))) + request.setOperation("copyQuestion") + val response = callActor(request, Props(new QuestionActor())) + assert("successful".equals(response.getParams.getStatus)) + } + + it should "return error response for 'copyQuestion' when createdFor & createdBy is missing" in { + implicit val oec: OntologyEngineContext = mock[OntologyEngineContext] + val request = getInvalidQuestionSetCopyRequest() + request.putAll(mapAsJavaMap(Map("identifier" -> "do_1234", "mode" -> "", "copyType"-> "deep"))) + request.setOperation("copyQuestion") + val response = callActor(request, Props(new QuestionActor())) + assert("failed".equals(response.getParams.getStatus)) + } + + it should "return error response for 'copyQuestion' when visibility is Parent" in { + implicit val oec: OntologyEngineContext = mock[OntologyEngineContext] + val graphDB = mock[GraphService] + (oec.graphService _).expects().returns(graphDB).anyNumberOfTimes() + val request = getQuestionCopyRequest() + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects("domain", "do_1234", false, *).returns(Future(getQuestionNode())).anyNumberOfTimes() + request.putAll(mapAsJavaMap(Map("identifier" -> "do_1234", "mode" -> "", "copyType"-> "deep"))) + request.setOperation("copyQuestion") + val response = callActor(request, Props(new QuestionActor())) + assert("failed".equals(response.getParams.getStatus)) + } + private def getQuestionRequest(): Request = { val request = new Request() request.setContext(new java.util.HashMap[String, AnyRef]() { diff --git a/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/QuestionSetActorTest.scala b/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/QuestionSetActorTest.scala index 74f7d2dad..25cf6c6d1 100644 --- a/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/QuestionSetActorTest.scala +++ b/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/QuestionSetActorTest.scala @@ -19,7 +19,7 @@ import scala.collection.JavaConverters._ import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future -class QuestionSetActorTest extends BaseSpec with MockFactory { +class QuestionSetActorTest extends BaseSpec with MockFactory with copyTrait { "questionSetActor" should "return failed response for 'unknown' operation" in { implicit val oec: OntologyEngineContext = new OntologyEngineContext @@ -538,6 +538,62 @@ class QuestionSetActorTest extends BaseSpec with MockFactory { assert("successful".equals(response.getParams.getStatus)) } + it should "return success response for 'copyQuestionSet' (Deep)" in { + implicit val oec: OntologyEngineContext = mock[OntologyEngineContext] + val graphDB = mock[GraphService] + (oec.graphService _).expects().returns(graphDB).anyNumberOfTimes() + val nodes: util.List[Node] = getCategoryNode() + (graphDB.getNodeByUniqueIds(_: String, _: SearchCriteria)).expects(*, *).returns(Future(nodes)).anyNumberOfTimes() + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects("domain", "do_1234", false, *).returns(Future(getExistingRootNode())).anyNumberOfTimes() + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects("domain", "do_9876", false, *).returns(Future(getNewRootNode())).anyNumberOfTimes() + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects("domain", "do_9876.img", false, *).returns(Future(getNewRootNode())).anyNumberOfTimes() + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects("domain", *, false, *).returns(Future(getQuestionNode())).anyNumberOfTimes() + (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, List("objectMetadata")).returns(Future(getSuccessfulResponse())).anyNumberOfTimes() + (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, *).returns(Future(getExternalPropsResponseWithData())).anyNumberOfTimes() + (graphDB.updateExternalProps(_: Request)).expects(*).returns(Future(getSuccessfulResponse())).anyNumberOfTimes + (graphDB.saveExternalProps(_: Request)).expects(*).returns(Future(getSuccessfulResponse())).anyNumberOfTimes + (graphDB.upsertNode(_: String, _: Node, _: Request)).expects(*, *, *).returns(Future(getUpsertNode())).anyNumberOfTimes() + inSequence { + (graphDB.addNode(_: String, _: Node)).expects(*, *).returns(Future(getNewRootNode())) + (graphDB.addNode(_: String, _: Node)).expects(*, *).returns(Future(getQuestionNode())) + } + val request = getQuestionSetCopyRequest() + request.putAll(mapAsJavaMap(Map("identifier" -> "do_1234", "mode" -> "", "copyType"-> "deep"))) + request.setOperation("copyQuestionSet") + val response = callActor(request, Props(new QuestionSetActor())) + assert("successful".equals(response.getParams.getStatus)) + } + + it should "return success response for 'copyQuestionSet' (Shallow)" in { + implicit val oec: OntologyEngineContext = mock[OntologyEngineContext] + val graphDB = mock[GraphService] + (oec.graphService _).expects().returns(graphDB).anyNumberOfTimes() + val nodes: util.List[Node] = getCategoryNode() + (graphDB.getNodeByUniqueIds(_: String, _: SearchCriteria)).expects(*, *).returns(Future(nodes)).anyNumberOfTimes() + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects("domain", "do_1234", false, *).returns(Future(getExistingRootNode())).anyNumberOfTimes() + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects("domain", "do_5678", false, *).returns(Future(getNewRootNode())).anyNumberOfTimes() + (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, List("objectMetadata")).returns(Future(getSuccessfulResponse())).anyNumberOfTimes() + (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, *).returns(Future(getExternalPropsResponseWithData())).anyNumberOfTimes() + (graphDB.addNode(_: String, _: Node)).expects(*, *).returns(Future(getQuestionNode())) + (graphDB.saveExternalProps(_: Request)).expects(*).returns(Future(getSuccessfulResponse())).anyNumberOfTimes + (graphDB.upsertNode(_: String, _: Node, _: Request)).expects(*, *, *).returns(Future(getUpsertNode())).anyNumberOfTimes() + (graphDB.updateExternalProps(_: Request)).expects(*).returns(Future(getSuccessfulResponse())).anyNumberOfTimes + val request = getQuestionSetCopyRequest() + request.putAll(mapAsJavaMap(Map("identifier" -> "do_1234", "mode" -> "", "copyType"-> "shallow"))) + request.setOperation("copyQuestionSet") + val response = callActor(request, Props(new QuestionSetActor())) + assert("successful".equals(response.getParams.getStatus)) + } + + it should "return error response for 'copyQuestionSet' when createdFor & createdBy is missing" in { + implicit val oec: OntologyEngineContext = mock[OntologyEngineContext] + val request = getInvalidQuestionCopyRequest() + request.putAll(mapAsJavaMap(Map("identifier" -> "do_1234", "mode" -> "", "copyType"-> "deep"))) + request.setOperation("copyQuestionSet") + val response = callActor(request, Props(new QuestionSetActor())) + assert("failed".equals(response.getParams.getStatus)) + } + private def getQuestionSetRequest(): Request = { val request = new Request() request.setContext(new java.util.HashMap[String, AnyRef]() { diff --git a/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/copyTrait.scala b/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/copyTrait.scala new file mode 100644 index 000000000..5f5478c4d --- /dev/null +++ b/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/copyTrait.scala @@ -0,0 +1,264 @@ +package org.sunbird.actors + +import org.mortbay.util.StringUtil +import org.sunbird.common.dto.{Request, Response, ResponseParams} +import org.sunbird.graph.dac.model.Node +import org.sunbird.utils.AssessmentConstants +import org.sunbird.common.exception.{ResponseCode} + +import java.util + +trait copyTrait { + + private def getQuestionSetRequest(): Request = { + val request = new Request() + request.setContext(new java.util.HashMap[String, AnyRef]() { + { + put("graph_id", "domain") + put("version", "1.0") + put("objectType", "QuestionSet") + put("schemaName", "questionset") + } + }) + request.setObjectType("QuestionSet") + request + } + + def getQuestionSetCopyRequest(): Request = { + val request = getQuestionSetRequest() + request.putAll(new util.HashMap[String, AnyRef]() { + { + put("createdBy", "Shikshalokam") + put("createdFor", new util.ArrayList[String]() { + { + add("Shikshalokam") + } + }) + put("name", "NewRootNode") + } + }) + request + } + + def getInvalidQuestionSetCopyRequest(): Request = { + val request = getQuestionSetRequest() + request.putAll(new util.HashMap[String, AnyRef]() { + { + put("name", "NewRootNode") + } + }) + request + } + + def getInvalidQuestionCopyRequest(): Request = { + val request = getQuestionRequest() + request.putAll(new util.HashMap[String, AnyRef]() { + { + put("name", "NewQuestion") + } + }) + request + } + + private def getQuestionRequest(): Request = { + val request = new Request() + request.setContext(new java.util.HashMap[String, AnyRef]() { + { + put("graph_id", "domain") + put("version", "1.0") + put("objectType", "Question") + put("schemaName", "question") + } + }) + request.setObjectType("Question") + request + } + + def getQuestionCopyRequest(): Request = { + val request = getQuestionRequest() + request.putAll(new util.HashMap[String, AnyRef]() { + { + put("createdBy", "Shikshalokam") + put("createdFor", new util.ArrayList[String]() { + { + add("Shikshalokam") + } + }) + put("name", "NewQuestion") + } + }) + request + } + + private def getNode(objectType: String, identifier: String, primaryCategory: String, visibility: String, name: String, id: Long, + status: String): Node = { + val node = new Node("domain", "DATA_NODE", objectType) + node.setGraphId("domain") + node.setIdentifier(identifier) + node.setId(id) + node.setNodeType("DATA_NODE") + node.setObjectType(objectType) + node.setMetadata(new util.HashMap[String, AnyRef]() { + { + put("code", "xyz") + put("mimeType", { + if (StringUtil.endsWithIgnoreCase(objectType, AssessmentConstants.QUESTIONSET_SCHEMA_NAME)) { + AssessmentConstants.QUESTIONSET_MIME_TYPE + } else { + AssessmentConstants.QUESTION_MIME_TYPE + } + }) + put("createdOn", "2022-03-16T14:35:11.040+0530") + put("objectType", objectType) + put("primaryCategory", primaryCategory) + put("contentDisposition", "inline") + put("contentEncoding", "gzip") + put("lastUpdatedOn", "2022-03-16T14:38:51.287+0530") + put("showSolutions", "No") + put("allowAnonymousAccess", "Yes") + put("identifier", identifier) + put("lastStatusChangedOn", "2022-03-16T14:35:11.040+0530") + put("visibility", visibility) + put("showTimer", "No") + put("version", 1.asInstanceOf[Number]) + put("showFeedback", "No") + put("versionKey", "1234") + put("license", "CC BY 4.0") + put("compatibilityLevel", 5.asInstanceOf[Number]) + put("name", name) + put("status", status) + } + }) + node + } + + def getExistingRootNode(): Node = { + val node = getNode("QuestionSet", "do_1234", "Observation", AssessmentConstants.VISIBILITY_DEFAULT, "ExistingRootNode", 1234, + "Live") + node.getMetadata.put("childNodes", Array("do_5678")) + node + } + + def getNewRootNode(): Node = { + val node = getNode("QuestionSet", "do_9876", "Observation", AssessmentConstants.VISIBILITY_DEFAULT, "NewRootNode", 0, "Draft") + node.getMetadata.put("origin", "do_1234") + node.getMetadata.put("originData", "{\"name\":\"ExistingRootNode\",\"copyType\":\"deep\"}") + node.getMetadata.put("createdFor", Array("ShikshaLokam")) + node.getMetadata.put("createdBy", "ShikshaLokam") + node.setExternalData(new util.HashMap[String, AnyRef]() { + { + put("instructions", "This is the instruction.") + put("outcomeDeclaration", "This is outcomeDeclaration.") + } + }) + node + } + + def getExistingQuestionNode(): Node = { + val node = getNode("Question", "do_1234", "Slider", AssessmentConstants.VISIBILITY_DEFAULT, "ExistingQuestionNode", 1234, + "Live") + node + } + + def getQuestionNode(): Node = { + val node = getNode("Question", "do_5678", "Slider", AssessmentConstants.VISIBILITY_PARENT, "Question1", 0, "Draft") + node.setExternalData(new util.HashMap[String, AnyRef]() { + { + put("answer", "This is Answer.") + put("body", "This is Body.") + } + }) + node + } + + def getNewQuestionNode(): Node = { + val node = getNode("Question", "do_5678", "Slider", AssessmentConstants.VISIBILITY_DEFAULT, "NewQuestion", 0, "Draft") + node.setExternalData(new util.HashMap[String, AnyRef]() { + { + put("answer", "This is Answer.") + put("body", "This is Body.") + } + }) + node.getMetadata.put("origin", "do_1234") + node.getMetadata.put("originData", "{\\\"name\\\":\\\"Q2\\\",\\\"copyType\\\":\\\"deep\\\",\\\"license\\\":\\\"CC BY 4.0\\\"}") + node.getMetadata.put("createdFor", Array("ShikshaLokam")) + node.getMetadata.put("createdBy", "ShikshaLokam") + node + } + + def getSuccessfulResponse(): Response = { + val response = new Response + response.setVer("3.0") + val responseParams = new ResponseParams + responseParams.setStatus("successful") + response.setParams(responseParams) + response.setResponseCode(ResponseCode.OK) + response + } + + def getExternalPropsRequest(): Request = { + val request = getQuestionSetRequest() + request.putAll(new util.HashMap[String, AnyRef]() { + { + put("instructions", "This is the instruction.") + put("outcomeDeclaration", "This is outcomeDeclaration.") + } + }) + request + } + + def getExternalPropsResponseWithData(): Response = { + val response = getSuccessfulResponse() + response.put("instructions", "This is the instruction for this QuestionSet") + response.put("outcomeDeclaration", "This is the outcomeDeclaration for this QuestionSet") + response.put("hierarchy", "{\"code\":\"ExistingRootNode\",\"allowSkip\":\"Yes\",\"containsUserData\":\"No\"," + + "\"channel\":\"{{all}}\",\"language\":[\"English\"],\"showHints\":\"No\",\"mimeType\":\"application/vnd" + "" + ".sunbird" + "" + + ".questionset\",\"createdOn\":\"2022-03-16T14:35:11.040+0530\",\"objectType\":\"QuestionSet\"," + + "\"primaryCategory\":\"Observation\",\"contentDisposition\":\"inline\",\"contentEncoding\":\"gzip\"," + + "\"lastUpdatedOn\":\"2022-03-16T14:38:51.287+0530\",\"generateDIALCodes\":\"No\",\"showSolutions\":\"No\"," + + "\"allowAnonymousAccess\":\"Yes\",\"identifier\":\"do_1234\"," + "\"lastStatusChangedOn\":\"2022-03-16T14:35:11.040+0530\"," + + "\"requiresSubmit\":\"No\",\"visibility\":\"Default\"," + "" + "" + "\"IL_SYS_NODE_TYPE\":\"DATA_NODE\",\"showTimer\":\"No\"," + + "\"childNodes\":[\"do_113495678820704256110\"]," + "\"setType\":\"materialised\",\"version\":1," + "\"showFeedback\":\"No\"," + + "\"versionKey\":\"1647421731287\"," + "\"license\":\"CC BY 4.0\",\"depth\":0," + "\"compatibilityLevel\":5," + + "\"IL_FUNC_OBJECT_TYPE\":\"QuestionSet\"," + "\"allowBranching\":\"No\"," + "\"navigationMode\":\"non-linear\"," + + "\"name\":\"CopyQuestionSet\",\"shuffle\":true," + "\"IL_UNIQUE_ID\":\"do_11349567701798912019\",\"status\":\"Live\"," + + "\"children\":[{\"parent\":\"do_11349567701798912019\",\"code\":\"Q1\",\"channel\":\"{{channel_id}}\"," + + "\"language\":[\"English\"],\"mimeType\":\"application/vnd.sunbird.question\"," + + "\"createdOn\":\"2022-03-16T14:38:51.043+0530\",\"objectType\":\"Question\",\"primaryCategory\":\"Slider\"," + + "\"contentDisposition\":\"inline\",\"lastUpdatedOn\":\"2022-03-16T14:38:51.042+0530\"," + "\"contentEncoding\":\"gzip\"," + + "\"showSolutions\":\"No\",\"allowAnonymousAccess\":\"Yes\"," + "\"identifier\":\"do_113495678820704256110\"," + + "\"lastStatusChangedOn\":\"2022-03-16T14:38:51.043+0530\"," + "\"visibility\":\"Parent\",\"showTimer\":\"No\",\"index\":1," + + "\"languageCode\":[\"en\"],\"version\":1," + "\"versionKey\":\"1647421731066\",\"showFeedback\":\"No\",\"license\":\"CC BY " + + "4.0\",\"depth\":1," + "\"compatibilityLevel\":4,\"name\":\"Q1\",\"status\":\"Live\"}]}") + response.put("body", "This is Body") + response.put("answer", "This is Answer") + response + } + + def getReadPropsResponseForQuestion(): Response = { + val response = getSuccessfulResponse() + response.put("answer", "This is Answer 2") + response.put("body", "This is Body 2") + response + } + + def getUpsertNode(): Node = { + val node = getNewRootNode() + node.setExternalData(new util.HashMap[String, AnyRef]() { + { + put("hierarchy", "{\\\"identifier\\\":\\\"do_9876\\\"," + "\\\"children\\\":[{\\\"parent\\\":\\\"do_9876\\\"," + + "\\\"code\\\":\\\"b65f36d1-a243-4043-9df7-da14a2dd83b9\\\",\\\"channel\\\":\\\"{{channel_id}}\\\"," + + "\\\"language\\\":[\\\"English\\\"],\\\"mimeType\\\":\\\"application/vnd.sunbird.question\\\"," + + "\\\"createdOn\\\":\\\"2022-03-23T15:45:28.620+0530\\\",\\\"objectType\\\":\\\"Question\\\"," + + "\\\"primaryCategory\\\":\\\"Slider\\\",\\\"contentDisposition\\\":\\\"inline\\\"," + + "\\\"lastUpdatedOn\\\":\\\"2022-03-23T15:45:28.616+0530\\\",\\\"contentEncoding\\\":\\\"gzip\\\"," + + "\\\"showSolutions\\\":\\\"No\\\",\\\"allowAnonymousAccess\\\":\\\"Yes\\\"," + + "\\\"identifier\\\":\\\"do_11350066609045504013\\\",\\\"lastStatusChangedOn\\\":\\\"2022-03-23T15:45:28.621+0530\\\"," + + "\\\"visibility\\\":\\\"Parent\\\",\\\"showTimer\\\":\\\"No\\\",\\\"index\\\":1,\\\"languageCode\\\":[\\\"en\\\"]," + + "\\\"version\\\":1,\\\"versionKey\\\":\\\"1648030746815\\\",\\\"showFeedback\\\":\\\"No\\\",\\\"license\\\":\\\"CC BY " + + "4.0\\\",\\\"depth\\\":1,\\\"compatibilityLevel\\\":4,\\\"name\\\":\\\"Q1\\\",\\\"status\\\":\\\"Draft\\\"}]}") + } + }) + node + } +} diff --git a/assessment-api/assessment-service/app/controllers/v4/QuestionController.scala b/assessment-api/assessment-service/app/controllers/v4/QuestionController.scala index 0af9f3676..d929bb272 100644 --- a/assessment-api/assessment-service/app/controllers/v4/QuestionController.scala +++ b/assessment-api/assessment-service/app/controllers/v4/QuestionController.scala @@ -2,6 +2,8 @@ package controllers.v4 import akka.actor.{ActorRef, ActorSystem} import controllers.BaseController +import org.sunbird.utils.AssessmentConstants + import javax.inject.{Inject, Named} import play.api.mvc.ControllerComponents import utils.{ActorNames, ApiId, QuestionOperations} @@ -130,4 +132,15 @@ class QuestionController @Inject()(@Named(ActorNames.QUESTION_ACTOR) questionAct questionRequest.getContext.put("identifier", identifier) getResult(ApiId.REJECT_QUESTION, questionActor, questionRequest) } + + def copy(identifier: String, mode: Option[String]) = Action.async { implicit request => + val headers = commonHeaders() + val body = requestBody() + val question = body.getOrDefault("question", new java.util.HashMap()).asInstanceOf[java.util.Map[String, Object]]; + question.putAll(headers) + question.putAll(Map("identifier" -> identifier, "mode" -> mode.getOrElse(""), "copyType" -> AssessmentConstants.COPY_TYPE_DEEP).asJava) + val questionRequest = getRequest(question, headers, QuestionOperations.copyQuestion.toString) + setRequestContext(questionRequest, version, objectType, schemaName) + getResult(ApiId.COPY_QUESTION, questionActor, questionRequest) + } } diff --git a/assessment-api/assessment-service/app/controllers/v4/QuestionSetController.scala b/assessment-api/assessment-service/app/controllers/v4/QuestionSetController.scala index a82f320ae..f2fa9cdce 100644 --- a/assessment-api/assessment-service/app/controllers/v4/QuestionSetController.scala +++ b/assessment-api/assessment-service/app/controllers/v4/QuestionSetController.scala @@ -158,4 +158,15 @@ class QuestionSetController @Inject()(@Named(ActorNames.QUESTION_SET_ACTOR) ques questionSetRequest.getContext.put("identifier", identifier); getResult(ApiId.SYSTEM_UPDATE_QUESTION_SET, questionSetActor, questionSetRequest) } + + def copy(identifier: String, mode: Option[String], copyType: String) = Action.async { implicit request => + val headers = commonHeaders() + val body = requestBody() + val questionSet = body.getOrDefault("questionset", new java.util.HashMap()).asInstanceOf[java.util.Map[String, Object]]; + questionSet.putAll(headers) + questionSet.putAll(Map("identifier" -> identifier, "mode" -> mode.getOrElse(""), "copyType" -> copyType).asJava) + val questionSetRequest = getRequest(questionSet, headers, QuestionSetOperations.copyQuestionSet.toString) + setRequestContext(questionSetRequest, version, objectType, schemaName) + getResult(ApiId.COPY_QUESTION_SET, questionSetActor, questionSetRequest) + } } diff --git a/assessment-api/assessment-service/app/utils/ApiId.scala b/assessment-api/assessment-service/app/utils/ApiId.scala index d45e57597..062340338 100644 --- a/assessment-api/assessment-service/app/utils/ApiId.scala +++ b/assessment-api/assessment-service/app/utils/ApiId.scala @@ -24,6 +24,7 @@ object ApiId { val SYSTEM_UPDATE_QUESTION = "api.question.system.update" val LIST_QUESTIONS = "api.questions.list" val REJECT_QUESTION = "api.question.reject" + val COPY_QUESTION = "api.question.copy" //QuestionSet APIs val CREATE_QUESTION_SET = "api.questionset.create" @@ -40,5 +41,5 @@ object ApiId { val REJECT_QUESTION_SET = "api.questionset.reject" val IMPORT_QUESTION_SET = "api.questionset.import" val SYSTEM_UPDATE_QUESTION_SET = "api.questionset.system.update" - + val COPY_QUESTION_SET = "api.questionset.copy" } diff --git a/assessment-api/assessment-service/app/utils/QuestionOperations.scala b/assessment-api/assessment-service/app/utils/QuestionOperations.scala index 57e9e3815..8d71d88e1 100644 --- a/assessment-api/assessment-service/app/utils/QuestionOperations.scala +++ b/assessment-api/assessment-service/app/utils/QuestionOperations.scala @@ -1,5 +1,5 @@ package utils object QuestionOperations extends Enumeration { - val createQuestion, readQuestion, readPrivateQuestion, updateQuestion, reviewQuestion, publishQuestion, retireQuestion, importQuestion, systemUpdateQuestion, listQuestions, rejectQuestion = Value + val createQuestion, readQuestion, readPrivateQuestion, updateQuestion, reviewQuestion, publishQuestion, retireQuestion, importQuestion, systemUpdateQuestion, listQuestions, rejectQuestion, copyQuestion = Value } diff --git a/assessment-api/assessment-service/app/utils/QuestionSetOperations.scala b/assessment-api/assessment-service/app/utils/QuestionSetOperations.scala index afcd22e2b..43f0266b1 100644 --- a/assessment-api/assessment-service/app/utils/QuestionSetOperations.scala +++ b/assessment-api/assessment-service/app/utils/QuestionSetOperations.scala @@ -1,7 +1,7 @@ package utils object QuestionSetOperations extends Enumeration { - val createQuestionSet, readQuestionSet, readPrivateQuestionSet, updateQuestionSet, reviewQuestionSet, publishQuestionSet, - retireQuestionSet, addQuestion, removeQuestion, updateHierarchyQuestion, readHierarchyQuestion, - rejectQuestionSet, importQuestionSet, systemUpdateQuestionSet = Value + val createQuestionSet, readQuestionSet, readPrivateQuestionSet, updateQuestionSet, reviewQuestionSet, publishQuestionSet, + retireQuestionSet, addQuestion, removeQuestion, updateHierarchyQuestion, readHierarchyQuestion, + rejectQuestionSet, importQuestionSet, systemUpdateQuestionSet, copyQuestionSet = Value } diff --git a/assessment-api/assessment-service/conf/application.conf b/assessment-api/assessment-service/conf/application.conf index 82fe873b5..5e82dfa27 100644 --- a/assessment-api/assessment-service/conf/application.conf +++ b/assessment-api/assessment-service/conf/application.conf @@ -420,4 +420,14 @@ import { } } -root_node_visibility=["Default","Private"] \ No newline at end of file +root_node_visibility=["Default","Private"] +assessment.copy.origin_data=["name", "author", "license", "organisation"] +assessment.copy.props_to_remove=["downloadUrl", "artifactUrl", "variants", + "createdOn", "collections", "children", "lastUpdatedOn", "SYS_INTERNAL_LAST_UPDATED_ON", + "versionKey", "s3Key", "status", "pkgVersion", "toc_url", "mimeTypesCount", + "contentTypesCount", "leafNodesCount", "childNodes", "prevState", "lastPublishedOn", + "flagReasons", "compatibilityLevel", "size", "publishChecklist", "publishComment", + "LastPublishedBy", "rejectReasons", "rejectComment", "gradeLevel", "subject", + "medium", "board", "topic", "purpose", "subtopic", "contentCredits", + "owner", "collaborators", "creators", "contributors", "badgeAssertions", "dialcodes", + "concepts", "keywords", "reservedDialcodes", "dialcodeRequired", "leafNodes", "sYS_INTERNAL_LAST_UPDATED_ON", "prevStatus", "lastPublishedBy", "streamingUrl"] \ No newline at end of file diff --git a/assessment-api/assessment-service/conf/routes b/assessment-api/assessment-service/conf/routes index e33c9c1da..b76bb70a9 100644 --- a/assessment-api/assessment-service/conf/routes +++ b/assessment-api/assessment-service/conf/routes @@ -19,10 +19,11 @@ PATCH /question/v4/update/:identifier controllers.v4.QuestionControl POST /question/v4/review/:identifier controllers.v4.QuestionController.review(identifier:String) POST /question/v4/publish/:identifier controllers.v4.QuestionController.publish(identifier:String) DELETE /question/v4/retire/:identifier controllers.v4.QuestionController.retire(identifier:String) -POST /question/v4/import controllers.v4.QuestionController.importQuestion() +POST /question/v4/import controllers.v4.QuestionController.importQuestion() PATCH /question/v4/system/update/:identifier controllers.v4.QuestionController.systemUpdate(identifier:String) POST /question/v4/list controllers.v4.QuestionController.list(fields:Option[String]) POST /question/v4/reject/:identifier controllers.v4.QuestionController.reject(identifier:String) +POST /question/v4/copy/:identifier controllers.v4.QuestionController.copy(identifier:String, mode:Option[String]) # QuestionSet API's POST /questionset/v4/create controllers.v4.QuestionSetController.create @@ -37,5 +38,6 @@ DELETE /questionset/v4/remove controllers.v4.QuestionSetC PATCH /questionset/v4/hierarchy/update controllers.v4.QuestionSetController.updateHierarchy GET /questionset/v4/hierarchy/:identifier controllers.v4.QuestionSetController.getHierarchy(identifier:String, mode:Option[String]) POST /questionset/v4/reject/:identifier controllers.v4.QuestionSetController.reject(identifier:String) -POST /questionset/v4/import controllers.v4.QuestionSetController.importQuestionSet() -PATCH /questionset/v4/system/update/:identifier controllers.v4.QuestionSetController.systemUpdate(identifier:String) \ No newline at end of file +POST /questionset/v4/import controllers.v4.QuestionSetController.importQuestionSet() +PATCH /questionset/v4/system/update/:identifier controllers.v4.QuestionSetController.systemUpdate(identifier:String) +POST /questionset/v4/copy/:identifier controllers.v4.QuestionSetController.copy(identifier:String, mode:Option[String], type:String?="deep") \ No newline at end of file diff --git a/schemas/question/1.0/schema.json b/schemas/question/1.0/schema.json index 670d548e5..6054a39e3 100644 --- a/schemas/question/1.0/schema.json +++ b/schemas/question/1.0/schema.json @@ -574,6 +574,12 @@ "items": { "type": "object" } + }, + "origin": { + "type": "string" + }, + "originData": { + "type": "object" } }, "additionalProperties": false diff --git a/schemas/questionset/1.0/schema.json b/schemas/questionset/1.0/schema.json index 02516dc47..fcc691a25 100644 --- a/schemas/questionset/1.0/schema.json +++ b/schemas/questionset/1.0/schema.json @@ -650,6 +650,12 @@ "outcomeDeclaration": { "type": "object", "description": "External Property" + }, + "origin": { + "type": "string" + }, + "originData": { + "type": "object" } }, "additionalProperties": false From 255791b46e385c5abfa74fcb6292cd16f8fb646d Mon Sep 17 00:00:00 2001 From: Kumar Gauraw Date: Mon, 28 Mar 2022 12:37:20 +0530 Subject: [PATCH 007/490] Issue #SB-28779 fix: fixed review and publish flow for children status --- .../org/sunbird/actors/QuestionSetActor.scala | 1 + .../sunbird/managers/AssessmentManager.scala | 26 ++++++++++--------- .../scala/org/sunbird/actors/BaseSpec.scala | 16 ++++++++++++ .../sunbird/actors/QuestionSetActorTest.scala | 16 ++++++++++-- 4 files changed, 45 insertions(+), 14 deletions(-) diff --git a/assessment-api/assessment-actors/src/main/scala/org/sunbird/actors/QuestionSetActor.scala b/assessment-api/assessment-actors/src/main/scala/org/sunbird/actors/QuestionSetActor.scala index 1a013aae5..f5b5742d9 100644 --- a/assessment-api/assessment-actors/src/main/scala/org/sunbird/actors/QuestionSetActor.scala +++ b/assessment-api/assessment-actors/src/main/scala/org/sunbird/actors/QuestionSetActor.scala @@ -66,6 +66,7 @@ class QuestionSetActor @Inject()(implicit oec: OntologyEngineContext) extends Ba def publish(request: Request): Future[Response] = { request.getRequest.put("identifier", request.getContext.get("identifier")) + request.put("mode", "edit") AssessmentManager.getValidatedNodeForPublish(request, "ERR_QUESTION_SET_PUBLISH").flatMap(node => { AssessmentManager.getQuestionSetHierarchy(request, node).map(hierarchyString => { AssessmentManager.validateQuestionSetHierarchy(hierarchyString.asInstanceOf[String], node.getMetadata.getOrDefault("createdBy", "").asInstanceOf[String]) diff --git a/assessment-api/assessment-actors/src/main/scala/org/sunbird/managers/AssessmentManager.scala b/assessment-api/assessment-actors/src/main/scala/org/sunbird/managers/AssessmentManager.scala index e06035026..f05d14307 100644 --- a/assessment-api/assessment-actors/src/main/scala/org/sunbird/managers/AssessmentManager.scala +++ b/assessment-api/assessment-actors/src/main/scala/org/sunbird/managers/AssessmentManager.scala @@ -21,6 +21,7 @@ import scala.collection.JavaConverters._ object AssessmentManager { val skipValidation: Boolean = Platform.getBoolean("assessment.skip.validation", false) + val validStatus = List("Draft", "Review") def create(request: Request, errCode: String)(implicit oec: OntologyEngineContext, ec: ExecutionContext): Future[Response] = { val visibility: String = request.getRequest.getOrDefault("visibility", "").asInstanceOf[String] @@ -140,15 +141,12 @@ object AssessmentManager { } def getQuestionSetHierarchy(request: Request, rootNode: Node)(implicit ec: ExecutionContext, oec: OntologyEngineContext): Future[Any] = { - oec.graphService.readExternalProps(request, List("hierarchy")).flatMap(response => { - if (ResponseHandler.checkError(response) && ResponseHandler.isResponseNotFoundError(response)) { - if (StringUtils.equalsIgnoreCase("Live", rootNode.getMetadata.get("status").asInstanceOf[String])) - throw new ServerException("ERR_QUESTION_SET_REVIEW", "No hierarchy is present in cassandra for identifier:" + rootNode.getIdentifier) - request.put("identifier", if (!rootNode.getIdentifier.endsWith(".img")) rootNode.getIdentifier + ".img" else rootNode.getIdentifier) - oec.graphService.readExternalProps(request, List("hierarchy")).map(resp => { - resp.getResult.toMap.getOrElse("hierarchy", "{}").asInstanceOf[String] - }) recover { case e: ResourceNotFoundException => TelemetryManager.log("No hierarchy is present in cassandra for identifier:" + request.get("identifier")) } - } else Future(response.getResult.toMap.getOrElse("hierarchy", "{}").asInstanceOf[String]) + request.put("rootId", request.get("identifier").asInstanceOf[String]) + HierarchyManager.getUnPublishedHierarchy(request).map(resp => { + if (!ResponseHandler.checkError(resp) && resp.getResponseCode.code() == 200) { + val hierarchy = resp.getResult.get("questionSet").asInstanceOf[util.Map[String, AnyRef]] + JsonUtils.serialize(hierarchy) + } else throw new ServerException("ERR_QUESTION_SET_HIERARCHY", "No hierarchy is present in cassandra for identifier:" + rootNode.getIdentifier) }) } @@ -171,10 +169,12 @@ object AssessmentManager { } def updateHierarchy(hierarchyString: String, status: String, rootUserId: String): (java.util.Map[String, AnyRef], java.util.List[String]) = { - val hierarchy = if (!hierarchyString.asInstanceOf[String].isEmpty) { + val hierarchy: java.util.Map[String, AnyRef] = if (!hierarchyString.asInstanceOf[String].isEmpty) { JsonUtils.deserialize(hierarchyString.asInstanceOf[String], classOf[java.util.Map[String, AnyRef]]) } else new java.util.HashMap[String, AnyRef]() + val keys = List("identifier", "children").asJava + hierarchy.keySet().retainAll(keys) val children = hierarchy.getOrDefault("children", new util.ArrayList[java.util.Map[String, AnyRef]]).asInstanceOf[util.List[java.util.Map[String, AnyRef]]] hierarchy.put("status", status) val childrenToUpdate: List[String] = updateChildrenRecursive(children, status, List(), rootUserId) @@ -183,13 +183,15 @@ object AssessmentManager { private def updateChildrenRecursive(children: util.List[util.Map[String, AnyRef]], status: String, idList: List[String], rootUserId: String): List[String] = { children.toList.flatMap(content => { + val objectType = content.getOrDefault("objectType", "").asInstanceOf[String] val updatedIdList: List[String] = - if (StringUtils.equalsAnyIgnoreCase(content.getOrDefault("visibility", "").asInstanceOf[String], "Parent") || (StringUtils.equalsAnyIgnoreCase(content.getOrDefault("visibility", "").asInstanceOf[String], "Default") && StringUtils.equalsIgnoreCase("Draft", content.getOrDefault("status", "").asInstanceOf[String]) && StringUtils.equals(rootUserId, content.getOrDefault("createdBy", "").asInstanceOf[String]))) { + if (StringUtils.equalsAnyIgnoreCase(content.getOrDefault("visibility", "").asInstanceOf[String], "Parent") || (StringUtils.equalsIgnoreCase( objectType, "Question") && StringUtils.equalsAnyIgnoreCase(content.getOrDefault("visibility", "").asInstanceOf[String], "Default") && validStatus.contains(content.getOrDefault("status", "").asInstanceOf[String]) && StringUtils.equals(rootUserId, content.getOrDefault("createdBy", "").asInstanceOf[String]))) { content.put("lastStatusChangedOn", DateUtils.formatCurrentDate) + content.put("prevStatus", content.getOrDefault("status", "Draft")) content.put("status", status) content.put("prevStatus", "Draft") content.put("lastUpdatedOn", DateUtils.formatCurrentDate) - content.get("identifier").asInstanceOf[String] :: idList + if(StringUtils.equalsAnyIgnoreCase(objectType, "Question")) content.get("identifier").asInstanceOf[String] :: idList else idList } else idList val list = updateChildrenRecursive(content.getOrDefault("children", new util.ArrayList[Map[String, AnyRef]]).asInstanceOf[util.List[util.Map[String, AnyRef]]], status, updatedIdList, rootUserId) list ++ updatedIdList diff --git a/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/BaseSpec.scala b/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/BaseSpec.scala index 8fc9d3e99..b1abb5a29 100644 --- a/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/BaseSpec.scala +++ b/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/BaseSpec.scala @@ -59,4 +59,20 @@ class BaseSpec extends FlatSpec with Matchers { }) util.Arrays.asList(node) } + + def getNode(identifier: String, objectType: String, metadata: Option[util.Map[String, AnyRef]]): Node = { + val node = new Node("domain", "DATA_NODE", objectType) + node.setGraphId("domain") + val nodeMetadata = metadata.getOrElse(new util.HashMap[String, AnyRef]() {{ + put("name", "Question 1") + put("code", "ques.1") + put("status", "Draft") + put("primaryCategory", "Multiple Choice Question") + }}) + node.setNodeType("DATA_NODE") + node.setMetadata(nodeMetadata) + node.setObjectType(objectType) + node.setIdentifier(identifier) + node + } } diff --git a/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/QuestionSetActorTest.scala b/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/QuestionSetActorTest.scala index 8383b0c08..705b30481 100644 --- a/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/QuestionSetActorTest.scala +++ b/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/QuestionSetActorTest.scala @@ -255,7 +255,7 @@ class QuestionSetActorTest extends BaseSpec with MockFactory { val kfClient = mock[KafkaClient] (oec.kafkaClient _).expects().returns(kfClient).anyNumberOfTimes() (oec.graphService _).expects().returns(graphDB).anyNumberOfTimes() - val node = getNode("QuestionSet", None) + val node = getNode("do_11348469558523494411","QuestionSet", None) node.getMetadata.putAll(mapAsJavaMap(Map("name" -> "question_1", "visibility" -> "Default", "code" -> "finemanfine", @@ -268,9 +268,15 @@ class QuestionSetActorTest extends BaseSpec with MockFactory { "showHints" -> "Yes", "summaryType" -> "Complete", "mimeType" -> "application/vnd.sunbird.questionset", + "createdBy" -> "g-001", "primaryCategory" -> "Practice Question Set"))) + val nodeList = new util.ArrayList[Node]() {{ + add(getNode("do_11348469662446387212","Question", Some(Map("visibility"-> "Parent", "createdBy"-> "g-001")))) + add(getNode("do_11348469662607769614","Question", Some(Map("visibility"-> "Default", "createdBy"-> "g-002")))) + }} (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, *, *, *).returns(Future(node)).atLeastOnce() - (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, *).returns(Future(getCassandraHierarchy())).anyNumberOfTimes + (graphDB.getNodeByUniqueIds(_ :String, _: SearchCriteria)).expects(*, *).returns(Future(nodeList)).atLeastOnce() + (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, *).returns(Future(getDraftCassandraHierarchy)).anyNumberOfTimes (kfClient.send(_: String, _: String)).expects(*, *).once() val request = getQuestionSetRequest() request.getContext.put("identifier", "do1234") @@ -600,6 +606,12 @@ class QuestionSetActorTest extends BaseSpec with MockFactory { response.put("hierarchy", hierarchyString) } + def getDraftCassandraHierarchy(): Response = { + val hierarchyString: String = """{"identifier":"do_11348469558523494411","children":[{"parent":"do_11348469558523494411","code":"Q1","description":"Q1","language":["English"],"mimeType":"application/vnd.sunbird.question","createdOn":"2022-03-01T02:15:30.917+0530","objectType":"Question","primaryCategory":"Multiple Choice question","contentDisposition":"inline","lastUpdatedOn":"2022-03-01T02:15:30.915+0530","contentEncoding":"gzip","showSolutions":"No","allowAnonymousAccess":"Yes","identifier":"do_11348469662446387212","lastStatusChangedOn":"2022-03-01T02:15:30.917+0530","visibility":"Parent","showTimer":"No","index":1,"languageCode":["en"],"version":1,"versionKey":"1646081131087","showFeedback":"No","license":"CC BY 4.0","depth":1,"createdBy":"g-001","compatibilityLevel":4,"name":"Q1","status":"Draft"},{"parent":"do_11348469558523494411","code":"Q2","description":"Q2","language":["English"],"mimeType":"application/vnd.sunbird.question","createdOn":"2022-03-01T02:15:31.113+0530","objectType":"Question","primaryCategory":"Multiple Choice question","contentDisposition":"inline","lastUpdatedOn":"2022-03-01T02:15:31.126+0530","contentEncoding":"gzip","showSolutions":"No","allowAnonymousAccess":"Yes","identifier":"do_11348469662607769614","lastStatusChangedOn":"2022-03-01T02:15:31.113+0530","visibility":"Default","showTimer":"No","index":2,"languageCode":["en"],"version":1,"versionKey":"1646081131126","showFeedback":"No","license":"CC BY 4.0","depth":1,"createdBy":"g-002","compatibilityLevel":4,"name":"Q2","status":"Draft"}]}""" + val response = new Response + response.put("hierarchy", hierarchyString) + } + def getEmptyCassandraHierarchy(): Response = { val response = new Response response.put("hierarchy", "{}") From 6c569789900e85ecfb49e01b2ff76fbe310c9290 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Tue, 29 Mar 2022 15:33:53 +0530 Subject: [PATCH 008/490] Issue #SB-24965 feat: Updating Local Setup details --- README.md | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9fcbe2a83..96b6758a6 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Repository for Knowledge Platform - 2.0 -## Content-Service local setup +## Knowledge-platform local setup This readme file contains the instruction to set up and run the content-service in local machine. ### Prerequisites: * Neo4j @@ -140,7 +140,8 @@ Example: kafka-topics.sh --create --zookeeper zookeeper:2181 --replication-factor 1 --partitions 1 --topic test_topic ``` -### Running content-service: +### Running Content Service: +### (Content V3+V4 APIs & Collection V4 APIs & Assets V4 APIs & Channel V3 APIs & License V3 APIs & Event V4 APIs & EventSet V4 APIs) 1. Go to the path: /knowledge-platform and run the below maven command to build the application. ```shell mvn clean install -DskipTests @@ -149,7 +150,35 @@ mvn clean install -DskipTests ```shell mvn play2:run ``` -3. Using the below command we can verify whether the databases(neoj,redis & cassandra) connection is established or not. If all connections are good, health is shown as 'true' otherwise it will be 'false'. +3. Using the below command we can verify whether the databases(neo4j,redis & cassandra) connection is established or not. If all connections are good, health is shown as 'true' otherwise it will be 'false'. ```shell curl http://localhost:9000/health ``` + +### Running Assets/Composite Search Service: +1. Go to the path: /knowledge-platform and run the below maven command to build the application. +```shell +mvn clean install -DskipTests +``` +2. Go to the path: /knowledge-platform/search-api/search-service and run the below maven command to run the netty server. +```shell +mvn play2:run +``` +3. Using the below command we can verify whether the databases(neo4j,redis & cassandra) connection is established or not. If all connections are good, health is shown as 'true' otherwise it will be 'false'. +```shell +curl http://localhost:9000/health +``` + +### Running Object Category Service: +1. Go to the path: /knowledge-platform and run the below maven command to build the application. +```shell +mvn clean install -DskipTests +``` +2. Go to the path: /knowledge-platform/taxonomy-api/taxonomy-service and run the below maven command to run the netty server. +```shell +mvn play2:run +``` +3. Using the below command we can verify whether the databases(neo4j,redis & cassandra) connection is established or not. If all connections are good, health is shown as 'true' otherwise it will be 'false'. +```shell +curl http://localhost:9000/health +``` \ No newline at end of file From 92e5e0c1830fcb7fe9c1681f825d0e61b4937e84 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Tue, 29 Mar 2022 18:09:52 +0530 Subject: [PATCH 009/490] Issue #SB-24965 feat: Updating Local Setup details (#810) --- README.md | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9fcbe2a83..96b6758a6 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Repository for Knowledge Platform - 2.0 -## Content-Service local setup +## Knowledge-platform local setup This readme file contains the instruction to set up and run the content-service in local machine. ### Prerequisites: * Neo4j @@ -140,7 +140,8 @@ Example: kafka-topics.sh --create --zookeeper zookeeper:2181 --replication-factor 1 --partitions 1 --topic test_topic ``` -### Running content-service: +### Running Content Service: +### (Content V3+V4 APIs & Collection V4 APIs & Assets V4 APIs & Channel V3 APIs & License V3 APIs & Event V4 APIs & EventSet V4 APIs) 1. Go to the path: /knowledge-platform and run the below maven command to build the application. ```shell mvn clean install -DskipTests @@ -149,7 +150,35 @@ mvn clean install -DskipTests ```shell mvn play2:run ``` -3. Using the below command we can verify whether the databases(neoj,redis & cassandra) connection is established or not. If all connections are good, health is shown as 'true' otherwise it will be 'false'. +3. Using the below command we can verify whether the databases(neo4j,redis & cassandra) connection is established or not. If all connections are good, health is shown as 'true' otherwise it will be 'false'. ```shell curl http://localhost:9000/health ``` + +### Running Assets/Composite Search Service: +1. Go to the path: /knowledge-platform and run the below maven command to build the application. +```shell +mvn clean install -DskipTests +``` +2. Go to the path: /knowledge-platform/search-api/search-service and run the below maven command to run the netty server. +```shell +mvn play2:run +``` +3. Using the below command we can verify whether the databases(neo4j,redis & cassandra) connection is established or not. If all connections are good, health is shown as 'true' otherwise it will be 'false'. +```shell +curl http://localhost:9000/health +``` + +### Running Object Category Service: +1. Go to the path: /knowledge-platform and run the below maven command to build the application. +```shell +mvn clean install -DskipTests +``` +2. Go to the path: /knowledge-platform/taxonomy-api/taxonomy-service and run the below maven command to run the netty server. +```shell +mvn play2:run +``` +3. Using the below command we can verify whether the databases(neo4j,redis & cassandra) connection is established or not. If all connections are good, health is shown as 'true' otherwise it will be 'false'. +```shell +curl http://localhost:9000/health +``` \ No newline at end of file From 76401a1166162f32bd44de941f300e46b1ef7a27 Mon Sep 17 00:00:00 2001 From: joffinjoy <35325730+joffinjoy@users.noreply.github.com> Date: Fri, 1 Apr 2022 20:17:50 +0530 Subject: [PATCH 010/490] Issue #SB-29145 feat: Added BranchingLogic Copy --- .../org/sunbird/managers/CopyManager.scala | 148 +++++++++++++++++- .../sunbird/utils/AssessmentContants.scala | 9 ++ 2 files changed, 150 insertions(+), 7 deletions(-) diff --git a/assessment-api/assessment-actors/src/main/scala/org/sunbird/managers/CopyManager.scala b/assessment-api/assessment-actors/src/main/scala/org/sunbird/managers/CopyManager.scala index 5c5a5fa73..609acdc71 100644 --- a/assessment-api/assessment-actors/src/main/scala/org/sunbird/managers/CopyManager.scala +++ b/assessment-api/assessment-actors/src/main/scala/org/sunbird/managers/CopyManager.scala @@ -1,9 +1,11 @@ package org.sunbird.managers +import com.google.gson.{Gson, GsonBuilder} +import com.google.gson.reflect.TypeToken import org.apache.commons.collections.CollectionUtils import org.apache.commons.collections4.MapUtils import org.apache.commons.lang.StringUtils -import org.sunbird.common.Platform +import org.sunbird.common.{JsonUtils, Platform} import org.sunbird.common.dto.{Request, Response, ResponseHandler} import org.sunbird.common.exception.{ClientException, ServerException} import org.sunbird.graph.OntologyEngineContext @@ -13,11 +15,13 @@ import org.sunbird.graph.nodes.DataNode import org.sunbird.graph.schema.DefinitionNode import org.sunbird.graph.utils.{NodeUtil, ScalaJsonUtils} import org.sunbird.telemetry.logger.TelemetryManager -import org.sunbird.utils.AssessmentConstants +import org.sunbird.utils.{AssessmentConstants, HierarchyConstants} import java.util import java.util.concurrent.{CompletionException, TimeUnit} +import java.util.stream.Collectors import java.util.{Optional, UUID} +import scala.collection.JavaConversions.{asScalaBuffer, mapAsScalaMap} import scala.collection.JavaConverters._ import scala.concurrent.duration.{Duration, FiniteDuration} import scala.concurrent.{Await, ExecutionContext, Future} @@ -97,15 +101,144 @@ object CopyManager { }).flatMap(f => f) } - def updateHierarchy(request: Request, node: Node, originNode: Node, originHierarchy: util.Map[String, AnyRef], copyType: String)(implicit ec: ExecutionContext, oec: OntologyEngineContext): Future[Node] = { + + def generateNodeBLRecord(nodesModified: util.HashMap[String, AnyRef]): util.HashMap[String, AnyRef] = { + val idSet = nodesModified.keySet().asScala.toList + val nodeBLRecord = new util.HashMap[String, AnyRef]() + idSet.map(id => { + val nodeMetaData = nodesModified.getOrDefault(id, new util.HashMap()).asInstanceOf[util.Map[String, AnyRef]].getOrDefault(AssessmentConstants.METADATA, new util.HashMap()).asInstanceOf[util.Map[String, AnyRef]] + val containsBL = nodeMetaData.containsKey(AssessmentConstants.BRANCHING_LOGIC) + nodeBLRecord.put(id, new util.HashMap[String, AnyRef]() { + { + if (containsBL) put(AssessmentConstants.BRANCHING_LOGIC, nodeMetaData.get(AssessmentConstants.BRANCHING_LOGIC)) + put(AssessmentConstants.CONTAINS_BL, containsBL.asInstanceOf[AnyRef]) + put(AssessmentConstants.COPY_OF, nodeMetaData.get(AssessmentConstants.COPY_OF).asInstanceOf[String]) + } + }) + if (containsBL) nodeMetaData.remove(AssessmentConstants.BRANCHING_LOGIC) + nodeMetaData.remove(AssessmentConstants.COPY_OF) + }) + nodeBLRecord + } + + def branchingLogicArrayHandler(nodeBL: util.HashMap[String, AnyRef], name: String, oldToNewIdMap: util.Map[String, String]) = { + val array = nodeBL.getOrDefault(name, new util.ArrayList[String]).asInstanceOf[util.ArrayList[String]] + val newArray = new util.ArrayList[String]() + array.map(id => { + if (oldToNewIdMap.containsKey(id)) { + newArray.add(oldToNewIdMap.get(id)) + } else newArray.add(id) + }) + nodeBL.remove(name) + nodeBL.put(name, newArray) + } + + def preConditionHandler(nodeBL: util.HashMap[String, AnyRef], oldToNewIdMap: util.Map[String, String]): Unit = { + val preCondition = nodeBL.get(AssessmentConstants.PRE_CONDITION).asInstanceOf[util.HashMap[String, AnyRef]] + preCondition.keySet().asScala.toList.map(key => { + val conjunctionArray = preCondition.get(key).asInstanceOf[util.ArrayList[String]] + val condition = conjunctionArray.get(0).asInstanceOf[util.HashMap[String, AnyRef]] + condition.keySet().asScala.toList.map(logicOp => { + val conditionArray = condition.get(logicOp).asInstanceOf[util.ArrayList[String]] + val sourceQuestionRecord = conditionArray.get(0).asInstanceOf[util.HashMap[String, AnyRef]] + val preConditionVar = sourceQuestionRecord.get(AssessmentConstants.PRE_CONDITION_VAR).asInstanceOf[String] + val stringArray = preConditionVar.split("\\.") + if (oldToNewIdMap.containsKey(stringArray(0))) { + val newString = oldToNewIdMap.get(stringArray(0)) + "." + stringArray.drop(1).mkString(".") + sourceQuestionRecord.remove(AssessmentConstants.PRE_CONDITION_VAR) + sourceQuestionRecord.put(AssessmentConstants.PRE_CONDITION_VAR, newString) + } + }) + }) + } + + def branchingLogicModifier(branchingLogic: util.HashMap[String, AnyRef], oldToNewIdMap: util.Map[String, String]): Unit = { + branchingLogic.keySet().asScala.toList.map(identifier => { + val nodeBL = branchingLogic.get(identifier).asInstanceOf[util.HashMap[String, AnyRef]] + nodeBL.keySet().asScala.toList.map(key => { + if (StringUtils.equalsIgnoreCase(key, AssessmentConstants.TARGET)) branchingLogicArrayHandler(nodeBL, AssessmentConstants.TARGET, oldToNewIdMap) + else if (StringUtils.equalsIgnoreCase(key, AssessmentConstants.PRE_CONDITION)) preConditionHandler(nodeBL, oldToNewIdMap) + else if (StringUtils.equalsIgnoreCase(key, AssessmentConstants.SOURCE)) branchingLogicArrayHandler(nodeBL, AssessmentConstants.SOURCE, oldToNewIdMap) + }) + if (oldToNewIdMap.containsKey(identifier)) { + branchingLogic.put(oldToNewIdMap.get(identifier), nodeBL) + branchingLogic.remove(identifier) + } + }) + } + + def generateOldToNewIdMap(nodeBLRecord: util.HashMap[String, AnyRef], identifiers: util.Map[String, String]): util.Map[String, String] = { + val oldToNewIdMap = new util.HashMap[String, String]() + nodeBLRecord.keySet().asScala.toList.map(id => { + val nodeInfo = nodeBLRecord.get(id).asInstanceOf[util.HashMap[String, AnyRef]] + val newId = identifiers.get(id) + val oldId = nodeInfo.get(AssessmentConstants.COPY_OF).asInstanceOf[String] + oldToNewIdMap.put(oldId, newId) + }) + oldToNewIdMap + } + + def hierarchyRequestModifier(request: Request, nodeBLRecord: util.HashMap[String, AnyRef], identifiers: util.Map[String, String]): Unit = { + val nodesModified: java.util.HashMap[String, AnyRef] = request.getRequest.get(HierarchyConstants.NODES_MODIFIED).asInstanceOf[java.util.HashMap[String, AnyRef]] + val hierarchy: java.util.HashMap[String, AnyRef] = request.getRequest.get(HierarchyConstants.HIERARCHY).asInstanceOf[java.util.HashMap[String, AnyRef]] + val oldToNewIdMap = generateOldToNewIdMap(nodeBLRecord, identifiers) + nodeBLRecord.keySet().asScala.toList.map(id => { + val nodeInfo = nodeBLRecord.get(id).asInstanceOf[util.HashMap[String, AnyRef]] + val node = nodesModified.get(id).asInstanceOf[util.HashMap[String, AnyRef]] + val nodeMetaData = node.get(AssessmentConstants.METADATA).asInstanceOf[util.HashMap[String, AnyRef]] + val newId = identifiers.get(id) + if (nodeInfo.get(AssessmentConstants.CONTAINS_BL).asInstanceOf[Boolean]) { + val branchingLogic = nodeInfo.get(AssessmentConstants.BRANCHING_LOGIC).asInstanceOf[util.HashMap[String, AnyRef]] + branchingLogicModifier(branchingLogic, oldToNewIdMap) + nodeMetaData.put(AssessmentConstants.BRANCHING_LOGIC, branchingLogic) + } + node.remove(AssessmentConstants.IS_NEW) + node.put(AssessmentConstants.IS_NEW, false.asInstanceOf[AnyRef]) + nodesModified.remove(id) + nodesModified.put(newId, node) + }) + hierarchy.keySet().asScala.toList.map(id => { + val nodeHierarchy = hierarchy.get(id).asInstanceOf[util.HashMap[String, AnyRef]] + val children = nodeHierarchy.get(AssessmentConstants.CHILDREN).asInstanceOf[util.ArrayList[String]] + val newChildrenList = new util.ArrayList[String] + children.map(identifier => { + if (identifiers.containsKey(identifier)) newChildrenList.add(identifiers.get(identifier)) else newChildrenList.add(identifier) + }) + nodeHierarchy.remove(AssessmentConstants.CHILDREN) + nodeHierarchy.put(AssessmentConstants.CHILDREN, newChildrenList) + if (identifiers.containsKey(id)) { + hierarchy.remove(id) + hierarchy.put(identifiers.get(id), nodeHierarchy) + } + }) + } + + def updateHierarchy(request: Request, node: Node, originNode: Node, originHierarchy: util.Map[String, AnyRef], copyType: String) + (implicit ec: ExecutionContext, oec: OntologyEngineContext): Future[Node] = { prepareHierarchyRequest(originHierarchy, originNode, node, copyType, request).map(req => { val hierarchyRequest = new Request(request) hierarchyRequest.putAll(req) - hierarchyRequest.getContext.put(AssessmentConstants.SCHEMA_NAME, AssessmentConstants.QUESTIONSET_SCHEMA_NAME) - hierarchyRequest.getContext.put(AssessmentConstants.VERSION, AssessmentConstants.SCHEMA_VERSION) + val nodesModified: java.util.HashMap[String, AnyRef] = hierarchyRequest.getRequest.get(HierarchyConstants.NODES_MODIFIED) + .asInstanceOf[java.util.HashMap[String, AnyRef]] + val nodeBLRecord = generateNodeBLRecord(nodesModified) + val newUpdateRequest = JsonUtils.deserialize(ScalaJsonUtils.serialize(hierarchyRequest), classOf[Request]) UpdateHierarchyManager.updateHierarchy(hierarchyRequest).map(response => { - if (!ResponseHandler.checkError(response)) node else { - TelemetryManager.info(s"Update Hierarchy Failed For Copy Question Set Having Identifier: ${node.getIdentifier} | Response is : " + response) + if (!ResponseHandler.checkError(response)) { + val identifiers = response.getResult.get(AssessmentConstants.IDENTIFIERS).asInstanceOf[util.Map[String, String]] + hierarchyRequestModifier(newUpdateRequest, nodeBLRecord, identifiers) + UpdateHierarchyManager.updateHierarchy(newUpdateRequest).map(response_ => { + if (!ResponseHandler.checkError(response_)) { + node + } else { + TelemetryManager.info(s"Update Hierarchy Failed For Copy Question Set Having Identifier: ${node.getIdentifier} | Response " + + s"is " + s": " + response) + throw new ServerException("ERR_QUESTIONSET_COPY", "Something Went Wrong, Please Try Again") + } + }) + node + } else { + TelemetryManager.info(s"Update Hierarchy Failed For Copy Question Set Having Identifier: ${node.getIdentifier} | Response is " + + s": " + response) throw new ServerException("ERR_QUESTIONSET_COPY", "Something Went Wrong, Please Try Again") } }) @@ -167,6 +300,7 @@ object CopyManager { put(AssessmentConstants.METADATA, cleanUpCopiedData(new util.HashMap[String, AnyRef]() { { putAll(child) + put("copyOf", child.getOrDefault(AssessmentConstants.IDENTIFIER,"")) put(AssessmentConstants.CHILDREN, new util.ArrayList()) internalHierarchyProps.map(key => remove(key)) } diff --git a/assessment-api/assessment-actors/src/main/scala/org/sunbird/utils/AssessmentContants.scala b/assessment-api/assessment-actors/src/main/scala/org/sunbird/utils/AssessmentContants.scala index c422c3256..16e47ccde 100644 --- a/assessment-api/assessment-actors/src/main/scala/org/sunbird/utils/AssessmentContants.scala +++ b/assessment-api/assessment-actors/src/main/scala/org/sunbird/utils/AssessmentContants.scala @@ -36,4 +36,13 @@ object AssessmentConstants { val QUESTION_SCHEMA_NAME: String = "question" val VISIBILITY_PARENT: String = "Parent" val VISIBILITY_DEFAULT: String = "Default" + val BRANCHING_LOGIC: String = "branchingLogic" + val COPY_OF: String = "copyOf" + val CONTAINS_BL: String = "containsBL" + val IDENTIFIERS: String = "identifiers" + val IS_NEW: String = "isNew" + val TARGET: String = "target" + val PRE_CONDITION: String = "preCondition" + val SOURCE: String = "source" + val PRE_CONDITION_VAR : String = "var" } From 30d04fd7bc21992bb96758c19b672f41795d3a4b Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Mon, 4 Apr 2022 12:49:40 +0530 Subject: [PATCH 011/490] Issue #SB-24965 feat: Updating Local Setup details --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 96b6758a6..7cd2d2872 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,11 @@ Repository for Knowledge Platform - 2.0 ## Knowledge-platform local setup This readme file contains the instruction to set up and run the content-service in local machine. + +### System Requirements: + ### Prerequisites: -* Neo4j -* Redis -* Cassandra +* Java 11 ### Prepare folders for database data and logs @@ -141,7 +142,7 @@ kafka-topics.sh --create --zookeeper zookeeper:2181 --replication-factor 1 --par ``` ### Running Content Service: -### (Content V3+V4 APIs & Collection V4 APIs & Assets V4 APIs & Channel V3 APIs & License V3 APIs & Event V4 APIs & EventSet V4 APIs) +### (Content V3+V4 APIs, Collection V4 APIs, Assets V4 APIs, Channel V3 APIs, License V3 APIs, Event V4 APIs, EventSet V4 APIs) 1. Go to the path: /knowledge-platform and run the below maven command to build the application. ```shell mvn clean install -DskipTests From cdfcc47a19edccb8e295c3a6a2c043f14c2d48b9 Mon Sep 17 00:00:00 2001 From: Kumar Gauraw Date: Tue, 5 Apr 2022 11:51:32 +0530 Subject: [PATCH 012/490] Issue #SB-29323 fix: Fixed for visibility parent object --- .../java/org/sunbird/actors/SearchActor.java | 2 +- .../app/controllers/SearchController.scala | 22 ++++++++++++------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/search-api/search-actors/src/main/java/org/sunbird/actors/SearchActor.java b/search-api/search-actors/src/main/java/org/sunbird/actors/SearchActor.java index 8dd9dcd92..d59d7f4d6 100644 --- a/search-api/search-actors/src/main/java/org/sunbird/actors/SearchActor.java +++ b/search-api/search-actors/src/main/java/org/sunbird/actors/SearchActor.java @@ -515,7 +515,7 @@ private List> getSearchFilterProperties(Map properties.add(property); } - if (request != null && !StringUtils.equalsIgnoreCase((String) request.getContext().getOrDefault("setDefaultVisibility",""),"false") && setDefaultVisibility(filters)) { + if (request != null && StringUtils.equalsIgnoreCase((String) request.getContext().getOrDefault("setDefaultVisibility",""),"true") && setDefaultVisibility(filters)) { Map property = getFilterProperty("visibility", SearchConstants.SEARCH_OPERATION_EQUAL, Arrays.asList(new String[] { "Default" })); properties.add(property); } diff --git a/search-api/search-service/app/controllers/SearchController.scala b/search-api/search-service/app/controllers/SearchController.scala index e76bcc5e3..0b94f1f05 100644 --- a/search-api/search-service/app/controllers/SearchController.scala +++ b/search-api/search-service/app/controllers/SearchController.scala @@ -9,6 +9,10 @@ import org.sunbird.search.util.SearchConstants import play.api.mvc.ControllerComponents import utils.{ActorNames, ApiId} import java.util + +import org.apache.commons.lang3.StringUtils +import org.sunbird.common.Platform + import scala.collection.JavaConverters._ import scala.concurrent.ExecutionContext @@ -22,17 +26,19 @@ class SearchController @Inject()(@Named(ActorNames.SEARCH_ACTOR) searchActor: Ac val internalReq = getRequest(ApiId.APPLICATION_SEARCH) setHeaderContext(internalReq) val filters = internalReq.getRequest.getOrDefault(SearchConstants.filters, new java.util.HashMap()).asInstanceOf[java.util.Map[String, Object]] - val visibilityObject = filters.getOrDefault("visibility","") - var visibility:util.List[String] = null - if (visibilityObject != null) { - if (visibilityObject.isInstanceOf[util.ArrayList[_]]) visibility = visibilityObject.asInstanceOf[util.ArrayList[String]] - else if (visibilityObject.isInstanceOf[String]) visibility = util.Arrays.asList(visibilityObject.asInstanceOf[String]) + val visibilityReq = filters.getOrDefault("visibility", new util.ArrayList[String]()) + val visibility: List[String] = visibilityReq match { + case visibilityReq: util.List[_] => visibilityReq.asInstanceOf[util.List[String]].asScala.toList.map(x => if (StringUtils.isNotBlank(x)) x.toLowerCase).asInstanceOf[List[String]] + case visibilityReq: String => List(visibilityReq).map(x => if (StringUtils.isNotBlank(x)) x.toLowerCase).asInstanceOf[List[String]] + case _ => List() } - if (visibility.contains("Private")) { + + if (visibility.nonEmpty && visibility.contains("private")) getErrorResponse(ApiId.APPLICATION_SEARCH, apiVersion, SearchConstants.ERR_ACCESS_DENIED, "Cannot access private content through public search api") - } else { - internalReq.getContext.put(SearchConstants.setDefaultVisibility, "true") + val searchableVisibility: List[String] = Platform.getStringList("object.searchableVisibility", util.Arrays.asList("Default", "Parent", "Protected")).asScala.toList.map(x => x.toLowerCase) + val setDefaultVisibility: String = if (visibility.nonEmpty && searchableVisibility.containsSlice(visibility)) "false" else "true" + internalReq.getContext.put(SearchConstants.setDefaultVisibility, setDefaultVisibility) getResult(mgr.search(internalReq, searchActor), ApiId.APPLICATION_SEARCH) } } From a823ad3cf1550274b56f26d3a4cc8cd5d5295342 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Wed, 6 Apr 2022 15:41:22 +0530 Subject: [PATCH 013/490] Using config parameter for request timeout --- .../content-service/app/controllers/BaseController.scala | 3 ++- content-api/content-service/conf/application.conf | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/content-api/content-service/app/controllers/BaseController.scala b/content-api/content-service/app/controllers/BaseController.scala index 4fc36e06e..3b7125e19 100644 --- a/content-api/content-service/app/controllers/BaseController.scala +++ b/content-api/content-service/app/controllers/BaseController.scala @@ -26,6 +26,7 @@ abstract class BaseController(protected val cc: ControllerComponents)(implicit e new util.HashMap[String, AnyRef]()).asInstanceOf[java.util.Map[String, AnyRef]] val mimeTypesToCheck = List("application/vnd.ekstep.h5p-archive", "application/vnd.ekstep.html-archive", "application/vnd.android.package-archive", "video/webm", "video/x-youtube", "video/mp4") + val actorTimeout: Long = Platform.getLong("actor.timeoutMillisec", 120000L) def requestBody()(implicit request: Request[AnyContent]) = { val body = request.body.asJson.getOrElse("{}").toString @@ -83,7 +84,7 @@ abstract class BaseController(protected val cc: ControllerComponents)(implicit e } def getResult(apiId: String, actor: ActorRef, request: org.sunbird.common.dto.Request, categoryMapping: Boolean = false, version: String = "3.0") : Future[Result] = { - val future = Patterns.ask(actor, request, 30000) recoverWith {case e: Exception => Future(ResponseHandler.getErrorResponse(e))} + val future = Patterns.ask(actor, request, actorTimeout) recoverWith {case e: Exception => Future(ResponseHandler.getErrorResponse(e))} future.map(f => { val result: Response = f.asInstanceOf[Response] result.setId(apiId) diff --git a/content-api/content-service/conf/application.conf b/content-api/content-service/conf/application.conf index 33ac60598..be2552755 100644 --- a/content-api/content-service/conf/application.conf +++ b/content-api/content-service/conf/application.conf @@ -736,3 +736,6 @@ collection { } } } + +#Request timeout +actor.timeoutMillisec = 120000 \ No newline at end of file From f05328de25acba194e6210dda606e333ba12f6c7 Mon Sep 17 00:00:00 2001 From: joffinjoy <35325730+joffinjoy@users.noreply.github.com> Date: Thu, 7 Apr 2022 10:19:21 +0530 Subject: [PATCH 014/490] Issue #SB-29145 test: Added BranchingLogic Copy Test-Case --- .../sunbird/actors/QuestionSetActorTest.scala | 33 +++ .../scala/org/sunbird/actors/copyTrait.scala | 229 ++++++++++++++++++ 2 files changed, 262 insertions(+) diff --git a/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/QuestionSetActorTest.scala b/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/QuestionSetActorTest.scala index 25cf6c6d1..9d6019aea 100644 --- a/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/QuestionSetActorTest.scala +++ b/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/QuestionSetActorTest.scala @@ -594,6 +594,39 @@ class QuestionSetActorTest extends BaseSpec with MockFactory with copyTrait { assert("failed".equals(response.getParams.getStatus)) } + it should "return success response for 'copyQuestionSet' (Branching Logic Copy)" in { + implicit val oec: OntologyEngineContext = mock[OntologyEngineContext] + val graphDB = mock[GraphService] + (oec.graphService _).expects().returns(graphDB).anyNumberOfTimes() + val nodes: util.List[Node] = getCategoryNode() + (graphDB.getNodeByUniqueIds(_: String, _: SearchCriteria)).expects(*, *).returns(Future(nodes)).anyNumberOfTimes() + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, "do_1234", *, *).returns(Future(getRootNodeWithBL("do_1234", "do_2222", true, true))).anyNumberOfTimes() + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, "do_9876", *, *).returns(Future(getRootNodeWithBL("do_9876", "do_3333", false, false))).anyNumberOfTimes() + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, "do_9876.img", *, *).returns(Future(getRootNodeWithBL("do_9876", "do_3333", false, false))).anyNumberOfTimes() + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, "do_5555", *, *).returns(Future(getQuestionNodeBL("do_5555"))).anyNumberOfTimes() + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, "do_7777", *, *).returns(Future(getQuestionNodeBL("do_7777"))).anyNumberOfTimes() + (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, List("objectMetadata")).returns(Future(getSuccessfulResponse)).anyNumberOfTimes() + (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, List("instructions", "outcomeDeclaration")).returns(Future(getSuccessfulResponse)).anyNumberOfTimes() + (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, List("solutions", "body", "editorState", "interactions", "hints", "responseDeclaration", "media", "answer", "instructions")).returns(Future(getResourceNotFoundResponse)).anyNumberOfTimes() + (graphDB.addNode(_: String, _: Node)).expects(*, *).returns(Future(getRootNodeWithBL("do_9876", "do_3333", false, false))).anyNumberOfTimes + (graphDB.saveExternalProps(_: Request)).expects(*).returns(Future(getSuccessfulResponse)).anyNumberOfTimes + (graphDB.updateExternalProps(_: Request)).expects(*).returns(Future(getSuccessfulResponse)).anyNumberOfTimes + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, *, *, *).returns(Future(getRootNodeWithBL("do_9876", "do_3333", true, true))).anyNumberOfTimes() + inSequence { + (graphDB.upsertNode(_: String, _: Node, _: Request)).expects(*, *, *).returns(Future(getUpsertNodeBLWithoutBL)) + (graphDB.upsertNode(_: String, _: Node, _: Request)).expects(*, *, *).returns(Future(getUpsertNodeBLWithBL)).anyNumberOfTimes + } + inSequence { + (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, List("hierarchy")).returns(Future(getRootExternalPropsResponseBL)) + (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, List("hierarchy")).returns(Future(getNewRootExternalPropsResponseBL)).anyNumberOfTimes + } + val request = getQuestionSetCopyRequest() + request.putAll(mapAsJavaMap(Map("identifier" -> "do_1234", "mode" -> "", "copyType" -> "deep"))) + request.setOperation("copyQuestionSet") + val response = callActor(request, Props(new QuestionSetActor())) + assert("successful".equals(response.getParams.getStatus)) + } + private def getQuestionSetRequest(): Request = { val request = new Request() request.setContext(new java.util.HashMap[String, AnyRef]() { diff --git a/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/copyTrait.scala b/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/copyTrait.scala index 5f5478c4d..3bf221a77 100644 --- a/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/copyTrait.scala +++ b/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/copyTrait.scala @@ -261,4 +261,233 @@ trait copyTrait { }) node } + + def getRootNodeWithBL(rootId: String, sectionId: String, addBranchingLogic: Boolean, withChildren: Boolean): Node = { + val node = getNode("QuestionSet", rootId, "Observation", AssessmentConstants.VISIBILITY_DEFAULT, "ExistingRootNode", 1234, "Live") + if (withChildren) { + val section = getNode("QuestionSet", sectionId, "Observation", AssessmentConstants.VISIBILITY_DEFAULT, "Section_1", 1234, "Live") + val children = new util.ArrayList[util.Map[String, AnyRef]]() + children.add(getNode("Question", "do_5555", "Slider", AssessmentConstants.VISIBILITY_DEFAULT, "Question1", 1234, "Live") + .getMetadata) + children.add(getNode("Question", "do_7777", "Slider", AssessmentConstants.VISIBILITY_DEFAULT, "Question2", 1234, "Live") + .getMetadata) + if (addBranchingLogic) { + section.getMetadata.put("branchingLogic", new util.HashMap[String, AnyRef]() { + { + put("do_5555", new util.HashMap[String, AnyRef]() { + put("target", new util.ArrayList[String]() { + { + add("do_7777") + } + }) + put("preCondition", new util.HashMap[String, AnyRef]()) + put("source", new util.ArrayList[String]()) + }) + put("do_7777", new util.HashMap[String, AnyRef]() { + put("target", new util.ArrayList[String]()) + put("preCondition", new util.HashMap[String, AnyRef]() { + { + put("and", new util.ArrayList[util.HashMap[String, AnyRef]]() { + add(new util.HashMap[String, AnyRef]() { + put("eq", new util.ArrayList[AnyRef]() { + { + add(new util.HashMap[String, String]() { + put("var", "do_5555" + ".response1.value") + put("type", "responseDeclaration") + }) + add("0") + } + }) + }) + }) + } + }) + put("source", new util.ArrayList[String]() { + { + add("do_5555") + } + }) + }) + } + }) + } + node.getMetadata.put("childNodes", new util.ArrayList[String]() { + { + add(sectionId) + add("do_5555") + add("do_7777") + } + }) + section.getMetadata.put("children", children) + node.getMetadata.put("children", new util.ArrayList[util.Map[String, AnyRef]]() { + { + add(section.getMetadata) + } + }) + } + node + } + + def getQuestionNodeBL(identifier: String): Node = { + val node = getNode("Question", identifier, "Slider", AssessmentConstants.VISIBILITY_DEFAULT, identifier, 1234, "Live") + node + } + + def getUpsertNodeBLWithoutBL(): Node = { + val node = getRootNodeWithBL("do_9876", "do_3333", false, false) + node.setExternalData(new util.HashMap[String, AnyRef]() { + { + put("hierarchy", "{\"identifier\":\"do_9876\",\"children\":[{\"parent\":\"do_9876\"," + + "\"code\":\"9f0332ad-c3e3-4803-b673-50174aff24e3\",\"allowSkip\":\"Yes\",\"containsUserData\":\"No\"," + + "\"channel\":\"{{channel_id}}\",\"language\":[\"English\"],\"mimeType\":\"application/vnd.sunbird.questionset\"," + + "\"showHints\":\"No\",\"createdOn\":\"2022-04-06T12:51:53.592+0530\",\"objectType\":\"QuestionSet\"," + + "\"primaryCategory\":\"Observation\",\"children\":[{\"parent\":\"do_3333\",\"code\":\"Q1\"," + + "\"channel\":\"{{channel_id}}\"," + "\"language\":[\"English\"],\"mimeType\":\"application/vnd.sunbird.question\"," + + "\"createdOn\":\"2022-04-06T10:13:32.859+0530\"," + "\"objectType\":\"Question\",\"primaryCategory\":\"Slider\"," + + "\"contentDisposition\":\"inline\"," + "\"lastUpdatedOn\":\"2022-04-06T10:13:32.911+0530\",\"contentEncoding\":\"gzip\"," + + "" + "\"showSolutions\":\"No\"," + "\"allowAnonymousAccess\":\"Yes\",\"identifier\":\"do_5555\"," + + "\"lastStatusChangedOn\":\"2022-04-06T10:13:32.859+0530\"," + "\"visibility\":\"Default\",\"showTimer\":\"No\"," + + "\"index\":1," + "\"languageCode\":[\"en\"],\"version\":1," + "\"versionKey\":\"1649220212911\",\"showFeedback\":\"No\"," + + "\"license\":\"CC BY " + "4.0\",\"depth\":2,\"compatibilityLevel\":4," + "\"name\":\"Q1\",\"status\":\"Live\"}," + + "{\"parent\":\"do_3333\",\"code\":\"Q2\",\"channel\":\"{{channel_id}}\"," + "\"language\":[\"English\"]," + + "\"mimeType\":\"application/vnd.sunbird.question\",\"createdOn\":\"2022-04-06T10:13:32.896+0530\"," + + "\"objectType\":\"Question\",\"primaryCategory\":\"Slider\",\"contentDisposition\":\"inline\"," + + "\"lastUpdatedOn\":\"2022-04-06T10:13:32.954+0530\",\"contentEncoding\":\"gzip\",\"showSolutions\":\"No\"," + + "\"allowAnonymousAccess\":\"Yes\",\"identifier\":\"do_7777\"," + + "\"lastStatusChangedOn\":\"2022-04-06T10:13:32.896+0530\"," + "\"visibility\":\"Default\",\"showTimer\":\"No\"," + + "\"index\":2," + "\"languageCode\":[\"en\"],\"version\":1," + "\"versionKey\":\"1649220212954\",\"showFeedback\":\"No\"," + + "\"license\":\"CC BY " + "4.0\",\"depth\":2,\"compatibilityLevel\":4," + "\"name\":\"Q2\",\"status\":\"Live\"}]," + + "\"contentDisposition\":\"inline\"," + "\"lastUpdatedOn\":\"2022-04-06T12:51:53.591+0530\"," + + "\"contentEncoding\":\"gzip\",\"generateDIALCodes\":\"No\"," + "\"showSolutions\":\"No\"," + + "\"allowAnonymousAccess\":\"Yes\"," + "\"identifier\":\"do_3333\"," + + "\"lastStatusChangedOn\":\"2022-04-06T12:51:53.592+0530\",\"requiresSubmit\":\"No\"," + "\"visibility\":\"Parent\"," + + "\"showTimer\":\"No\",\"index\":1,\"setType\":\"materialised\",\"languageCode\":[\"en\"],\"version\":1," + "" + "" + + "\"versionKey\":\"1649229713592\",\"showFeedback\":\"No\",\"license\":\"CC BY 4.0\",\"depth\":1,\"name\":\"S1\"," + + "\"navigationMode\":\"non-linear\",\"allowBranching\":\"Yes\",\"shuffle\":true,\"status\":\"Draft\"}]}") + } + }) + node + } + + def getUpsertNodeBLWithBL(): Node = { + val node = getRootNodeWithBL("do_9876", "do_3333", false, false) + node.setExternalData(new util.HashMap[String, AnyRef]() { + { + put("hierarchy", "{\"identifier\":\"do_9876\",\"children\":[{\"parent\":\"do_9876\"," + "\"code\":\"S1\"," + + "\"allowSkip\":\"Yes\",\"containsUserData\":\"No\",\"channel\":\"{{channel_id}}\"," + + "\"branchingLogic\":{\"do_7777\":{\"preCondition\":{\"and\":[{\"eq\":[{\"type\":\"responseDeclaration" + "\"," + + "\"var\":\"do_5555.response1.value\"},\"0\"]}]},\"target\":[]," + "\"source\":[\"do_5555\"]}," + + "\"do_5555\":{\"preCondition\":{}," + "\"target\":[\"do_7777\"],\"source\":[]}},\"description\":\"Section 1\"," + + "\"language\":[\"English\"]," + "\"mimeType\":\"application/vnd" + ".sunbird.questionset\",\"showHints\":\"No\"," + + "\"createdOn\":\"2022-04-04T16:30:59.566+0530\"," + "\"objectType\":\"QuestionSet\"," + + "\"primaryCategory\":\"Observation\"," + "\"children\":[{\"parent\":\"do_3333\",\"code\":\"Q1\"," + + "\"channel\":\"{{channel_id}}\"," + "\"description\":\"Q1\",\"language\":[\"English\"],\"mimeType\":\"application/vnd" + + ".sunbird" + ".question\"," + "\"createdOn\":\"2022-04-04T16:30:59.539+0530\",\"objectType\":\"Question\"," + + "\"primaryCategory\":\"Slider\"," + "\"contentDisposition\":\"inline\"," + + "\"lastUpdatedOn\":\"2022-04-04T16:32:46.200+0530\",\"contentEncoding\":\"gzip\"," + "\"showSolutions\":\"No\"," + + "\"allowAnonymousAccess\":\"Yes\",\"identifier\":\"do_5555\"," + + "\"lastStatusChangedOn\":\"2022-03-29T15:37:42.837+0530\",\"visibility\":\"Parent\",\"showTimer\":\"No\",\"index\":1," + + "\"languageCode\":[\"en\"],\"version\":1,\"versionKey\":\"1649070166325\",\"showFeedback\":\"No\",\"license\":\"CC BY " + + "4.0\"," + "\"depth\":2,\"compatibilityLevel\":4,\"name\":\"Q1\",\"status\":\"Draft\"}," + "{\"parent\":\"do_3333\"," + + "\"code\":\"Q2\"," + "\"channel\":\"{{channel_id}}\",\"description\":\"Q2\"," + "\"language\":[\"English\"]," + + "\"mimeType\":\"application/vnd.sunbird" + "" + ".question\"," + "\"createdOn\":\"2022-03-29T15:37:42.852+0530\"," + + "\"objectType\":\"Question\",\"primaryCategory\":\"Slider\"," + "\"contentDisposition\":\"inline\"," + + "\"lastUpdatedOn\":\"2022-03-29T15:37:42.896+0530\",\"contentEncoding\":\"gzip\"," + "\"showSolutions\":\"No\"," + + "\"allowAnonymousAccess\":\"Yes\",\"identifier\":\"do_7777\"," + + "\"lastStatusChangedOn\":\"2022-03-29T15:37:42.852+0530\",\"visibility\":\"Default\",\"showTimer\":\"No\",\"index\":2," + + "\"languageCode\":[\"en\"],\"version\":1,\"versionKey\":\"1648548462896\",\"showFeedback\":\"No\",\"license\":\"CC BY " + + "" + "4.0\"," + "\"depth\":2,\"compatibilityLevel\":4,\"name\":\"Q2\",\"status\":\"Live\"}]," + + "\"contentDisposition\":\"inline\"," + "\"lastUpdatedOn\":\"2022-04-04T16:32:46.273+0530\",\"contentEncoding\":\"gzip\"," + + "\"generateDIALCodes\":\"No\"," + "\"showSolutions\":\"No\",\"allowAnonymousAccess\":\"Yes\",\"identifier\":\"do_3333\"," + + "" + "\"lastStatusChangedOn\":\"2022-03-29T15:37:42.872+0530\",\"requiresSubmit\":\"No\",\"visibility\":\"Parent\"," + + "\"showTimer\":\"No\",\"index\":1,\"setType\":\"materialised\",\"languageCode\":[\"en\"],\"version\":1," + + "\"versionKey\":\"1649070059566\",\"showFeedback\":\"No\",\"license\":\"CC BY 4.0\",\"depth\":1,\"name\":\"S1\"," + + "\"navigationMode\":\"non-linear\",\"allowBranching\":\"Yes\",\"shuffle\":true,\"status\":\"Draft\"}]}") + } + }) + node + } + + def getRootExternalPropsResponseBL(): Response = { + val response = getSuccessfulResponse() + response.put("hierarchy", "{\"code\":\"CopyQuestionSetv21\",\"allowSkip\":\"Yes\",\"containsUserData\":\"No\"," + + "\"channel\":\"{{channel_id}}\",\"language\":[\"English\"],\"showHints\":\"No\",\"mimeType\":\"application/vnd.sunbird" + "" + + "" + ".questionset\",\"createdOn\":\"2022-04-06T10:13:15.975+0530\",\"objectType\":\"QuestionSet\"," + + "\"primaryCategory\":\"Observation\",\"contentDisposition\":\"inline\",\"contentEncoding\":\"gzip\"," + + "\"lastUpdatedOn\":\"2022-04-06T10:16:05.263+0530\",\"generateDIALCodes\":\"No\",\"showSolutions\":\"No\"," + + "\"allowAnonymousAccess\":\"Yes\",\"identifier\":\"do_1234\"," + "\"lastStatusChangedOn\":\"2022-04-06T10:13:15.975+0530\"," + + "\"requiresSubmit\":\"No\",\"visibility\":\"Default\"," + "\"IL_SYS_NODE_TYPE\":\"DATA_NODE\",\"showTimer\":\"No\"," + + "\"childNodes\":[\"do_5555\"," + "\"do_2222\",\"do_7777\"],\"setType\":\"materialised\",\"version\":1,\"showFeedback\":\"No\"," + + "\"versionKey\":\"1649220365263\",\"license\":\"CC BY 4.0\",\"depth\":0,\"compatibilityLevel\":5," + + "\"IL_FUNC_OBJECT_TYPE\":\"QuestionSet\",\"allowBranching\":\"No\",\"navigationMode\":\"non-linear\"," + + "\"name\":\"CopyQuestionSetv21\",\"shuffle\":true,\"IL_UNIQUE_ID\":\"do_1234\",\"status\":\"Live\"," + + "\"children\":[{\"parent\":\"do_1234\",\"code\":\"S1\",\"allowSkip\":\"Yes\",\"containsUserData\":\"No\"," + + "\"channel\":\"{{channel_id}}\",\"branchingLogic\":{\"do_5555\":{\"target\":[\"do_7777\"]," + "\"preCondition\":{}," + + "\"source\":[]}," + "\"do_7777\":{\"target\":[]," + "\"preCondition\":{\"and\":[{\"eq\":[{\"var\":\"do_5555.response1.value\"," + + "\"type\":\"responseDeclaration\"}," + "\"0\"]}]},\"source\":[\"do_5555\"]}},\"language\":[\"English\"]," + + "\"mimeType\":\"application/vnd" + ".sunbird" + ".questionset\",\"showHints\":\"No\"," + + "\"createdOn\":\"2022-04-06T10:13:32.949+0530\"," + "\"objectType\":\"QuestionSet\"," + "\"primaryCategory\":\"Observation\"," + + "\"children\":[{\"parent\":\"do_2222\",\"code\":\"Q1\"," + "\"channel\":\"{{channel_id}}\"," + "\"language\":[\"English\"]," + + "\"mimeType\":\"application/vnd.sunbird.question\"," + "\"createdOn\":\"2022-04-06T10:13:32.859+0530\"," + + "\"objectType\":\"Question\",\"primaryCategory\":\"Slider\"," + "\"contentDisposition\":\"inline\"," + + "\"lastUpdatedOn\":\"2022-04-06T10:13:32.911+0530\",\"contentEncoding\":\"gzip\"," + "\"showSolutions\":\"No\"," + + "\"allowAnonymousAccess\":\"Yes\",\"identifier\":\"do_5555\"," + "\"lastStatusChangedOn\":\"2022-04-06T10:13:32.859+0530\"," + + "\"visibility\":\"Default\",\"showTimer\":\"No\",\"index\":1," + "\"languageCode\":[\"en\"],\"version\":1," + + "\"versionKey\":\"1649220212911\",\"showFeedback\":\"No\",\"license\":\"CC BY 4.0\"," + "\"depth\":2,\"compatibilityLevel\":4," + + "\"name\":\"Q1\",\"status\":\"Live\"},{\"parent\":\"do_2222\"," + "\"code\":\"Q2\"," + "\"channel\":\"{{channel_id}}\"," + + "\"language\":[\"English\"],\"mimeType\":\"application/vnd.sunbird.question\"," + + "\"createdOn\":\"2022-04-06T10:13:32.896+0530\"," + "\"objectType\":\"Question\",\"primaryCategory\":\"Slider\"," + + "\"contentDisposition\":\"inline\"," + "\"lastUpdatedOn\":\"2022-04-06T10:13:32.954+0530\",\"contentEncoding\":\"gzip\"," + + "\"showSolutions\":\"No\"," + "\"allowAnonymousAccess\":\"Yes\",\"identifier\":\"do_7777\"," + + "\"lastStatusChangedOn\":\"2022-04-06T10:13:32.896+0530\"," + "\"visibility\":\"Default\",\"showTimer\":\"No\",\"index\":2," + + "\"languageCode\":[\"en\"],\"version\":1," + "\"versionKey\":\"1649220212954\",\"showFeedback\":\"No\",\"license\":\"CC BY " + + "4.0\"," + "\"depth\":2,\"compatibilityLevel\":4," + "\"name\":\"Q2\",\"status\":\"Live\"}],\"contentDisposition\":\"inline\"," + + "\"lastUpdatedOn\":\"2022-04-06T10:16:05.061+0530\"," + "\"contentEncoding\":\"gzip\",\"generateDIALCodes\":\"No\"," + + "\"showSolutions\":\"No\",\"allowAnonymousAccess\":\"Yes\"," + "\"identifier\":\"do_2222\"," + + "\"lastStatusChangedOn\":\"2022-04-06T10:13:32.949+0530\",\"requiresSubmit\":\"No\"," + "\"visibility\":\"Parent\"," + + "\"showTimer\":\"No\",\"index\":1,\"setType\":\"materialised\",\"languageCode\":[\"en\"],\"version\":1," + "" + + "\"versionKey\":\"1649220212949\",\"showFeedback\":\"No\",\"license\":\"CC BY 4.0\",\"depth\":1,\"compatibilityLevel\":5," + + "\"name\":\"S1\",\"navigationMode\":\"non-linear\",\"allowBranching\":\"Yes\",\"shuffle\":true,\"status\":\"Live\"}]}") + response + } + + def getNewRootExternalPropsResponseBL(): Response = { + val response = getSuccessfulResponse() + response.put("hierarchy", "{\"identifier\":\"do_9876\",\"children\":[{\"parent\":\"do_9876\"," + + "\"code\":\"1911de43-48aa-4533-b93e-2e342e9f6ec7\",\"allowSkip\":\"Yes\",\"containsUserData\":\"No\"," + + "\"channel\":\"{{channel_id}}\",\"language\":[\"English\"],\"mimeType\":\"application/vnd.sunbird.questionset\"," + + "\"showHints\":\"No\",\"createdOn\":\"2022-04-06T14:10:31.187+0530\",\"objectType\":\"QuestionSet\"," + + "\"primaryCategory\":\"Observation\",\"children\":[{\"parent\":\"do_3333\",\"code\":\"Q1\"," + "\"channel\":\"{{channel_id}}\"," + + "\"language\":[\"English\"],\"mimeType\":\"application/vnd.sunbird.question\"," + + "\"createdOn\":\"2022-04-06T10:13:32.859+0530\"," + "\"objectType\":\"Question\",\"primaryCategory\":\"Slider\"," + + "\"contentDisposition\":\"inline\"," + "\"lastUpdatedOn\":\"2022-04-06T10:13:32.911+0530\",\"contentEncoding\":\"gzip\"," + + "\"showSolutions\":\"No\"," + "\"allowAnonymousAccess\":\"Yes\",\"identifier\":\"do_5555\"," + + "\"lastStatusChangedOn\":\"2022-04-06T10:13:32.859+0530\"," + "\"visibility\":\"Default\",\"showTimer\":\"No\",\"index\":1," + + "\"languageCode\":[\"en\"],\"version\":1," + "\"versionKey\":\"1649220212911\",\"showFeedback\":\"No\",\"license\":\"CC BY " + + "4.0\"," + "\"depth\":2,\"compatibilityLevel\":4," + "\"name\":\"Q1\",\"status\":\"Live\"},{\"parent\":\"do_3333\"," + + "\"code\":\"Q2\",\"channel\":\"{{channel_id}}\"," + "\"language\":[\"English\"],\"mimeType\":\"application/vnd.sunbird" + + ".question\"," + "\"createdOn\":\"2022-04-06T10:13:32.896+0530\"," + "\"objectType\":\"Question\"," + + "\"primaryCategory\":\"Slider\"," + "\"contentDisposition\":\"inline\"," + "\"lastUpdatedOn\":\"2022-04-06T10:13:32.954+0530\"," + + "\"contentEncoding\":\"gzip\"," + "\"showSolutions\":\"No\"," + "\"allowAnonymousAccess\":\"Yes\",\"identifier\":\"do_7777\"," + + "\"lastStatusChangedOn\":\"2022-04-06T10:13:32.896+0530\"," + "\"visibility\":\"Default\",\"showTimer\":\"No\",\"index\":2," + + "\"languageCode\":[\"en\"],\"version\":1," + "\"versionKey\":\"1649220212954\",\"showFeedback\":\"No\",\"license\":\"CC BY " + + "4.0\"," + "\"depth\":2,\"compatibilityLevel\":4," + "\"name\":\"Q2\",\"status\":\"Live\"}],\"contentDisposition\":\"inline\"," + + "\"lastUpdatedOn\":\"2022-04-06T14:10:31.185+0530\"," + "\"contentEncoding\":\"gzip\",\"generateDIALCodes\":\"No\"," + + "\"showSolutions\":\"No\",\"allowAnonymousAccess\":\"Yes\"," + "\"identifier\":\"do_3333\"," + + "\"lastStatusChangedOn\":\"2022-04-06T14:10:31.187+0530\",\"requiresSubmit\":\"No\"," + "\"visibility\":\"Parent\"," + + "\"showTimer\":\"No\",\"index\":1,\"setType\":\"materialised\",\"languageCode\":[\"en\"],\"version\":1," + "" + "" + + "\"versionKey\":\"1649234431187\",\"showFeedback\":\"No\",\"license\":\"CC BY 4.0\",\"depth\":1,\"name\":\"S1\"," + + "\"navigationMode\":\"non-linear\",\"allowBranching\":\"Yes\",\"shuffle\":true,\"status\":\"Draft\"}]}") + response + } + + def getResourceNotFoundResponse(): Response = { + val response = new Response + response.setVer("3.0") + val responseParams = new ResponseParams + responseParams.setStatus("failed") + response.setParams(responseParams) + response.setResponseCode(ResponseCode.RESOURCE_NOT_FOUND) + response + } } From be08e1f7db9b2db8c8f404b33009949b0a82f3ae Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Fri, 8 Apr 2022 21:39:56 +0530 Subject: [PATCH 015/490] SB-29495 schema changes for iGOT --- schemas/asset/1.0/schema.json | 12 +++++++ schemas/collection/1.0/schema.json | 12 +++++++ schemas/content/1.0/schema.json | 15 +++++++- schemas/question/1.0/schema.json | 16 ++++++++- schemas/questionset/1.0/schema.json | 55 +++++++++++++++++++++++++++++ 5 files changed, 108 insertions(+), 2 deletions(-) diff --git a/schemas/asset/1.0/schema.json b/schemas/asset/1.0/schema.json index 8aa9ad085..5a4dc078b 100644 --- a/schemas/asset/1.0/schema.json +++ b/schemas/asset/1.0/schema.json @@ -1249,6 +1249,18 @@ "items": { "type": "string" } + }, + "taxonomyPaths_v2": { + "type": "array", + "items": { + "type": "object" + } + }, + "competencies_v3": { + "type": "array", + "items": { + "type": "object" + } } } } \ No newline at end of file diff --git a/schemas/collection/1.0/schema.json b/schemas/collection/1.0/schema.json index 112e93841..2f897db5a 100644 --- a/schemas/collection/1.0/schema.json +++ b/schemas/collection/1.0/schema.json @@ -1286,6 +1286,18 @@ "items": { "type": "object" } + }, + "taxonomyPaths_v2": { + "type": "array", + "items": { + "type": "object" + } + }, + "competencies_v3": { + "type": "array", + "items": { + "type": "object" + } } } } diff --git a/schemas/content/1.0/schema.json b/schemas/content/1.0/schema.json index 73181a850..9a0728d6b 100644 --- a/schemas/content/1.0/schema.json +++ b/schemas/content/1.0/schema.json @@ -87,7 +87,8 @@ "audio/webm", "audio/x-wav", "audio/wav", - "application/json" + "application/json", + "application/quiz" ] }, "osId": { @@ -1395,6 +1396,18 @@ "items": { "type": "object" } + }, + "taxonomyPaths_v2": { + "type": "array", + "items": { + "type": "object" + } + }, + "competencies_v3": { + "type": "array", + "items": { + "type": "object" + } } } } diff --git a/schemas/question/1.0/schema.json b/schemas/question/1.0/schema.json index 6054a39e3..ae0c3e9d3 100644 --- a/schemas/question/1.0/schema.json +++ b/schemas/question/1.0/schema.json @@ -425,7 +425,10 @@ "enum": [ "MCQ", "FTB", - "SA" + "SA", + "MCQ-MCA", + "MCQ-SCA", + "MTF" ] }, "scoringMode": { @@ -580,6 +583,17 @@ }, "originData": { "type": "object" + }, + "choices": { + "type": "object", + "description": "Choices which needs to be used in MCQ / MTF type question" + }, + "rhsChoices": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Choices which needs to be used in RHS of MTF type question" } }, "additionalProperties": false diff --git a/schemas/questionset/1.0/schema.json b/schemas/questionset/1.0/schema.json index fcc691a25..96da2520d 100644 --- a/schemas/questionset/1.0/schema.json +++ b/schemas/questionset/1.0/schema.json @@ -656,6 +656,61 @@ }, "originData": { "type": "object" + }, + "trackable": { + "type": "object", + "properties": { + "enabled": { + "type": "string", + "enum": [ + "Yes", + "No" + ], + "default": "No" + }, + "autoBatch": { + "type": "string", + "enum": [ + "Yes", + "No" + ], + "default": "No" + } + }, + "default": { + "enabled": "No", + "autoBatch": "No" + }, + "additionalProperties": false + }, + "purpose": { + "type": "string" + }, + "scoreCutoffType": { + "type": "string", + "enum": [ + "AssessmentLevel", + "SectionLevel" + ], + "default": "AssessmentLevel" + }, + "subTitle": { + "type": "string" + }, + "minimumPassPercentage": { + "type": "number" + }, + "additionalKeywords": { + "type": "array", + "items": { + "type": "string" + } + }, + "additionalInstructions": { + "type": "string" + }, + "reviewStatus": { + "type": "string" } }, "additionalProperties": false From 4a62c5536a61890aecfef360ec38a5b6b70a6894 Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Mon, 11 Apr 2022 18:27:30 +0530 Subject: [PATCH 016/490] Using 30 sec as default value --- .../content-service/app/controllers/BaseController.scala | 2 +- content-api/content-service/conf/application.conf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/content-api/content-service/app/controllers/BaseController.scala b/content-api/content-service/app/controllers/BaseController.scala index 3b7125e19..4e9b059db 100644 --- a/content-api/content-service/app/controllers/BaseController.scala +++ b/content-api/content-service/app/controllers/BaseController.scala @@ -26,7 +26,7 @@ abstract class BaseController(protected val cc: ControllerComponents)(implicit e new util.HashMap[String, AnyRef]()).asInstanceOf[java.util.Map[String, AnyRef]] val mimeTypesToCheck = List("application/vnd.ekstep.h5p-archive", "application/vnd.ekstep.html-archive", "application/vnd.android.package-archive", "video/webm", "video/x-youtube", "video/mp4") - val actorTimeout: Long = Platform.getLong("actor.timeoutMillisec", 120000L) + val actorTimeout: Long = Platform.getLong("actor.timeoutMillisec", 30000L) def requestBody()(implicit request: Request[AnyContent]) = { val body = request.body.asJson.getOrElse("{}").toString diff --git a/content-api/content-service/conf/application.conf b/content-api/content-service/conf/application.conf index be2552755..0e0c9a150 100644 --- a/content-api/content-service/conf/application.conf +++ b/content-api/content-service/conf/application.conf @@ -738,4 +738,4 @@ collection { } #Request timeout -actor.timeoutMillisec = 120000 \ No newline at end of file +actor.timeoutMillisec = 30000 \ No newline at end of file From c6822575b6612416e400555dcadf9b0719e2c598 Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Tue, 12 Apr 2022 16:32:54 +0530 Subject: [PATCH 017/490] SB-29494 - Configuration param to check index file in Zip content --- content-api/content-service/conf/application.conf | 5 ++++- .../org/sunbird/mimetype/mgr/impl/HtmlMimeTypeMgrImpl.scala | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/content-api/content-service/conf/application.conf b/content-api/content-service/conf/application.conf index 0e0c9a150..95d7befe5 100644 --- a/content-api/content-service/conf/application.conf +++ b/content-api/content-service/conf/application.conf @@ -738,4 +738,7 @@ collection { } #Request timeout -actor.timeoutMillisec = 30000 \ No newline at end of file +actor.timeoutMillisec = 30000 + +#Index file validation +isIndexHtmlValidationRequired=false diff --git a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/HtmlMimeTypeMgrImpl.scala b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/HtmlMimeTypeMgrImpl.scala index b977d3683..34014ebf3 100644 --- a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/HtmlMimeTypeMgrImpl.scala +++ b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/HtmlMimeTypeMgrImpl.scala @@ -9,6 +9,7 @@ import org.sunbird.graph.OntologyEngineContext import org.sunbird.graph.dac.model.Node import org.sunbird.mimetype.mgr.{BaseMimeTypeManager, MimeTypeManager} import org.sunbird.telemetry.logger.TelemetryManager +import org.sunbird.common.Platform import scala.concurrent.{ExecutionContext, Future} @@ -16,7 +17,9 @@ class HtmlMimeTypeMgrImpl(implicit ss: StorageService) extends BaseMimeTypeManag override def upload(objectId: String, node: Node, uploadFile: File, filePath: Option[String], params: UploadParams)(implicit ec: ExecutionContext): Future[Map[String, AnyRef]] = { validateUploadRequest(objectId, node, uploadFile) - if (isValidPackageStructure(uploadFile, List[String]("index.html"))) { + val isIndexHtmlValidationRequired: Boolean = if (Platform.config.hasPath("isIndexHtmlValidationRequired")) Platform.config.getBoolean("isIndexHtmlValidationRequired") else true + val isValidateSuccess: Boolean = if (isIndexHtmlValidationRequired) isValidPackageStructure(uploadFile, List[String]("index.html")) else true + if (isValidateSuccess) { val urls = uploadArtifactToCloud(uploadFile, objectId, filePath) node.getMetadata.put("s3Key", urls(IDX_S3_KEY)) node.getMetadata.put("artifactUrl", urls(IDX_S3_URL)) From 3b5a4558dba6eb51ae9bb9a8b2bc6eed37abf138 Mon Sep 17 00:00:00 2001 From: joffinjoy <35325730+joffinjoy@users.noreply.github.com> Date: Wed, 13 Apr 2022 09:18:11 +0530 Subject: [PATCH 018/490] Issue #SB-29145 refactor: Optimized UpdateHierarchy Method & Modified BranchingLogic Copy Test-Case --- .../org/sunbird/managers/CopyManager.scala | 47 ++- .../sunbird/actors/QuestionSetActorTest.scala | 44 +-- .../scala/org/sunbird/actors/copyTrait.scala | 303 ++++++------------ 3 files changed, 130 insertions(+), 264 deletions(-) diff --git a/assessment-api/assessment-actors/src/main/scala/org/sunbird/managers/CopyManager.scala b/assessment-api/assessment-actors/src/main/scala/org/sunbird/managers/CopyManager.scala index 609acdc71..06656c105 100644 --- a/assessment-api/assessment-actors/src/main/scala/org/sunbird/managers/CopyManager.scala +++ b/assessment-api/assessment-actors/src/main/scala/org/sunbird/managers/CopyManager.scala @@ -178,7 +178,7 @@ object CopyManager { oldToNewIdMap } - def hierarchyRequestModifier(request: Request, nodeBLRecord: util.HashMap[String, AnyRef], identifiers: util.Map[String, String]): Unit = { + def hierarchyRequestModifier(request: Request, nodeBLRecord: util.HashMap[String, AnyRef], identifiers: util.Map[String, String]) = { val nodesModified: java.util.HashMap[String, AnyRef] = request.getRequest.get(HierarchyConstants.NODES_MODIFIED).asInstanceOf[java.util.HashMap[String, AnyRef]] val hierarchy: java.util.HashMap[String, AnyRef] = request.getRequest.get(HierarchyConstants.HIERARCHY).asInstanceOf[java.util.HashMap[String, AnyRef]] val oldToNewIdMap = generateOldToNewIdMap(nodeBLRecord, identifiers) @@ -211,37 +211,36 @@ object CopyManager { hierarchy.put(identifiers.get(id), nodeHierarchy) } }) + request } - def updateHierarchy(request: Request, node: Node, originNode: Node, originHierarchy: util.Map[String, AnyRef], copyType: String) - (implicit ec: ExecutionContext, oec: OntologyEngineContext): Future[Node] = { + def updateHierarchy(request: Request, node: Node, originNode: Node, originHierarchy: util.Map[String, AnyRef], copyType: String)(implicit ec: ExecutionContext, oec: OntologyEngineContext): Future[Node] = { prepareHierarchyRequest(originHierarchy, originNode, node, copyType, request).map(req => { val hierarchyRequest = new Request(request) hierarchyRequest.putAll(req) - val nodesModified: java.util.HashMap[String, AnyRef] = hierarchyRequest.getRequest.get(HierarchyConstants.NODES_MODIFIED) - .asInstanceOf[java.util.HashMap[String, AnyRef]] + val nodesModified: java.util.HashMap[String, AnyRef] = hierarchyRequest.getRequest.get(HierarchyConstants.NODES_MODIFIED).asInstanceOf[java.util.HashMap[String, AnyRef]] val nodeBLRecord = generateNodeBLRecord(nodesModified) - val newUpdateRequest = JsonUtils.deserialize(ScalaJsonUtils.serialize(hierarchyRequest), classOf[Request]) - UpdateHierarchyManager.updateHierarchy(hierarchyRequest).map(response => { - if (!ResponseHandler.checkError(response)) { - val identifiers = response.getResult.get(AssessmentConstants.IDENTIFIERS).asInstanceOf[util.Map[String, String]] - hierarchyRequestModifier(newUpdateRequest, nodeBLRecord, identifiers) - UpdateHierarchyManager.updateHierarchy(newUpdateRequest).map(response_ => { - if (!ResponseHandler.checkError(response_)) { - node - } else { - TelemetryManager.info(s"Update Hierarchy Failed For Copy Question Set Having Identifier: ${node.getIdentifier} | Response " + - s"is " + s": " + response) + val originalRequest = JsonUtils.deserialize(ScalaJsonUtils.serialize(hierarchyRequest), classOf[Request]) + val BLExists = nodeBLRecord.exists(BLRecord => BLRecord._2.asInstanceOf[util.HashMap[String, AnyRef]].get(AssessmentConstants.CONTAINS_BL) == true) + val (firstUpdateRequest, secondUpdateRequest) = if (BLExists) (hierarchyRequest, JsonUtils.deserialize(ScalaJsonUtils.serialize(hierarchyRequest), classOf[Request])) else (originalRequest, new Request()) + UpdateHierarchyManager.updateHierarchy(firstUpdateRequest).map(response => { + if (!ResponseHandler.checkError(response)) response + else { + TelemetryManager.info(s"Update Hierarchy Failed For Copy Question Set Having Identifier: ${node.getIdentifier} | Response is: " + response) + throw new ServerException("ERR_QUESTIONSET_COPY", "Something Went Wrong, Please Try Again") + } + }).map(response => { + if (BLExists) { + hierarchyRequestModifier(secondUpdateRequest, nodeBLRecord, response.getResult.get(AssessmentConstants.IDENTIFIERS).asInstanceOf[util.Map[String, String]]) + UpdateHierarchyManager.updateHierarchy(secondUpdateRequest).map(response_ => { + if (!ResponseHandler.checkError(response_)) node + else { + TelemetryManager.info(s"Update Hierarchy Failed For Copy Question Set Having Identifier: ${node.getIdentifier} | Response is: " + response) throw new ServerException("ERR_QUESTIONSET_COPY", "Something Went Wrong, Please Try Again") } }) - node - } else { - TelemetryManager.info(s"Update Hierarchy Failed For Copy Question Set Having Identifier: ${node.getIdentifier} | Response is " - + s": " + response) - throw new ServerException("ERR_QUESTIONSET_COPY", "Something Went Wrong, Please Try Again") - } - }) + } else Future(node) + }).flatMap(f => f) }).flatMap(f => f) } @@ -300,7 +299,7 @@ object CopyManager { put(AssessmentConstants.METADATA, cleanUpCopiedData(new util.HashMap[String, AnyRef]() { { putAll(child) - put("copyOf", child.getOrDefault(AssessmentConstants.IDENTIFIER,"")) + put(AssessmentConstants.COPY_OF, child.getOrDefault(AssessmentConstants.IDENTIFIER,"")) put(AssessmentConstants.CHILDREN, new util.ArrayList()) internalHierarchyProps.map(key => remove(key)) } diff --git a/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/QuestionSetActorTest.scala b/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/QuestionSetActorTest.scala index 9d6019aea..721171d96 100644 --- a/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/QuestionSetActorTest.scala +++ b/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/QuestionSetActorTest.scala @@ -11,7 +11,8 @@ import org.sunbird.graph.nodes.DataNode.getRelationMap import org.sunbird.graph.utils.ScalaJsonUtils import org.sunbird.graph.{GraphService, OntologyEngineContext} import org.sunbird.kafka.client.KafkaClient -import org.sunbird.utils.JavaJsonUtils +import org.sunbird.managers.CopyManager +import org.sunbird.utils.{AssessmentConstants, JavaJsonUtils} import java.util import scala.collection.JavaConversions._ @@ -594,37 +595,16 @@ class QuestionSetActorTest extends BaseSpec with MockFactory with copyTrait { assert("failed".equals(response.getParams.getStatus)) } - it should "return success response for 'copyQuestionSet' (Branching Logic Copy)" in { - implicit val oec: OntologyEngineContext = mock[OntologyEngineContext] - val graphDB = mock[GraphService] - (oec.graphService _).expects().returns(graphDB).anyNumberOfTimes() - val nodes: util.List[Node] = getCategoryNode() - (graphDB.getNodeByUniqueIds(_: String, _: SearchCriteria)).expects(*, *).returns(Future(nodes)).anyNumberOfTimes() - (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, "do_1234", *, *).returns(Future(getRootNodeWithBL("do_1234", "do_2222", true, true))).anyNumberOfTimes() - (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, "do_9876", *, *).returns(Future(getRootNodeWithBL("do_9876", "do_3333", false, false))).anyNumberOfTimes() - (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, "do_9876.img", *, *).returns(Future(getRootNodeWithBL("do_9876", "do_3333", false, false))).anyNumberOfTimes() - (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, "do_5555", *, *).returns(Future(getQuestionNodeBL("do_5555"))).anyNumberOfTimes() - (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, "do_7777", *, *).returns(Future(getQuestionNodeBL("do_7777"))).anyNumberOfTimes() - (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, List("objectMetadata")).returns(Future(getSuccessfulResponse)).anyNumberOfTimes() - (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, List("instructions", "outcomeDeclaration")).returns(Future(getSuccessfulResponse)).anyNumberOfTimes() - (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, List("solutions", "body", "editorState", "interactions", "hints", "responseDeclaration", "media", "answer", "instructions")).returns(Future(getResourceNotFoundResponse)).anyNumberOfTimes() - (graphDB.addNode(_: String, _: Node)).expects(*, *).returns(Future(getRootNodeWithBL("do_9876", "do_3333", false, false))).anyNumberOfTimes - (graphDB.saveExternalProps(_: Request)).expects(*).returns(Future(getSuccessfulResponse)).anyNumberOfTimes - (graphDB.updateExternalProps(_: Request)).expects(*).returns(Future(getSuccessfulResponse)).anyNumberOfTimes - (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, *, *, *).returns(Future(getRootNodeWithBL("do_9876", "do_3333", true, true))).anyNumberOfTimes() - inSequence { - (graphDB.upsertNode(_: String, _: Node, _: Request)).expects(*, *, *).returns(Future(getUpsertNodeBLWithoutBL)) - (graphDB.upsertNode(_: String, _: Node, _: Request)).expects(*, *, *).returns(Future(getUpsertNodeBLWithBL)).anyNumberOfTimes - } - inSequence { - (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, List("hierarchy")).returns(Future(getRootExternalPropsResponseBL)) - (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, List("hierarchy")).returns(Future(getNewRootExternalPropsResponseBL)).anyNumberOfTimes - } - val request = getQuestionSetCopyRequest() - request.putAll(mapAsJavaMap(Map("identifier" -> "do_1234", "mode" -> "", "copyType" -> "deep"))) - request.setOperation("copyQuestionSet") - val response = callActor(request, Props(new QuestionSetActor())) - assert("successful".equals(response.getParams.getStatus)) + it should "return expected result for 'generateNodeBLRecord'" in { + val result = CopyManager.generateNodeBLRecord(generateNodesModified("afa2bef1-b5db-45d9-b0d7-aeea757906c3", true)) + assert(result == generateNodeBLRecord) + } + + it should "return expected result for 'hierarchyRequestModifier'" in { + val result = CopyManager.hierarchyRequestModifier(generateUpdateRequest(false, "afa2bef1-b5db-45d9-b0d7-aeea757906c3"), generateNodeBLRecord(), generateIdentifiers()) + val expectedResult = generateUpdateRequest(true, "do_11351201604857856013") + assert(result.getRequest.get(AssessmentConstants.NODES_MODIFIED) == expectedResult.getRequest.get(AssessmentConstants.NODES_MODIFIED)) + assert(result.getRequest.get(AssessmentConstants.HIERARCHY) == expectedResult.getRequest.get(AssessmentConstants.HIERARCHY)) } private def getQuestionSetRequest(): Request = { diff --git a/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/copyTrait.scala b/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/copyTrait.scala index 3bf221a77..8ee744cd7 100644 --- a/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/copyTrait.scala +++ b/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/copyTrait.scala @@ -4,9 +4,12 @@ import org.mortbay.util.StringUtil import org.sunbird.common.dto.{Request, Response, ResponseParams} import org.sunbird.graph.dac.model.Node import org.sunbird.utils.AssessmentConstants -import org.sunbird.common.exception.{ResponseCode} +import org.sunbird.common.exception.ResponseCode import java.util +import scala.collection.JavaConversions.mapAsJavaMap +import scala.collection.JavaConverters.asJavaIterableConverter +import scala.collection.mutable trait copyTrait { @@ -262,232 +265,116 @@ trait copyTrait { node } - def getRootNodeWithBL(rootId: String, sectionId: String, addBranchingLogic: Boolean, withChildren: Boolean): Node = { - val node = getNode("QuestionSet", rootId, "Observation", AssessmentConstants.VISIBILITY_DEFAULT, "ExistingRootNode", 1234, "Live") - if (withChildren) { - val section = getNode("QuestionSet", sectionId, "Observation", AssessmentConstants.VISIBILITY_DEFAULT, "Section_1", 1234, "Live") - val children = new util.ArrayList[util.Map[String, AnyRef]]() - children.add(getNode("Question", "do_5555", "Slider", AssessmentConstants.VISIBILITY_DEFAULT, "Question1", 1234, "Live") - .getMetadata) - children.add(getNode("Question", "do_7777", "Slider", AssessmentConstants.VISIBILITY_DEFAULT, "Question2", 1234, "Live") - .getMetadata) - if (addBranchingLogic) { - section.getMetadata.put("branchingLogic", new util.HashMap[String, AnyRef]() { - { - put("do_5555", new util.HashMap[String, AnyRef]() { - put("target", new util.ArrayList[String]() { - { - add("do_7777") - } - }) - put("preCondition", new util.HashMap[String, AnyRef]()) - put("source", new util.ArrayList[String]()) - }) - put("do_7777", new util.HashMap[String, AnyRef]() { - put("target", new util.ArrayList[String]()) - put("preCondition", new util.HashMap[String, AnyRef]() { - { - put("and", new util.ArrayList[util.HashMap[String, AnyRef]]() { - add(new util.HashMap[String, AnyRef]() { - put("eq", new util.ArrayList[AnyRef]() { - { - add(new util.HashMap[String, String]() { - put("var", "do_5555" + ".response1.value") - put("type", "responseDeclaration") - }) - add("0") - } + private def generateStaticBranchingLogic(): util.HashMap[String, AnyRef] = { + new util.HashMap[String, AnyRef]() { + { + put("do_11351041198373273619", new util.HashMap[String, AnyRef]() { + put("target", new util.ArrayList[String]() { + { + add("do_113510411984044032111") + } + }) + put("preCondition", new util.HashMap[String, AnyRef]()) + put("source", new util.ArrayList[String]()) + }) + put("do_113510411984044032111", new util.HashMap[String, AnyRef]() { + put("target", new util.ArrayList[String]()) + put("preCondition", new util.HashMap[String, AnyRef]() { + { + put("and", new util.ArrayList[util.HashMap[String, AnyRef]]() { + add(new util.HashMap[String, AnyRef]() { + put("eq", new util.ArrayList[AnyRef]() { + { + add(new util.HashMap[String, String]() { + put("var", "do_11351041198373273619" + ".response1.value") + put("type", "responseDeclaration") }) - }) + add("0") + } }) - } - }) - put("source", new util.ArrayList[String]() { - { - add("do_5555") - } + }) }) - }) - } + } + }) + put("source", new util.ArrayList[String]() { + { + add("do_11351041198373273619") + } + }) }) } - node.getMetadata.put("childNodes", new util.ArrayList[String]() { - { - add(sectionId) - add("do_5555") - add("do_7777") - } - }) - section.getMetadata.put("children", children) - node.getMetadata.put("children", new util.ArrayList[util.Map[String, AnyRef]]() { - { - add(section.getMetadata) - } - }) } - node - } - - def getQuestionNodeBL(identifier: String): Node = { - val node = getNode("Question", identifier, "Slider", AssessmentConstants.VISIBILITY_DEFAULT, identifier, 1234, "Live") - node } - def getUpsertNodeBLWithoutBL(): Node = { - val node = getRootNodeWithBL("do_9876", "do_3333", false, false) - node.setExternalData(new util.HashMap[String, AnyRef]() { + def generateNodesModified(identifier: String, withBranchingLogic: Boolean): util.HashMap[String, AnyRef] = { + val nodesModified = new util.HashMap[String, AnyRef]() + nodesModified.put(identifier, new util.HashMap[String, AnyRef]() { { - put("hierarchy", "{\"identifier\":\"do_9876\",\"children\":[{\"parent\":\"do_9876\"," + - "\"code\":\"9f0332ad-c3e3-4803-b673-50174aff24e3\",\"allowSkip\":\"Yes\",\"containsUserData\":\"No\"," + - "\"channel\":\"{{channel_id}}\",\"language\":[\"English\"],\"mimeType\":\"application/vnd.sunbird.questionset\"," + - "\"showHints\":\"No\",\"createdOn\":\"2022-04-06T12:51:53.592+0530\",\"objectType\":\"QuestionSet\"," + - "\"primaryCategory\":\"Observation\",\"children\":[{\"parent\":\"do_3333\",\"code\":\"Q1\"," + - "\"channel\":\"{{channel_id}}\"," + "\"language\":[\"English\"],\"mimeType\":\"application/vnd.sunbird.question\"," + - "\"createdOn\":\"2022-04-06T10:13:32.859+0530\"," + "\"objectType\":\"Question\",\"primaryCategory\":\"Slider\"," + - "\"contentDisposition\":\"inline\"," + "\"lastUpdatedOn\":\"2022-04-06T10:13:32.911+0530\",\"contentEncoding\":\"gzip\"," + - "" + "\"showSolutions\":\"No\"," + "\"allowAnonymousAccess\":\"Yes\",\"identifier\":\"do_5555\"," + - "\"lastStatusChangedOn\":\"2022-04-06T10:13:32.859+0530\"," + "\"visibility\":\"Default\",\"showTimer\":\"No\"," + - "\"index\":1," + "\"languageCode\":[\"en\"],\"version\":1," + "\"versionKey\":\"1649220212911\",\"showFeedback\":\"No\"," + - "\"license\":\"CC BY " + "4.0\",\"depth\":2,\"compatibilityLevel\":4," + "\"name\":\"Q1\",\"status\":\"Live\"}," + - "{\"parent\":\"do_3333\",\"code\":\"Q2\",\"channel\":\"{{channel_id}}\"," + "\"language\":[\"English\"]," + - "\"mimeType\":\"application/vnd.sunbird.question\",\"createdOn\":\"2022-04-06T10:13:32.896+0530\"," + - "\"objectType\":\"Question\",\"primaryCategory\":\"Slider\",\"contentDisposition\":\"inline\"," + - "\"lastUpdatedOn\":\"2022-04-06T10:13:32.954+0530\",\"contentEncoding\":\"gzip\",\"showSolutions\":\"No\"," + - "\"allowAnonymousAccess\":\"Yes\",\"identifier\":\"do_7777\"," + - "\"lastStatusChangedOn\":\"2022-04-06T10:13:32.896+0530\"," + "\"visibility\":\"Default\",\"showTimer\":\"No\"," + - "\"index\":2," + "\"languageCode\":[\"en\"],\"version\":1," + "\"versionKey\":\"1649220212954\",\"showFeedback\":\"No\"," + - "\"license\":\"CC BY " + "4.0\",\"depth\":2,\"compatibilityLevel\":4," + "\"name\":\"Q2\",\"status\":\"Live\"}]," + - "\"contentDisposition\":\"inline\"," + "\"lastUpdatedOn\":\"2022-04-06T12:51:53.591+0530\"," + - "\"contentEncoding\":\"gzip\",\"generateDIALCodes\":\"No\"," + "\"showSolutions\":\"No\"," + - "\"allowAnonymousAccess\":\"Yes\"," + "\"identifier\":\"do_3333\"," + - "\"lastStatusChangedOn\":\"2022-04-06T12:51:53.592+0530\",\"requiresSubmit\":\"No\"," + "\"visibility\":\"Parent\"," + - "\"showTimer\":\"No\",\"index\":1,\"setType\":\"materialised\",\"languageCode\":[\"en\"],\"version\":1," + "" + "" + - "\"versionKey\":\"1649229713592\",\"showFeedback\":\"No\",\"license\":\"CC BY 4.0\",\"depth\":1,\"name\":\"S1\"," + - "\"navigationMode\":\"non-linear\",\"allowBranching\":\"Yes\",\"shuffle\":true,\"status\":\"Draft\"}]}") + put("setDefaultValue", false.asInstanceOf[AnyRef]) + put("metadata", new util.HashMap[String, AnyRef]() { + { + putAll((getNode("QuestionSet", "do_5678", "Observation", AssessmentConstants.VISIBILITY_PARENT, "Observation", 0, + "Draft").getMetadata)) + put("copyOf", "do_113510411984478208113") + if (withBranchingLogic) put("branchingLogic", generateStaticBranchingLogic) + } + }) + put("root", false.asInstanceOf[AnyRef]) + put("isNew", (!withBranchingLogic).asInstanceOf[AnyRef]) + put("objectType", "QuestionSet") } }) - node + nodesModified } - def getUpsertNodeBLWithBL(): Node = { - val node = getRootNodeWithBL("do_9876", "do_3333", false, false) - node.setExternalData(new util.HashMap[String, AnyRef]() { + def generateNodeBLRecord(): util.HashMap[String, AnyRef] = { + val nodeBLRecord = new util.HashMap[String, AnyRef]() + nodeBLRecord.put("afa2bef1-b5db-45d9-b0d7-aeea757906c3", new util.HashMap[String, AnyRef]() { { - put("hierarchy", "{\"identifier\":\"do_9876\",\"children\":[{\"parent\":\"do_9876\"," + "\"code\":\"S1\"," + - "\"allowSkip\":\"Yes\",\"containsUserData\":\"No\",\"channel\":\"{{channel_id}}\"," + - "\"branchingLogic\":{\"do_7777\":{\"preCondition\":{\"and\":[{\"eq\":[{\"type\":\"responseDeclaration" + "\"," + - "\"var\":\"do_5555.response1.value\"},\"0\"]}]},\"target\":[]," + "\"source\":[\"do_5555\"]}," + - "\"do_5555\":{\"preCondition\":{}," + "\"target\":[\"do_7777\"],\"source\":[]}},\"description\":\"Section 1\"," + - "\"language\":[\"English\"]," + "\"mimeType\":\"application/vnd" + ".sunbird.questionset\",\"showHints\":\"No\"," + - "\"createdOn\":\"2022-04-04T16:30:59.566+0530\"," + "\"objectType\":\"QuestionSet\"," + - "\"primaryCategory\":\"Observation\"," + "\"children\":[{\"parent\":\"do_3333\",\"code\":\"Q1\"," + - "\"channel\":\"{{channel_id}}\"," + "\"description\":\"Q1\",\"language\":[\"English\"],\"mimeType\":\"application/vnd" + - ".sunbird" + ".question\"," + "\"createdOn\":\"2022-04-04T16:30:59.539+0530\",\"objectType\":\"Question\"," + - "\"primaryCategory\":\"Slider\"," + "\"contentDisposition\":\"inline\"," + - "\"lastUpdatedOn\":\"2022-04-04T16:32:46.200+0530\",\"contentEncoding\":\"gzip\"," + "\"showSolutions\":\"No\"," + - "\"allowAnonymousAccess\":\"Yes\",\"identifier\":\"do_5555\"," + - "\"lastStatusChangedOn\":\"2022-03-29T15:37:42.837+0530\",\"visibility\":\"Parent\",\"showTimer\":\"No\",\"index\":1," + - "\"languageCode\":[\"en\"],\"version\":1,\"versionKey\":\"1649070166325\",\"showFeedback\":\"No\",\"license\":\"CC BY " - + "4.0\"," + "\"depth\":2,\"compatibilityLevel\":4,\"name\":\"Q1\",\"status\":\"Draft\"}," + "{\"parent\":\"do_3333\"," + - "\"code\":\"Q2\"," + "\"channel\":\"{{channel_id}}\",\"description\":\"Q2\"," + "\"language\":[\"English\"]," + - "\"mimeType\":\"application/vnd.sunbird" + "" + ".question\"," + "\"createdOn\":\"2022-03-29T15:37:42.852+0530\"," + - "\"objectType\":\"Question\",\"primaryCategory\":\"Slider\"," + "\"contentDisposition\":\"inline\"," + - "\"lastUpdatedOn\":\"2022-03-29T15:37:42.896+0530\",\"contentEncoding\":\"gzip\"," + "\"showSolutions\":\"No\"," + - "\"allowAnonymousAccess\":\"Yes\",\"identifier\":\"do_7777\"," + - "\"lastStatusChangedOn\":\"2022-03-29T15:37:42.852+0530\",\"visibility\":\"Default\",\"showTimer\":\"No\",\"index\":2," - + "\"languageCode\":[\"en\"],\"version\":1,\"versionKey\":\"1648548462896\",\"showFeedback\":\"No\",\"license\":\"CC BY " + - "" + "4.0\"," + "\"depth\":2,\"compatibilityLevel\":4,\"name\":\"Q2\",\"status\":\"Live\"}]," + - "\"contentDisposition\":\"inline\"," + "\"lastUpdatedOn\":\"2022-04-04T16:32:46.273+0530\",\"contentEncoding\":\"gzip\"," + - "\"generateDIALCodes\":\"No\"," + "\"showSolutions\":\"No\",\"allowAnonymousAccess\":\"Yes\",\"identifier\":\"do_3333\"," + - "" + "\"lastStatusChangedOn\":\"2022-03-29T15:37:42.872+0530\",\"requiresSubmit\":\"No\",\"visibility\":\"Parent\"," + - "\"showTimer\":\"No\",\"index\":1,\"setType\":\"materialised\",\"languageCode\":[\"en\"],\"version\":1," + - "\"versionKey\":\"1649070059566\",\"showFeedback\":\"No\",\"license\":\"CC BY 4.0\",\"depth\":1,\"name\":\"S1\"," + - "\"navigationMode\":\"non-linear\",\"allowBranching\":\"Yes\",\"shuffle\":true,\"status\":\"Draft\"}]}") + put("containsBL", true.asInstanceOf[AnyRef]) + put("branchingLogic", generateStaticBranchingLogic()) + put("copyOf", "do_113510411984478208113") } }) - node - } - - def getRootExternalPropsResponseBL(): Response = { - val response = getSuccessfulResponse() - response.put("hierarchy", "{\"code\":\"CopyQuestionSetv21\",\"allowSkip\":\"Yes\",\"containsUserData\":\"No\"," + - "\"channel\":\"{{channel_id}}\",\"language\":[\"English\"],\"showHints\":\"No\",\"mimeType\":\"application/vnd.sunbird" + "" + - "" + ".questionset\",\"createdOn\":\"2022-04-06T10:13:15.975+0530\",\"objectType\":\"QuestionSet\"," + - "\"primaryCategory\":\"Observation\",\"contentDisposition\":\"inline\",\"contentEncoding\":\"gzip\"," + - "\"lastUpdatedOn\":\"2022-04-06T10:16:05.263+0530\",\"generateDIALCodes\":\"No\",\"showSolutions\":\"No\"," + - "\"allowAnonymousAccess\":\"Yes\",\"identifier\":\"do_1234\"," + "\"lastStatusChangedOn\":\"2022-04-06T10:13:15.975+0530\"," + - "\"requiresSubmit\":\"No\",\"visibility\":\"Default\"," + "\"IL_SYS_NODE_TYPE\":\"DATA_NODE\",\"showTimer\":\"No\"," + - "\"childNodes\":[\"do_5555\"," + "\"do_2222\",\"do_7777\"],\"setType\":\"materialised\",\"version\":1,\"showFeedback\":\"No\"," - + "\"versionKey\":\"1649220365263\",\"license\":\"CC BY 4.0\",\"depth\":0,\"compatibilityLevel\":5," + - "\"IL_FUNC_OBJECT_TYPE\":\"QuestionSet\",\"allowBranching\":\"No\",\"navigationMode\":\"non-linear\"," + - "\"name\":\"CopyQuestionSetv21\",\"shuffle\":true,\"IL_UNIQUE_ID\":\"do_1234\",\"status\":\"Live\"," + - "\"children\":[{\"parent\":\"do_1234\",\"code\":\"S1\",\"allowSkip\":\"Yes\",\"containsUserData\":\"No\"," + - "\"channel\":\"{{channel_id}}\",\"branchingLogic\":{\"do_5555\":{\"target\":[\"do_7777\"]," + "\"preCondition\":{}," + - "\"source\":[]}," + "\"do_7777\":{\"target\":[]," + "\"preCondition\":{\"and\":[{\"eq\":[{\"var\":\"do_5555.response1.value\"," - + "\"type\":\"responseDeclaration\"}," + "\"0\"]}]},\"source\":[\"do_5555\"]}},\"language\":[\"English\"]," + - "\"mimeType\":\"application/vnd" + ".sunbird" + ".questionset\",\"showHints\":\"No\"," + - "\"createdOn\":\"2022-04-06T10:13:32.949+0530\"," + "\"objectType\":\"QuestionSet\"," + "\"primaryCategory\":\"Observation\"," + - "\"children\":[{\"parent\":\"do_2222\",\"code\":\"Q1\"," + "\"channel\":\"{{channel_id}}\"," + "\"language\":[\"English\"]," + - "\"mimeType\":\"application/vnd.sunbird.question\"," + "\"createdOn\":\"2022-04-06T10:13:32.859+0530\"," + - "\"objectType\":\"Question\",\"primaryCategory\":\"Slider\"," + "\"contentDisposition\":\"inline\"," + - "\"lastUpdatedOn\":\"2022-04-06T10:13:32.911+0530\",\"contentEncoding\":\"gzip\"," + "\"showSolutions\":\"No\"," + - "\"allowAnonymousAccess\":\"Yes\",\"identifier\":\"do_5555\"," + "\"lastStatusChangedOn\":\"2022-04-06T10:13:32.859+0530\"," + - "\"visibility\":\"Default\",\"showTimer\":\"No\",\"index\":1," + "\"languageCode\":[\"en\"],\"version\":1," + - "\"versionKey\":\"1649220212911\",\"showFeedback\":\"No\",\"license\":\"CC BY 4.0\"," + "\"depth\":2,\"compatibilityLevel\":4," - + "\"name\":\"Q1\",\"status\":\"Live\"},{\"parent\":\"do_2222\"," + "\"code\":\"Q2\"," + "\"channel\":\"{{channel_id}}\"," + - "\"language\":[\"English\"],\"mimeType\":\"application/vnd.sunbird.question\"," + - "\"createdOn\":\"2022-04-06T10:13:32.896+0530\"," + "\"objectType\":\"Question\",\"primaryCategory\":\"Slider\"," + - "\"contentDisposition\":\"inline\"," + "\"lastUpdatedOn\":\"2022-04-06T10:13:32.954+0530\",\"contentEncoding\":\"gzip\"," + - "\"showSolutions\":\"No\"," + "\"allowAnonymousAccess\":\"Yes\",\"identifier\":\"do_7777\"," + - "\"lastStatusChangedOn\":\"2022-04-06T10:13:32.896+0530\"," + "\"visibility\":\"Default\",\"showTimer\":\"No\",\"index\":2," + - "\"languageCode\":[\"en\"],\"version\":1," + "\"versionKey\":\"1649220212954\",\"showFeedback\":\"No\",\"license\":\"CC BY " + - "4.0\"," + "\"depth\":2,\"compatibilityLevel\":4," + "\"name\":\"Q2\",\"status\":\"Live\"}],\"contentDisposition\":\"inline\"," - + "\"lastUpdatedOn\":\"2022-04-06T10:16:05.061+0530\"," + "\"contentEncoding\":\"gzip\",\"generateDIALCodes\":\"No\"," + - "\"showSolutions\":\"No\",\"allowAnonymousAccess\":\"Yes\"," + "\"identifier\":\"do_2222\"," + - "\"lastStatusChangedOn\":\"2022-04-06T10:13:32.949+0530\",\"requiresSubmit\":\"No\"," + "\"visibility\":\"Parent\"," + - "\"showTimer\":\"No\",\"index\":1,\"setType\":\"materialised\",\"languageCode\":[\"en\"],\"version\":1," + "" + - "\"versionKey\":\"1649220212949\",\"showFeedback\":\"No\",\"license\":\"CC BY 4.0\",\"depth\":1,\"compatibilityLevel\":5," + - "\"name\":\"S1\",\"navigationMode\":\"non-linear\",\"allowBranching\":\"Yes\",\"shuffle\":true,\"status\":\"Live\"}]}") - response + nodeBLRecord } - def getNewRootExternalPropsResponseBL(): Response = { - val response = getSuccessfulResponse() - response.put("hierarchy", "{\"identifier\":\"do_9876\",\"children\":[{\"parent\":\"do_9876\"," + - "\"code\":\"1911de43-48aa-4533-b93e-2e342e9f6ec7\",\"allowSkip\":\"Yes\",\"containsUserData\":\"No\"," + - "\"channel\":\"{{channel_id}}\",\"language\":[\"English\"],\"mimeType\":\"application/vnd.sunbird.questionset\"," + - "\"showHints\":\"No\",\"createdOn\":\"2022-04-06T14:10:31.187+0530\",\"objectType\":\"QuestionSet\"," + - "\"primaryCategory\":\"Observation\",\"children\":[{\"parent\":\"do_3333\",\"code\":\"Q1\"," + "\"channel\":\"{{channel_id}}\"," - + "\"language\":[\"English\"],\"mimeType\":\"application/vnd.sunbird.question\"," + - "\"createdOn\":\"2022-04-06T10:13:32.859+0530\"," + "\"objectType\":\"Question\",\"primaryCategory\":\"Slider\"," + - "\"contentDisposition\":\"inline\"," + "\"lastUpdatedOn\":\"2022-04-06T10:13:32.911+0530\",\"contentEncoding\":\"gzip\"," + - "\"showSolutions\":\"No\"," + "\"allowAnonymousAccess\":\"Yes\",\"identifier\":\"do_5555\"," + - "\"lastStatusChangedOn\":\"2022-04-06T10:13:32.859+0530\"," + "\"visibility\":\"Default\",\"showTimer\":\"No\",\"index\":1," + - "\"languageCode\":[\"en\"],\"version\":1," + "\"versionKey\":\"1649220212911\",\"showFeedback\":\"No\",\"license\":\"CC BY " + - "4.0\"," + "\"depth\":2,\"compatibilityLevel\":4," + "\"name\":\"Q1\",\"status\":\"Live\"},{\"parent\":\"do_3333\"," + - "\"code\":\"Q2\",\"channel\":\"{{channel_id}}\"," + "\"language\":[\"English\"],\"mimeType\":\"application/vnd.sunbird" + - ".question\"," + "\"createdOn\":\"2022-04-06T10:13:32.896+0530\"," + "\"objectType\":\"Question\"," + - "\"primaryCategory\":\"Slider\"," + "\"contentDisposition\":\"inline\"," + "\"lastUpdatedOn\":\"2022-04-06T10:13:32.954+0530\"," + - "\"contentEncoding\":\"gzip\"," + "\"showSolutions\":\"No\"," + "\"allowAnonymousAccess\":\"Yes\",\"identifier\":\"do_7777\"," + - "\"lastStatusChangedOn\":\"2022-04-06T10:13:32.896+0530\"," + "\"visibility\":\"Default\",\"showTimer\":\"No\",\"index\":2," + - "\"languageCode\":[\"en\"],\"version\":1," + "\"versionKey\":\"1649220212954\",\"showFeedback\":\"No\",\"license\":\"CC BY " + - "4.0\"," + "\"depth\":2,\"compatibilityLevel\":4," + "\"name\":\"Q2\",\"status\":\"Live\"}],\"contentDisposition\":\"inline\"," - + "\"lastUpdatedOn\":\"2022-04-06T14:10:31.185+0530\"," + "\"contentEncoding\":\"gzip\",\"generateDIALCodes\":\"No\"," + - "\"showSolutions\":\"No\",\"allowAnonymousAccess\":\"Yes\"," + "\"identifier\":\"do_3333\"," + - "\"lastStatusChangedOn\":\"2022-04-06T14:10:31.187+0530\",\"requiresSubmit\":\"No\"," + "\"visibility\":\"Parent\"," + - "\"showTimer\":\"No\",\"index\":1,\"setType\":\"materialised\",\"languageCode\":[\"en\"],\"version\":1," + "" + "" + - "\"versionKey\":\"1649234431187\",\"showFeedback\":\"No\",\"license\":\"CC BY 4.0\",\"depth\":1,\"name\":\"S1\"," + - "\"navigationMode\":\"non-linear\",\"allowBranching\":\"Yes\",\"shuffle\":true,\"status\":\"Draft\"}]}") - response + def generateIdentifiers(): util.Map[String, String] = { + val idMap: mutable.Map[String, String] = mutable.Map() + idMap += ("afa2bef1-b5db-45d9-b0d7-aeea757906c3" -> "do_11351201604857856013") + mapAsJavaMap(idMap) } - def getResourceNotFoundResponse(): Response = { - val response = new Response - response.setVer("3.0") - val responseParams = new ResponseParams - responseParams.setStatus("failed") - response.setParams(responseParams) - response.setResponseCode(ResponseCode.RESOURCE_NOT_FOUND) - response + def generateUpdateRequest(withBranchingLogic: Boolean, identifier: String): Request = { + val request = getQuestionSetRequest() + request.put(AssessmentConstants.NODES_MODIFIED, generateNodesModified(identifier, withBranchingLogic)) + request.put(AssessmentConstants.HIERARCHY, new util.HashMap[String, AnyRef]() { + { + put("do_11351201402236108811", new util.HashMap[String, AnyRef]() { + { + put(AssessmentConstants.CHILDREN, new util.ArrayList[String]() { + { + add(identifier) + } + }) + put(AssessmentConstants.PRIMARY_CATEGORY, "Observation") + put(AssessmentConstants.ROOT, true.asInstanceOf[AnyRef]) + } + }) + put(identifier, new util.HashMap[String, AnyRef]() { + { + put(AssessmentConstants.CHILDREN, new util.ArrayList[String]() { + { + add("do_11351041198373273619") + add("do_113510411984044032111") + } + }) + put(AssessmentConstants.PRIMARY_CATEGORY, "Observation") + put(AssessmentConstants.ROOT, false.asInstanceOf[AnyRef]) + } + }) + } + }) + request } } From 4fb9a50817b44b693b0c8ce4fcfaab1943bd840e Mon Sep 17 00:00:00 2001 From: Anil Gupta Date: Wed, 13 Apr 2022 10:19:37 +0530 Subject: [PATCH 019/490] Issue #SB-28950 chore: Updated the elasticsearch version from 6.2.4 to 6.8.22 --- pom.xml | 2 ++ search-api/search-core/pom.xml | 4 ++-- .../search/client/ElasticSearchUtil.java | 17 +++++++---------- vmsetup.sh | 4 ++-- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/pom.xml b/pom.xml index f3f14193a..e9dfed5b6 100644 --- a/pom.xml +++ b/pom.xml @@ -16,6 +16,8 @@ 2.11.12 3.0.8 2.9.8 + 11 + 11 platform-core diff --git a/search-api/search-core/pom.xml b/search-api/search-core/pom.xml index a68a641e4..f33636eb5 100644 --- a/search-api/search-core/pom.xml +++ b/search-api/search-core/pom.xml @@ -67,12 +67,12 @@ org.elasticsearch elasticsearch - 6.2.4 + 6.8.22 org.elasticsearch.client elasticsearch-rest-high-level-client - 6.2.4 + 6.8.22 junit diff --git a/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java b/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java index cc031b378..932eedd3d 100644 --- a/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java +++ b/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java @@ -11,14 +11,13 @@ import org.apache.http.HttpHost; import org.apache.http.client.config.RequestConfig; import org.apache.http.util.EntityUtils; -import org.elasticsearch.client.RestClientBuilder; +import org.elasticsearch.action.support.master.AcknowledgedResponse; +import org.elasticsearch.client.*; import org.sunbird.search.util.SearchConstants; import org.sunbird.telemetry.logger.TelemetryManager; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.admin.indices.create.CreateIndexRequest; -import org.elasticsearch.action.admin.indices.create.CreateIndexResponse; import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest; -import org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse; import org.elasticsearch.action.bulk.BulkRequest; import org.elasticsearch.action.bulk.BulkResponse; import org.elasticsearch.action.delete.DeleteRequest; @@ -34,9 +33,6 @@ import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.update.UpdateRequest; import org.elasticsearch.action.update.UpdateResponse; -import org.elasticsearch.client.Response; -import org.elasticsearch.client.RestClient; -import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.query.BoolQueryBuilder; @@ -170,9 +166,10 @@ public static boolean addIndex(String indexName, String documentType, String set createRequest.settings(Settings.builder().loadFromSource(settings, XContentType.JSON)); if (StringUtils.isNotBlank(documentType) && StringUtils.isNotBlank(mappings)) createRequest.mapping(documentType, mappings, XContentType.JSON); - CreateIndexResponse createIndexResponse = client.indices().create(createRequest); +// RequestOptions.Builder options = RequestOptions.DEFAULT.toBuilder(); +// CreateIndexResponse createIndexResponse = client.indices().create(createRequest); - response = createIndexResponse.isAcknowledged(); + response = client.indices().create(createRequest).isAcknowledged(); } return response; } @@ -228,8 +225,8 @@ public static void deleteDocumentsByQuery(QueryBuilder query, String indexName, TelemetryManager.log("Deleted Documents by Query" + EntityUtils.toString(response.getEntity())); } - public static void deleteIndex(String indexName) throws InterruptedException, ExecutionException, IOException { - DeleteIndexResponse response = getClient(indexName).indices().delete(new DeleteIndexRequest(indexName)); + public static void deleteIndex(String indexName) throws IOException { + AcknowledgedResponse response = getClient(indexName).indices().delete(new DeleteIndexRequest(indexName)); esClient.remove(indexName); TelemetryManager.log("Deleted Index" + indexName + " : " + response.isAcknowledged()); } diff --git a/vmsetup.sh b/vmsetup.sh index ef8a0094c..83b01344c 100644 --- a/vmsetup.sh +++ b/vmsetup.sh @@ -1,8 +1,8 @@ #!/bin/bash sudo apt update sudo apt install redis-server -y -curl -O https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.3.2.deb -sudo dpkg -i elasticsearch-6.3.2.deb +curl -O https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.8.22.deb +sudo dpkg -i elasticsearch-6.8.22.deb sudo service elasticsearch start sudo service elasticsearch status # This should go to the test cases - Start From 5b7686a3b3cf9aee5615ab37ed4fa1935ae275fd Mon Sep 17 00:00:00 2001 From: Anil Gupta Date: Wed, 13 Apr 2022 10:59:04 +0530 Subject: [PATCH 020/490] Issue #SB-28950 chore: Removed unused dependency. --- search-api/search-core/pom.xml | 5 ----- .../sunbird/search/client/ElasticSearchUtil.java | 14 +++++++------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/search-api/search-core/pom.xml b/search-api/search-core/pom.xml index f33636eb5..fbdf374ff 100644 --- a/search-api/search-core/pom.xml +++ b/search-api/search-core/pom.xml @@ -64,11 +64,6 @@ 2.4 jdk15 - - org.elasticsearch - elasticsearch - 6.8.22 - org.elasticsearch.client elasticsearch-rest-high-level-client diff --git a/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java b/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java index 932eedd3d..2b15206a4 100644 --- a/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java +++ b/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java @@ -11,6 +11,7 @@ import org.apache.http.HttpHost; import org.apache.http.client.config.RequestConfig; import org.apache.http.util.EntityUtils; +import org.elasticsearch.action.admin.indices.create.CreateIndexResponse; import org.elasticsearch.action.support.master.AcknowledgedResponse; import org.elasticsearch.client.*; import org.sunbird.search.util.SearchConstants; @@ -166,10 +167,9 @@ public static boolean addIndex(String indexName, String documentType, String set createRequest.settings(Settings.builder().loadFromSource(settings, XContentType.JSON)); if (StringUtils.isNotBlank(documentType) && StringUtils.isNotBlank(mappings)) createRequest.mapping(documentType, mappings, XContentType.JSON); -// RequestOptions.Builder options = RequestOptions.DEFAULT.toBuilder(); -// CreateIndexResponse createIndexResponse = client.indices().create(createRequest); + CreateIndexResponse createIndexResponse = client.indices().create(createRequest, RequestOptions.DEFAULT); - response = client.indices().create(createRequest).isAcknowledged(); + response = createIndexResponse.isAcknowledged(); } return response; } @@ -178,7 +178,7 @@ public static void addDocumentWithId(String indexName, String documentType, Stri try { Map doc = mapper.readValue(document, new TypeReference>() {}); Map updatedDoc = checkDocStringLength(doc); - IndexResponse response = getClient(indexName).index(new IndexRequest(indexName, documentType, documentId).source(updatedDoc)); + IndexResponse response = getClient(indexName).index(new IndexRequest(indexName, documentType, documentId).source(updatedDoc), RequestOptions.DEFAULT); TelemetryManager.log("Added " + response.getId() + " to index " + response.getIndex()); } catch (IOException e) { TelemetryManager.error("Error while adding document to index :" + indexName, e); @@ -189,7 +189,7 @@ public static void addDocument(String indexName, String documentType, String doc try { Map doc = mapper.readValue(document, new TypeReference>() {}); Map updatedDoc = checkDocStringLength(doc); - IndexResponse response = getClient(indexName).index(new IndexRequest(indexName, documentType).source(updatedDoc)); + IndexResponse response = getClient(indexName).index(new IndexRequest(indexName, documentType).source(updatedDoc), RequestOptions.DEFAULT); TelemetryManager.log("Added " + response.getId() + " to index " + response.getIndex()); } catch (IOException e) { TelemetryManager.error("Error while adding document to index :" + indexName, e); @@ -213,7 +213,7 @@ public static void updateDocument(String indexName, String documentType, String public static void deleteDocument(String indexName, String documentType, String documentId) throws IOException { - DeleteResponse response = getClient(indexName).delete(new DeleteRequest(indexName, documentType, documentId)); + DeleteResponse response = getClient(indexName).delete(new DeleteRequest(indexName, documentType, documentId), RequestOptions.DEFAULT); TelemetryManager.log("Deleted " + response.getId() + " to index " + response.getIndex()); } @@ -226,7 +226,7 @@ public static void deleteDocumentsByQuery(QueryBuilder query, String indexName, } public static void deleteIndex(String indexName) throws IOException { - AcknowledgedResponse response = getClient(indexName).indices().delete(new DeleteIndexRequest(indexName)); + AcknowledgedResponse response = getClient(indexName).indices().delete(new DeleteIndexRequest(indexName), RequestOptions.DEFAULT); esClient.remove(indexName); TelemetryManager.log("Deleted Index" + indexName + " : " + response.isAcknowledged()); } From 2b569770ea3f442a51f656e1bec019377d8a3549 Mon Sep 17 00:00:00 2001 From: Anil Gupta Date: Wed, 13 Apr 2022 11:45:41 +0530 Subject: [PATCH 021/490] Issue #SB-28950 chore: Removed unused dependency. --- pom.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/pom.xml b/pom.xml index e9dfed5b6..f3f14193a 100644 --- a/pom.xml +++ b/pom.xml @@ -16,8 +16,6 @@ 2.11.12 3.0.8 2.9.8 - 11 - 11 platform-core From 8a9cb74e0b10ec7b959bdca3f352a83f4559443f Mon Sep 17 00:00:00 2001 From: Anil Gupta Date: Wed, 13 Apr 2022 12:18:07 +0530 Subject: [PATCH 022/490] Issue #SB-28950 chore: Fixed the Sonar code smells. --- .../search/client/ElasticSearchUtil.java | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java b/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java index 2b15206a4..cb9c4c086 100644 --- a/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java +++ b/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java @@ -148,7 +148,7 @@ public String getTimeZone() { public static boolean isIndexExists(String indexName) { Response response; try { - response = getClient(indexName).getLowLevelClient().performRequest("HEAD", "/" + indexName); + response = getClient(indexName).getLowLevelClient().performRequest(new Request("HEAD", "/" + indexName)); return (200 == response.getStatusLine().getStatusCode()); } catch (IOException e) { return false; @@ -203,7 +203,7 @@ public static void updateDocument(String indexName, String documentType, String Map updatedDoc = checkDocStringLength(doc); IndexRequest indexRequest = new IndexRequest(indexName, documentType, documentId).source(updatedDoc); UpdateRequest request = new UpdateRequest().index(indexName).type(documentType).id(documentId).doc(updatedDoc).upsert(indexRequest); - UpdateResponse response = getClient(indexName).update(request); + UpdateResponse response = getClient(indexName).update(request, RequestOptions.DEFAULT); TelemetryManager.log("Updated " + response.getId() + " to index " + response.getIndex()); } catch (IOException e) { TelemetryManager.error("Error while updating document to index :" + indexName, e); @@ -219,8 +219,8 @@ public static void deleteDocument(String indexName, String documentType, String public static void deleteDocumentsByQuery(QueryBuilder query, String indexName, String indexType) throws IOException { - Response response = getClient(indexName).getLowLevelClient().performRequest("POST", - indexName + "/_delete_by_query" + query); + Response response = getClient(indexName).getLowLevelClient().performRequest(new Request("POST", + indexName + "/_delete_by_query" + query)); TelemetryManager.log("Deleted Documents by Query" + EntityUtils.toString(response.getEntity())); } @@ -233,7 +233,7 @@ public static void deleteIndex(String indexName) throws IOException { public static String getDocumentAsStringById(String indexName, String documentType, String documentId) throws IOException { - GetResponse response = getClient(indexName).get(new GetRequest(indexName, documentType, documentId)); + GetResponse response = getClient(indexName).get(new GetRequest(indexName, documentType, documentId), RequestOptions.DEFAULT); return response.getSourceAsString(); } @@ -242,7 +242,7 @@ public static List getMultiDocumentAsStringByIdList(String indexName, St List finalResult = new ArrayList(); MultiGetRequest request = new MultiGetRequest(); documentIdList.forEach(docId -> request.add(indexName, documentType, docId)); - MultiGetResponse multiGetItemResponses = getClient(indexName).multiGet(request); + MultiGetResponse multiGetItemResponses = getClient(indexName).mget(request, RequestOptions.DEFAULT); for (MultiGetItemResponse itemResponse : multiGetItemResponses) { GetResponse response = itemResponse.getResponse(); if (response.isExists()) { @@ -265,7 +265,7 @@ public static void bulkIndexWithIndexId(String indexName, String documentType, M request.add(new IndexRequest(indexName, documentType, key) .source((Map) jsonObjects.get(key))); if (count % BATCH_SIZE == 0 || (count % BATCH_SIZE < BATCH_SIZE && count == jsonObjects.size())) { - BulkResponse bulkResponse = client.bulk(request); + BulkResponse bulkResponse = client.bulk(request, RequestOptions.DEFAULT); if (bulkResponse.hasFailures()) { TelemetryManager .log("Failures in Elasticsearch bulkIndex : " + bulkResponse.buildFailureMessage()); @@ -290,7 +290,7 @@ public static void bulkIndexWithAutoGenerateIndexId(String indexName, String doc count++; request.add(new IndexRequest(indexName, documentType).source(json)); if (count % BATCH_SIZE == 0 || (count % BATCH_SIZE < BATCH_SIZE && count == jsonObjects.size())) { - BulkResponse bulkResponse = client.bulk(request); + BulkResponse bulkResponse = client.bulk(request, RequestOptions.DEFAULT); if (bulkResponse.hasFailures()) { TelemetryManager .log("Failures in Elasticsearch bulkIndex : " + bulkResponse.buildFailureMessage()); @@ -432,7 +432,7 @@ public static SearchResponse search(Map matchCriterias, Map search(String indexName, SearchSourceBuilder searchSourceBuilder) @@ -440,7 +440,7 @@ public static Future search(String indexName, SearchSourceBuilde TelemetryManager.log("searching in ES index: " + indexName); Promise promise = Futures.promise(); getClient(indexName).searchAsync(new SearchRequest().indices(indexName).source(searchSourceBuilder), - new ActionListener() { + RequestOptions.DEFAULT, new ActionListener() { @Override public void onResponse(SearchResponse response) { @@ -457,7 +457,7 @@ public void onFailure(Exception e) { public static int count(String indexName, SearchSourceBuilder searchSourceBuilder) throws IOException { SearchResponse response = getClient(indexName) - .search(new SearchRequest().indices(indexName).source(searchSourceBuilder)); + .search(new SearchRequest().indices(indexName).source(searchSourceBuilder), RequestOptions.DEFAULT); return (int) response.getHits().getTotalHits(); } @@ -688,7 +688,7 @@ public static void bulkDeleteDocumentById(String indexName, String documentType, count++; request.add(new DeleteRequest(indexName, documentType, documentId)); if (count % BATCH_SIZE == 0 || (count % BATCH_SIZE < BATCH_SIZE && count == identifiers.size())) { - BulkResponse bulkResponse = getClient(indexName).bulk(request); + BulkResponse bulkResponse = getClient(indexName).bulk(request, RequestOptions.DEFAULT); List failedIds = Arrays.stream(bulkResponse.getItems()).filter( itemResp -> !StringUtils.equals(itemResp.getResponse().getResult().getLowercase(),"deleted") ).map(r -> r.getResponse().getId()).collect(Collectors.toList()); From 8f3165fdbbb20ae6c50682aaffd76927f023d27f Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Wed, 13 Apr 2022 14:29:24 +0530 Subject: [PATCH 023/490] Issue #SB-28950 feat: Elasticsearch 6.8.22 upgrade for microservices --- search-api/search-core/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/search-api/search-core/pom.xml b/search-api/search-core/pom.xml index a68a641e4..f33636eb5 100644 --- a/search-api/search-core/pom.xml +++ b/search-api/search-core/pom.xml @@ -67,12 +67,12 @@ org.elasticsearch elasticsearch - 6.2.4 + 6.8.22 org.elasticsearch.client elasticsearch-rest-high-level-client - 6.2.4 + 6.8.22 junit From d7037be2c6d3cfffe11edcf722183fd36cdecbda Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Wed, 13 Apr 2022 14:36:58 +0530 Subject: [PATCH 024/490] Issue #SB-28950 feat: Elasticsearch 6.8.22 upgrade for microservices --- .../java/org/sunbird/search/client/ElasticSearchUtil.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java b/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java index cc031b378..a43ab09b9 100644 --- a/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java +++ b/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java @@ -11,14 +11,14 @@ import org.apache.http.HttpHost; import org.apache.http.client.config.RequestConfig; import org.apache.http.util.EntityUtils; +import org.elasticsearch.action.support.master.AcknowledgedResponse; import org.elasticsearch.client.RestClientBuilder; +import org.elasticsearch.client.indices.CreateIndexResponse; import org.sunbird.search.util.SearchConstants; import org.sunbird.telemetry.logger.TelemetryManager; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.admin.indices.create.CreateIndexRequest; -import org.elasticsearch.action.admin.indices.create.CreateIndexResponse; import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest; -import org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse; import org.elasticsearch.action.bulk.BulkRequest; import org.elasticsearch.action.bulk.BulkResponse; import org.elasticsearch.action.delete.DeleteRequest; @@ -229,7 +229,7 @@ public static void deleteDocumentsByQuery(QueryBuilder query, String indexName, } public static void deleteIndex(String indexName) throws InterruptedException, ExecutionException, IOException { - DeleteIndexResponse response = getClient(indexName).indices().delete(new DeleteIndexRequest(indexName)); + AcknowledgedResponse response = getClient(indexName).indices().delete(new DeleteIndexRequest(indexName)); esClient.remove(indexName); TelemetryManager.log("Deleted Index" + indexName + " : " + response.isAcknowledged()); } From 26bd671e0b3f93638f7fee051053e320177ff26f Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Wed, 13 Apr 2022 15:00:32 +0530 Subject: [PATCH 025/490] Issue #SB-28950 feat: Elasticsearch 6.8.22 upgrade for microservices --- vmsetup.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vmsetup.sh b/vmsetup.sh index ef8a0094c..83b01344c 100644 --- a/vmsetup.sh +++ b/vmsetup.sh @@ -1,8 +1,8 @@ #!/bin/bash sudo apt update sudo apt install redis-server -y -curl -O https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.3.2.deb -sudo dpkg -i elasticsearch-6.3.2.deb +curl -O https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.8.22.deb +sudo dpkg -i elasticsearch-6.8.22.deb sudo service elasticsearch start sudo service elasticsearch status # This should go to the test cases - Start From 2e40989c7ff7cb461ff7c1f256d62b6a845a9eaf Mon Sep 17 00:00:00 2001 From: Kumar Gauraw Date: Wed, 13 Apr 2022 16:01:45 +0530 Subject: [PATCH 026/490] Issue #SB-29568 fix: code changes to retain headers in context --- .../assessment-service/app/controllers/BaseController.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assessment-api/assessment-service/app/controllers/BaseController.scala b/assessment-api/assessment-service/app/controllers/BaseController.scala index a3b47974b..d388c4a31 100644 --- a/assessment-api/assessment-service/app/controllers/BaseController.scala +++ b/assessment-api/assessment-service/app/controllers/BaseController.scala @@ -68,6 +68,6 @@ abstract class BaseController(protected val cc: ControllerComponents)(implicit e put("schemaName", schemaName) }}; request.setObjectType(objectType); - request.setContext(contextMap) + request.getContext().putAll(contextMap) } } From 12dc8dbae58c9683a591259e6fc9388f6138359a Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Thu, 14 Apr 2022 10:22:41 +0530 Subject: [PATCH 027/490] Updated property as per review comment --- content-api/content-service/conf/application.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content-api/content-service/conf/application.conf b/content-api/content-service/conf/application.conf index 95d7befe5..635d21da7 100644 --- a/content-api/content-service/conf/application.conf +++ b/content-api/content-service/conf/application.conf @@ -741,4 +741,4 @@ collection { actor.timeoutMillisec = 30000 #Index file validation -isIndexHtmlValidationRequired=false +isIndexHtmlValidationRequired=true From ee96056459edbcbb9fe5bd5f56d63bd6c856da33 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Thu, 21 Apr 2022 10:47:30 +0530 Subject: [PATCH 028/490] Issue #SB-19655 feat: Collection DIAL link API refactor from LP to KP --- .../org/sunbird/content/dial/DIALErrors.scala | 2 + .../sunbird/content/dial/DIALManager.scala | 132 +++++++++++++++--- .../src/test/resources/application.conf | 1 + .../content-service/conf/application.conf | 1 + 4 files changed, 120 insertions(+), 16 deletions(-) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALErrors.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALErrors.scala index 8d70cfc7c..53fa49388 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALErrors.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALErrors.scala @@ -5,6 +5,7 @@ object DIALErrors { //Error Codes val ERR_DIALCODE_LINK_REQUEST: String = "ERR_DIALCODE_LINK_REQUEST" val ERR_DIALCODE_LINK: String = "ERR_DIALCODE_LINK" + val ERR_DUPLICATE_DIAL_CODES: String = "ERR_DUPLICATE_DIAL_CODES" //Error Messages val ERR_INVALID_REQ_MSG: String = "Invalid Request! Please Provide Valid Request." @@ -13,4 +14,5 @@ object DIALErrors { val ERR_DIAL_NOT_FOUND_MSG: String = "DIAL Code Not Found With Id(s): " val ERR_CONTENT_NOT_FOUND_MSG: String = "Content Not Found With Id(s): " val ERR_SERVER_ERROR_MSG: String = "Something Went Wrong While Processing Your Request. Please Try Again After Sometime!" + val ERR_DUPLICATE_DIAL_CODES_MSG: String = "QR Code should not be linked to multiple contents. Please validate: " } diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala index 487ac475c..06d047c25 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala @@ -3,21 +3,25 @@ package org.sunbird.content.dial import org.apache.commons.lang3.StringUtils import org.sunbird.common.Platform import org.sunbird.common.dto.{Request, Response, ResponseHandler} -import org.sunbird.common.exception.{ClientException, ErrorCodes, ResourceNotFoundException, ResponseCode, ServerException} +import org.sunbird.common.exception._ +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.utils.ScalaJsonUtils +import org.sunbird.managers.HierarchyManager + import java.util -import scala.collection.immutable.HashMap import scala.collection.JavaConverters._ +import scala.collection.immutable.{HashMap, Map} import scala.concurrent.{ExecutionContext, Future} object DIALManager { - val DIAL_SEARCH_API_URL = Platform.config.getString("dial_service.api.base_url") + "/dialcode/v3/search" - val DIAL_API_AUTH_KEY = "Bearer " + Platform.config.getString("dial_service.api.auth_key") - val PASSPORT_KEY = Platform.config.getString("graph.passport.key.base") + val DIAL_SEARCH_API_URL: String = Platform.config.getString("dial_service.api.base_url") + Platform.config.getString("dial_service.api.search") + val DIAL_API_AUTH_KEY: String = "Bearer " + Platform.config.getString("dial_service.api.auth_key") + val PASSPORT_KEY: String = Platform.config.getString("graph.passport.key.base") def link(request: Request)(implicit oec: OntologyEngineContext, ec: ExecutionContext): Future[Response] = { val linkType: String = request.getContext.getOrDefault(DIALConstants.LINK_TYPE, DIALConstants.CONTENT).asInstanceOf[String] @@ -52,13 +56,13 @@ object DIALManager { def validateAndGetRequestMap(channelId: String, requestList: List[Map[String, List[String]]])(implicit oec:OntologyEngineContext): Map[String, List[String]] = { var reqMap = HashMap[String, List[String]]() requestList.foreach(req => { - val contents: List[String] = req.get(DIALConstants.IDENTIFIER).get - val dialcodes: List[String] = req.get(DIALConstants.DIALCODE).get + val contents: List[String] = req(DIALConstants.IDENTIFIER) + val dialcodes: List[String] = req(DIALConstants.DIALCODE) validateReqStructure(dialcodes, contents) contents.foreach(id => reqMap += (id -> dialcodes)) }) if (Platform.getBoolean("content.link_dialcode.validation", true)) { - val dials = requestList.collect { case m if m.get(DIALConstants.DIALCODE).nonEmpty => m.get(DIALConstants.DIALCODE).get }.flatten + val dials = requestList.collect { case m if m.contains(DIALConstants.DIALCODE) => m(DIALConstants.DIALCODE) }.flatten validateDialCodes(channelId, dials) } reqMap @@ -73,7 +77,7 @@ object DIALManager { } def validateDialCodes(channelId: String, dialcodes: List[String])(implicit oec: OntologyEngineContext): Boolean = { - if (!dialcodes.isEmpty) { + if (dialcodes.nonEmpty) { val reqMap = new util.HashMap[String, AnyRef]() {{ put(DIALConstants.REQUEST, new util.HashMap[String, AnyRef]() {{ put(DIALConstants.SEARCH, new util.HashMap[String, AnyRef]() {{ @@ -81,7 +85,8 @@ object DIALManager { }}) }}) }} - val headerParam = HashMap[String, String](DIALConstants.X_CHANNEL_ID -> channelId, DIALConstants.AUTHORIZATION -> DIAL_API_AUTH_KEY).asJava + val headerParam = new util.HashMap[String, String]{put(DIALConstants.X_CHANNEL_ID, channelId); put(DIALConstants.AUTHORIZATION, DIAL_API_AUTH_KEY);} + val searchResponse = oec.httpUtil.post(DIAL_SEARCH_API_URL, reqMap, headerParam) if (searchResponse.getResponseCode.toString == "OK") { val result = searchResponse.getResult @@ -101,7 +106,7 @@ object DIALManager { validateContents(requestMap, reqContext).map(result => { val futureList: List[Future[Node]] = requestMap.filter(x => !result.contains(x._1)).map(map => { val updateReqMap = new util.HashMap[String, AnyRef]() {{ - val dials: util.List[String] = if (!map._2.isEmpty) map._2.asJava else new util.ArrayList[String]() + val dials: util.List[String] = if (map._2.nonEmpty) map._2.asJava else new util.ArrayList[String]() put(DIALConstants.DIALCODES, dials) put(DIALConstants.VERSION_KEY, PASSPORT_KEY) }} @@ -116,11 +121,62 @@ object DIALManager { }).flatMap(f => f) } - //TODO: Complete the implementation - def linkCollection(objectId: String, requestMap: Map[String, List[String]], getContext: util.Map[String, AnyRef])(implicit oec: OntologyEngineContext, ec: ExecutionContext): Future[Response] = { - Future { - ResponseHandler.OK() - } + def linkCollection(objectId: String, requestMap: Map[String, List[String]], reqContext: util.Map[String, AnyRef])(implicit oec: OntologyEngineContext, ec: ExecutionContext): Future[Response] = { + val request = new Request() + request.setContext(reqContext) + request.getContext.put(ContentConstants.SCHEMA_NAME, ContentConstants.COLLECTION_SCHEMA_NAME) + request.getContext.put(ContentConstants.VERSION, ContentConstants.SCHEMA_VERSION) + request.put(ContentConstants.ROOT_ID, objectId) + request.put(ContentConstants.MODE, "edit") + + val req = new Request(request) + req.put("identifier", request.get("rootId").asInstanceOf[String]) + req.put("mode", request.get("mode").asInstanceOf[String]) + DataNode.read(req).flatMap(rootNode => { + HierarchyManager.getHierarchy(request).flatMap(getHierarchyResponse => { + val collectionHierarchy = getHierarchyResponse.getResult.getOrDefault(ContentConstants.CONTENT, new java.util.HashMap[String, AnyRef]()).asInstanceOf[java.util.Map[String, AnyRef]] + val childrenHierarchy = collectionHierarchy.get("children").asInstanceOf[util.List[util.Map[String, AnyRef]]] + val updatedChildrenHierarchy = updateChildrenHierarchy(childrenHierarchy, requestMap) + val childrenDIALMap = getChildrenDIALMap(updatedChildrenHierarchy, requestMap) + val consolidatedUnitDIALMap = if (!requestMap.contains(objectId)) childrenDIALMap else childrenDIALMap ++ Map(objectId -> requestMap(objectId)) + + val duplicateDIALCodes: Map[String, Set[String]] = validateDuplicateDIALCodes(consolidatedUnitDIALMap.filter(rec => rec._2.asInstanceOf[List[String]].nonEmpty)) + if (duplicateDIALCodes.nonEmpty) + throw new ClientException(DIALErrors.ERR_DUPLICATE_DIAL_CODES, DIALErrors.ERR_DUPLICATE_DIAL_CODES_MSG + duplicateDIALCodes) + + val updatedHierarchy = new java.util.HashMap[String, AnyRef]() + updatedHierarchy.put("identifier", objectId) + updatedHierarchy.put("children", updatedChildrenHierarchy.asJava) + + val hierarchyReq = new Request(request) + hierarchyReq.put("hierarchy", ScalaJsonUtils.serialize(updatedHierarchy)) + hierarchyReq.put("identifier", rootNode.getIdentifier) + oec.graphService.saveExternalProps(hierarchyReq).flatMap(rec => if(requestMap.contains(objectId)) { + val updateReq = new Request(request) + updateReq.put("identifier", rootNode.getIdentifier) + val rootNodeMetadata = rootNode.getMetadata + rootNodeMetadata.remove("discussionForum") + rootNodeMetadata.remove("credentials") + rootNodeMetadata.remove("trackable") + + if(rootNodeMetadata.containsKey("dialcodes")) + rootNodeMetadata.remove("dialcodes") + + if(requestMap(objectId).isEmpty) + updateReq.put(DIALConstants.DIALCODES, null) + else + updateReq.put(DIALConstants.DIALCODES, requestMap(objectId).toArray[String]) + + updateReq.getRequest.putAll(rootNodeMetadata) + + DataNode.update(updateReq).flatMap(response => { + getResponseCollectionLink(requestMap, consolidatedUnitDIALMap.keySet.toList, requestMap.keySet.diff(consolidatedUnitDIALMap.keySet).toList) + }) + } else { + getResponseCollectionLink(requestMap, consolidatedUnitDIALMap.keySet.toList, requestMap.keySet.diff(consolidatedUnitDIALMap.keySet).toList) + }) + }) + }) } def validateContents(requestMap: Map[String, List[String]], reqContext: util.Map[String, AnyRef])(implicit ec: ExecutionContext, oec:OntologyEngineContext): Future[List[String]] = { @@ -149,4 +205,48 @@ object DIALManager { }) } + def getResponseCollectionLink(requestMap: Map[String, List[String]], updatedUnits: List[String], invalidIds: List[String])(implicit ec: ExecutionContext): Future[Response] = { + val response = if (requestMap.keySet.size == updatedUnits.size) + ResponseHandler.OK + else if (invalidIds.nonEmpty && updatedUnits.isEmpty) + ResponseHandler.ERROR(ResponseCode.RESOURCE_NOT_FOUND, DIALErrors.ERR_DIALCODE_LINK, DIALErrors.ERR_CONTENT_NOT_FOUND_MSG + invalidIds.asJava) + else + ResponseHandler.ERROR(ResponseCode.PARTIAL_SUCCESS, DIALErrors.ERR_DIALCODE_LINK, DIALErrors.ERR_CONTENT_NOT_FOUND_MSG + invalidIds.asJava) + + Future(response) + } + + def updateChildrenHierarchy(childrenHierarchy: util.List[util.Map[String, AnyRef]], requestMap: Map[String, List[String]]): List[util.Map[String, AnyRef]] = { + childrenHierarchy.asScala.toList.map(child => { + if (requestMap.contains(child.get("identifier").toString) && StringUtils.equalsIgnoreCase("Parent", child.get("visibility").toString)) { + if (requestMap.getOrElse(child.get("identifier").toString, List.empty).nonEmpty && requestMap(child.get("identifier").toString).exists(rec => rec.trim.nonEmpty)) + child.put("dialcodes", requestMap(child.get("identifier").toString)) + else + child.remove("dialcodes") + } + if(child.get("children")!=null) + updateChildrenHierarchy(child.get("children").asInstanceOf[util.List[util.Map[String, AnyRef]]], requestMap) + child + }) + } + + def getChildrenDIALMap(childrenHierarchy: List[util.Map[String, AnyRef]], requestMap: Map[String, List[String]]): Map[String, AnyRef] = { + childrenHierarchy.map(child => { + val subChildrenDIALMap = if(child.get("children")!=null) + getChildrenDIALMap(child.get("children").asInstanceOf[util.List[util.Map[String, AnyRef]]].asScala.toList, requestMap) + else Map.empty[String, String] + + val childDIALMap = if(requestMap.contains(child.get("identifier").toString) && child.get("dialcodes")!=null) + Map(child.get("identifier").toString -> child.get("dialcodes")) + else if(requestMap.contains(child.get("identifier").toString)) + Map(child.get("identifier").toString -> List.empty) + else Map.empty + + subChildrenDIALMap ++ childDIALMap + }).filter(msg => msg.nonEmpty).flatten.toMap[String, AnyRef] + } + + def validateDuplicateDIALCodes(unitDIALCodesMap: Map[String, AnyRef]): Map[String, Set[String]] = { + unitDIALCodesMap.groupBy(_._2).collect { case (key, group: Map[String, AnyRef]) if group.size > 1 => (key.asInstanceOf[List[String]].head, group.keySet) } + } } diff --git a/content-api/content-actors/src/test/resources/application.conf b/content-api/content-actors/src/test/resources/application.conf index 06d8e8d15..aa9400e59 100644 --- a/content-api/content-actors/src/test/resources/application.conf +++ b/content-api/content-actors/src/test/resources/application.conf @@ -495,6 +495,7 @@ dial_service { api { base_url = "https://qa.ekstep.in/api" auth_key = "auth_key" + search = "/dialcode/v1/search" } } content.link_dialcode.validation=true diff --git a/content-api/content-service/conf/application.conf b/content-api/content-service/conf/application.conf index 0e0c9a150..b1c49e9c4 100644 --- a/content-api/content-service/conf/application.conf +++ b/content-api/content-service/conf/application.conf @@ -603,6 +603,7 @@ dial_service { api { base_url = "https://qa.ekstep.in/api" auth_key = "auth_key" + search = "/dialcode/v1/search" } } content.link_dialcode.validation=true From 97ebd097f5d5d3b2f37ba87a15177bc367c9750a Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Thu, 21 Apr 2022 11:30:46 +0530 Subject: [PATCH 029/490] Issue #SB-19655 feat: Collection DIAL link API refactor from LP to KP --- search-api/search-core/pom.xml | 5 ----- .../java/org/sunbird/search/client/ElasticSearchUtil.java | 6 +++--- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/search-api/search-core/pom.xml b/search-api/search-core/pom.xml index f33636eb5..fbdf374ff 100644 --- a/search-api/search-core/pom.xml +++ b/search-api/search-core/pom.xml @@ -64,11 +64,6 @@ 2.4 jdk15 - - org.elasticsearch - elasticsearch - 6.8.22 - org.elasticsearch.client elasticsearch-rest-high-level-client diff --git a/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java b/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java index a43ab09b9..cef61b0a6 100644 --- a/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java +++ b/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java @@ -12,7 +12,7 @@ import org.apache.http.client.config.RequestConfig; import org.apache.http.util.EntityUtils; import org.elasticsearch.action.support.master.AcknowledgedResponse; -import org.elasticsearch.client.RestClientBuilder; +import org.elasticsearch.client.*; import org.elasticsearch.client.indices.CreateIndexResponse; import org.sunbird.search.util.SearchConstants; import org.sunbird.telemetry.logger.TelemetryManager; @@ -228,8 +228,8 @@ public static void deleteDocumentsByQuery(QueryBuilder query, String indexName, TelemetryManager.log("Deleted Documents by Query" + EntityUtils.toString(response.getEntity())); } - public static void deleteIndex(String indexName) throws InterruptedException, ExecutionException, IOException { - AcknowledgedResponse response = getClient(indexName).indices().delete(new DeleteIndexRequest(indexName)); + public static void deleteIndex(String indexName) throws IOException { + AcknowledgedResponse response = getClient(indexName).indices().delete(new DeleteIndexRequest(indexName), RequestOptions.DEFAULT); esClient.remove(indexName); TelemetryManager.log("Deleted Index" + indexName + " : " + response.isAcknowledged()); } From 1808b920d55306ea42bd5cf24023400bdf961090 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Thu, 21 Apr 2022 11:32:58 +0530 Subject: [PATCH 030/490] Issue #SB-19655 feat: Collection DIAL link API refactor from LP to KP --- .../content/dial/DIALManagerTest.scala | 35 ++++++++++++------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala b/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala index 6cfa80930..675577eee 100644 --- a/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala +++ b/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala @@ -107,20 +107,19 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory val input = new util.ArrayList[String]() val result:List[String] = DIALManager.getList(input) assert(result.isEmpty) - assert(result.size==0) } "validateAndGetRequestMap with valid input" should "return the request map" in { (oec.httpUtil _).expects().returns(httpUtil) - (httpUtil.post(_: String, _:java.util.Map[String, AnyRef], _:java.util.Map[String, String])).expects(*, *, *).returns(getDIALSearchResponse) + (httpUtil.post(_: String, _:java.util.Map[String, AnyRef], _:java.util.Map[String, String])).expects(*, *, *).returns(getDIALSearchResponse()) val input = getRequestData() val result = DIALManager.validateAndGetRequestMap("test", input) assert(result.nonEmpty) assert(result.size==5) - assert(result.get("do_88888").get.contains("L4A6W8")) - assert(result.get("do_88888").get.contains("D2E1J9")) - assert(result.get("do_2222").get.size==1) - assert(result.get("do_2222").get.contains("R4X2P2")) + assert(result("do_88888").contains("L4A6W8")) + assert(result("do_88888").contains("D2E1J9")) + assert(result("do_2222").size==1) + assert(result("do_2222").contains("R4X2P2")) } "validateReqStructure with valid request" should "not throw any exception" in { @@ -175,7 +174,7 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory "link DIAL with valid request for content" should "update the contents successfully" in { (oec.httpUtil _).expects().returns(httpUtil) (oec.graphService _).expects().returns(graphDB).anyNumberOfTimes() - (httpUtil.post(_: String, _:java.util.Map[String, AnyRef], _:java.util.Map[String, String])).expects(*, *, *).returns(getDIALSearchResponse) + (httpUtil.post(_: String, _:java.util.Map[String, AnyRef], _:java.util.Map[String, String])).expects(*, *, *).returns(getLinkDIALSearchResponse) (graphDB.getNodeByUniqueIds(_: String, _: SearchCriteria)).expects(*, *).returns(Future(getNodes())).noMoreThanOnce() val nodes: util.List[Node] = getCategoryNode() @@ -185,8 +184,10 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, *, *, *).returns(Future(getNode("do_1111"))) (graphDB.upsertNode(_: String, _: Node, _: Request)).expects(*, *, *).returns(Future(getNode("do_1111"))) val request = getContentDIALRequest() - val resFuture = DIALManager.link(request) - resFuture.map(result => { + + println("DIALManagerTest:: link content:: request:: " + request) + + DIALManager.link(request).map(result => { assert(result.getResponseCode.toString=="OK") }) } @@ -195,6 +196,9 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory (oec.httpUtil _).expects().returns(httpUtil) (httpUtil.post(_: String, _:java.util.Map[String, AnyRef], _:java.util.Map[String, String])).expects(*, *, *).returns(getDIALSearchResponse) val request = getCollectionDIALRequest() + + println("DIALManagerTest:: link collection:: request:: " + request) + val response = DIALManager.link(request) response.map(result => { assert(result.getResponseCode.toString=="OK") @@ -206,6 +210,11 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory JsonUtils.deserialize(resString, classOf[Response]) } + def getLinkDIALSearchResponse():Response = { + val resString = "{\n \"id\": \"sunbird.dialcode.search\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-04-21T19:39:14ZZ\",\n \"params\": {\n \"resmsgid\": \"1dfcc25b-6c37-49f8-a6c3-7185063e8752\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"dialcodes\": [\n {\n \"dialcode_index\": 7609876,\n \"identifier\": \"N4Z7D5\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.603+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n } ],\n \"count\": 1\n }\n}"; + JsonUtils.deserialize(resString, classOf[Response]) + } + def getRequestData(): List[Map[String, List[String]]] = { val reqMap : java.util.Map[String, AnyRef] = new util.HashMap[String, AnyRef](){{ put("content", new util.ArrayList[util.Map[String, AnyRef]](){{ @@ -251,10 +260,10 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory put("identifier","do_1111") put("dialcode", new util.ArrayList[String](){{ add("N4Z7D5") - add("E8B7Z6") - add("R4X2P2") - add("L4A6W8") - add("D2E1J9") +// add("E8B7Z6") +// add("R4X2P2") +// add("L4A6W8") +// add("D2E1J9") }}) }}) }}) From bb3f724c3cccc1288843df8abc850e9f396293dd Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Thu, 21 Apr 2022 11:36:00 +0530 Subject: [PATCH 031/490] Issue #SB-19655 feat: Collection DIAL link API refactor from LP to KP --- .../search/client/ElasticSearchUtil.java | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java b/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java index be9964049..0e7b323f2 100644 --- a/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java +++ b/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java @@ -1,6 +1,3 @@ -/** - * - */ package org.sunbird.search.client; import akka.dispatch.Futures; @@ -11,9 +8,9 @@ import org.apache.http.HttpHost; import org.apache.http.client.config.RequestConfig; import org.apache.http.util.EntityUtils; +import org.elasticsearch.action.admin.indices.create.CreateIndexResponse; import org.elasticsearch.action.support.master.AcknowledgedResponse; -import org.elasticsearch.client.RestClientBuilder; -import org.elasticsearch.client.indices.CreateIndexResponse; +import org.elasticsearch.client.*; import org.sunbird.search.util.SearchConstants; import org.sunbird.telemetry.logger.TelemetryManager; import org.elasticsearch.action.ActionListener; @@ -225,9 +222,8 @@ public static void deleteDocumentsByQuery(QueryBuilder query, String indexName, TelemetryManager.log("Deleted Documents by Query" + EntityUtils.toString(response.getEntity())); } - - public static void deleteIndex(String indexName) throws InterruptedException, ExecutionException, IOException { - AcknowledgedResponse response = getClient(indexName).indices().delete(new DeleteIndexRequest(indexName)); + public static void deleteIndex(String indexName) throws IOException { + AcknowledgedResponse response = getClient(indexName).indices().delete(new DeleteIndexRequest(indexName), RequestOptions.DEFAULT); esClient.remove(indexName); TelemetryManager.log("Deleted Index" + indexName + " : " + response.isAcknowledged()); } @@ -239,7 +235,7 @@ public static String getDocumentAsStringById(String indexName, String documentTy } public static List getMultiDocumentAsStringByIdList(String indexName, String documentType, - List documentIdList) throws IOException { + List documentIdList) throws IOException { List finalResult = new ArrayList(); MultiGetRequest request = new MultiGetRequest(); documentIdList.forEach(docId -> request.add(indexName, documentType, docId)); @@ -280,7 +276,7 @@ public static void bulkIndexWithIndexId(String indexName, String documentType, M } public static void bulkIndexWithAutoGenerateIndexId(String indexName, String documentType, - List> jsonObjects) + List> jsonObjects) throws Exception { if (isIndexExists(indexName)) { RestHighLevelClient client = getClient(indexName); @@ -306,7 +302,7 @@ public static void bulkIndexWithAutoGenerateIndexId(String indexName, String doc @SuppressWarnings("rawtypes") public static List textSearch(Class objectClass, Map matchCriterias, String indexName, - String indexType, int limit) throws Exception { + String indexType, int limit) throws Exception { SearchResponse result = search(matchCriterias, null, indexName, indexType, null, false, limit); return getDocumentsFromSearchResult(result, objectClass); } @@ -344,7 +340,7 @@ public static List getDocumentsFromHitsWithScore(SearchHits hits) { @SuppressWarnings({ "rawtypes" }) public static List textSearchReturningId(Map matchCriterias, String indexName, - String indexType) + String indexType) throws Exception { SearchResponse result = search(matchCriterias, null, indexName, indexType, null, false, 100); return getDocumentsFromSearchResultWithId(result); @@ -369,7 +365,7 @@ public static List getDocumentsFromHitsWithId(SearchHits hits) { @SuppressWarnings({ "rawtypes" }) public static List wildCardSearch(Class objectClass, String textKeyWord, String wordWildCard, - String indexName, String indexType, int limit) throws Exception { + String indexName, String indexType, int limit) throws Exception { SearchResponse result = wildCardSearch(textKeyWord, wordWildCard, indexName, indexType, limit); return getDocumentsFromSearchResult(result, objectClass); } From 7948db1ef852247264568397db553cf6eb911ea2 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Thu, 21 Apr 2022 11:42:56 +0530 Subject: [PATCH 032/490] Issue #SB-19655 feat: Collection DIAL link API refactor from LP to KP --- .../search/client/ElasticSearchUtil.java | 51 +++++++++---------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java b/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java index 0e7b323f2..8d725f96c 100644 --- a/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java +++ b/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java @@ -1,3 +1,6 @@ +/** + * + */ package org.sunbird.search.client; import akka.dispatch.Futures; @@ -235,7 +238,7 @@ public static String getDocumentAsStringById(String indexName, String documentTy } public static List getMultiDocumentAsStringByIdList(String indexName, String documentType, - List documentIdList) throws IOException { + List documentIdList) throws IOException { List finalResult = new ArrayList(); MultiGetRequest request = new MultiGetRequest(); documentIdList.forEach(docId -> request.add(indexName, documentType, docId)); @@ -276,7 +279,7 @@ public static void bulkIndexWithIndexId(String indexName, String documentType, M } public static void bulkIndexWithAutoGenerateIndexId(String indexName, String documentType, - List> jsonObjects) + List> jsonObjects) throws Exception { if (isIndexExists(indexName)) { RestHighLevelClient client = getClient(indexName); @@ -302,7 +305,7 @@ public static void bulkIndexWithAutoGenerateIndexId(String indexName, String doc @SuppressWarnings("rawtypes") public static List textSearch(Class objectClass, Map matchCriterias, String indexName, - String indexType, int limit) throws Exception { + String indexType, int limit) throws Exception { SearchResponse result = search(matchCriterias, null, indexName, indexType, null, false, limit); return getDocumentsFromSearchResult(result, objectClass); } @@ -339,9 +342,7 @@ public static List getDocumentsFromHitsWithScore(SearchHits hits) { } @SuppressWarnings({ "rawtypes" }) - public static List textSearchReturningId(Map matchCriterias, String indexName, - String indexType) - throws Exception { + public static List textSearchReturningId(Map matchCriterias, String indexName, String indexType) throws Exception { SearchResponse result = search(matchCriterias, null, indexName, indexType, null, false, 100); return getDocumentsFromSearchResultWithId(result); } @@ -365,14 +366,13 @@ public static List getDocumentsFromHitsWithId(SearchHits hits) { @SuppressWarnings({ "rawtypes" }) public static List wildCardSearch(Class objectClass, String textKeyWord, String wordWildCard, - String indexName, String indexType, int limit) throws Exception { + String indexName, String indexType, int limit) throws Exception { SearchResponse result = wildCardSearch(textKeyWord, wordWildCard, indexName, indexType, limit); return getDocumentsFromSearchResult(result, objectClass); } public static SearchResponse wildCardSearch(String textKeyWord, String wordWildCard, String indexName, - String indexType, int limit) - throws Exception { + String indexType, int limit) throws Exception { SearchSourceBuilder query = buildJsonForWildCardQuery(textKeyWord, wordWildCard, indexName); query.size(limit); return search(indexName, indexType, query); @@ -380,16 +380,15 @@ public static SearchResponse wildCardSearch(String textKeyWord, String wordWildC @SuppressWarnings({ "rawtypes" }) public static List textFiltersSearch(Class objectClass, Map searchCriteria, - Map textFiltersMap, String indexName, String indexType, int limit) - throws Exception { + Map textFiltersMap, String indexName, String indexType, int limit) throws Exception { SearchResponse result = search(searchCriteria, textFiltersMap, indexName, indexType, null, false, limit); return getDocumentsFromSearchResult(result, objectClass); } @SuppressWarnings("rawtypes") public static Map textFiltersGroupBySearch(Class objectClass, Map searchCriteria, - Map textFiltersMap, List> groupByList, String indexName, - String indexType) throws Exception { + Map textFiltersMap, List> groupByList, String indexName, + String indexType) throws Exception { SearchResponse result = search(searchCriteria, textFiltersMap, indexName, indexType, groupByList, false, resultLimit); List documents = getDocumentsFromSearchResult(result, objectClass); @@ -405,30 +404,28 @@ public static Map textFiltersGroupBySearch(Class objectClass, Ma @SuppressWarnings("rawtypes") public static List textSearch(Class objectClass, Map matchCriterias, - Map textFiltersMap, String indexName, String indexType) throws Exception { + Map textFiltersMap, String indexName, String indexType) throws Exception { SearchResponse result = search(matchCriterias, textFiltersMap, indexName, indexType, null, false, resultLimit); return getDocumentsFromSearchResult(result, objectClass); } @SuppressWarnings("rawtypes") public static List textSearch(Class objectClass, Map matchCriterias, - Map textFiltersMap, String indexName, String indexType, - List> groupByList, int limit) throws Exception { - SearchResponse result = search(matchCriterias, textFiltersMap, indexName, indexType, groupByList, false, - limit); + Map textFiltersMap, String indexName, String indexType, + List> groupByList, int limit) throws Exception { + SearchResponse result = search(matchCriterias, textFiltersMap, indexName, indexType, groupByList, false, limit); return getDocumentsFromSearchResult(result, objectClass); } public static SearchResponse search(Map matchCriterias, Map textFiltersMap, - String indexName, String indexType, List> groupBy, boolean isDistinct, int limit) + String indexName, String indexType, List> groupBy, boolean isDistinct, int limit) throws Exception { SearchSourceBuilder query = buildJsonForQuery(matchCriterias, textFiltersMap, groupBy, isDistinct, indexName); query.size(limit); return search(indexName, indexType, query); } - public static SearchResponse search(String indexName, String indexType, SearchSourceBuilder query) - throws Exception { + public static SearchResponse search(String indexName, String indexType, SearchSourceBuilder query) throws Exception { return getClient(indexName).search(new SearchRequest(indexName).source(query), RequestOptions.DEFAULT); } @@ -502,8 +499,7 @@ public static Map getCountFromAggregation(Aggregations aggregati @SuppressWarnings("rawtypes") public static Map getCountOfSearch(Class objectClass, Map matchCriterias, - String indexName, String indexType, List> groupByList, int limit) - throws Exception { + String indexName, String indexType, List> groupByList, int limit) throws Exception { SearchResponse result = search(matchCriterias, null, indexName, indexType, groupByList, false, limit); Aggregations aggregations = result.getAggregations(); return getCountFromAggregation(aggregations, groupByList); @@ -511,7 +507,7 @@ public static Map getCountOfSearch(Class objectClass, Map getDistinctCountOfSearch(Map matchCriterias, String IndexName, - String IndexType, List> groupByList) throws Exception { + String IndexType, List> groupByList) throws Exception { Map countMap = new HashMap(); SearchResponse result = search(matchCriterias, null, IndexName, IndexType, groupByList, true, 0); Aggregations aggregations = result.getAggregations(); @@ -540,8 +536,8 @@ public static Map getDistinctCountOfSearch(Map m @SuppressWarnings("unchecked") public static SearchSourceBuilder buildJsonForQuery(Map matchCriterias, - Map textFiltersMap, List> groupByList, boolean isDistinct, - String indexName) { + Map textFiltersMap, List> groupByList, boolean isDistinct, + String indexName) { SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); @@ -598,8 +594,7 @@ public static SearchSourceBuilder buildJsonForQuery(Map matchCri return searchSourceBuilder; } - private static SearchSourceBuilder buildJsonForWildCardQuery(String textKeyWord, String wordWildCard, - String indexName) { + private static SearchSourceBuilder buildJsonForWildCardQuery(String textKeyWord, String wordWildCard, String indexName) { return new SearchSourceBuilder().query(QueryBuilders.wildcardQuery(textKeyWord, wordWildCard)); } From 8f698061af7c5352c3c58ad66db6442bedfbde2b Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Thu, 21 Apr 2022 11:45:07 +0530 Subject: [PATCH 033/490] Issue #SB-19655 feat: Collection DIAL link API refactor from LP to KP --- .../main/scala/org/sunbird/content/dial/DIALManager.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala index 06d047c25..100e688b1 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala @@ -232,9 +232,9 @@ object DIALManager { def getChildrenDIALMap(childrenHierarchy: List[util.Map[String, AnyRef]], requestMap: Map[String, List[String]]): Map[String, AnyRef] = { childrenHierarchy.map(child => { - val subChildrenDIALMap = if(child.get("children")!=null) - getChildrenDIALMap(child.get("children").asInstanceOf[util.List[util.Map[String, AnyRef]]].asScala.toList, requestMap) - else Map.empty[String, String] + val subChildrenDIALMap = if(child.get("children")!=null) + getChildrenDIALMap(child.get("children").asInstanceOf[util.List[util.Map[String, AnyRef]]].asScala.toList, requestMap) + else Map.empty[String, String] val childDIALMap = if(requestMap.contains(child.get("identifier").toString) && child.get("dialcodes")!=null) Map(child.get("identifier").toString -> child.get("dialcodes")) From 9dd45231d50a4818ccd11273207d9f1c276f2271 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Thu, 21 Apr 2022 15:29:10 +0530 Subject: [PATCH 034/490] Issue #SB-19655 feat: Collection DIAL link API refactor from LP to KPe --- .../org/sunbird/content/dial/DIALManagerTest.scala | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala b/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala index 675577eee..683500f10 100644 --- a/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala +++ b/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala @@ -174,7 +174,7 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory "link DIAL with valid request for content" should "update the contents successfully" in { (oec.httpUtil _).expects().returns(httpUtil) (oec.graphService _).expects().returns(graphDB).anyNumberOfTimes() - (httpUtil.post(_: String, _:java.util.Map[String, AnyRef], _:java.util.Map[String, String])).expects(*, *, *).returns(getLinkDIALSearchResponse) + (httpUtil.post(_: String, _:java.util.Map[String, AnyRef], _:java.util.Map[String, String])).expects(*, *, *).returns(getDIALSearchResponse) (graphDB.getNodeByUniqueIds(_: String, _: SearchCriteria)).expects(*, *).returns(Future(getNodes())).noMoreThanOnce() val nodes: util.List[Node] = getCategoryNode() @@ -260,10 +260,10 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory put("identifier","do_1111") put("dialcode", new util.ArrayList[String](){{ add("N4Z7D5") -// add("E8B7Z6") -// add("R4X2P2") -// add("L4A6W8") -// add("D2E1J9") + add("E8B7Z6") + add("R4X2P2") + add("L4A6W8") + add("D2E1J9") }}) }}) }}) From 67224383beb85c60a98122d09c91a2803f83e95a Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Thu, 21 Apr 2022 16:14:39 +0530 Subject: [PATCH 035/490] Issue #SB-19655 feat: Collection DIAL link API refactor from LP to KPe --- .../content/dial/DIALManagerTest.scala | 60 ++++++++++++------- 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala b/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala index 683500f10..9a1b442e8 100644 --- a/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala +++ b/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala @@ -192,18 +192,30 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory }) } - "link DIAL with valid request for collections" should "update the collection content successfully" in { - (oec.httpUtil _).expects().returns(httpUtil) - (httpUtil.post(_: String, _:java.util.Map[String, AnyRef], _:java.util.Map[String, String])).expects(*, *, *).returns(getDIALSearchResponse) - val request = getCollectionDIALRequest() - - println("DIALManagerTest:: link collection:: request:: " + request) - - val response = DIALManager.link(request) - response.map(result => { - assert(result.getResponseCode.toString=="OK") - }) - } +// "link DIAL with valid request for collections" should "update the collection content successfully" in { +// (oec.httpUtil _).expects().returns(httpUtil) +// (oec.graphService _).expects().returns(graphDB).anyNumberOfTimes() +// (httpUtil.post(_: String, _:java.util.Map[String, AnyRef], _:java.util.Map[String, String])).expects(*, *, *).returns(getDIALSearchResponse) +// +// (graphDB.getNodeByUniqueIds(_: String, _: SearchCriteria)).expects(*, *).returns(Future(getNodes())).noMoreThanOnce() +// val nodes: util.List[Node] = getCategoryNode() +// (graphDB.getNodeByUniqueIds(_: String, _: SearchCriteria)).expects(*, *).returns(Future(nodes)).noMoreThanOnce() +// +// (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, *).returns(Future(new Response())) +// (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, *, *, *).returns(Future(getNode("do_1111"))) +// (graphDB.upsertNode(_: String, _: Node, _: Request)).expects(*, *, *).returns(Future(getNode("do_1111"))) +// (graphDB.saveExternalProps(_: Request)).expects(*).returns(Future(new Response())) +// +// +// val request = getCollectionDIALRequest() +// +// println("DIALManagerTest:: link collection:: request:: " + request) +// +// val response = DIALManager.link(request) +// response.map(result => { +// assert(result.getResponseCode.toString=="OK") +// }) +// } def getDIALSearchResponse():Response = { val resString = "{\n \"id\": \"sunbird.dialcode.search\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-04-21T19:39:14ZZ\",\n \"params\": {\n \"resmsgid\": \"1dfcc25b-6c37-49f8-a6c3-7185063e8752\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"dialcodes\": [\n {\n \"dialcode_index\": 7609876,\n \"identifier\": \"N4Z7D5\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.603+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n },\n {\n \"dialcode_index\": 7610113,\n \"identifier\": \"E8B7Z6\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.635+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n },\n {\n \"dialcode_index\": 7610117,\n \"identifier\": \"R4X2P2\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.637+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n },\n {\n \"dialcode_index\": 7610961,\n \"identifier\": \"L4A6W8\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.734+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n },\n {\n \"dialcode_index\": 7611164,\n \"identifier\": \"D2E1J9\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.759+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n }\n ],\n \"count\": 5\n }\n}"; @@ -301,25 +313,27 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory put("dialcode", new util.ArrayList[String](){{ add("N4Z7D5") add("E8B7Z6") + add("L4A6W8") + add("D2E1J9") }}) }}) add(new util.HashMap[String, AnyRef](){{ put("identifier",new util.ArrayList[String](){{ add("do_2222") - add("do_3333") +// add("do_3333") }}) put("dialcode", "R4X2P2") }}) - add(new util.HashMap[String, AnyRef](){{ - put("identifier",new util.ArrayList[String](){{ - add("do_4444") - add("do_5555") - }}) - put("dialcode", new util.ArrayList[String](){{ - add("L4A6W8") - add("D2E1J9") - }}) - }}) +// add(new util.HashMap[String, AnyRef](){{ +// put("identifier",new util.ArrayList[String](){{ +// add("do_4444") +// add("do_5555") +// }}) +// put("dialcode", new util.ArrayList[String](){{ +// add("L4A6W8") +// add("D2E1J9") +// }}) +// }}) }}) }} reqMap From 96030e8910492105ea049ff015714faf8506315c Mon Sep 17 00:00:00 2001 From: joffinjoy <35325730+joffinjoy@users.noreply.github.com> Date: Fri, 22 Apr 2022 12:04:31 +0530 Subject: [PATCH 036/490] Issue #SB-29145 refactor: PR Review Based Changes --- .../org/sunbird/managers/CopyManager.scala | 52 ++++++++----------- 1 file changed, 21 insertions(+), 31 deletions(-) diff --git a/assessment-api/assessment-actors/src/main/scala/org/sunbird/managers/CopyManager.scala b/assessment-api/assessment-actors/src/main/scala/org/sunbird/managers/CopyManager.scala index 06656c105..1aa02a4f1 100644 --- a/assessment-api/assessment-actors/src/main/scala/org/sunbird/managers/CopyManager.scala +++ b/assessment-api/assessment-actors/src/main/scala/org/sunbird/managers/CopyManager.scala @@ -44,7 +44,6 @@ object CopyManager { copyQuestionSet(node, request) case AssessmentConstants.QUESTION_MIME_TYPE => node.setInRelations(null) - validateCopyQuestionReq(request, node) //Check if the question has got "Default" visibility. copyNode(node, request) } copiedNodeFuture.map(copiedNode => { @@ -60,19 +59,12 @@ object CopyManager { }).flatMap(f => f) recoverWith { case e: CompletionException => throw e.getCause } } - def validateCopyQuestionReq(request: Request, node: Node) = { - val visibility = node.getMetadata.getOrDefault(AssessmentConstants.VISIBILITY, AssessmentConstants.VISIBILITY_PARENT).asInstanceOf[String] - if (StringUtils.equalsIgnoreCase(visibility, AssessmentConstants.VISIBILITY_PARENT)) { - throw new ClientException(AssessmentConstants.ERR_INVALID_REQUEST, "Question With Visibility Parent Cannot Be Copied Individually!") - } - } - def validateExistingNode(request: Request, node: Node) = { val requestObjectType = request.getObjectType val nodeObjectType = node.getObjectType - if (!StringUtils.equalsIgnoreCase(requestObjectType, nodeObjectType)) { - throw new ClientException(AssessmentConstants.ERR_INVALID_OBJECT_TYPE, "Invalid Object Type: " + requestObjectType) - } + if (!StringUtils.equalsIgnoreCase(requestObjectType, nodeObjectType)) throw new ClientException(AssessmentConstants.ERR_INVALID_OBJECT_TYPE, s"Please Provide Valid ${requestObjectType} Identifier") + if (StringUtils.equalsIgnoreCase(node.getObjectType, AssessmentConstants.QUESTION) && StringUtils.equalsIgnoreCase(node.getMetadata.getOrDefault(AssessmentConstants.VISIBILITY, AssessmentConstants.VISIBILITY_PARENT).asInstanceOf[String], AssessmentConstants.VISIBILITY_PARENT)) + throw new ClientException(AssessmentConstants.ERR_INVALID_REQUEST, "Question With Visibility Parent Cannot Be Copied Individually!") } def copyQuestionSet(originNode: Node, request: Request)(implicit ex: ExecutionContext, oec: OntologyEngineContext): Future[Node] = { @@ -332,8 +324,8 @@ object CopyManager { def updateShallowHierarchy(request: Request, node: Node, originNode: Node, originHierarchy: util.Map[String, AnyRef])(implicit ec: ExecutionContext, oec: OntologyEngineContext): Future[Node] = { val childrenHierarchy = originHierarchy.get("children").asInstanceOf[util.List[util.Map[String, AnyRef]]] val req = new Request(request) - req.getContext.put(AssessmentConstants.SCHEMA_NAME, AssessmentConstants.QUESTIONSET_SCHEMA_NAME) - req.getContext.put(AssessmentConstants.VERSION, AssessmentConstants.SCHEMA_VERSION) + req.getContext.put(AssessmentConstants.SCHEMA_NAME, request.getContext.getOrDefault(AssessmentConstants.SCHEMA_NAME, AssessmentConstants.QUESTIONSET_SCHEMA_NAME)) + req.getContext.put(AssessmentConstants.VERSION, request.getContext.getOrDefault(AssessmentConstants.VERSION, AssessmentConstants.SCHEMA_VERSION)) req.getContext.put(AssessmentConstants.IDENTIFIER, node.getIdentifier) req.put(AssessmentConstants.HIERARCHY, ScalaJsonUtils.serialize(new java.util.HashMap[String, AnyRef]() { { @@ -345,7 +337,7 @@ object CopyManager { } def getCopyRequest(node: Node, request: Request)(implicit ec: ExecutionContext, oec: OntologyEngineContext): Future[Request] = { - val metadata: util.Map[String, AnyRef] = NodeUtil.serialize(node, new util.ArrayList(), node.getObjectType.toLowerCase.replace("image", ""), AssessmentConstants.SCHEMA_VERSION) + val metadata: util.Map[String, AnyRef] = NodeUtil.serialize(node, new util.ArrayList(), node.getObjectType.toLowerCase.replace("image", ""), request.getContext.getOrDefault(AssessmentConstants.VERSION, "").asInstanceOf[String]) val requestMap = request.getRequest requestMap.remove(AssessmentConstants.MODE) requestMap.remove(AssessmentConstants.COPY_SCHEME).asInstanceOf[String] @@ -361,8 +353,6 @@ object CopyManager { request.getContext().put(AssessmentConstants.SCHEMA_NAME, node.getObjectType.toLowerCase.replace("image", "")) val req = new Request(request) req.setRequest(metadata) - - val graphId = request.getContext.getOrDefault("graph_id", "").asInstanceOf[String] val version = request.getContext.getOrDefault("version", "").asInstanceOf[String] val externalProps = if (StringUtils.equalsIgnoreCase(AssessmentConstants.QUESTIONSET_MIME_TYPE, node.getMetadata.getOrDefault("mimeType", "").asInstanceOf[String])) { @@ -374,18 +364,17 @@ object CopyManager { readReq.setContext(request.getContext) readReq.put("identifier", node.getIdentifier) readReq.put("fields", externalProps.asJava) - val maxWaitTime: FiniteDuration = Duration(5, TimeUnit.SECONDS) - Await.result( - DataNode.read(readReq).map(node => { - val metadata: util.Map[String, AnyRef] = NodeUtil.serialize(node, externalProps.asJava, node.getObjectType.toLowerCase.replace("image", ""), request.getContext.get("version").asInstanceOf[String]) - externalProps.foreach(prop => { - val propValue = metadata.get(prop) - if (metadata.containsKey(prop) && propValue != null) { - req.put(prop, propValue) - } - }) - }), maxWaitTime) - Future(req) + DataNode.read(readReq).map(node => { + val metadata: util.Map[String, AnyRef] = NodeUtil.serialize(node, externalProps.asJava, node.getObjectType.toLowerCase.replace + ("image", ""), request.getContext.get("version").asInstanceOf[String]) + externalProps.foreach(prop => { + val propValue = metadata.get(prop) + if (metadata.containsKey(prop) && propValue != null) { + req.put(prop, propValue) + } + }) + Future(req) + }).flatMap(f=>f) } def getOriginData(metadata: util.Map[String, AnyRef], copyType: String): java.util.Map[String, AnyRef] = { @@ -406,8 +395,9 @@ object CopyManager { def validateShallowCopyReq(node: Node, request: Request) = { val copyType: String = request.getRequest.get("copyType").asInstanceOf[String] if (StringUtils.equalsIgnoreCase("shallow", copyType) && !StringUtils.equalsIgnoreCase("Live", node.getMetadata.get("status").asInstanceOf[String])) - throw new ClientException(AssessmentConstants.ERR_INVALID_REQUEST, "QuestionSet with status " + node.getMetadata.get(AssessmentConstants.STATUS).asInstanceOf[String].toLowerCase + " cannot be partially (shallow) copied.") - //TODO: check if need to throw client exception for combination of copyType=shallow and mode=edit + throw new ClientException(AssessmentConstants.ERR_INVALID_REQUEST, "QuestionSet With Status " + node.getMetadata.get(AssessmentConstants.STATUS).asInstanceOf[String].toLowerCase + " Cannot Be Partially (Shallow) Copied.") + if(StringUtils.equalsIgnoreCase("shallow", copyType) && StringUtils.equalsIgnoreCase(request.get(AssessmentConstants.MODE).asInstanceOf[String], "edit")) + request.getRequest.remove(AssessmentConstants.MODE) } def emptyCheckFilter(key: AnyRef): Boolean = key match { @@ -423,4 +413,4 @@ object CopyManager { } else metadata.keySet().removeAll(metadataNotTobeCopied) metadata } -} +} \ No newline at end of file From 4916195d601720fbf7ac981bf7827407138cdae5 Mon Sep 17 00:00:00 2001 From: joffinjoy <35325730+joffinjoy@users.noreply.github.com> Date: Fri, 22 Apr 2022 12:51:32 +0530 Subject: [PATCH 037/490] Issue #SB-29145 refactor: Origin Data Map Fix --- .../main/scala/org/sunbird/managers/CopyManager.scala | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/assessment-api/assessment-actors/src/main/scala/org/sunbird/managers/CopyManager.scala b/assessment-api/assessment-actors/src/main/scala/org/sunbird/managers/CopyManager.scala index 1aa02a4f1..269b53040 100644 --- a/assessment-api/assessment-actors/src/main/scala/org/sunbird/managers/CopyManager.scala +++ b/assessment-api/assessment-actors/src/main/scala/org/sunbird/managers/CopyManager.scala @@ -377,14 +377,7 @@ object CopyManager { }).flatMap(f=>f) } - def getOriginData(metadata: util.Map[String, AnyRef], copyType: String): java.util.Map[String, AnyRef] = { - new java.util.HashMap[String, AnyRef]() { - { - putAll(originMetadataKeys.asScala.filter(key => metadata.containsKey(key)).map(key => key -> metadata.get(key)).toMap.asJava) - put(AssessmentConstants.COPY_TYPE, copyType) - } - } - } + def getOriginData(metadata: util.Map[String, AnyRef], copyType: String): java.util.Map[String, AnyRef] = (Map(AssessmentConstants.COPY_TYPE -> copyType) ++ originMetadataKeys.asScala.filter(key => metadata.containsKey(key)).map(key => key -> metadata.get(key)).toMap).asJava def validateRequest(request: Request)(implicit ec: ExecutionContext, oec: OntologyEngineContext): Unit = { val keysNotPresent = AssessmentConstants.REQUIRED_KEYS.filter(key => emptyCheckFilter(request.getRequest.getOrDefault(key, ""))) From d44cd4df1f2aa5ece3a08c09fcdc1bd40eabf8cd Mon Sep 17 00:00:00 2001 From: Kumar Gauraw Date: Fri, 22 Apr 2022 17:21:15 +0530 Subject: [PATCH 038/490] Issue #SB-29666 fix: updated config to restrict props --- schemas/question/1.0/config.json | 1 + schemas/questionset/1.0/config.json | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/schemas/question/1.0/config.json b/schemas/question/1.0/config.json index 621554e0b..b37306e3a 100644 --- a/schemas/question/1.0/config.json +++ b/schemas/question/1.0/config.json @@ -9,6 +9,7 @@ }, "restrictProps": { "create" : [ + "status" ], "update" : [ "visibility", "code", "status", "mimeType" diff --git a/schemas/questionset/1.0/config.json b/schemas/questionset/1.0/config.json index e6719f81f..f8764d715 100644 --- a/schemas/questionset/1.0/config.json +++ b/schemas/questionset/1.0/config.json @@ -48,7 +48,8 @@ "schema_restrict_api": true, "restrictProps": { "create": [ - "children" + "children", + "status" ], "update": [ "children", From 00ceb6ba8ce85cf995a2c1e888b06ba5c7b99d10 Mon Sep 17 00:00:00 2001 From: Kumar Gauraw Date: Mon, 25 Apr 2022 11:33:07 +0530 Subject: [PATCH 039/490] Issue #SB-29666 fix: updated code to restrict props --- .../src/main/scala/org/sunbird/managers/AssessmentManager.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/assessment-api/assessment-actors/src/main/scala/org/sunbird/managers/AssessmentManager.scala b/assessment-api/assessment-actors/src/main/scala/org/sunbird/managers/AssessmentManager.scala index fc3d99331..407d2d534 100644 --- a/assessment-api/assessment-actors/src/main/scala/org/sunbird/managers/AssessmentManager.scala +++ b/assessment-api/assessment-actors/src/main/scala/org/sunbird/managers/AssessmentManager.scala @@ -12,6 +12,7 @@ import org.sunbird.graph.nodes.DataNode import org.sunbird.graph.utils.NodeUtil import org.sunbird.telemetry.logger.TelemetryManager import org.sunbird.telemetry.util.LogTelemetryEventUtil +import org.sunbird.utils.RequestUtil import scala.concurrent.{ExecutionContext, Future} import scala.collection.JavaConversions._ @@ -27,6 +28,7 @@ object AssessmentManager { val visibility: String = request.getRequest.getOrDefault("visibility", "").asInstanceOf[String] if (StringUtils.isNotBlank(visibility) && StringUtils.equalsIgnoreCase(visibility, "Parent")) throw new ClientException(errCode, "Visibility cannot be Parent!") + RequestUtil.restrictProperties(request) DataNode.create(request).map(node => { val response = ResponseHandler.OK response.putAll(Map("identifier" -> node.getIdentifier, "versionKey" -> node.getMetadata.get("versionKey")).asJava) From 9494b5ea53e7a804f822e351435b1132db80569d Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Mon, 25 Apr 2022 17:12:54 +0530 Subject: [PATCH 040/490] Issue #SB-19655 feat: Collection DIAL link API refactor from LP to KPe --- .../content/dial/DIALManagerTest.scala | 109 +++++++++++------- 1 file changed, 66 insertions(+), 43 deletions(-) diff --git a/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala b/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala index 9a1b442e8..86fef60ff 100644 --- a/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala +++ b/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala @@ -1,24 +1,23 @@ package org.sunbird.content.dial -import java.util - import org.scalamock.matchers.Matchers import org.scalamock.scalatest.AsyncMockFactory import org.scalatest.AsyncFlatSpec -import org.sunbird.common.{HttpUtil, JsonUtils} -import org.sunbird.common.dto.{Request, Response} +import org.sunbird.common.dto.{Property, Request, Response} import org.sunbird.common.exception.{ClientException, ResourceNotFoundException, ResponseCode, ServerException} +import org.sunbird.common.{HttpUtil, JsonUtils} import org.sunbird.graph.dac.model.{Node, SearchCriteria} import org.sunbird.graph.{GraphService, OntologyEngineContext} +import java.util import scala.concurrent.Future class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory { implicit val oec: OntologyEngineContext = mock[OntologyEngineContext] - val graphDB = mock[GraphService] - val httpUtil = mock[HttpUtil] - + val graphDB: GraphService = mock[GraphService] + val httpUtil: HttpUtil = mock[HttpUtil] + "getRequestData with list input" should "return request data as list with scala types" in { val reqMap : java.util.Map[String, AnyRef] = new util.HashMap[String, AnyRef](){{ put("content", new util.ArrayList[util.Map[String, AnyRef]](){{ @@ -52,8 +51,8 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory assert(result.isInstanceOf[List[AnyRef]]) assert(result.size==3) assert(result(1).nonEmpty) - assert(result(1).get("identifier").get.isInstanceOf[List[String]]) - assert(result(1).get("dialcode").get.isInstanceOf[List[String]]) + assert(result(1)("identifier").isInstanceOf[List[String]]) + assert(result(1)("dialcode").isInstanceOf[List[String]]) } "getRequestData with map input" should "return request data as list with scala types" in { @@ -72,9 +71,9 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory assert(null!=result && result.nonEmpty) assert(result.isInstanceOf[List[AnyRef]]) assert(result.size==1) - assert(result(0).nonEmpty) - assert(result(0).get("identifier").get.isInstanceOf[List[String]]) - assert(result(0).get("dialcode").get.isInstanceOf[List[String]]) + assert(result.head.nonEmpty) + assert(result.head("identifier").isInstanceOf[List[String]]) + assert(result.head("dialcode").isInstanceOf[List[String]]) } "getRequestData with invalid input" should "throw client exception" in { @@ -111,7 +110,7 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory "validateAndGetRequestMap with valid input" should "return the request map" in { (oec.httpUtil _).expects().returns(httpUtil) - (httpUtil.post(_: String, _:java.util.Map[String, AnyRef], _:java.util.Map[String, String])).expects(*, *, *).returns(getDIALSearchResponse()) + (httpUtil.post(_: String, _:java.util.Map[String, AnyRef], _:java.util.Map[String, String])).expects(*, *, *).returns(getDIALSearchResponse) val input = getRequestData() val result = DIALManager.validateAndGetRequestMap("test", input) assert(result.nonEmpty) @@ -192,38 +191,33 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory }) } -// "link DIAL with valid request for collections" should "update the collection content successfully" in { -// (oec.httpUtil _).expects().returns(httpUtil) -// (oec.graphService _).expects().returns(graphDB).anyNumberOfTimes() -// (httpUtil.post(_: String, _:java.util.Map[String, AnyRef], _:java.util.Map[String, String])).expects(*, *, *).returns(getDIALSearchResponse) -// -// (graphDB.getNodeByUniqueIds(_: String, _: SearchCriteria)).expects(*, *).returns(Future(getNodes())).noMoreThanOnce() -// val nodes: util.List[Node] = getCategoryNode() -// (graphDB.getNodeByUniqueIds(_: String, _: SearchCriteria)).expects(*, *).returns(Future(nodes)).noMoreThanOnce() -// -// (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, *).returns(Future(new Response())) -// (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, *, *, *).returns(Future(getNode("do_1111"))) -// (graphDB.upsertNode(_: String, _: Node, _: Request)).expects(*, *, *).returns(Future(getNode("do_1111"))) -// (graphDB.saveExternalProps(_: Request)).expects(*).returns(Future(new Response())) -// -// -// val request = getCollectionDIALRequest() -// -// println("DIALManagerTest:: link collection:: request:: " + request) -// -// val response = DIALManager.link(request) -// response.map(result => { -// assert(result.getResponseCode.toString=="OK") -// }) -// } - - def getDIALSearchResponse():Response = { - val resString = "{\n \"id\": \"sunbird.dialcode.search\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-04-21T19:39:14ZZ\",\n \"params\": {\n \"resmsgid\": \"1dfcc25b-6c37-49f8-a6c3-7185063e8752\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"dialcodes\": [\n {\n \"dialcode_index\": 7609876,\n \"identifier\": \"N4Z7D5\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.603+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n },\n {\n \"dialcode_index\": 7610113,\n \"identifier\": \"E8B7Z6\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.635+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n },\n {\n \"dialcode_index\": 7610117,\n \"identifier\": \"R4X2P2\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.637+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n },\n {\n \"dialcode_index\": 7610961,\n \"identifier\": \"L4A6W8\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.734+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n },\n {\n \"dialcode_index\": 7611164,\n \"identifier\": \"D2E1J9\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.759+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n }\n ],\n \"count\": 5\n }\n}"; - JsonUtils.deserialize(resString, classOf[Response]) + "link DIAL with valid request for collections" should "update the collection content successfully" in { + + (oec.httpUtil _).expects().returns(httpUtil) + (oec.graphService _).expects().returns(graphDB).anyNumberOfTimes() + (httpUtil.post(_: String, _:java.util.Map[String, AnyRef], _:java.util.Map[String, String])).expects(*, *, *).returns(getDIALSearchResponse) + + val nodes: util.List[Node] = getCategoryNode() + (graphDB.getNodeByUniqueIds(_: String, _: SearchCriteria)).expects(*, *).returns(Future(nodes)).anyNumberOfTimes() + + (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, *).returns(Future(getCassandraHierarchy())).anyNumberOfTimes() + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, *, *, *).returns(Future(getNode("do_1111"))).anyNumberOfTimes() + (graphDB.upsertNode(_: String, _: Node, _: Request)).expects(*, *, *).returns(Future(getNode("do_1111"))) + (graphDB.saveExternalProps(_: Request)).expects(*).returns(Future(new Response())) + (graphDB.getNodeProperty(_: String, _: String, _: String)).expects(*, *, *).returns(Future(new Property("versionKey", new org.neo4j.driver.internal.value.StringValue("1234")))) + + val request = getCollectionDIALRequest() + + println("DIALManagerTest:: link collection:: request:: " + request) + + val response = DIALManager.link(request) + response.map(result => { + assert(result.getResponseCode.toString=="OK") + }) } - def getLinkDIALSearchResponse():Response = { - val resString = "{\n \"id\": \"sunbird.dialcode.search\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-04-21T19:39:14ZZ\",\n \"params\": {\n \"resmsgid\": \"1dfcc25b-6c37-49f8-a6c3-7185063e8752\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"dialcodes\": [\n {\n \"dialcode_index\": 7609876,\n \"identifier\": \"N4Z7D5\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.603+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n } ],\n \"count\": 1\n }\n}"; + def getDIALSearchResponse:Response = { + val resString = "{\n \"id\": \"sunbird.dialcode.search\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-04-21T19:39:14ZZ\",\n \"params\": {\n \"resmsgid\": \"1dfcc25b-6c37-49f8-a6c3-7185063e8752\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"dialcodes\": [\n {\n \"dialcode_index\": 7609876,\n \"identifier\": \"N4Z7D5\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.603+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n },\n {\n \"dialcode_index\": 7610113,\n \"identifier\": \"E8B7Z6\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.635+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n },\n {\n \"dialcode_index\": 7610117,\n \"identifier\": \"R4X2P2\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.637+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n },\n {\n \"dialcode_index\": 7610961,\n \"identifier\": \"L4A6W8\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.734+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n },\n {\n \"dialcode_index\": 7611164,\n \"identifier\": \"D2E1J9\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.759+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n }\n ],\n \"count\": 5\n }\n}"; JsonUtils.deserialize(resString, classOf[Response]) } @@ -406,4 +400,33 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory util.Arrays.asList(node) } + def getCassandraHierarchy(): Response = { + val hierarchyString: String = + """{"ownershipType": ["createdBy"],"subject": ["Mathematics"],"channel": "0126825293972439041","organisation": ["Sunbird"], + |"language": ["English"],"mimeType": "application/vnd.ekstep.content-collection","objectType": "Content","gradeLevel": ["Class 4"], + |"primaryCategory": "Digital Textbook","children": [{"ownershipType": ["createdBy"],"parent": "do_1111","code": "do_1132828084877148161531", + |"keywords": [],"credentials": {"enabled": "No"},"channel": "0126825293972439041","description": "This chapter describes about human body", + |"language": ["English"],"mimeType": "application/vnd.ekstep.content-collection","idealScreenSize": "normal","createdOn": "2021-05-20T08:58:33.470+0000", + |"objectType": "Content","primaryCategory": "Textbook Unit","children": [{"ownershipType": ["createdBy"],"parent": "do_2222", + |"code": "do_1132828084876574721523","keywords": [],"credentials": {"enabled": "No"},"channel": "0126825293972439041", + |"description": "This section describes about various part of the body such as head, hands, legs etc.","language": ["English"], + |"mimeType": "application/vnd.ekstep.content-collection","idealScreenSize": "normal","createdOn": "2021-05-20T08:58:33.466+0000", + |"objectType": "Content","primaryCategory": "Textbook Unit","children": [{"ownershipType": ["createdBy"],"parent": "do_1132833371215134721712", + |"code": "do_1132828084876738561525","keywords": ["legs etc."],"credentials": {"enabled": "No"},"channel": "0126825293972439041","description": "xyz", + |"language": ["English"],"mimeType": "application/vnd.ekstep.content-collection","idealScreenSize": "normal","createdOn": "2021-05-20T08:58:33.475+0000", + |"objectType": "Content","primaryCategory": "Textbook Unit","children": [{"ownershipType": ["createdBy"],"parent": "do_1132833371215872001720", + |"previewUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/assets/do_1132339274094346241120/test-874-kb.mp4", + |"channel": "b00bc992ef25f1a9a8d63291e20efc8d", + |"downloadUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_1132339274094346241120/untitled-content_1616331971279_do_1132339274094346241120_14.0.ecar", + |"organisation": ["Sunbird"],"language": ["English"],"mimeType": "video/mp4","variants": {"spine": + |{"ecarUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_1132339274094346241120/untitled-content_1616331975047_do_1132339274094346241120_14.0_spine.ecar","size": 1381.0}},"objectType": "Content","primaryCategory": "Learning Resource","appId": "local.sunbird.portal","contentEncoding": "identity","artifactUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/assets/do_1132339274094346241120/test-874-kb.mp4", + |"lockKey": "34a029c4-ac81-4934-9792-11b7a57d6c13","sYS_INTERNAL_LAST_UPDATED_ON": "2021-03-21T13:20:24.579+0000","contentType": "Resource", + |"trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_1132339274094346241120","audience": ["Student"],"visibility": "Default", + |"consumerId": "273f3b18-5dda-4a27-984a-060c7cd398d3","index": 1,"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"], + |"lastPublishedBy": "8454cb21-3ce9-4e30-85b5-fade097880d8","version": 2,"license": "CC BY 4.0","prevState": "Live","size": 849897.0, + |"lastPublishedOn": "2021-03-21T13:06:11.272+0000","name": "Untitled Content","status": "Live","code": "a88b0257-670b-455b-98b8-6e359ebac009","credentials": {"enabled": "No"},"prevStatus": "Processing","description": "updated","streamingUrl": "https://sunbirddevmedia-inct.streaming.media.azure.net/9c0ebb33-af08-403f-afb4-eb24749f40a1/test-874-kb.ism/manifest(format=m3u8-aapl-v3)","idealScreenSize": "normal","createdOn": "2021-03-11T13:34:14.475+0000","contentDisposition": "inline","lastUpdatedOn": "2021-03-21T13:06:09.526+0000","dialcodeRequired": "No","lastStatusChangedOn": "2021-03-21T18:36:15.799+0530","createdFor": ["ORG_001"],"creator": "Reviewer User","os": ["All"],"se_FWIds": ["NCFCOPY"],"pkgVersion": 14.0,"versionKey": "1616331969523","idealScreenDensity": "hdpi","framework": "NCFCOPY","depth": 4,"s3Key": "ecar_files/do_1132339274094346241120/untitled-content_1616331971279_do_1132339274094346241120_14.0.ecar","createdBy": "95e4942d-cbe8-477d-aebd-ad8e6de4bfc8","compatibilityLevel": 1,"resourceType": "Learn"},{"ownershipType": ["createdBy"],"parent": "do_1132833371215872001720","unitIdentifiers": ["do_1132239562839900161634"],"copyright": "2021 MIT","previewUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/ecml/do_113223967141863424174-latest","plugins": [{"identifier": "org.sunbird.questionunit.quml","semanticVersion": "1.1"}],"subject": ["Hindi"],"channel": "01309282781705830427","downloadUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_113223967141863424174/esa_1614253812772_do_113223967141863424174_1.0.ecar","language": ["English"],"source": "https://dock.sunbirded.org/api/content/v1/read/do_1132239617341767681638","mimeType": "application/vnd.ekstep.ecml-archive","variants": {"spine": {"ecarUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_113223967141863424174/esa_1614253813394_do_113223967141863424174_1.0_spine.ecar","size": 24293.0}},"objectType": "Content","se_mediums": ["English"],"gradeLevel": ["Class 10"],"primaryCategory": "Exam Question","appId": "dev.dock.portal","contentEncoding": "gzip","artifactUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/do_113223967141863424174/artifact/1614253223147_do_1132239617341767681638.zip","sYS_INTERNAL_LAST_UPDATED_ON": "2021-02-25T11:50:16.032+0000","contentType": "Resource","se_gradeLevels": ["Class 10"],"trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_113223967141863424174","audience": ["Student"],"visibility": "Default","author": "anusha","maxQuestions": 1,"consumerId": "b7054510-3ca4-49fd-b373-b100b3f65e18","index": 2,"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"],"lastPublishedBy": "5a587cc1-e018-4859-a0a8-e842650b9d64","version": 2,"se_subjects": ["Hindi"],"license": "CC BY 4.0","prevState": "Review","size": 384798.0,"lastPublishedOn": "2021-02-25T11:50:12.771+0000","name": "esa","topic": ["तोप"],"status": "Live","code": "d19f43ce-753d-2c70-a9fd-70302af424a0","credentials": {"enabled": "No"},"prevStatus": "Processing","origin": "do_1132239617341767681638","streamingUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/ecml/do_113223967141863424174-latest","medium": ["English"],"idealScreenSize": "normal","createdOn": "2021-02-25T11:50:01.500+0000","se_boards": ["CBSE"],"processId": "2b00cca7-42d5-4e35-aa03-1c22dfe03de8","contentDisposition": "inline","lastUpdatedOn": "2021-02-25T11:50:12.678+0000","originData": {"identifier": "do_1132239617341767681638","repository": "https://dock.sunbirded.org/api/content/v1/read/do_1132239617341767681638"},"collectionId": "do_1132239562836049921627","dialcodeRequired": "No","editorVersion": 3,"lastStatusChangedOn": "2021-02-25T11:50:16.017+0000","creator": "anusha","os": ["All"],"questionCategories": ["SA"],"cloudStorageKey": "content/do_113223967141863424174/artifact/1614253223147_do_1132239617341767681638.zip","se_FWIds": ["ekstep_ncert_k-12"],"marks": "12","bloomsLevel": ["Apply"],"pkgVersion": 1.0,"versionKey": "1614253812678","idealScreenDensity": "hdpi","framework": "ekstep_ncert_k-12","depth": 4,"s3Key": "ecar_files/do_113223967141863424174/esa_1614253812772_do_113223967141863424174_1.0.ecar","lastSubmittedOn": "2021-02-25T11:50:11.539+0000","createdBy": "19ba0e4e-9285-4335-8dd0-f674bf03fa4d","se_topics": ["तोप"],"compatibilityLevel": 1,"itemSetPreviewUrl": "https://dockstorage.blob.core.windows.net/sunbird-content-dock/content/do_1132239617341767681638/artifact/do_1132239617341767681638_1614253222002.pdf","board": "CBSE","programId": "463cfa30-775c-11eb-8c56-93946e419809"}],"contentDisposition": "inline","lastUpdatedOn": "2021-05-20T08:58:33.475+0000","contentEncoding": "gzip","contentType": "TextBookUnit","dialcodeRequired": "Yes","identifier": "do_1132833371215872001720","lastStatusChangedOn": "2021-05-20T08:58:33.475+0000","audience": ["Student"],"os": ["All"],"visibility": "Parent","discussionForum": {"enabled": "Yes"},"index": 1,"mediaType": "content","osId": "org.ekstep.launcher","languageCode": ["en"],"version": 2,"versionKey": "1621501113475","license": "CC BY 4.0","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 3,"compatibilityLevel": 1,"name": "5.1.1 Key parts in the head","topic": [],"status": "Draft"},{"ownershipType": ["createdBy"],"parent": "do_1132833371215134721712","code": "do_1132828084876165121519","keywords": [],"credentials": {"enabled": "No"},"channel": "0126825293972439041","description": "","language": ["English"],"mimeType": "application/vnd.ekstep.content-collection","idealScreenSize": "normal","createdOn": "2021-05-20T08:58:33.473+0000","objectType": "Content","primaryCategory": "Textbook Unit","contentDisposition": "inline","lastUpdatedOn": "2021-05-20T08:58:33.473+0000","contentEncoding": "gzip","contentType": "TextBookUnit","dialcodeRequired": "No","identifier": "do_1132833371215708161718","lastStatusChangedOn": "2021-05-20T08:58:33.473+0000","audience": ["Student"],"os": ["All"],"visibility": "Parent","discussionForum": {"enabled": "Yes"},"index": 2,"mediaType": "content","osId": "org.ekstep.launcher","languageCode": ["en"],"version": 2,"versionKey": "1621501113473","license": "CC BY 4.0","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 3,"compatibilityLevel": 1,"name": "5.1.2 Other parts","topic": [],"status": "Draft"},{"ownershipType": ["createdBy"],"parent": "do_1132833371215134721712","unitIdentifiers": ["do_11323721176414617611924"],"copyright": "2021 MIT","organisationId": "e7328d77-42a7-44c8-84f4-8cfea235f07d","previewUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/ecml/do_1132372524622561281279-latest","plugins": [{"identifier": "org.sunbird.questionunit.quml","semanticVersion": "1.1"}],"subject": ["Mathematics"],"channel": "01309282781705830427","downloadUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_1132372524622561281279/untitled_1615875562931_do_1132372524622561281279_1.0.ecar","language": ["English"],"source": "https://dock.sunbirded.org/api/content/v1/read/do_11323724954450329611930","mimeType": "application/vnd.ekstep.ecml-archive","variants": {"spine": {"ecarUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_1132372524622561281279/untitled_1615875563539_do_1132372524622561281279_1.0_spine.ecar","size": 19563.0}},"objectType": "Content","se_mediums": ["English"],"gradeLevel": ["Class 5"],"primaryCategory": "Exam Question","appId": "dev.dock.portal","contentEncoding": "gzip","artifactUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/do_1132372524622561281279/artifact/1615875430184_do_11323724954450329611930.zip","sYS_INTERNAL_LAST_UPDATED_ON": "2021-03-16T06:19:26.162+0000","contentType": "Resource","se_gradeLevels": ["Class 5"],"trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_1132372524622561281279","audience": ["Student"],"visibility": "Default","author": "N18","maxQuestions": 1,"consumerId": "f73cfcc5-4d43-4fa0-8b81-46166c81bc2b","learningOutcome": ["identify the need to find area and perimeter of rectangle and square."],"index": 3,"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"],"lastPublishedBy": "5a587cc1-e018-4859-a0a8-e842650b9d64","version": 2,"se_subjects": ["Mathematics"],"license": "CC BY 4.0","prevState": "Review","size": 374996.0,"lastPublishedOn": "2021-03-16T06:19:22.931+0000","name": "Untitled","topic": ["Speed, Distance and Time"],"status": "Live","code": "2544c8b8-7946-b6c0-e1c7-ced4aee4ea8c","credentials": {"enabled": "No"},"prevStatus": "Processing","origin": "do_11323724954450329611930","streamingUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/ecml/do_1132372524622561281279-latest","medium": ["English"],"idealScreenSize": "normal","createdOn": "2021-03-16T06:19:04.712+0000","se_boards": ["CBSE"],"processId": "9995e013-a7c9-4da1-b2c9-2f59da33414a","contentDisposition": "inline","lastUpdatedOn": "2021-03-16T06:19:20.817+0000","originData": {"identifier": "do_11323724954450329611930","repository": "https://dock.sunbirded.org/api/content/v1/read/do_11323724954450329611930"},"collectionId": "do_11323721176353996811921","dialcodeRequired": "No","editorVersion": 3,"lastStatusChangedOn": "2021-03-16T06:19:26.147+0000","creator": "N18","os": ["All"],"questionCategories": ["MTF"],"cloudStorageKey": "content/do_1132372524622561281279/artifact/1615875430184_do_11323724954450329611930.zip","se_FWIds": ["ekstep_ncert_k-12"],"marks": "2","bloomsLevel": ["Apply"],"pkgVersion": 1.0,"versionKey": "1615875560817","idealScreenDensity": "hdpi","framework": "ekstep_ncert_k-12","depth": 3,"s3Key": "ecar_files/do_1132372524622561281279/untitled_1615875562931_do_1132372524622561281279_1.0.ecar","lastSubmittedOn": "2021-03-16T06:19:17.005+0000","createdBy": "60f91e9e-34ee-4f9f-a907-d312d0e8063e","se_topics": ["Speed, Distance and Time"],"compatibilityLevel": 1,"itemSetPreviewUrl": "https://dockstorage.blob.core.windows.net/sunbird-content-dock/content/do_11323724954450329611930/artifact/do_11323724954450329611930_1615875429226.pdf","board": "CBSE","programId": "800eb440-8613-11eb-a663-4f63bbe94184"}],"contentDisposition": "inline","lastUpdatedOn": "2021-05-20T08:58:33.466+0000","contentEncoding": "gzip","contentType": "TextBookUnit","dialcodeRequired": "Yes","identifier": "do_1132833371215134721712","lastStatusChangedOn": "2021-05-20T08:58:33.466+0000","audience": ["Student"],"os": ["All"],"visibility": "Parent","discussionForum": {"enabled": "Yes"},"index": 1,"mediaType": "content","osId": "org.ekstep.launcher","languageCode": ["en"],"version": 2,"versionKey": "1621501113466","license": "CC BY 4.0","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 2,"compatibilityLevel": 1,"name": "5.1 Parts of Body","topic": ["Role Of The Sense Organs"],"status": "Draft"},{"ownershipType": ["createdBy"],"parent": "do_2222","code": "do_1132828084877066241529","keywords": [],"credentials": {"enabled": "No"},"channel": "0126825293972439041","description": "","language": ["English"],"mimeType": "application/vnd.ekstep.content-collection","idealScreenSize": "normal","createdOn": "2021-05-20T08:58:33.476+0000","objectType": "Content","primaryCategory": "Textbook Unit","children": [{"ownershipType": ["createdBy"],"parent": "do_1132833371215953921722","code": "do_1132828084876492801521","keywords": ["test key","check"],"credentials": {"enabled": "No"},"channel": "0126825293972439041","description": "","language": ["English"],"mimeType": "application/vnd.ekstep.content-collection","idealScreenSize": "normal","createdOn": "2021-05-20T08:58:33.468+0000","objectType": "Content","primaryCategory": "Textbook Unit","children": [{"ownershipType": ["createdBy"],"parent": "do_1132833371215298561714","code": "do_1132828084876820481527","keywords": ["abcd","cgf"],"credentials": {"enabled": "No"},"channel": "0126825293972439041","description": "labeled new","language": ["English"],"mimeType": "application/vnd.ekstep.content-collection","idealScreenSize": "normal","createdOn": "2021-05-20T08:58:33.464+0000","objectType": "Content","primaryCategory": "Textbook Unit","children": [{"ownershipType": ["createdBy"],"parent": "do_1132833371214970881710","previewUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/assets/do_1132338069147811841118/test-874-kb.mp4","channel": "b00bc992ef25f1a9a8d63291e20efc8d","downloadUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_1132338069147811841118/untitled-content_1615468830522_do_1132338069147811841118_2.0.ecar","organisation": ["Sunbird"],"language": ["English"],"mimeType": "video/mp4","variants": {"spine": {"ecarUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_1132338069147811841118/untitled-content_1615468834470_do_1132338069147811841118_2.0_spine.ecar","size": 1361.0}},"objectType": "Content","primaryCategory": "Learning Resource","appId": "dev.sunbird.portal","contentEncoding": "identity","artifactUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/assets/do_1132338069147811841118/test-874-kb.mp4","lockKey": "d73707c8-9999-4fc9-9b34-0207f74faf43","sYS_INTERNAL_LAST_UPDATED_ON": "2021-03-12T08:10:31.335+0000","contentType": "Resource","trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_1132338069147811841118","audience": ["Student"],"visibility": "Default","consumerId": "273f3b18-5dda-4a27-984a-060c7cd398d3","index": 1,"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"],"lastPublishedBy": "8454cb21-3ce9-4e30-85b5-fade097880d8","version": 2,"license": "CC BY 4.0","prevState": "Live","size": 849876.0,"lastPublishedOn": "2021-03-11T13:20:30.514+0000","name": "Untitled Content","status": "Live","code": "9deb2c69-7240-472a-98e7-ed438e76262b","credentials": {"enabled": "No"},"prevStatus": "Processing","streamingUrl": "https://sunbirddevmedia-inct.streaming.media.azure.net/f17bccc5-cab3-4da8-a5eb-11d7211f1507/test-874-kb.ism/manifest(format=m3u8-aapl-v3)","idealScreenSize": "normal","createdOn": "2021-03-11T09:29:05.654+0000","contentDisposition": "inline","lastUpdatedOn": "2021-03-11T13:20:28.256+0000","dialcodeRequired": "No","lastStatusChangedOn": "2021-03-11T18:50:28.256+0530","createdFor": ["ORG_001"],"creator": "Reviewer User","os": ["All"],"se_FWIds": ["NCFCOPY"],"pkgVersion": 2.0,"versionKey": "1615455090358","idealScreenDensity": "hdpi","framework": "NCFCOPY","depth": 5,"s3Key": "ecar_files/do_1132338069147811841118/untitled-content_1615468830522_do_1132338069147811841118_2.0.ecar","createdBy": "95e4942d-cbe8-477d-aebd-ad8e6de4bfc8","compatibilityLevel": 1,"resourceType": "Learn"},{"ownershipType": ["createdBy"],"parent": "do_1132833371214970881710","unitIdentifiers": ["do_1132238266042040321422"],"copyright": "2021 MIT","previewUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/ecml/do_11322383952751820816-latest","plugins": [{"identifier": "org.sunbird.questionunit.quml","semanticVersion": "1.1"}],"subject": ["Hindi"],"channel": "01309282781705830427","downloadUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_11322383952751820816/sapractice_1614238238045_do_11322383952751820816_1.0.ecar","language": ["English"],"source": "https://dock.sunbirded.org/api/content/v1/read/do_1132238287156183041424","mimeType": "application/vnd.ekstep.ecml-archive","variants": {"spine": {"ecarUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_11322383952751820816/sapractice_1614238238800_do_11322383952751820816_1.0_spine.ecar","size": 13171.0}},"objectType": "Content","se_mediums": ["English"],"gradeLevel": ["Class 10"],"primaryCategory": "Exam Question","appId": "dev.dock.portal","contentEncoding": "gzip","artifactUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/do_11322383952751820816/artifact/1614237122171_do_1132238287156183041424.zip","sYS_INTERNAL_LAST_UPDATED_ON": "2021-02-25T07:30:44.916+0000","contentType": "Resource","se_gradeLevels": ["Class 10"],"trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_11322383952751820816","audience": ["Student"],"visibility": "Default","author": "anusha","maxQuestions": 1,"consumerId": "273f3b18-5dda-4a27-984a-060c7cd398d3","index": 2,"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"],"lastPublishedBy": "5a587cc1-e018-4859-a0a8-e842650b9d64","version": 2,"se_subjects": ["Hindi"],"license": "CC BY 4.0","prevState": "Review","size": 362236.0,"lastPublishedOn": "2021-02-25T07:30:38.043+0000","name": "sa:practice","status": "Live","code": "f239c77e-ed71-9133-0145-7468a92bce79","credentials": {"enabled": "No"},"prevStatus": "Processing","origin": "do_1132238287156183041424","streamingUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/ecml/do_11322383952751820816-latest","medium": ["English"],"idealScreenSize": "normal","createdOn": "2021-02-25T07:30:23.577+0000","se_boards": ["CBSE"],"processId": "04d5aec9-ed09-4a57-963d-9fa654fecf8d","contentDisposition": "inline","lastUpdatedOn": "2021-02-25T07:30:37.956+0000","originData": {"identifier": "do_1132238287156183041424","repository": "https://dock.sunbirded.org/api/content/v1/read/do_1132238287156183041424"},"collectionId": "do_1132238266036551681415","dialcodeRequired": "No","editorVersion": 3,"lastStatusChangedOn": "2021-02-25T07:30:44.908+0000","creator": "anusha","os": ["All"],"questionCategories": ["SA"],"cloudStorageKey": "content/do_11322383952751820816/artifact/1614237122171_do_1132238287156183041424.zip","se_FWIds": ["ekstep_ncert_k-12"],"marks": "12","pkgVersion": 1.0,"versionKey": "1614238237956","idealScreenDensity": "hdpi","framework": "ekstep_ncert_k-12","depth": 5,"s3Key": "ecar_files/do_11322383952751820816/sapractice_1614238238045_do_11322383952751820816_1.0.ecar","lastSubmittedOn": "2021-02-25T07:30:36.709+0000","createdBy": "19ba0e4e-9285-4335-8dd0-f674bf03fa4d","compatibilityLevel": 1,"itemSetPreviewUrl": "https://dockstorage.blob.core.windows.net/sunbird-content-dock/content/do_1132238287156183041424/artifact/do_1132238287156183041424_1614237121022.pdf","board": "CBSE","programId": "94564340-7737-11eb-96e0-29a9f8ed81cf"},{"ownershipType": ["createdBy"],"parent": "do_1132833371214970881710","unitIdentifiers": ["do_11322165488232038412588"],"copyright": "2021 MIT","previewUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/ecml/do_1132216902566133761410-latest","plugins": [{"identifier": "org.sunbird.questionunit.quml","semanticVersion": "1.1"}],"subject": ["Environmental Studies"],"channel": "01309282781705830427","downloadUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_1132216902566133761410/mcqmcq_1613975872529_do_1132216902566133761410_1.0.ecar","language": ["English"],"source": "https://dock.sunbirded.org/api/content/v1/read/do_11322168163282944012605","mimeType": "application/vnd.ekstep.ecml-archive","variants": {"spine": {"ecarUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_1132216902566133761410/mcqmcq_1613975873161_do_1132216902566133761410_1.0_spine.ecar","size": 17182.0}},"objectType": "Content","se_mediums": ["English"],"gradeLevel": ["Class 10"],"primaryCategory": "Exam Question","appId": "dev.dock.portal","contentEncoding": "gzip","artifactUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/do_1132216902566133761410/artifact/1613975740738_do_11322168163282944012605.zip","sYS_INTERNAL_LAST_UPDATED_ON": "2021-02-22T06:37:55.328+0000","contentType": "Resource","se_gradeLevels": ["Class 10"],"trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_1132216902566133761410","audience": ["Student"],"visibility": "Default","author": "color4","maxQuestions": 1,"consumerId": "7411b6bd-89f3-40ec-98d1-229dc64ce77d","learningOutcome": ["Understand the importance of values in life"],"index": 3,"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"],"lastPublishedBy": "ae94b68c-a535-4dce-8e7a-fb9662b0ad68","version": 2,"se_subjects": ["Environmental Studies"],"license": "CC BY 4.0","prevState": "Review","size": 370363.0,"lastPublishedOn": "2021-02-22T06:37:52.529+0000","name": "MCQMCQ","topic": ["Animals"],"status": "Live","code": "0cbae0f8-e3eb-1d31-e2e5-0337dc7d697d","credentials": {"enabled": "No"},"prevStatus": "Processing","origin": "do_11322168163282944012605","streamingUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/ecml/do_1132216902566133761410-latest","medium": ["English"],"idealScreenSize": "normal","createdOn": "2021-02-22T06:37:41.405+0000","se_boards": ["CBSE"],"processId": "fbcec2af-cb7a-4ed1-8683-ff04b475947e","contentDisposition": "inline","lastUpdatedOn": "2021-02-22T06:37:52.447+0000","originData": {"identifier": "do_11322168163282944012605","repository": "https://dock.sunbirded.org/api/content/v1/read/do_11322168163282944012605"},"collectionId": "do_11322165488181248012584","dialcodeRequired": "No","editorVersion": 3,"lastStatusChangedOn": "2021-02-22T06:37:55.314+0000","creator": "color4","os": ["All"],"questionCategories": ["MCQ"],"cloudStorageKey": "content/do_1132216902566133761410/artifact/1613975740738_do_11322168163282944012605.zip","se_FWIds": ["ekstep_ncert_k-12"],"marks": "1","bloomsLevel": ["Understand"],"pkgVersion": 1.0,"versionKey": "1613975872447","idealScreenDensity": "hdpi","framework": "ekstep_ncert_k-12","depth": 5,"s3Key": "ecar_files/do_1132216902566133761410/mcqmcq_1613975872529_do_1132216902566133761410_1.0.ecar","lastSubmittedOn": "2021-02-22T06:37:51.179+0000","createdBy": "0ce5b67e-b48e-489b-a818-e938e8bfc14b","se_topics": ["Animals"],"compatibilityLevel": 1,"itemSetPreviewUrl": "https://dockstorage.blob.core.windows.net/sunbird-content-dock/content/do_11322168163282944012605/artifact/do_11322168163282944012605_1613975739805.pdf","board": "CBSE","programId": "b2433a00-74cd-11eb-9f3c-f39a9ab9f5ce"}],"contentDisposition": "inline","lastUpdatedOn": "2021-05-20T08:58:33.464+0000","contentEncoding": "gzip","contentType": "TextBookUnit","dialcodeRequired": "Yes","identifier": "do_1132833371214970881710","lastStatusChangedOn": "2021-05-20T08:58:33.464+0000","audience": ["Student"],"os": ["All"],"visibility": "Parent","discussionForum": {"enabled": "Yes"},"index": 1,"mediaType": "content","osId": "org.ekstep.launcher","languageCode": ["en"],"version": 2,"versionKey": "1621501113464","license": "CC BY 4.0","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 4,"compatibilityLevel": 1,"name": "dsffgdg","topic": [],"status": "Draft"}],"contentDisposition": "inline","lastUpdatedOn": "2021-05-20T08:58:33.468+0000","contentEncoding": "gzip","contentType": "TextBookUnit","dialcodeRequired": "No","identifier": "do_1132833371215298561714","lastStatusChangedOn": "2021-05-20T08:58:33.468+0000","audience": ["Student"],"os": ["All"],"visibility": "Parent","discussionForum": {"enabled": "Yes"},"index": 1,"mediaType": "content","osId": "org.ekstep.launcher","languageCode": ["en"],"version": 2,"versionKey": "1621501113468","license": "CC BY 4.0","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 3,"compatibilityLevel": 1,"name": "5.2.1 Respiratory System","topic": ["Look and say","Role Of The Sense Organs"],"status": "Draft"}],"contentDisposition": "inline","lastUpdatedOn": "2021-05-20T08:58:33.476+0000","contentEncoding": "gzip","contentType": "TextBookUnit","dialcodeRequired": "No","identifier": "do_1132833371215953921722","lastStatusChangedOn": "2021-05-20T08:58:33.476+0000","audience": ["Student"],"os": ["All"],"visibility": "Parent","discussionForum": {"enabled": "Yes"},"index": 2,"mediaType": "content","osId": "org.ekstep.launcher","languageCode": ["en"],"version": 2,"versionKey": "1621501113476","license": "CC BY 4.0","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 2,"compatibilityLevel": 1,"name": "5.2 Organ Systems","topic": [],"status": "Draft"},{"ownershipType": ["createdBy"],"parent": "do_2222","copyright": "Sunbird","previewUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/assets/do_1132344630588948481134/test-874-kb.mp4","subject": ["Math"],"channel": "b00bc992ef25f1a9a8d63291e20efc8d","downloadUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_1132344630588948481134/untitled-content_1615535618825_do_1132344630588948481134_2.0.ecar","organisation": ["Sunbird"],"showNotification": true,"language": ["English"],"mimeType": "video/mp4","variants": {"spine": {"ecarUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_1132344630588948481134/untitled-content_1615535619590_do_1132344630588948481134_2.0_spine.ecar","size": 35301.0}},"objectType": "Content","se_mediums": ["English"],"gradeLevel": ["Grade 1"],"appIcon": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/do_1132344630588948481134/artifact/2a4b8abd789184932399d222d03d9b5c.thumb.jpg","primaryCategory": "Learning Resource","appId": "dev.sunbird.portal","contentEncoding": "identity","artifactUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/assets/do_1132344630588948481134/test-874-kb.mp4","lockKey": "1d28d983-2704-44bd-803e-5feb4e62da62","sYS_INTERNAL_LAST_UPDATED_ON": "2021-03-12T08:10:34.367+0000","contentType": "Resource","se_gradeLevels": ["Grade 1"],"trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_1132344630588948481134","lastUpdatedBy": "95e4942d-cbe8-477d-aebd-ad8e6de4bfc8","audience": ["Student"],"visibility": "Default","consumerId": "273f3b18-5dda-4a27-984a-060c7cd398d3","index": 3,"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"],"lastPublishedBy": "8454cb21-3ce9-4e30-85b5-fade097880d8","version": 2,"se_subjects": ["Math"],"license": "CC BY 4.0","prevState": "Review","size": 883817.0,"lastPublishedOn": "2021-03-12T07:53:38.825+0000","name": "Untitled Content","status": "Live","code": "8851e754-6e20-44d4-9070-e1a9664163ad","credentials": {"enabled": "No"},"prevStatus": "Review","description": "updated desrciption","streamingUrl": "https://sunbirddevmedia-inct.streaming.media.azure.net/40ae07aa-069e-4056-8f2b-014bc9a2d21b/test-874-kb.ism/manifest(format=m3u8-aapl-v3)","medium": ["English"],"posterImage": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/do_11299104587967692816/artifact/2a4b8abd789184932399d222d03d9b5c.jpg","idealScreenSize": "normal","createdOn": "2021-03-12T07:44:01.371+0000","se_boards": ["NCERT"],"copyrightYear": 2020,"contentDisposition": "inline","licenseterms": "By creating any type of content (resources, books, courses etc.) on DIKSHA, you consent to publish it under the Creative Commons License Framework. Please choose the applicable creative commons license you wish to apply to your content.","lastUpdatedOn": "2021-03-12T07:53:38.505+0000","dialcodeRequired": "No","lastStatusChangedOn": "2021-03-12T07:53:38.494+0000","createdFor": ["ORG_001"],"creator": "Reviewer User","os": ["All"],"se_FWIds": ["NCFCOPY"],"pkgVersion": 2.0,"versionKey": "1615535618583","idealScreenDensity": "hdpi","framework": "NCFCOPY","depth": 2,"s3Key": "ecar_files/do_1132344630588948481134/untitled-content_1615535618825_do_1132344630588948481134_2.0.ecar","lastSubmittedOn": "2021-03-12T07:53:10.005+0000","createdBy": "95e4942d-cbe8-477d-aebd-ad8e6de4bfc8","compatibilityLevel": 1,"board": "NCERT","resourceType": "Learn"}],"contentDisposition": "inline","lastUpdatedOn": "2021-05-20T08:58:33.470+0000","contentEncoding": "gzip","contentType": "TextBookUnit","dialcodeRequired": "No","identifier": "do_2222","lastStatusChangedOn": "2021-05-20T08:58:33.470+0000","audience": ["Student"],"os": ["All"],"visibility": "Parent","discussionForum": {"enabled": "Yes"},"index": 1,"mediaType": "content","osId": "org.ekstep.launcher","languageCode": ["en"],"version": 2,"versionKey": "1621501113470","license": "CC BY 4.0","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 1,"compatibilityLevel": 1,"name": "5. Human Body","topic": [],"status": "Draft"}],"appId": "dev.sunbird.portal","contentEncoding": "gzip","sYS_INTERNAL_LAST_UPDATED_ON": "2021-05-20T09:12:06.988+0000","contentType": "TextBook","trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_1111","audience": ["Student"],"visibility": "Default","consumerId": "01814e02-fc27-4165-ae53-3d1816e55817","childNodes": ["do_1132339274094346241120","do_1132833371215872001720","do_1132833371215134721712","do_2222","do_113223967141863424174","do_1132833371214970881710","do_1132833371215708161718","do_1132372524622561281279","do_1132338069147811841118","do_1132833371215298561714","do_1132833371215953921722","do_11322383952751820816","do_1132216902566133761410","do_1132344630588948481134"],"discussionForum": {"enabled": "Yes"},"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"],"version": 2,"license": "CC BY 4.0","name": "TestCSVUpload","status": "Draft","code": "org.sunbird.yhqB6L","credentials": {"enabled": "No"},"description": "Enter description for TextBook","medium": ["English"],"idealScreenSize": "normal","createdOn": "2021-05-19T15:00:44.279+0000","contentDisposition": "inline","additionalCategories": ["Textbook"],"lastUpdatedOn": "2021-05-20T07:10:32.805+0000","dialcodeRequired": "No","lastStatusChangedOn": "2021-05-19T15:00:44.279+0000","createdFor": ["0126825293972439041"],"creator": "Book Creator","os": ["All"],"versionKey": "1621501113536","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 0,"createdBy": "8454cb21-3ce9-4e30-85b5-fade097880d8","compatibilityLevel": 1,"userConsent": "Yes","board": "State (Tamil Nadu)","resourceType": "Book"}""".stripMargin + val response = new Response + response.put("hierarchy", hierarchyString) + } + } \ No newline at end of file From b0734f2daab7af50e8eb95e1a0c308fd1fe963b7 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Mon, 25 Apr 2022 21:35:20 +0530 Subject: [PATCH 041/490] Issue #SB-19655 feat: Collection DIAL link API refactor from LP to KP --- .../sunbird/content/dial/DIALManager.scala | 16 +++++--- .../content/dial/DIALManagerTest.scala | 41 +++++++++++++++++++ 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala index 100e688b1..9365e136e 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala @@ -140,9 +140,7 @@ object DIALManager { val childrenDIALMap = getChildrenDIALMap(updatedChildrenHierarchy, requestMap) val consolidatedUnitDIALMap = if (!requestMap.contains(objectId)) childrenDIALMap else childrenDIALMap ++ Map(objectId -> requestMap(objectId)) - val duplicateDIALCodes: Map[String, Set[String]] = validateDuplicateDIALCodes(consolidatedUnitDIALMap.filter(rec => rec._2.asInstanceOf[List[String]].nonEmpty)) - if (duplicateDIALCodes.nonEmpty) - throw new ClientException(DIALErrors.ERR_DUPLICATE_DIAL_CODES, DIALErrors.ERR_DUPLICATE_DIAL_CODES_MSG + duplicateDIALCodes) + validateDuplicateDIALCodes(consolidatedUnitDIALMap.filter(rec => rec._2.asInstanceOf[List[String]].nonEmpty)) val updatedHierarchy = new java.util.HashMap[String, AnyRef]() updatedHierarchy.put("identifier", objectId) @@ -246,7 +244,15 @@ object DIALManager { }).filter(msg => msg.nonEmpty).flatten.toMap[String, AnyRef] } - def validateDuplicateDIALCodes(unitDIALCodesMap: Map[String, AnyRef]): Map[String, Set[String]] = { - unitDIALCodesMap.groupBy(_._2).collect { case (key, group: Map[String, AnyRef]) if group.size > 1 => (key.asInstanceOf[List[String]].head, group.keySet) } + def validateDuplicateDIALCodes(unitDIALCodesMap: Map[String, AnyRef]): Unit = { + val duplicateDIALCodes = unitDIALCodesMap.flatMap(mapRec => mapRec._2.asInstanceOf[List[String]].flatMap(listRec => { + val dupUnitsList: List[String] = unitDIALCodesMap.flatMap(loopMapRec => if(loopMapRec._1 != mapRec._1 && loopMapRec._2.asInstanceOf[List[String]].contains(listRec)) { + List(loopMapRec._1, mapRec._1) + } else List.empty[String]).filter(unitRec => unitRec.nonEmpty).toList + Map(listRec -> dupUnitsList) + })).filter(unitRec => unitRec._2.nonEmpty) + + if (duplicateDIALCodes.nonEmpty) + throw new ClientException(DIALErrors.ERR_DUPLICATE_DIAL_CODES, DIALErrors.ERR_DUPLICATE_DIAL_CODES_MSG + duplicateDIALCodes) } } diff --git a/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala b/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala index 86fef60ff..8895136f7 100644 --- a/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala +++ b/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala @@ -216,6 +216,14 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory }) } + "validateDuplicateDIALCodes with duplicate dial codes" should "throw client exception" in { + val exception = intercept[ClientException] { + DIALManager.validateDuplicateDIALCodes(Map("do_2222" -> List("E8B7Z6", "R4X2P2"), "do_1111" -> List("N4Z7D5", "E8B7Z6", "L4A6W8", "D2E1J9", "R4X2P2"))) + } + println("validateDuplicateDIALCodes:: exception:: " + exception.getMessage) + assert(exception.getErrCode == "ERR_DUPLICATE_DIAL_CODES") + } + def getDIALSearchResponse:Response = { val resString = "{\n \"id\": \"sunbird.dialcode.search\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-04-21T19:39:14ZZ\",\n \"params\": {\n \"resmsgid\": \"1dfcc25b-6c37-49f8-a6c3-7185063e8752\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"dialcodes\": [\n {\n \"dialcode_index\": 7609876,\n \"identifier\": \"N4Z7D5\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.603+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n },\n {\n \"dialcode_index\": 7610113,\n \"identifier\": \"E8B7Z6\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.635+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n },\n {\n \"dialcode_index\": 7610117,\n \"identifier\": \"R4X2P2\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.637+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n },\n {\n \"dialcode_index\": 7610961,\n \"identifier\": \"L4A6W8\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.734+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n },\n {\n \"dialcode_index\": 7611164,\n \"identifier\": \"D2E1J9\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.759+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n }\n ],\n \"count\": 5\n }\n}"; JsonUtils.deserialize(resString, classOf[Response]) @@ -365,6 +373,39 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory node } + def getCollectionDuplicateDIALRequest(): Request = { + val request = new Request() + request.setObjectType("Content") + request.setContext(getContext()) + request.getContext.put("linkType","collection") + request.getContext.put("identifier","do_1111") + + val reqMap : java.util.Map[String, AnyRef] = new util.HashMap[String, AnyRef](){{ + put("content", new util.ArrayList[util.Map[String, AnyRef]](){{ + add(new util.HashMap[String, AnyRef](){{ + put("identifier","do_1111") + put("dialcode", new util.ArrayList[String](){{ + add("N4Z7D5") + add("E8B7Z6") + add("L4A6W8") + add("D2E1J9") + add("R4X2P2") + }}) + }}) + add(new util.HashMap[String, AnyRef](){{ + put("identifier",new util.ArrayList[String](){{ + add("do_2222") + }}) + put("dialcode", "R4X2P2") + }}) + + }}) + }} + + request.putAll(reqMap) + request + } + private def getCategoryDefinitionNode(identifier: String): Node = { val node = new Node() node.setIdentifier(identifier) From f06382be4ef4a09caad67df52403a0b168830946 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Mon, 25 Apr 2022 21:54:50 +0530 Subject: [PATCH 042/490] Issue #SB-19655 feat: Collection DIAL link API refactor from LP to KP --- .../content/dial/DIALManagerTest.scala | 84 ++++++++++++++++--- 1 file changed, 74 insertions(+), 10 deletions(-) diff --git a/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala b/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala index 8895136f7..0ddc60dac 100644 --- a/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala +++ b/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala @@ -184,15 +184,12 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory (graphDB.upsertNode(_: String, _: Node, _: Request)).expects(*, *, *).returns(Future(getNode("do_1111"))) val request = getContentDIALRequest() - println("DIALManagerTest:: link content:: request:: " + request) - DIALManager.link(request).map(result => { assert(result.getResponseCode.toString=="OK") }) } - "link DIAL with valid request for collections" should "update the collection content successfully" in { - + "link DIAL with valid request for collections" should "update the collection successfully" in { (oec.httpUtil _).expects().returns(httpUtil) (oec.graphService _).expects().returns(graphDB).anyNumberOfTimes() (httpUtil.post(_: String, _:java.util.Map[String, AnyRef], _:java.util.Map[String, String])).expects(*, *, *).returns(getDIALSearchResponse) @@ -208,8 +205,6 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory val request = getCollectionDIALRequest() - println("DIALManagerTest:: link collection:: request:: " + request) - val response = DIALManager.link(request) response.map(result => { assert(result.getResponseCode.toString=="OK") @@ -220,10 +215,51 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory val exception = intercept[ClientException] { DIALManager.validateDuplicateDIALCodes(Map("do_2222" -> List("E8B7Z6", "R4X2P2"), "do_1111" -> List("N4Z7D5", "E8B7Z6", "L4A6W8", "D2E1J9", "R4X2P2"))) } - println("validateDuplicateDIALCodes:: exception:: " + exception.getMessage) assert(exception.getErrCode == "ERR_DUPLICATE_DIAL_CODES") } + "link DIAL with valid node invalid unit Id for collections" should "update the collection partially" in { + (oec.httpUtil _).expects().returns(httpUtil) + (oec.graphService _).expects().returns(graphDB).anyNumberOfTimes() + (httpUtil.post(_: String, _:java.util.Map[String, AnyRef], _:java.util.Map[String, String])).expects(*, *, *).returns(getDIALSearchResponse) + + val nodes: util.List[Node] = getCategoryNode() + (graphDB.getNodeByUniqueIds(_: String, _: SearchCriteria)).expects(*, *).returns(Future(nodes)).anyNumberOfTimes() + + (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, *).returns(Future(getCassandraHierarchy())).anyNumberOfTimes() + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, *, *, *).returns(Future(getNode("do_1111"))).anyNumberOfTimes() + (graphDB.upsertNode(_: String, _: Node, _: Request)).expects(*, *, *).returns(Future(getNode("do_1111"))) + (graphDB.saveExternalProps(_: Request)).expects(*).returns(Future(new Response())) + (graphDB.getNodeProperty(_: String, _: String, _: String)).expects(*, *, *).returns(Future(new Property("versionKey", new org.neo4j.driver.internal.value.StringValue("1234")))) + + val request = getCollectionPartialSuccessRequest() + + val response = DIALManager.link(request) + response.map(result => { + assert(result.getResponseCode.toString=="PARTIAL_SUCCESS") + }) + } + + "link DIAL with invalid unit Id request for collection" should "respond with Resource not found message" in { + (oec.httpUtil _).expects().returns(httpUtil) + (oec.graphService _).expects().returns(graphDB).anyNumberOfTimes() + (httpUtil.post(_: String, _:java.util.Map[String, AnyRef], _:java.util.Map[String, String])).expects(*, *, *).returns(getDIALSearchResponse) + + val nodes: util.List[Node] = getCategoryNode() + (graphDB.getNodeByUniqueIds(_: String, _: SearchCriteria)).expects(*, *).returns(Future(nodes)).anyNumberOfTimes() + + (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, *).returns(Future(getCassandraHierarchy())).anyNumberOfTimes() + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, *, *, *).returns(Future(getNode("do_1111"))).anyNumberOfTimes() + (graphDB.saveExternalProps(_: Request)).expects(*).returns(Future(new Response())) + + val request = getCollectionRNFRequest() + + val response = DIALManager.link(request) + response.map(result => { + assert(result.getResponseCode.toString=="RESOURCE_NOT_FOUND") + }) + } + def getDIALSearchResponse:Response = { val resString = "{\n \"id\": \"sunbird.dialcode.search\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-04-21T19:39:14ZZ\",\n \"params\": {\n \"resmsgid\": \"1dfcc25b-6c37-49f8-a6c3-7185063e8752\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"dialcodes\": [\n {\n \"dialcode_index\": 7609876,\n \"identifier\": \"N4Z7D5\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.603+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n },\n {\n \"dialcode_index\": 7610113,\n \"identifier\": \"E8B7Z6\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.635+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n },\n {\n \"dialcode_index\": 7610117,\n \"identifier\": \"R4X2P2\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.637+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n },\n {\n \"dialcode_index\": 7610961,\n \"identifier\": \"L4A6W8\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.734+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n },\n {\n \"dialcode_index\": 7611164,\n \"identifier\": \"D2E1J9\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.759+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n }\n ],\n \"count\": 5\n }\n}"; JsonUtils.deserialize(resString, classOf[Response]) @@ -373,7 +409,7 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory node } - def getCollectionDuplicateDIALRequest(): Request = { + def getCollectionPartialSuccessRequest(): Request = { val request = new Request() request.setObjectType("Content") request.setContext(getContext()) @@ -389,12 +425,11 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory add("E8B7Z6") add("L4A6W8") add("D2E1J9") - add("R4X2P2") }}) }}) add(new util.HashMap[String, AnyRef](){{ put("identifier",new util.ArrayList[String](){{ - add("do_2222") + add("do_22223") }}) put("dialcode", "R4X2P2") }}) @@ -406,6 +441,35 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory request } + def getCollectionRNFRequest(): Request = { + val request = new Request() + request.setObjectType("Content") + request.setContext(getContext()) + request.getContext.put("linkType","collection") + request.getContext.put("identifier","do_1111") + + val reqMap : java.util.Map[String, AnyRef] = new util.HashMap[String, AnyRef](){{ + put("content", new util.ArrayList[util.Map[String, AnyRef]](){{ + add(new util.HashMap[String, AnyRef](){{ + put("identifier",new util.ArrayList[String](){{ + add("do_22223") + }}) + put("dialcode", new util.ArrayList[String](){{ + add("N4Z7D5") + add("E8B7Z6") + add("L4A6W8") + add("D2E1J9") + add("R4X2P2") + }}) + }}) + + }}) + }} + + request.putAll(reqMap) + request + } + private def getCategoryDefinitionNode(identifier: String): Node = { val node = new Node() node.setIdentifier(identifier) From 72e467fcd471278d95a1cf2e5f0a744002e1c621 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Tue, 26 Apr 2022 10:35:08 +0530 Subject: [PATCH 043/490] Issue #SB-19655 feat: Collection DIAL link API refactor from LP to KP --- .../search/client/ElasticSearchUtil.java | 48 +++++++++++-------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java b/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java index 8d725f96c..ac54818bd 100644 --- a/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java +++ b/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java @@ -238,7 +238,7 @@ public static String getDocumentAsStringById(String indexName, String documentTy } public static List getMultiDocumentAsStringByIdList(String indexName, String documentType, - List documentIdList) throws IOException { + List documentIdList) throws IOException { List finalResult = new ArrayList(); MultiGetRequest request = new MultiGetRequest(); documentIdList.forEach(docId -> request.add(indexName, documentType, docId)); @@ -279,7 +279,7 @@ public static void bulkIndexWithIndexId(String indexName, String documentType, M } public static void bulkIndexWithAutoGenerateIndexId(String indexName, String documentType, - List> jsonObjects) + List> jsonObjects) throws Exception { if (isIndexExists(indexName)) { RestHighLevelClient client = getClient(indexName); @@ -305,7 +305,7 @@ public static void bulkIndexWithAutoGenerateIndexId(String indexName, String doc @SuppressWarnings("rawtypes") public static List textSearch(Class objectClass, Map matchCriterias, String indexName, - String indexType, int limit) throws Exception { + String indexType, int limit) throws Exception { SearchResponse result = search(matchCriterias, null, indexName, indexType, null, false, limit); return getDocumentsFromSearchResult(result, objectClass); } @@ -342,7 +342,9 @@ public static List getDocumentsFromHitsWithScore(SearchHits hits) { } @SuppressWarnings({ "rawtypes" }) - public static List textSearchReturningId(Map matchCriterias, String indexName, String indexType) throws Exception { + public static List textSearchReturningId(Map matchCriterias, String indexName, + String indexType) + throws Exception { SearchResponse result = search(matchCriterias, null, indexName, indexType, null, false, 100); return getDocumentsFromSearchResultWithId(result); } @@ -366,13 +368,14 @@ public static List getDocumentsFromHitsWithId(SearchHits hits) { @SuppressWarnings({ "rawtypes" }) public static List wildCardSearch(Class objectClass, String textKeyWord, String wordWildCard, - String indexName, String indexType, int limit) throws Exception { + String indexName, String indexType, int limit) throws Exception { SearchResponse result = wildCardSearch(textKeyWord, wordWildCard, indexName, indexType, limit); return getDocumentsFromSearchResult(result, objectClass); } public static SearchResponse wildCardSearch(String textKeyWord, String wordWildCard, String indexName, - String indexType, int limit) throws Exception { + String indexType, int limit) + throws Exception { SearchSourceBuilder query = buildJsonForWildCardQuery(textKeyWord, wordWildCard, indexName); query.size(limit); return search(indexName, indexType, query); @@ -380,15 +383,16 @@ public static SearchResponse wildCardSearch(String textKeyWord, String wordWildC @SuppressWarnings({ "rawtypes" }) public static List textFiltersSearch(Class objectClass, Map searchCriteria, - Map textFiltersMap, String indexName, String indexType, int limit) throws Exception { + Map textFiltersMap, String indexName, String indexType, int limit) + throws Exception { SearchResponse result = search(searchCriteria, textFiltersMap, indexName, indexType, null, false, limit); return getDocumentsFromSearchResult(result, objectClass); } @SuppressWarnings("rawtypes") public static Map textFiltersGroupBySearch(Class objectClass, Map searchCriteria, - Map textFiltersMap, List> groupByList, String indexName, - String indexType) throws Exception { + Map textFiltersMap, List> groupByList, String indexName, + String indexType) throws Exception { SearchResponse result = search(searchCriteria, textFiltersMap, indexName, indexType, groupByList, false, resultLimit); List documents = getDocumentsFromSearchResult(result, objectClass); @@ -404,28 +408,30 @@ public static Map textFiltersGroupBySearch(Class objectClass, Ma @SuppressWarnings("rawtypes") public static List textSearch(Class objectClass, Map matchCriterias, - Map textFiltersMap, String indexName, String indexType) throws Exception { + Map textFiltersMap, String indexName, String indexType) throws Exception { SearchResponse result = search(matchCriterias, textFiltersMap, indexName, indexType, null, false, resultLimit); return getDocumentsFromSearchResult(result, objectClass); } @SuppressWarnings("rawtypes") public static List textSearch(Class objectClass, Map matchCriterias, - Map textFiltersMap, String indexName, String indexType, - List> groupByList, int limit) throws Exception { - SearchResponse result = search(matchCriterias, textFiltersMap, indexName, indexType, groupByList, false, limit); + Map textFiltersMap, String indexName, String indexType, + List> groupByList, int limit) throws Exception { + SearchResponse result = search(matchCriterias, textFiltersMap, indexName, indexType, groupByList, false, + limit); return getDocumentsFromSearchResult(result, objectClass); } public static SearchResponse search(Map matchCriterias, Map textFiltersMap, - String indexName, String indexType, List> groupBy, boolean isDistinct, int limit) + String indexName, String indexType, List> groupBy, boolean isDistinct, int limit) throws Exception { SearchSourceBuilder query = buildJsonForQuery(matchCriterias, textFiltersMap, groupBy, isDistinct, indexName); query.size(limit); return search(indexName, indexType, query); } - public static SearchResponse search(String indexName, String indexType, SearchSourceBuilder query) throws Exception { + public static SearchResponse search(String indexName, String indexType, SearchSourceBuilder query) + throws Exception { return getClient(indexName).search(new SearchRequest(indexName).source(query), RequestOptions.DEFAULT); } @@ -499,7 +505,8 @@ public static Map getCountFromAggregation(Aggregations aggregati @SuppressWarnings("rawtypes") public static Map getCountOfSearch(Class objectClass, Map matchCriterias, - String indexName, String indexType, List> groupByList, int limit) throws Exception { + String indexName, String indexType, List> groupByList, int limit) + throws Exception { SearchResponse result = search(matchCriterias, null, indexName, indexType, groupByList, false, limit); Aggregations aggregations = result.getAggregations(); return getCountFromAggregation(aggregations, groupByList); @@ -507,7 +514,7 @@ public static Map getCountOfSearch(Class objectClass, Map getDistinctCountOfSearch(Map matchCriterias, String IndexName, - String IndexType, List> groupByList) throws Exception { + String IndexType, List> groupByList) throws Exception { Map countMap = new HashMap(); SearchResponse result = search(matchCriterias, null, IndexName, IndexType, groupByList, true, 0); Aggregations aggregations = result.getAggregations(); @@ -536,8 +543,8 @@ public static Map getDistinctCountOfSearch(Map m @SuppressWarnings("unchecked") public static SearchSourceBuilder buildJsonForQuery(Map matchCriterias, - Map textFiltersMap, List> groupByList, boolean isDistinct, - String indexName) { + Map textFiltersMap, List> groupByList, boolean isDistinct, + String indexName) { SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); @@ -594,7 +601,8 @@ public static SearchSourceBuilder buildJsonForQuery(Map matchCri return searchSourceBuilder; } - private static SearchSourceBuilder buildJsonForWildCardQuery(String textKeyWord, String wordWildCard, String indexName) { + private static SearchSourceBuilder buildJsonForWildCardQuery(String textKeyWord, String wordWildCard, + String indexName) { return new SearchSourceBuilder().query(QueryBuilders.wildcardQuery(textKeyWord, wordWildCard)); } From 0bf890492e1ab752f38f715e5cfd81a76db895d9 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Tue, 26 Apr 2022 10:37:14 +0530 Subject: [PATCH 044/490] Issue #SB-19655 feat: Collection DIAL link API refactor from LP to KP --- .../org/sunbird/search/client/ElasticSearchUtil.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java b/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java index ac54818bd..eb87683f8 100644 --- a/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java +++ b/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java @@ -238,7 +238,7 @@ public static String getDocumentAsStringById(String indexName, String documentTy } public static List getMultiDocumentAsStringByIdList(String indexName, String documentType, - List documentIdList) throws IOException { + List documentIdList) throws IOException { List finalResult = new ArrayList(); MultiGetRequest request = new MultiGetRequest(); documentIdList.forEach(docId -> request.add(indexName, documentType, docId)); @@ -279,7 +279,7 @@ public static void bulkIndexWithIndexId(String indexName, String documentType, M } public static void bulkIndexWithAutoGenerateIndexId(String indexName, String documentType, - List> jsonObjects) + List> jsonObjects) throws Exception { if (isIndexExists(indexName)) { RestHighLevelClient client = getClient(indexName); @@ -305,7 +305,7 @@ public static void bulkIndexWithAutoGenerateIndexId(String indexName, String doc @SuppressWarnings("rawtypes") public static List textSearch(Class objectClass, Map matchCriterias, String indexName, - String indexType, int limit) throws Exception { + String indexType, int limit) throws Exception { SearchResponse result = search(matchCriterias, null, indexName, indexType, null, false, limit); return getDocumentsFromSearchResult(result, objectClass); } @@ -343,7 +343,7 @@ public static List getDocumentsFromHitsWithScore(SearchHits hits) { @SuppressWarnings({ "rawtypes" }) public static List textSearchReturningId(Map matchCriterias, String indexName, - String indexType) + String indexType) throws Exception { SearchResponse result = search(matchCriterias, null, indexName, indexType, null, false, 100); return getDocumentsFromSearchResultWithId(result); @@ -368,7 +368,7 @@ public static List getDocumentsFromHitsWithId(SearchHits hits) { @SuppressWarnings({ "rawtypes" }) public static List wildCardSearch(Class objectClass, String textKeyWord, String wordWildCard, - String indexName, String indexType, int limit) throws Exception { + String indexName, String indexType, int limit) throws Exception { SearchResponse result = wildCardSearch(textKeyWord, wordWildCard, indexName, indexType, limit); return getDocumentsFromSearchResult(result, objectClass); } From 173a091c6ab93f629f286a11779b0a61a23ceca8 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Tue, 26 Apr 2022 10:39:12 +0530 Subject: [PATCH 045/490] Issue #SB-19655 feat: Collection DIAL link API refactor from LP to KP --- .../main/java/org/sunbird/search/client/ElasticSearchUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java b/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java index eb87683f8..2f51c7cc9 100644 --- a/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java +++ b/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java @@ -238,7 +238,7 @@ public static String getDocumentAsStringById(String indexName, String documentTy } public static List getMultiDocumentAsStringByIdList(String indexName, String documentType, - List documentIdList) throws IOException { + List documentIdList) throws IOException { List finalResult = new ArrayList(); MultiGetRequest request = new MultiGetRequest(); documentIdList.forEach(docId -> request.add(indexName, documentType, docId)); From d6cc670de32767169243f78aa561e2311850f2e7 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Tue, 26 Apr 2022 10:41:13 +0530 Subject: [PATCH 046/490] Issue #SB-19655 feat: Collection DIAL link API refactor from LP to KP --- .../java/org/sunbird/search/client/ElasticSearchUtil.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java b/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java index 2f51c7cc9..cb9c4c086 100644 --- a/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java +++ b/search-api/search-core/src/main/java/org/sunbird/search/client/ElasticSearchUtil.java @@ -279,7 +279,7 @@ public static void bulkIndexWithIndexId(String indexName, String documentType, M } public static void bulkIndexWithAutoGenerateIndexId(String indexName, String documentType, - List> jsonObjects) + List> jsonObjects) throws Exception { if (isIndexExists(indexName)) { RestHighLevelClient client = getClient(indexName); @@ -305,7 +305,7 @@ public static void bulkIndexWithAutoGenerateIndexId(String indexName, String doc @SuppressWarnings("rawtypes") public static List textSearch(Class objectClass, Map matchCriterias, String indexName, - String indexType, int limit) throws Exception { + String indexType, int limit) throws Exception { SearchResponse result = search(matchCriterias, null, indexName, indexType, null, false, limit); return getDocumentsFromSearchResult(result, objectClass); } @@ -343,7 +343,7 @@ public static List getDocumentsFromHitsWithScore(SearchHits hits) { @SuppressWarnings({ "rawtypes" }) public static List textSearchReturningId(Map matchCriterias, String indexName, - String indexType) + String indexType) throws Exception { SearchResponse result = search(matchCriterias, null, indexName, indexType, null, false, 100); return getDocumentsFromSearchResultWithId(result); @@ -368,7 +368,7 @@ public static List getDocumentsFromHitsWithId(SearchHits hits) { @SuppressWarnings({ "rawtypes" }) public static List wildCardSearch(Class objectClass, String textKeyWord, String wordWildCard, - String indexName, String indexType, int limit) throws Exception { + String indexName, String indexType, int limit) throws Exception { SearchResponse result = wildCardSearch(textKeyWord, wordWildCard, indexName, indexType, limit); return getDocumentsFromSearchResult(result, objectClass); } From 1a80b02668d8dd8214d373e61961b7ffb1489fe9 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Tue, 26 Apr 2022 10:42:34 +0530 Subject: [PATCH 047/490] Issue #SB-19655 feat: Collection DIAL link API refactor from LP to KP --- .../org/sunbird/content/dial/DIALManagerTest.scala | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala b/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala index 0ddc60dac..eac0e8686 100644 --- a/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala +++ b/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala @@ -358,20 +358,9 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory add(new util.HashMap[String, AnyRef](){{ put("identifier",new util.ArrayList[String](){{ add("do_2222") -// add("do_3333") }}) put("dialcode", "R4X2P2") }}) -// add(new util.HashMap[String, AnyRef](){{ -// put("identifier",new util.ArrayList[String](){{ -// add("do_4444") -// add("do_5555") -// }}) -// put("dialcode", new util.ArrayList[String](){{ -// add("L4A6W8") -// add("D2E1J9") -// }}) -// }}) }}) }} reqMap From c702e4a34eb6413fa5ac16c9bff5cead887cddc2 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Wed, 27 Apr 2022 11:55:26 +0530 Subject: [PATCH 048/490] Issue #SB-19655 feat: Collection DIAL link API refactoring from LP to KP --- .../sunbird/content/dial/DIALConstants.scala | 3 ++ .../sunbird/content/dial/DIALManager.scala | 52 +++++++++---------- 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALConstants.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALConstants.scala index be127079b..70d8f74da 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALConstants.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALConstants.scala @@ -16,5 +16,8 @@ object DIALConstants { val AUTHORIZATION: String = "Authorization" val X_CHANNEL_ID: String = "X-Channel-Id" val VERSION_KEY: String = "versionKey" + val DISCUSSION_FORUM: String = "discussionForum" + val CREDENTIALS: String = "credentials" + val TRACKABLE: String = "trackable" } \ No newline at end of file diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala index 9365e136e..08ecb4f24 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala @@ -127,15 +127,15 @@ object DIALManager { request.getContext.put(ContentConstants.SCHEMA_NAME, ContentConstants.COLLECTION_SCHEMA_NAME) request.getContext.put(ContentConstants.VERSION, ContentConstants.SCHEMA_VERSION) request.put(ContentConstants.ROOT_ID, objectId) - request.put(ContentConstants.MODE, "edit") + request.put(ContentConstants.MODE, ContentConstants.EDIT_MODE) val req = new Request(request) - req.put("identifier", request.get("rootId").asInstanceOf[String]) - req.put("mode", request.get("mode").asInstanceOf[String]) + req.put(ContentConstants.IDENTIFIER, objectId) + req.put(ContentConstants.MODE, ContentConstants.EDIT_MODE) DataNode.read(req).flatMap(rootNode => { HierarchyManager.getHierarchy(request).flatMap(getHierarchyResponse => { val collectionHierarchy = getHierarchyResponse.getResult.getOrDefault(ContentConstants.CONTENT, new java.util.HashMap[String, AnyRef]()).asInstanceOf[java.util.Map[String, AnyRef]] - val childrenHierarchy = collectionHierarchy.get("children").asInstanceOf[util.List[util.Map[String, AnyRef]]] + val childrenHierarchy = collectionHierarchy.get(ContentConstants.CHILDREN).asInstanceOf[util.List[util.Map[String, AnyRef]]] val updatedChildrenHierarchy = updateChildrenHierarchy(childrenHierarchy, requestMap) val childrenDIALMap = getChildrenDIALMap(updatedChildrenHierarchy, requestMap) val consolidatedUnitDIALMap = if (!requestMap.contains(objectId)) childrenDIALMap else childrenDIALMap ++ Map(objectId -> requestMap(objectId)) @@ -143,22 +143,22 @@ object DIALManager { validateDuplicateDIALCodes(consolidatedUnitDIALMap.filter(rec => rec._2.asInstanceOf[List[String]].nonEmpty)) val updatedHierarchy = new java.util.HashMap[String, AnyRef]() - updatedHierarchy.put("identifier", objectId) - updatedHierarchy.put("children", updatedChildrenHierarchy.asJava) + updatedHierarchy.put(ContentConstants.IDENTIFIER, objectId) + updatedHierarchy.put(ContentConstants.CHILDREN, updatedChildrenHierarchy.asJava) val hierarchyReq = new Request(request) - hierarchyReq.put("hierarchy", ScalaJsonUtils.serialize(updatedHierarchy)) - hierarchyReq.put("identifier", rootNode.getIdentifier) + hierarchyReq.put(ContentConstants.HIERARCHY, ScalaJsonUtils.serialize(updatedHierarchy)) + hierarchyReq.put(ContentConstants.IDENTIFIER, rootNode.getIdentifier) oec.graphService.saveExternalProps(hierarchyReq).flatMap(rec => if(requestMap.contains(objectId)) { val updateReq = new Request(request) - updateReq.put("identifier", rootNode.getIdentifier) + updateReq.put(ContentConstants.IDENTIFIER, rootNode.getIdentifier) val rootNodeMetadata = rootNode.getMetadata - rootNodeMetadata.remove("discussionForum") - rootNodeMetadata.remove("credentials") - rootNodeMetadata.remove("trackable") + rootNodeMetadata.remove(DIALConstants.DISCUSSION_FORUM) + rootNodeMetadata.remove(DIALConstants.CREDENTIALS) + rootNodeMetadata.remove(DIALConstants.TRACKABLE) - if(rootNodeMetadata.containsKey("dialcodes")) - rootNodeMetadata.remove("dialcodes") + if(rootNodeMetadata.containsKey(DIALConstants.DIALCODES)) + rootNodeMetadata.remove(DIALConstants.DIALCODES) if(requestMap(objectId).isEmpty) updateReq.put(DIALConstants.DIALCODES, null) @@ -216,28 +216,28 @@ object DIALManager { def updateChildrenHierarchy(childrenHierarchy: util.List[util.Map[String, AnyRef]], requestMap: Map[String, List[String]]): List[util.Map[String, AnyRef]] = { childrenHierarchy.asScala.toList.map(child => { - if (requestMap.contains(child.get("identifier").toString) && StringUtils.equalsIgnoreCase("Parent", child.get("visibility").toString)) { - if (requestMap.getOrElse(child.get("identifier").toString, List.empty).nonEmpty && requestMap(child.get("identifier").toString).exists(rec => rec.trim.nonEmpty)) - child.put("dialcodes", requestMap(child.get("identifier").toString)) + if (requestMap.contains(child.get(ContentConstants.IDENTIFIER).toString) && StringUtils.equalsIgnoreCase("Parent", child.get(ContentConstants.VISIBILITY).toString)) { + if (requestMap.getOrElse(child.get(ContentConstants.IDENTIFIER).toString, List.empty).nonEmpty && requestMap(child.get(ContentConstants.IDENTIFIER).toString).exists(rec => rec.trim.nonEmpty)) + child.put(DIALConstants.DIALCODES, requestMap(child.get(ContentConstants.IDENTIFIER).toString)) else - child.remove("dialcodes") + child.remove(DIALConstants.DIALCODES) } - if(child.get("children")!=null) - updateChildrenHierarchy(child.get("children").asInstanceOf[util.List[util.Map[String, AnyRef]]], requestMap) + if(child.get(ContentConstants.CHILDREN)!=null) + updateChildrenHierarchy(child.get(ContentConstants.CHILDREN).asInstanceOf[util.List[util.Map[String, AnyRef]]], requestMap) child }) } def getChildrenDIALMap(childrenHierarchy: List[util.Map[String, AnyRef]], requestMap: Map[String, List[String]]): Map[String, AnyRef] = { childrenHierarchy.map(child => { - val subChildrenDIALMap = if(child.get("children")!=null) - getChildrenDIALMap(child.get("children").asInstanceOf[util.List[util.Map[String, AnyRef]]].asScala.toList, requestMap) + val subChildrenDIALMap = if(child.get(ContentConstants.CHILDREN)!=null) + getChildrenDIALMap(child.get(ContentConstants.CHILDREN).asInstanceOf[util.List[util.Map[String, AnyRef]]].asScala.toList, requestMap) else Map.empty[String, String] - val childDIALMap = if(requestMap.contains(child.get("identifier").toString) && child.get("dialcodes")!=null) - Map(child.get("identifier").toString -> child.get("dialcodes")) - else if(requestMap.contains(child.get("identifier").toString)) - Map(child.get("identifier").toString -> List.empty) + val childDIALMap = if(requestMap.contains(child.get(ContentConstants.IDENTIFIER).toString) && child.get(DIALConstants.DIALCODES)!=null) + Map(child.get(ContentConstants.IDENTIFIER).toString -> child.get(DIALConstants.DIALCODES)) + else if(requestMap.contains(child.get(ContentConstants.IDENTIFIER).toString)) + Map(child.get(ContentConstants.IDENTIFIER).toString -> List.empty) else Map.empty subChildrenDIALMap ++ childDIALMap From e3ceaf5a39195d1d9b48154052c5414c4418c0dc Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Wed, 27 Apr 2022 12:09:41 +0530 Subject: [PATCH 049/490] Issue #SB-19655 feat: Collection DIAL link API refactoring from LP to KP --- .../sunbird/content/dial/DIALManager.scala | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala index 08ecb4f24..a62dcfe28 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala @@ -122,18 +122,15 @@ object DIALManager { } def linkCollection(objectId: String, requestMap: Map[String, List[String]], reqContext: util.Map[String, AnyRef])(implicit oec: OntologyEngineContext, ec: ExecutionContext): Future[Response] = { - val request = new Request() - request.setContext(reqContext) - request.getContext.put(ContentConstants.SCHEMA_NAME, ContentConstants.COLLECTION_SCHEMA_NAME) - request.getContext.put(ContentConstants.VERSION, ContentConstants.SCHEMA_VERSION) - request.put(ContentConstants.ROOT_ID, objectId) - request.put(ContentConstants.MODE, ContentConstants.EDIT_MODE) - - val req = new Request(request) + val req = new Request() + req.setContext(reqContext) req.put(ContentConstants.IDENTIFIER, objectId) req.put(ContentConstants.MODE, ContentConstants.EDIT_MODE) DataNode.read(req).flatMap(rootNode => { - HierarchyManager.getHierarchy(request).flatMap(getHierarchyResponse => { + req.getContext.put(ContentConstants.SCHEMA_NAME, ContentConstants.COLLECTION_SCHEMA_NAME) + req.getContext.put(ContentConstants.VERSION, ContentConstants.SCHEMA_VERSION) + req.put(ContentConstants.ROOT_ID, objectId) + HierarchyManager.getHierarchy(req).flatMap(getHierarchyResponse => { val collectionHierarchy = getHierarchyResponse.getResult.getOrDefault(ContentConstants.CONTENT, new java.util.HashMap[String, AnyRef]()).asInstanceOf[java.util.Map[String, AnyRef]] val childrenHierarchy = collectionHierarchy.get(ContentConstants.CHILDREN).asInstanceOf[util.List[util.Map[String, AnyRef]]] val updatedChildrenHierarchy = updateChildrenHierarchy(childrenHierarchy, requestMap) @@ -146,11 +143,11 @@ object DIALManager { updatedHierarchy.put(ContentConstants.IDENTIFIER, objectId) updatedHierarchy.put(ContentConstants.CHILDREN, updatedChildrenHierarchy.asJava) - val hierarchyReq = new Request(request) + val hierarchyReq = new Request(req) hierarchyReq.put(ContentConstants.HIERARCHY, ScalaJsonUtils.serialize(updatedHierarchy)) hierarchyReq.put(ContentConstants.IDENTIFIER, rootNode.getIdentifier) oec.graphService.saveExternalProps(hierarchyReq).flatMap(rec => if(requestMap.contains(objectId)) { - val updateReq = new Request(request) + val updateReq = new Request(req) updateReq.put(ContentConstants.IDENTIFIER, rootNode.getIdentifier) val rootNodeMetadata = rootNode.getMetadata rootNodeMetadata.remove(DIALConstants.DISCUSSION_FORUM) From 69d68b9c71483338810cddfdb3e0b0b776388f46 Mon Sep 17 00:00:00 2001 From: Kumar Gauraw Date: Wed, 4 May 2022 16:10:50 +0530 Subject: [PATCH 050/490] Issue #SB-29738 fix: updated question schema --- schemas/question/1.0/schema.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/schemas/question/1.0/schema.json b/schemas/question/1.0/schema.json index ae0c3e9d3..ad5234e9c 100644 --- a/schemas/question/1.0/schema.json +++ b/schemas/question/1.0/schema.json @@ -594,6 +594,10 @@ "type": "string" }, "description": "Choices which needs to be used in RHS of MTF type question" + }, + "downloadUrl": { + "type": "string", + "format": "url" } }, "additionalProperties": false From 696d0d3a7c0aa906e404173e051e4a176390f988 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Fri, 6 May 2022 14:55:06 +0530 Subject: [PATCH 051/490] SB-29856 | Fix for hierarchy update --- .../managers/UpdateHierarchyManager.scala | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/content-api/hierarchy-manager/src/main/scala/org/sunbird/managers/UpdateHierarchyManager.scala b/content-api/hierarchy-manager/src/main/scala/org/sunbird/managers/UpdateHierarchyManager.scala index 01c245c6a..fbb73934a 100644 --- a/content-api/hierarchy-manager/src/main/scala/org/sunbird/managers/UpdateHierarchyManager.scala +++ b/content-api/hierarchy-manager/src/main/scala/org/sunbird/managers/UpdateHierarchyManager.scala @@ -345,7 +345,7 @@ object UpdateHierarchyManager { if (MapUtils.isNotEmpty(childrenIdentifiersMap)) { val updatedNodeList = getTempNode(nodeList, rootId) :: List() updateHierarchyRelatedData(childrenIdentifiersMap.getOrElse(rootId, Map[String, Int]()), 1, - rootId, nodeList, childrenIdentifiersMap, updatedNodeList, request).map(finalEnrichedNodeList => { + rootId, nodeList, childrenIdentifiersMap, updatedNodeList, request, rootId).map(finalEnrichedNodeList => { TelemetryManager.info("Final enriched list size: " + finalEnrichedNodeList.size) val childNodeIds = finalEnrichedNodeList.map(node => node.getIdentifier.replaceAll(".img", "")).filterNot(id => StringUtils.containsIgnoreCase(rootId, id)).distinct TelemetryManager.info("Final enriched ids (childNodes): " + childNodeIds + " :: size: " + childNodeIds.size) @@ -368,7 +368,8 @@ object UpdateHierarchyManager { } @throws[Exception] - private def updateHierarchyRelatedData(childrenIds: Map[String, Int], depth: Int, parent: String, nodeList: List[Node], hierarchyStructure: Map[String, Map[String, Int]], enrichedNodeList: scala.collection.immutable.List[Node], request: Request)(implicit oec: OntologyEngineContext, ec: ExecutionContext): Future[List[Node]] = { + private def updateHierarchyRelatedData(childrenIds: Map[String, Int], depth: Int, parent: String, nodeList: List[Node], hierarchyStructure: Map[String, Map[String, Int]], enrichedNodeList: scala.collection.immutable.List[Node], request: Request, rootId: String)(implicit oec: OntologyEngineContext, ec: ExecutionContext): Future[List[Node]] = { + val rootResourceChange: Boolean = if (Platform.config.hasPath("hierarchyUpdate.allow.resource.at.root.level")) Platform.config.getBoolean("hierarchyUpdate.allow.resource.at.root.level") else false val futures = childrenIds.map(child => { val id = child._1 val index = child._2 + 1 @@ -378,13 +379,19 @@ object UpdateHierarchyManager { val nxtEnrichedNodeList = tempNode :: enrichedNodeList if (MapUtils.isNotEmpty(hierarchyStructure.getOrDefault(child._1, Map[String, Int]()))) updateHierarchyRelatedData(hierarchyStructure.getOrDefault(child._1, Map[String, Int]()), - tempNode.getMetadata.get(HierarchyConstants.DEPTH).asInstanceOf[Int] + 1, id, nodeList, hierarchyStructure, nxtEnrichedNodeList, request) + tempNode.getMetadata.get(HierarchyConstants.DEPTH).asInstanceOf[Int] + 1, id, nodeList, hierarchyStructure, nxtEnrichedNodeList, request, rootId) else Future(nxtEnrichedNodeList) } else { // TelemetryManager.info("Get ContentNode as TempNode is null for ID: " + id) getContentNode(id, HierarchyConstants.TAXONOMY_ID).map(node => { - val parentNode: Node = nodeList.find(p => p.getIdentifier.equals(parent)).orNull + val parentNode: Node = if (rootResourceChange && nodeList.find(p => p.getIdentifier.equals(parent)).orNull == null) { + if (nodeList.find(p => p.getIdentifier.equals(rootId)).orNull == null) + nodeList.find(p => p.getIdentifier.equals(rootId + ".img")).orNull + else + nodeList.find(p => p.getIdentifier.equals(rootId)).orNull + } else + nodeList.find(p => p.getIdentifier.equals(parent)).orNull val nxtEnrichedNodeList = if (null != parentNode) { TelemetryManager.info(s"ObjectType for $parent is ${parentNode.getObjectType}...") val parentMetadata: java.util.Map[String, AnyRef] = NodeUtil.serialize(parentNode, new java.util.ArrayList[String](), parentNode.getObjectType.toLowerCase, "1.0") @@ -401,7 +408,7 @@ object UpdateHierarchyManager { enrichedNodeList } if (MapUtils.isNotEmpty(hierarchyStructure.getOrDefault(id, Map[String, Int]()))) { - updateHierarchyRelatedData(hierarchyStructure.getOrDefault(id, Map[String, Int]()), node.getMetadata.get(HierarchyConstants.DEPTH).asInstanceOf[Int] + 1, id, nodeList, hierarchyStructure, nxtEnrichedNodeList, request) + updateHierarchyRelatedData(hierarchyStructure.getOrDefault(id, Map[String, Int]()), node.getMetadata.get(HierarchyConstants.DEPTH).asInstanceOf[Int] + 1, id, nodeList, hierarchyStructure, nxtEnrichedNodeList, request, rootId) } else Future(nxtEnrichedNodeList) }).flatMap(f => f) recoverWith { case e: CompletionException => throw e.getCause } From c57d1a236c5d0141fa4bde3fda7d01aedfe385c1 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Tue, 10 May 2022 13:34:07 +0530 Subject: [PATCH 052/490] Issue #SB-29808 debug: Review API failing --- .../mimetype/ecml/processor/LocalizeAssetProcessor.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/ecml/processor/LocalizeAssetProcessor.scala b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/ecml/processor/LocalizeAssetProcessor.scala index d3d44e204..978cc60bd 100644 --- a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/ecml/processor/LocalizeAssetProcessor.scala +++ b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/ecml/processor/LocalizeAssetProcessor.scala @@ -2,12 +2,12 @@ package org.sunbird.mimetype.ecml.processor import java.io.{File, IOException} import java.net.URL - import org.apache.commons.io.{FileUtils, FilenameUtils} import org.apache.commons.lang3.StringUtils import org.sunbird.cloudstore.StorageService import org.sunbird.common.Platform import org.sunbird.common.exception.ClientException +import org.sunbird.telemetry.logger.TelemetryManager import scala.concurrent.{Await, ExecutionContext, Future} import scala.concurrent.duration.Duration @@ -62,6 +62,8 @@ trait LocalizeAssetProcessor extends IProcessor { } def downloadFile(downloadPath: String, fileUrl: String): File = try { + TelemetryManager.info("LocalizeAssetProcessor:: downloadFile:: downloadPath:: " + downloadPath) + TelemetryManager.info("LocalizeAssetProcessor:: downloadFile:: fileUrl:: " + fileUrl) createDirectory(downloadPath) val file = new File(downloadPath + File.separator + getFileNameFromURL(fileUrl)) FileUtils.copyURLToFile(new URL(fileUrl), file) From 433f22a2e402f3c8cca6fc10eeda6836f05dbcd7 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Tue, 10 May 2022 13:41:42 +0530 Subject: [PATCH 053/490] Issue #SB-29808 debug: Review API failing --- .../mimetype/ecml/processor/LocalizeAssetProcessor.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/ecml/processor/LocalizeAssetProcessor.scala b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/ecml/processor/LocalizeAssetProcessor.scala index d3d44e204..978cc60bd 100644 --- a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/ecml/processor/LocalizeAssetProcessor.scala +++ b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/ecml/processor/LocalizeAssetProcessor.scala @@ -2,12 +2,12 @@ package org.sunbird.mimetype.ecml.processor import java.io.{File, IOException} import java.net.URL - import org.apache.commons.io.{FileUtils, FilenameUtils} import org.apache.commons.lang3.StringUtils import org.sunbird.cloudstore.StorageService import org.sunbird.common.Platform import org.sunbird.common.exception.ClientException +import org.sunbird.telemetry.logger.TelemetryManager import scala.concurrent.{Await, ExecutionContext, Future} import scala.concurrent.duration.Duration @@ -62,6 +62,8 @@ trait LocalizeAssetProcessor extends IProcessor { } def downloadFile(downloadPath: String, fileUrl: String): File = try { + TelemetryManager.info("LocalizeAssetProcessor:: downloadFile:: downloadPath:: " + downloadPath) + TelemetryManager.info("LocalizeAssetProcessor:: downloadFile:: fileUrl:: " + fileUrl) createDirectory(downloadPath) val file = new File(downloadPath + File.separator + getFileNameFromURL(fileUrl)) FileUtils.copyURLToFile(new URL(fileUrl), file) From ae11d31f0d62d326a4572a29b66ba5f454d6e9c8 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Tue, 10 May 2022 14:54:30 +0530 Subject: [PATCH 054/490] Issue #SB-29808 debug: Review API failing --- .../mimetype/ecml/processor/LocalizeAssetProcessor.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/ecml/processor/LocalizeAssetProcessor.scala b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/ecml/processor/LocalizeAssetProcessor.scala index 978cc60bd..f71743c0d 100644 --- a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/ecml/processor/LocalizeAssetProcessor.scala +++ b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/ecml/processor/LocalizeAssetProcessor.scala @@ -62,15 +62,16 @@ trait LocalizeAssetProcessor extends IProcessor { } def downloadFile(downloadPath: String, fileUrl: String): File = try { - TelemetryManager.info("LocalizeAssetProcessor:: downloadFile:: downloadPath:: " + downloadPath) TelemetryManager.info("LocalizeAssetProcessor:: downloadFile:: fileUrl:: " + fileUrl) createDirectory(downloadPath) val file = new File(downloadPath + File.separator + getFileNameFromURL(fileUrl)) FileUtils.copyURLToFile(new URL(fileUrl), file) file } catch { - case e: IOException => + case e: IOException => { + TelemetryManager.info("LocalizeAssetProcessor:: downloadFile:: IOException:: " + e.getMessage) throw new ClientException("ERR_INVALID_FILE_URL", "Please Provide Valid File Url!") + } } def createDirectory(directoryName: String): Unit = { From 341836853a104ce71792a00c721c7f2ac69e7dfc Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Tue, 10 May 2022 15:59:32 +0530 Subject: [PATCH 055/490] Issue #SB-29808 debug: Review API failing --- .../mimetype/ecml/processor/LocalizeAssetProcessor.scala | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/ecml/processor/LocalizeAssetProcessor.scala b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/ecml/processor/LocalizeAssetProcessor.scala index f71743c0d..56c682426 100644 --- a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/ecml/processor/LocalizeAssetProcessor.scala +++ b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/ecml/processor/LocalizeAssetProcessor.scala @@ -57,7 +57,9 @@ trait LocalizeAssetProcessor extends IProcessor { def getUrlWithPrefix(src: String) = { if (StringUtils.isNotBlank(src) && !src.startsWith("http")) { - if (src.contains("content-plugins/")) PLUGIN_MEDIA_BASE_URL + File.separator + src else CONTENT_MEDIA_BASE_URL + src + if (src.contains("content-plugins/")) PLUGIN_MEDIA_BASE_URL + File.separator + src + else if(src.startsWith("assets/")) CONTENT_MEDIA_BASE_URL + File.separator + src + else CONTENT_MEDIA_BASE_URL + src } else src } @@ -69,7 +71,7 @@ trait LocalizeAssetProcessor extends IProcessor { file } catch { case e: IOException => { - TelemetryManager.info("LocalizeAssetProcessor:: downloadFile:: IOException:: " + e.getMessage) + e.printStackTrace() throw new ClientException("ERR_INVALID_FILE_URL", "Please Provide Valid File Url!") } } From b1ee2fcf2f71b84e45a85f0ebd95750e69b5c7fb Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Tue, 10 May 2022 16:00:12 +0530 Subject: [PATCH 056/490] Issue #SB-29808 debug: Review API failing --- .../sunbird/mimetype/ecml/processor/LocalizeAssetProcessor.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/ecml/processor/LocalizeAssetProcessor.scala b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/ecml/processor/LocalizeAssetProcessor.scala index 56c682426..cf8007ab2 100644 --- a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/ecml/processor/LocalizeAssetProcessor.scala +++ b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/ecml/processor/LocalizeAssetProcessor.scala @@ -64,7 +64,6 @@ trait LocalizeAssetProcessor extends IProcessor { } def downloadFile(downloadPath: String, fileUrl: String): File = try { - TelemetryManager.info("LocalizeAssetProcessor:: downloadFile:: fileUrl:: " + fileUrl) createDirectory(downloadPath) val file = new File(downloadPath + File.separator + getFileNameFromURL(fileUrl)) FileUtils.copyURLToFile(new URL(fileUrl), file) From 415e44f9310ae52aa19f5311c541872ad8a2d158 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Tue, 10 May 2022 16:00:39 +0530 Subject: [PATCH 057/490] Issue #SB-29808 debug: Review API failing --- .../mimetype/ecml/processor/LocalizeAssetProcessor.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/ecml/processor/LocalizeAssetProcessor.scala b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/ecml/processor/LocalizeAssetProcessor.scala index cf8007ab2..d90680eca 100644 --- a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/ecml/processor/LocalizeAssetProcessor.scala +++ b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/ecml/processor/LocalizeAssetProcessor.scala @@ -58,7 +58,7 @@ trait LocalizeAssetProcessor extends IProcessor { def getUrlWithPrefix(src: String) = { if (StringUtils.isNotBlank(src) && !src.startsWith("http")) { if (src.contains("content-plugins/")) PLUGIN_MEDIA_BASE_URL + File.separator + src - else if(src.startsWith("assets/")) CONTENT_MEDIA_BASE_URL + File.separator + src + else if(src.startsWith("assets")) CONTENT_MEDIA_BASE_URL + File.separator + src else CONTENT_MEDIA_BASE_URL + src } else src } From 8978b8dc5fd0d1b644487447ed1b75fcea7a89a9 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Tue, 10 May 2022 16:08:18 +0530 Subject: [PATCH 058/490] Issue #SB-29808 debug: Review API failing --- .../mimetype/ecml/processor/LocalizeAssetProcessor.scala | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/ecml/processor/LocalizeAssetProcessor.scala b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/ecml/processor/LocalizeAssetProcessor.scala index d90680eca..e732a3e37 100644 --- a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/ecml/processor/LocalizeAssetProcessor.scala +++ b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/ecml/processor/LocalizeAssetProcessor.scala @@ -7,7 +7,6 @@ import org.apache.commons.lang3.StringUtils import org.sunbird.cloudstore.StorageService import org.sunbird.common.Platform import org.sunbird.common.exception.ClientException -import org.sunbird.telemetry.logger.TelemetryManager import scala.concurrent.{Await, ExecutionContext, Future} import scala.concurrent.duration.Duration @@ -57,9 +56,8 @@ trait LocalizeAssetProcessor extends IProcessor { def getUrlWithPrefix(src: String) = { if (StringUtils.isNotBlank(src) && !src.startsWith("http")) { - if (src.contains("content-plugins/")) PLUGIN_MEDIA_BASE_URL + File.separator + src - else if(src.startsWith("assets")) CONTENT_MEDIA_BASE_URL + File.separator + src - else CONTENT_MEDIA_BASE_URL + src + if(!src.startsWith("/")) PLUGIN_MEDIA_BASE_URL + File.separator + src + else PLUGIN_MEDIA_BASE_URL + src } else src } From 17a470fde37a129e800977f6d9737be2c3cba361 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Tue, 10 May 2022 16:09:07 +0530 Subject: [PATCH 059/490] Issue #SB-29808 debug: Review API failing --- .../mimetype/ecml/processor/LocalizeAssetProcessor.scala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/ecml/processor/LocalizeAssetProcessor.scala b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/ecml/processor/LocalizeAssetProcessor.scala index e732a3e37..629953a1a 100644 --- a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/ecml/processor/LocalizeAssetProcessor.scala +++ b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/ecml/processor/LocalizeAssetProcessor.scala @@ -56,8 +56,7 @@ trait LocalizeAssetProcessor extends IProcessor { def getUrlWithPrefix(src: String) = { if (StringUtils.isNotBlank(src) && !src.startsWith("http")) { - if(!src.startsWith("/")) PLUGIN_MEDIA_BASE_URL + File.separator + src - else PLUGIN_MEDIA_BASE_URL + src + if(!src.startsWith("/")) PLUGIN_MEDIA_BASE_URL + File.separator + src else PLUGIN_MEDIA_BASE_URL + src } else src } From c6a6916e6fd0068773735c57ede3ca7903cb8b58 Mon Sep 17 00:00:00 2001 From: joffinjoy <35325730+joffinjoy@users.noreply.github.com> Date: Wed, 11 May 2022 12:21:26 +0530 Subject: [PATCH 060/490] Issue #SB-29145 refactor: Moved Branching Functions To BranchingUtil --- .../org/sunbird/managers/CopyManager.scala | 137 ++---------------- .../org/sunbird/utils/BranchingUtil.scala | 122 ++++++++++++++++ .../sunbird/actors/QuestionSetActorTest.scala | 6 +- 3 files changed, 135 insertions(+), 130 deletions(-) create mode 100644 assessment-api/assessment-actors/src/main/scala/org/sunbird/utils/BranchingUtil.scala diff --git a/assessment-api/assessment-actors/src/main/scala/org/sunbird/managers/CopyManager.scala b/assessment-api/assessment-actors/src/main/scala/org/sunbird/managers/CopyManager.scala index 269b53040..dc73939e9 100644 --- a/assessment-api/assessment-actors/src/main/scala/org/sunbird/managers/CopyManager.scala +++ b/assessment-api/assessment-actors/src/main/scala/org/sunbird/managers/CopyManager.scala @@ -1,7 +1,5 @@ package org.sunbird.managers -import com.google.gson.{Gson, GsonBuilder} -import com.google.gson.reflect.TypeToken import org.apache.commons.collections.CollectionUtils import org.apache.commons.collections4.MapUtils import org.apache.commons.lang.StringUtils @@ -15,16 +13,14 @@ import org.sunbird.graph.nodes.DataNode import org.sunbird.graph.schema.DefinitionNode import org.sunbird.graph.utils.{NodeUtil, ScalaJsonUtils} import org.sunbird.telemetry.logger.TelemetryManager -import org.sunbird.utils.{AssessmentConstants, HierarchyConstants} +import org.sunbird.utils.{AssessmentConstants, BranchingUtil, HierarchyConstants} import java.util -import java.util.concurrent.{CompletionException, TimeUnit} -import java.util.stream.Collectors +import java.util.concurrent.{CompletionException} import java.util.{Optional, UUID} -import scala.collection.JavaConversions.{asScalaBuffer, mapAsScalaMap} +import scala.collection.JavaConversions.{mapAsScalaMap} import scala.collection.JavaConverters._ -import scala.concurrent.duration.{Duration, FiniteDuration} -import scala.concurrent.{Await, ExecutionContext, Future} +import scala.concurrent.{ExecutionContext, Future} object CopyManager { @@ -93,129 +89,16 @@ object CopyManager { }).flatMap(f => f) } - - def generateNodeBLRecord(nodesModified: util.HashMap[String, AnyRef]): util.HashMap[String, AnyRef] = { - val idSet = nodesModified.keySet().asScala.toList - val nodeBLRecord = new util.HashMap[String, AnyRef]() - idSet.map(id => { - val nodeMetaData = nodesModified.getOrDefault(id, new util.HashMap()).asInstanceOf[util.Map[String, AnyRef]].getOrDefault(AssessmentConstants.METADATA, new util.HashMap()).asInstanceOf[util.Map[String, AnyRef]] - val containsBL = nodeMetaData.containsKey(AssessmentConstants.BRANCHING_LOGIC) - nodeBLRecord.put(id, new util.HashMap[String, AnyRef]() { - { - if (containsBL) put(AssessmentConstants.BRANCHING_LOGIC, nodeMetaData.get(AssessmentConstants.BRANCHING_LOGIC)) - put(AssessmentConstants.CONTAINS_BL, containsBL.asInstanceOf[AnyRef]) - put(AssessmentConstants.COPY_OF, nodeMetaData.get(AssessmentConstants.COPY_OF).asInstanceOf[String]) - } - }) - if (containsBL) nodeMetaData.remove(AssessmentConstants.BRANCHING_LOGIC) - nodeMetaData.remove(AssessmentConstants.COPY_OF) - }) - nodeBLRecord - } - - def branchingLogicArrayHandler(nodeBL: util.HashMap[String, AnyRef], name: String, oldToNewIdMap: util.Map[String, String]) = { - val array = nodeBL.getOrDefault(name, new util.ArrayList[String]).asInstanceOf[util.ArrayList[String]] - val newArray = new util.ArrayList[String]() - array.map(id => { - if (oldToNewIdMap.containsKey(id)) { - newArray.add(oldToNewIdMap.get(id)) - } else newArray.add(id) - }) - nodeBL.remove(name) - nodeBL.put(name, newArray) - } - - def preConditionHandler(nodeBL: util.HashMap[String, AnyRef], oldToNewIdMap: util.Map[String, String]): Unit = { - val preCondition = nodeBL.get(AssessmentConstants.PRE_CONDITION).asInstanceOf[util.HashMap[String, AnyRef]] - preCondition.keySet().asScala.toList.map(key => { - val conjunctionArray = preCondition.get(key).asInstanceOf[util.ArrayList[String]] - val condition = conjunctionArray.get(0).asInstanceOf[util.HashMap[String, AnyRef]] - condition.keySet().asScala.toList.map(logicOp => { - val conditionArray = condition.get(logicOp).asInstanceOf[util.ArrayList[String]] - val sourceQuestionRecord = conditionArray.get(0).asInstanceOf[util.HashMap[String, AnyRef]] - val preConditionVar = sourceQuestionRecord.get(AssessmentConstants.PRE_CONDITION_VAR).asInstanceOf[String] - val stringArray = preConditionVar.split("\\.") - if (oldToNewIdMap.containsKey(stringArray(0))) { - val newString = oldToNewIdMap.get(stringArray(0)) + "." + stringArray.drop(1).mkString(".") - sourceQuestionRecord.remove(AssessmentConstants.PRE_CONDITION_VAR) - sourceQuestionRecord.put(AssessmentConstants.PRE_CONDITION_VAR, newString) - } - }) - }) - } - - def branchingLogicModifier(branchingLogic: util.HashMap[String, AnyRef], oldToNewIdMap: util.Map[String, String]): Unit = { - branchingLogic.keySet().asScala.toList.map(identifier => { - val nodeBL = branchingLogic.get(identifier).asInstanceOf[util.HashMap[String, AnyRef]] - nodeBL.keySet().asScala.toList.map(key => { - if (StringUtils.equalsIgnoreCase(key, AssessmentConstants.TARGET)) branchingLogicArrayHandler(nodeBL, AssessmentConstants.TARGET, oldToNewIdMap) - else if (StringUtils.equalsIgnoreCase(key, AssessmentConstants.PRE_CONDITION)) preConditionHandler(nodeBL, oldToNewIdMap) - else if (StringUtils.equalsIgnoreCase(key, AssessmentConstants.SOURCE)) branchingLogicArrayHandler(nodeBL, AssessmentConstants.SOURCE, oldToNewIdMap) - }) - if (oldToNewIdMap.containsKey(identifier)) { - branchingLogic.put(oldToNewIdMap.get(identifier), nodeBL) - branchingLogic.remove(identifier) - } - }) - } - - def generateOldToNewIdMap(nodeBLRecord: util.HashMap[String, AnyRef], identifiers: util.Map[String, String]): util.Map[String, String] = { - val oldToNewIdMap = new util.HashMap[String, String]() - nodeBLRecord.keySet().asScala.toList.map(id => { - val nodeInfo = nodeBLRecord.get(id).asInstanceOf[util.HashMap[String, AnyRef]] - val newId = identifiers.get(id) - val oldId = nodeInfo.get(AssessmentConstants.COPY_OF).asInstanceOf[String] - oldToNewIdMap.put(oldId, newId) - }) - oldToNewIdMap - } - - def hierarchyRequestModifier(request: Request, nodeBLRecord: util.HashMap[String, AnyRef], identifiers: util.Map[String, String]) = { - val nodesModified: java.util.HashMap[String, AnyRef] = request.getRequest.get(HierarchyConstants.NODES_MODIFIED).asInstanceOf[java.util.HashMap[String, AnyRef]] - val hierarchy: java.util.HashMap[String, AnyRef] = request.getRequest.get(HierarchyConstants.HIERARCHY).asInstanceOf[java.util.HashMap[String, AnyRef]] - val oldToNewIdMap = generateOldToNewIdMap(nodeBLRecord, identifiers) - nodeBLRecord.keySet().asScala.toList.map(id => { - val nodeInfo = nodeBLRecord.get(id).asInstanceOf[util.HashMap[String, AnyRef]] - val node = nodesModified.get(id).asInstanceOf[util.HashMap[String, AnyRef]] - val nodeMetaData = node.get(AssessmentConstants.METADATA).asInstanceOf[util.HashMap[String, AnyRef]] - val newId = identifiers.get(id) - if (nodeInfo.get(AssessmentConstants.CONTAINS_BL).asInstanceOf[Boolean]) { - val branchingLogic = nodeInfo.get(AssessmentConstants.BRANCHING_LOGIC).asInstanceOf[util.HashMap[String, AnyRef]] - branchingLogicModifier(branchingLogic, oldToNewIdMap) - nodeMetaData.put(AssessmentConstants.BRANCHING_LOGIC, branchingLogic) - } - node.remove(AssessmentConstants.IS_NEW) - node.put(AssessmentConstants.IS_NEW, false.asInstanceOf[AnyRef]) - nodesModified.remove(id) - nodesModified.put(newId, node) - }) - hierarchy.keySet().asScala.toList.map(id => { - val nodeHierarchy = hierarchy.get(id).asInstanceOf[util.HashMap[String, AnyRef]] - val children = nodeHierarchy.get(AssessmentConstants.CHILDREN).asInstanceOf[util.ArrayList[String]] - val newChildrenList = new util.ArrayList[String] - children.map(identifier => { - if (identifiers.containsKey(identifier)) newChildrenList.add(identifiers.get(identifier)) else newChildrenList.add(identifier) - }) - nodeHierarchy.remove(AssessmentConstants.CHILDREN) - nodeHierarchy.put(AssessmentConstants.CHILDREN, newChildrenList) - if (identifiers.containsKey(id)) { - hierarchy.remove(id) - hierarchy.put(identifiers.get(id), nodeHierarchy) - } - }) - request - } - def updateHierarchy(request: Request, node: Node, originNode: Node, originHierarchy: util.Map[String, AnyRef], copyType: String)(implicit ec: ExecutionContext, oec: OntologyEngineContext): Future[Node] = { prepareHierarchyRequest(originHierarchy, originNode, node, copyType, request).map(req => { val hierarchyRequest = new Request(request) hierarchyRequest.putAll(req) val nodesModified: java.util.HashMap[String, AnyRef] = hierarchyRequest.getRequest.get(HierarchyConstants.NODES_MODIFIED).asInstanceOf[java.util.HashMap[String, AnyRef]] - val nodeBLRecord = generateNodeBLRecord(nodesModified) + val branchingRecord = BranchingUtil.generateBranchingRecord(nodesModified) val originalRequest = JsonUtils.deserialize(ScalaJsonUtils.serialize(hierarchyRequest), classOf[Request]) - val BLExists = nodeBLRecord.exists(BLRecord => BLRecord._2.asInstanceOf[util.HashMap[String, AnyRef]].get(AssessmentConstants.CONTAINS_BL) == true) - val (firstUpdateRequest, secondUpdateRequest) = if (BLExists) (hierarchyRequest, JsonUtils.deserialize(ScalaJsonUtils.serialize(hierarchyRequest), classOf[Request])) else (originalRequest, new Request()) - UpdateHierarchyManager.updateHierarchy(firstUpdateRequest).map(response => { + val BLExists = branchingRecord.exists(BLRecord => BLRecord._2.asInstanceOf[util.HashMap[String, AnyRef]].get(AssessmentConstants.CONTAINS_BL) == true) + val (updateHierarchyReq, branchingUpdateReq) = if (BLExists) (hierarchyRequest, JsonUtils.deserialize(ScalaJsonUtils.serialize(hierarchyRequest), classOf[Request])) else (originalRequest, new Request()) + UpdateHierarchyManager.updateHierarchy(updateHierarchyReq).map(response => { if (!ResponseHandler.checkError(response)) response else { TelemetryManager.info(s"Update Hierarchy Failed For Copy Question Set Having Identifier: ${node.getIdentifier} | Response is: " + response) @@ -223,8 +106,8 @@ object CopyManager { } }).map(response => { if (BLExists) { - hierarchyRequestModifier(secondUpdateRequest, nodeBLRecord, response.getResult.get(AssessmentConstants.IDENTIFIERS).asInstanceOf[util.Map[String, String]]) - UpdateHierarchyManager.updateHierarchy(secondUpdateRequest).map(response_ => { + BranchingUtil.hierarchyRequestModifier(branchingUpdateReq, branchingRecord, response.getResult.get(AssessmentConstants.IDENTIFIERS).asInstanceOf[util.Map[String, String]]) + UpdateHierarchyManager.updateHierarchy(branchingUpdateReq).map(response_ => { if (!ResponseHandler.checkError(response_)) node else { TelemetryManager.info(s"Update Hierarchy Failed For Copy Question Set Having Identifier: ${node.getIdentifier} | Response is: " + response) diff --git a/assessment-api/assessment-actors/src/main/scala/org/sunbird/utils/BranchingUtil.scala b/assessment-api/assessment-actors/src/main/scala/org/sunbird/utils/BranchingUtil.scala new file mode 100644 index 000000000..17e5d4a8b --- /dev/null +++ b/assessment-api/assessment-actors/src/main/scala/org/sunbird/utils/BranchingUtil.scala @@ -0,0 +1,122 @@ +package org.sunbird.utils + +import org.apache.commons.lang.StringUtils +import org.sunbird.common.dto.Request + +import java.util +import scala.collection.JavaConverters._ +import scala.collection.JavaConversions.{asScalaBuffer} + +object BranchingUtil { + + def generateBranchingRecord(nodesModified: util.HashMap[String, AnyRef]): util.HashMap[String, AnyRef] = { + val idSet = nodesModified.keySet().asScala.toList + val branchingRecord = new util.HashMap[String, AnyRef]() + idSet.map(id => { + val nodeMetaData = nodesModified.getOrDefault(id, new util.HashMap()).asInstanceOf[util.Map[String, AnyRef]].getOrDefault(AssessmentConstants.METADATA, new util.HashMap()).asInstanceOf[util.Map[String, AnyRef]] + val containsBL = nodeMetaData.containsKey(AssessmentConstants.BRANCHING_LOGIC) + branchingRecord.put(id, new util.HashMap[String, AnyRef]() { + { + if (containsBL) put(AssessmentConstants.BRANCHING_LOGIC, nodeMetaData.get(AssessmentConstants.BRANCHING_LOGIC)) + put(AssessmentConstants.CONTAINS_BL, containsBL.asInstanceOf[AnyRef]) + put(AssessmentConstants.COPY_OF, nodeMetaData.get(AssessmentConstants.COPY_OF).asInstanceOf[String]) + } + }) + if (containsBL) nodeMetaData.remove(AssessmentConstants.BRANCHING_LOGIC) + nodeMetaData.remove(AssessmentConstants.COPY_OF) + }) + branchingRecord + } + + def hierarchyRequestModifier(request: Request, branchingRecord: util.HashMap[String, AnyRef], identifiers: util.Map[String, String]) = { + val nodesModified: java.util.HashMap[String, AnyRef] = request.getRequest.get(HierarchyConstants.NODES_MODIFIED).asInstanceOf[java.util.HashMap[String, AnyRef]] + val hierarchy: java.util.HashMap[String, AnyRef] = request.getRequest.get(HierarchyConstants.HIERARCHY).asInstanceOf[java.util.HashMap[String, AnyRef]] + val oldToNewIdMap = generateOldToNewIdMap(branchingRecord, identifiers) + branchingRecord.keySet().asScala.toList.map(id => { + val nodeInfo = branchingRecord.get(id).asInstanceOf[util.HashMap[String, AnyRef]] + val node = nodesModified.get(id).asInstanceOf[util.HashMap[String, AnyRef]] + val nodeMetaData = node.get(AssessmentConstants.METADATA).asInstanceOf[util.HashMap[String, AnyRef]] + val newId = identifiers.get(id) + if (nodeInfo.get(AssessmentConstants.CONTAINS_BL).asInstanceOf[Boolean]) { + val branchingLogic = nodeInfo.get(AssessmentConstants.BRANCHING_LOGIC).asInstanceOf[util.HashMap[String, AnyRef]] + branchingLogicModifier(branchingLogic, oldToNewIdMap) + nodeMetaData.put(AssessmentConstants.BRANCHING_LOGIC, branchingLogic) + } + node.put(AssessmentConstants.IS_NEW, false.asInstanceOf[AnyRef]) + nodesModified.remove(id) + nodesModified.put(newId, node) + }) + hierarchy.keySet().asScala.toList.map(id => { + val nodeHierarchy = hierarchy.get(id).asInstanceOf[util.HashMap[String, AnyRef]] + val children = nodeHierarchy.get(AssessmentConstants.CHILDREN).asInstanceOf[util.ArrayList[String]] + val newChildrenList = new util.ArrayList[String] + children.map(identifier => { + if (identifiers.containsKey(identifier)) newChildrenList.add(identifiers.get(identifier)) else newChildrenList.add(identifier) + }) + nodeHierarchy.remove(AssessmentConstants.CHILDREN) + nodeHierarchy.put(AssessmentConstants.CHILDREN, newChildrenList) + if (identifiers.containsKey(id)) { + hierarchy.remove(id) + hierarchy.put(identifiers.get(id), nodeHierarchy) + } + }) + request + } + + def branchingLogicModifier(branchingLogic: util.HashMap[String, AnyRef], oldToNewIdMap: util.Map[String, String]): Unit = { + branchingLogic.keySet().asScala.toList.map(identifier => { + val nodeBL = branchingLogic.get(identifier).asInstanceOf[util.HashMap[String, AnyRef]] + nodeBL.keySet().asScala.toList.map(key => { + if (StringUtils.equalsIgnoreCase(key, AssessmentConstants.TARGET)) branchingLogicArrayHandler(nodeBL, AssessmentConstants.TARGET, oldToNewIdMap) + else if (StringUtils.equalsIgnoreCase(key, AssessmentConstants.PRE_CONDITION)) preConditionHandler(nodeBL, oldToNewIdMap) + else if (StringUtils.equalsIgnoreCase(key, AssessmentConstants.SOURCE)) branchingLogicArrayHandler(nodeBL, AssessmentConstants.SOURCE, oldToNewIdMap) + }) + if (oldToNewIdMap.containsKey(identifier)) { + branchingLogic.put(oldToNewIdMap.get(identifier), nodeBL) + branchingLogic.remove(identifier) + } + }) + } + + def generateOldToNewIdMap(branchingRecord: util.HashMap[String, AnyRef], identifiers: util.Map[String, String]): util.Map[String, String] = { + val oldToNewIdMap = new util.HashMap[String, String]() + branchingRecord.keySet().asScala.toList.map(id => { + val nodeInfo = branchingRecord.get(id).asInstanceOf[util.HashMap[String, AnyRef]] + val newId = identifiers.get(id) + val oldId = nodeInfo.get(AssessmentConstants.COPY_OF).asInstanceOf[String] + oldToNewIdMap.put(oldId, newId) + }) + oldToNewIdMap + } + + def branchingLogicArrayHandler(nodeBL: util.HashMap[String, AnyRef], name: String, oldToNewIdMap: util.Map[String, String]) = { + val branchingLogicArray = nodeBL.getOrDefault(name, new util.ArrayList[String]).asInstanceOf[util.ArrayList[String]] + val newBranchingLogicArray = new util.ArrayList[String]() + branchingLogicArray.map(id => { + if (oldToNewIdMap.containsKey(id)) { + newBranchingLogicArray.add(oldToNewIdMap.get(id)) + } else newBranchingLogicArray.add(id) + }) + nodeBL.remove(name) + nodeBL.put(name, newBranchingLogicArray) + } + + def preConditionHandler(nodeBL: util.HashMap[String, AnyRef], oldToNewIdMap: util.Map[String, String]): Unit = { + val preCondition = nodeBL.get(AssessmentConstants.PRE_CONDITION).asInstanceOf[util.HashMap[String, AnyRef]] + preCondition.keySet().asScala.toList.map(key => { + val conjunctionArray = preCondition.get(key).asInstanceOf[util.ArrayList[String]] + val condition = conjunctionArray.get(0).asInstanceOf[util.HashMap[String, AnyRef]] + condition.keySet().asScala.toList.map(logicOp => { + val conditionArray = condition.get(logicOp).asInstanceOf[util.ArrayList[String]] + val sourceQuestionRecord = conditionArray.get(0).asInstanceOf[util.HashMap[String, AnyRef]] + val preConditionVar = sourceQuestionRecord.get(AssessmentConstants.PRE_CONDITION_VAR).asInstanceOf[String] + val stringArray = preConditionVar.split("\\.") + if (oldToNewIdMap.containsKey(stringArray(0))) { + val newString = oldToNewIdMap.get(stringArray(0)) + "." + stringArray.drop(1).mkString(".") + sourceQuestionRecord.remove(AssessmentConstants.PRE_CONDITION_VAR) + sourceQuestionRecord.put(AssessmentConstants.PRE_CONDITION_VAR, newString) + } + }) + }) + } +} diff --git a/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/QuestionSetActorTest.scala b/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/QuestionSetActorTest.scala index a984f460a..25df70a3b 100644 --- a/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/QuestionSetActorTest.scala +++ b/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/QuestionSetActorTest.scala @@ -12,7 +12,7 @@ import org.sunbird.graph.utils.ScalaJsonUtils import org.sunbird.graph.{GraphService, OntologyEngineContext} import org.sunbird.kafka.client.KafkaClient import org.sunbird.managers.CopyManager -import org.sunbird.utils.{AssessmentConstants, JavaJsonUtils} +import org.sunbird.utils.{AssessmentConstants, BranchingUtil, JavaJsonUtils} import java.util import scala.collection.JavaConversions._ @@ -597,12 +597,12 @@ class QuestionSetActorTest extends BaseSpec with MockFactory with copyTrait { } it should "return expected result for 'generateNodeBLRecord'" in { - val result = CopyManager.generateNodeBLRecord(generateNodesModified("afa2bef1-b5db-45d9-b0d7-aeea757906c3", true)) + val result = BranchingUtil.generateBranchingRecord(generateNodesModified("afa2bef1-b5db-45d9-b0d7-aeea757906c3", true)) assert(result == generateNodeBLRecord) } it should "return expected result for 'hierarchyRequestModifier'" in { - val result = CopyManager.hierarchyRequestModifier(generateUpdateRequest(false, "afa2bef1-b5db-45d9-b0d7-aeea757906c3"), generateNodeBLRecord(), generateIdentifiers()) + val result = BranchingUtil.hierarchyRequestModifier(generateUpdateRequest(false, "afa2bef1-b5db-45d9-b0d7-aeea757906c3"), generateNodeBLRecord(), generateIdentifiers()) val expectedResult = generateUpdateRequest(true, "do_11351201604857856013") assert(result.getRequest.get(AssessmentConstants.NODES_MODIFIED) == expectedResult.getRequest.get(AssessmentConstants.NODES_MODIFIED)) assert(result.getRequest.get(AssessmentConstants.HIERARCHY) == expectedResult.getRequest.get(AssessmentConstants.HIERARCHY)) From 47a50c9553f6aef404df9f34a8338f7b4c7ac5dc Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Wed, 11 May 2022 15:23:13 +0530 Subject: [PATCH 061/490] Issue #SB-29808 debug: Review API failing for H5P contents --- .../mimetype/mgr/BaseMimeTypeManager.scala | 22 ++++++++++--------- .../mgr/impl/H5PMimeTypeMgrImpl.scala | 7 +++++- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/BaseMimeTypeManager.scala b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/BaseMimeTypeManager.scala index c357443c5..0d53fdbf1 100644 --- a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/BaseMimeTypeManager.scala +++ b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/BaseMimeTypeManager.scala @@ -21,7 +21,7 @@ import scala.concurrent.{ExecutionContext, Future} class BaseMimeTypeManager(implicit ss: StorageService) { - protected val TEMP_FILE_LOCATION = Platform.getString("content.upload.temp_location", "/tmp/content") + protected val TEMP_FILE_LOCATION: String = Platform.getString("content.upload.temp_location", "/tmp/content") private val CONTENT_FOLDER = "cloud_storage.content.folder" private val ARTIFACT_FOLDER = "cloud_storage.artifact.folder" private val validator = new UrlValidator() @@ -51,10 +51,11 @@ class BaseMimeTypeManager(implicit ss: StorageService) { throw new ClientException("ERR_INVALID_NODE", "Please Provide Valid Node!") if (null == data) throw new ClientException("ERR_INVALID_DATA", "Please Provide Valid File Or File Url!") - if (data.isInstanceOf[String]) - validateUrl(data.toString) - else if (data.isInstanceOf[File]) - validateFile(data.asInstanceOf[File]) + data match { + case _: String => validateUrl(data.toString) + case file: File => validateFile(file) + case _ => + } } def validateFile(file: File): Unit = { @@ -102,7 +103,7 @@ class BaseMimeTypeManager(implicit ss: StorageService) { def getFileNameFromURL(fileUrl: String): String = { var fileName = FilenameUtils.getBaseName(fileUrl) + "_" + System.currentTimeMillis - if (!FilenameUtils.getExtension(fileUrl).isEmpty) fileName += "." + FilenameUtils.getExtension(fileUrl) + if (FilenameUtils.getExtension(fileUrl).nonEmpty) fileName += "." + FilenameUtils.getExtension(fileUrl) fileName } @@ -114,12 +115,13 @@ class BaseMimeTypeManager(implicit ss: StorageService) { if (null != file && file.exists()) { val zipFile: ZipFile = new ZipFile(file) try { - val entries = checkParams - .filter(fileName => null != zipFile.getEntry(fileName)) - null != entries && !entries.isEmpty + val entries = checkParams.filter(fileName => null != zipFile.getEntry(fileName)) + null != entries && entries.nonEmpty } catch { - case e: Exception => throw new ClientException("ERR_INVALID_FILE", "Please Provide Valid File!") + case e: Exception => + TelemetryManager.error("Error while verifying zip file: ", e) + throw new ClientException("ERR_INVALID_FILE", "Please Provide Valid File!") } finally { if (null != zipFile) zipFile.close() } diff --git a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/H5PMimeTypeMgrImpl.scala b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/H5PMimeTypeMgrImpl.scala index 64d83adaa..bea9318f2 100644 --- a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/H5PMimeTypeMgrImpl.scala +++ b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/H5PMimeTypeMgrImpl.scala @@ -19,8 +19,13 @@ class H5PMimeTypeMgrImpl(implicit ss: StorageService) extends BaseMimeTypeManage override def upload(objectId: String, node: Node, uploadFile: File, filePath: Option[String], params: UploadParams)(implicit ec: ExecutionContext): Future[Map[String, AnyRef]] = { validateUploadRequest(objectId, node, uploadFile) + TelemetryManager.info("H5P content upload params:: " + params) val validationParams = if (StringUtils.equalsIgnoreCase(params.fileFormat.getOrElse(""), COMPOSED_H5P_ZIP)) List[String]("/content/h5p.json", "content/h5p.json") else List[String]("h5p.json", "/h5p.json") + + TelemetryManager.info("H5P content uploadFile exists:: " + uploadFile.exists()) + TelemetryManager.info("H5P content validationParams:: " + validationParams) + if (isValidPackageStructure(uploadFile, validationParams)) { val extractionBasePath = getBasePath(objectId) val zipFile = if (!StringUtils.equalsIgnoreCase(params.fileFormat.getOrElse(""), COMPOSED_H5P_ZIP)) { @@ -41,7 +46,7 @@ class H5PMimeTypeMgrImpl(implicit ss: StorageService) extends BaseMimeTypeManage } Future { Map[String, AnyRef]("identifier" -> objectId, "artifactUrl" -> urls(IDX_S3_URL), "size" -> getFileSize(uploadFile).asInstanceOf[AnyRef], "s3Key" -> urls(IDX_S3_KEY)) } } else { - TelemetryManager.error("ERR_INVALID_FILE" + "Please Provide Valid File! with file name: " + uploadFile.getName) + TelemetryManager.error("ERR_INVALID_FILE:: " + "Please Provide Valid File! with file name: " + uploadFile.getName) throw new ClientException("ERR_INVALID_FILE", "Please Provide Valid File!") } } From f4666a09e9b45c6b42c3f48eacc7eadc6f2c9473 Mon Sep 17 00:00:00 2001 From: joffinjoy <35325730+joffinjoy@users.noreply.github.com> Date: Wed, 11 May 2022 18:07:02 +0530 Subject: [PATCH 062/490] Issue #SB-29145 refactor: Minor Optimization & Renamed Functions --- .../scala/org/sunbird/utils/BranchingUtil.scala | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/assessment-api/assessment-actors/src/main/scala/org/sunbird/utils/BranchingUtil.scala b/assessment-api/assessment-actors/src/main/scala/org/sunbird/utils/BranchingUtil.scala index 17e5d4a8b..fa9098816 100644 --- a/assessment-api/assessment-actors/src/main/scala/org/sunbird/utils/BranchingUtil.scala +++ b/assessment-api/assessment-actors/src/main/scala/org/sunbird/utils/BranchingUtil.scala @@ -17,12 +17,14 @@ object BranchingUtil { val containsBL = nodeMetaData.containsKey(AssessmentConstants.BRANCHING_LOGIC) branchingRecord.put(id, new util.HashMap[String, AnyRef]() { { - if (containsBL) put(AssessmentConstants.BRANCHING_LOGIC, nodeMetaData.get(AssessmentConstants.BRANCHING_LOGIC)) + if (containsBL) { + put(AssessmentConstants.BRANCHING_LOGIC, nodeMetaData.get(AssessmentConstants.BRANCHING_LOGIC)) + nodeMetaData.remove(AssessmentConstants.BRANCHING_LOGIC) + } put(AssessmentConstants.CONTAINS_BL, containsBL.asInstanceOf[AnyRef]) put(AssessmentConstants.COPY_OF, nodeMetaData.get(AssessmentConstants.COPY_OF).asInstanceOf[String]) } }) - if (containsBL) nodeMetaData.remove(AssessmentConstants.BRANCHING_LOGIC) nodeMetaData.remove(AssessmentConstants.COPY_OF) }) branchingRecord @@ -31,7 +33,7 @@ object BranchingUtil { def hierarchyRequestModifier(request: Request, branchingRecord: util.HashMap[String, AnyRef], identifiers: util.Map[String, String]) = { val nodesModified: java.util.HashMap[String, AnyRef] = request.getRequest.get(HierarchyConstants.NODES_MODIFIED).asInstanceOf[java.util.HashMap[String, AnyRef]] val hierarchy: java.util.HashMap[String, AnyRef] = request.getRequest.get(HierarchyConstants.HIERARCHY).asInstanceOf[java.util.HashMap[String, AnyRef]] - val oldToNewIdMap = generateOldToNewIdMap(branchingRecord, identifiers) + val oldToNewIdMap = getIdentifierMapping(branchingRecord, identifiers) branchingRecord.keySet().asScala.toList.map(id => { val nodeInfo = branchingRecord.get(id).asInstanceOf[util.HashMap[String, AnyRef]] val node = nodesModified.get(id).asInstanceOf[util.HashMap[String, AnyRef]] @@ -53,7 +55,6 @@ object BranchingUtil { children.map(identifier => { if (identifiers.containsKey(identifier)) newChildrenList.add(identifiers.get(identifier)) else newChildrenList.add(identifier) }) - nodeHierarchy.remove(AssessmentConstants.CHILDREN) nodeHierarchy.put(AssessmentConstants.CHILDREN, newChildrenList) if (identifiers.containsKey(id)) { hierarchy.remove(id) @@ -78,7 +79,7 @@ object BranchingUtil { }) } - def generateOldToNewIdMap(branchingRecord: util.HashMap[String, AnyRef], identifiers: util.Map[String, String]): util.Map[String, String] = { + def getIdentifierMapping(branchingRecord: util.HashMap[String, AnyRef], identifiers: util.Map[String, String]): util.Map[String, String] = { val oldToNewIdMap = new util.HashMap[String, String]() branchingRecord.keySet().asScala.toList.map(id => { val nodeInfo = branchingRecord.get(id).asInstanceOf[util.HashMap[String, AnyRef]] @@ -97,7 +98,6 @@ object BranchingUtil { newBranchingLogicArray.add(oldToNewIdMap.get(id)) } else newBranchingLogicArray.add(id) }) - nodeBL.remove(name) nodeBL.put(name, newBranchingLogicArray) } @@ -113,7 +113,6 @@ object BranchingUtil { val stringArray = preConditionVar.split("\\.") if (oldToNewIdMap.containsKey(stringArray(0))) { val newString = oldToNewIdMap.get(stringArray(0)) + "." + stringArray.drop(1).mkString(".") - sourceQuestionRecord.remove(AssessmentConstants.PRE_CONDITION_VAR) sourceQuestionRecord.put(AssessmentConstants.PRE_CONDITION_VAR, newString) } }) From e52aedcc2981ac61b91cfd431ef734781dbb45a2 Mon Sep 17 00:00:00 2001 From: joffinjoy <35325730+joffinjoy@users.noreply.github.com> Date: Mon, 16 May 2022 15:49:38 +0530 Subject: [PATCH 063/490] Issue #SB-29145 test: Modified Test-Cases --- .../{copyTrait.scala => CopySpec.scala} | 4 +- .../sunbird/actors/QuestionActorTest.scala | 20 +++---- .../sunbird/actors/QuestionSetActorTest.scala | 56 +++++++++---------- 3 files changed, 40 insertions(+), 40 deletions(-) rename assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/{copyTrait.scala => CopySpec.scala} (99%) diff --git a/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/copyTrait.scala b/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/CopySpec.scala similarity index 99% rename from assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/copyTrait.scala rename to assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/CopySpec.scala index 8ee744cd7..772a23d23 100644 --- a/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/copyTrait.scala +++ b/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/CopySpec.scala @@ -11,7 +11,7 @@ import scala.collection.JavaConversions.mapAsJavaMap import scala.collection.JavaConverters.asJavaIterableConverter import scala.collection.mutable -trait copyTrait { +object CopySpec { private def getQuestionSetRequest(): Request = { val request = new Request() @@ -327,7 +327,7 @@ trait copyTrait { nodesModified } - def generateNodeBLRecord(): util.HashMap[String, AnyRef] = { + def generateBranchingRecord(): util.HashMap[String, AnyRef] = { val nodeBLRecord = new util.HashMap[String, AnyRef]() nodeBLRecord.put("afa2bef1-b5db-45d9-b0d7-aeea757906c3", new util.HashMap[String, AnyRef]() { { diff --git a/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/QuestionActorTest.scala b/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/QuestionActorTest.scala index e0a2d4139..349bc916a 100644 --- a/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/QuestionActorTest.scala +++ b/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/QuestionActorTest.scala @@ -16,7 +16,7 @@ import scala.collection.JavaConverters._ import scala.concurrent.Future import scala.concurrent.ExecutionContext.Implicits.global -class QuestionActorTest extends BaseSpec with MockFactory with copyTrait { +class QuestionActorTest extends BaseSpec with MockFactory { "questionActor" should "return failed response for 'unknown' operation" in { implicit val oec: OntologyEngineContext = new OntologyEngineContext @@ -321,12 +321,12 @@ class QuestionActorTest extends BaseSpec with MockFactory with copyTrait { (oec.graphService _).expects().returns(graphDB).anyNumberOfTimes() val nodes: util.List[Node] = getCategoryNode() (graphDB.getNodeByUniqueIds(_: String, _: SearchCriteria)).expects(*, *).returns(Future(nodes)).anyNumberOfTimes() - (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects("domain", "do_1234", false, *).returns(Future(getExistingQuestionNode())).anyNumberOfTimes() - (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, List("objectMetadata")).returns(Future(getSuccessfulResponse())).anyNumberOfTimes() - (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, *).returns(Future(getReadPropsResponseForQuestion())).anyNumberOfTimes() - (graphDB.addNode(_: String, _: Node)).expects(*, *).returns(Future(getNewQuestionNode())) - (graphDB.saveExternalProps(_: Request)).expects(*).returns(Future(getSuccessfulResponse())).anyNumberOfTimes - val request = getQuestionCopyRequest() + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects("domain", "do_1234", false, *).returns(Future(CopySpec.getExistingQuestionNode())).anyNumberOfTimes() + (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, List("objectMetadata")).returns(Future(CopySpec.getSuccessfulResponse())).anyNumberOfTimes() + (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, *).returns(Future(CopySpec.getReadPropsResponseForQuestion())).anyNumberOfTimes() + (graphDB.addNode(_: String, _: Node)).expects(*, *).returns(Future(CopySpec.getNewQuestionNode())) + (graphDB.saveExternalProps(_: Request)).expects(*).returns(Future(CopySpec.getSuccessfulResponse())).anyNumberOfTimes + val request = CopySpec.getQuestionCopyRequest() request.putAll(mapAsJavaMap(Map("identifier" -> "do_1234", "mode" -> "", "copyType"-> "deep"))) request.setOperation("copyQuestion") val response = callActor(request, Props(new QuestionActor())) @@ -335,7 +335,7 @@ class QuestionActorTest extends BaseSpec with MockFactory with copyTrait { it should "return error response for 'copyQuestion' when createdFor & createdBy is missing" in { implicit val oec: OntologyEngineContext = mock[OntologyEngineContext] - val request = getInvalidQuestionSetCopyRequest() + val request = CopySpec.getInvalidQuestionSetCopyRequest() request.putAll(mapAsJavaMap(Map("identifier" -> "do_1234", "mode" -> "", "copyType"-> "deep"))) request.setOperation("copyQuestion") val response = callActor(request, Props(new QuestionActor())) @@ -346,8 +346,8 @@ class QuestionActorTest extends BaseSpec with MockFactory with copyTrait { implicit val oec: OntologyEngineContext = mock[OntologyEngineContext] val graphDB = mock[GraphService] (oec.graphService _).expects().returns(graphDB).anyNumberOfTimes() - val request = getQuestionCopyRequest() - (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects("domain", "do_1234", false, *).returns(Future(getQuestionNode())).anyNumberOfTimes() + val request = CopySpec.getQuestionCopyRequest() + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects("domain", "do_1234", false, *).returns(Future(CopySpec.getQuestionNode())).anyNumberOfTimes() request.putAll(mapAsJavaMap(Map("identifier" -> "do_1234", "mode" -> "", "copyType"-> "deep"))) request.setOperation("copyQuestion") val response = callActor(request, Props(new QuestionActor())) diff --git a/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/QuestionSetActorTest.scala b/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/QuestionSetActorTest.scala index 25df70a3b..25591cd35 100644 --- a/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/QuestionSetActorTest.scala +++ b/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/QuestionSetActorTest.scala @@ -20,7 +20,7 @@ import scala.collection.JavaConverters._ import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future -class QuestionSetActorTest extends BaseSpec with MockFactory with copyTrait { +class QuestionSetActorTest extends BaseSpec with MockFactory { "questionSetActor" should "return failed response for 'unknown' operation" in { implicit val oec: OntologyEngineContext = new OntologyEngineContext @@ -546,20 +546,20 @@ class QuestionSetActorTest extends BaseSpec with MockFactory with copyTrait { (oec.graphService _).expects().returns(graphDB).anyNumberOfTimes() val nodes: util.List[Node] = getCategoryNode() (graphDB.getNodeByUniqueIds(_: String, _: SearchCriteria)).expects(*, *).returns(Future(nodes)).anyNumberOfTimes() - (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects("domain", "do_1234", false, *).returns(Future(getExistingRootNode())).anyNumberOfTimes() - (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects("domain", "do_9876", false, *).returns(Future(getNewRootNode())).anyNumberOfTimes() - (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects("domain", "do_9876.img", false, *).returns(Future(getNewRootNode())).anyNumberOfTimes() - (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects("domain", *, false, *).returns(Future(getQuestionNode())).anyNumberOfTimes() - (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, List("objectMetadata")).returns(Future(getSuccessfulResponse())).anyNumberOfTimes() - (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, *).returns(Future(getExternalPropsResponseWithData())).anyNumberOfTimes() - (graphDB.updateExternalProps(_: Request)).expects(*).returns(Future(getSuccessfulResponse())).anyNumberOfTimes - (graphDB.saveExternalProps(_: Request)).expects(*).returns(Future(getSuccessfulResponse())).anyNumberOfTimes - (graphDB.upsertNode(_: String, _: Node, _: Request)).expects(*, *, *).returns(Future(getUpsertNode())).anyNumberOfTimes() + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects("domain", "do_1234", false, *).returns(Future(CopySpec.getExistingRootNode())).anyNumberOfTimes() + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects("domain", "do_9876", false, *).returns(Future(CopySpec.getNewRootNode())).anyNumberOfTimes() + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects("domain", "do_9876.img", false, *).returns(Future(CopySpec.getNewRootNode())).anyNumberOfTimes() + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects("domain", *, false, *).returns(Future(CopySpec.getQuestionNode())).anyNumberOfTimes() + (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, List("objectMetadata")).returns(Future(CopySpec.getSuccessfulResponse())).anyNumberOfTimes() + (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, *).returns(Future(CopySpec.getExternalPropsResponseWithData())).anyNumberOfTimes() + (graphDB.updateExternalProps(_: Request)).expects(*).returns(Future(CopySpec.getSuccessfulResponse())).anyNumberOfTimes + (graphDB.saveExternalProps(_: Request)).expects(*).returns(Future(CopySpec.getSuccessfulResponse())).anyNumberOfTimes + (graphDB.upsertNode(_: String, _: Node, _: Request)).expects(*, *, *).returns(Future(CopySpec.getUpsertNode())).anyNumberOfTimes() inSequence { - (graphDB.addNode(_: String, _: Node)).expects(*, *).returns(Future(getNewRootNode())) - (graphDB.addNode(_: String, _: Node)).expects(*, *).returns(Future(getQuestionNode())) + (graphDB.addNode(_: String, _: Node)).expects(*, *).returns(Future(CopySpec.getNewRootNode())) + (graphDB.addNode(_: String, _: Node)).expects(*, *).returns(Future(CopySpec.getQuestionNode())) } - val request = getQuestionSetCopyRequest() + val request = CopySpec.getQuestionSetCopyRequest() request.putAll(mapAsJavaMap(Map("identifier" -> "do_1234", "mode" -> "", "copyType"-> "deep"))) request.setOperation("copyQuestionSet") val response = callActor(request, Props(new QuestionSetActor())) @@ -572,15 +572,15 @@ class QuestionSetActorTest extends BaseSpec with MockFactory with copyTrait { (oec.graphService _).expects().returns(graphDB).anyNumberOfTimes() val nodes: util.List[Node] = getCategoryNode() (graphDB.getNodeByUniqueIds(_: String, _: SearchCriteria)).expects(*, *).returns(Future(nodes)).anyNumberOfTimes() - (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects("domain", "do_1234", false, *).returns(Future(getExistingRootNode())).anyNumberOfTimes() - (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects("domain", "do_5678", false, *).returns(Future(getNewRootNode())).anyNumberOfTimes() - (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, List("objectMetadata")).returns(Future(getSuccessfulResponse())).anyNumberOfTimes() - (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, *).returns(Future(getExternalPropsResponseWithData())).anyNumberOfTimes() - (graphDB.addNode(_: String, _: Node)).expects(*, *).returns(Future(getQuestionNode())) - (graphDB.saveExternalProps(_: Request)).expects(*).returns(Future(getSuccessfulResponse())).anyNumberOfTimes - (graphDB.upsertNode(_: String, _: Node, _: Request)).expects(*, *, *).returns(Future(getUpsertNode())).anyNumberOfTimes() - (graphDB.updateExternalProps(_: Request)).expects(*).returns(Future(getSuccessfulResponse())).anyNumberOfTimes - val request = getQuestionSetCopyRequest() + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects("domain", "do_1234", false, *).returns(Future(CopySpec.getExistingRootNode())).anyNumberOfTimes() + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects("domain", "do_5678", false, *).returns(Future(CopySpec.getNewRootNode())).anyNumberOfTimes() + (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, List("objectMetadata")).returns(Future(CopySpec.getSuccessfulResponse())).anyNumberOfTimes() + (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, *).returns(Future(CopySpec.getExternalPropsResponseWithData())).anyNumberOfTimes() + (graphDB.addNode(_: String, _: Node)).expects(*, *).returns(Future(CopySpec.getQuestionNode())) + (graphDB.saveExternalProps(_: Request)).expects(*).returns(Future(CopySpec.getSuccessfulResponse())).anyNumberOfTimes + (graphDB.upsertNode(_: String, _: Node, _: Request)).expects(*, *, *).returns(Future(CopySpec.getUpsertNode())).anyNumberOfTimes() + (graphDB.updateExternalProps(_: Request)).expects(*).returns(Future(CopySpec.getSuccessfulResponse())).anyNumberOfTimes + val request = CopySpec.getQuestionSetCopyRequest() request.putAll(mapAsJavaMap(Map("identifier" -> "do_1234", "mode" -> "", "copyType"-> "shallow"))) request.setOperation("copyQuestionSet") val response = callActor(request, Props(new QuestionSetActor())) @@ -589,21 +589,21 @@ class QuestionSetActorTest extends BaseSpec with MockFactory with copyTrait { it should "return error response for 'copyQuestionSet' when createdFor & createdBy is missing" in { implicit val oec: OntologyEngineContext = mock[OntologyEngineContext] - val request = getInvalidQuestionCopyRequest() + val request = CopySpec.getInvalidQuestionCopyRequest() request.putAll(mapAsJavaMap(Map("identifier" -> "do_1234", "mode" -> "", "copyType"-> "deep"))) request.setOperation("copyQuestionSet") val response = callActor(request, Props(new QuestionSetActor())) assert("failed".equals(response.getParams.getStatus)) } - it should "return expected result for 'generateNodeBLRecord'" in { - val result = BranchingUtil.generateBranchingRecord(generateNodesModified("afa2bef1-b5db-45d9-b0d7-aeea757906c3", true)) - assert(result == generateNodeBLRecord) + it should "return expected result for 'generateBranchingRecord'" in { + val result = BranchingUtil.generateBranchingRecord(CopySpec.generateNodesModified("afa2bef1-b5db-45d9-b0d7-aeea757906c3", true)) + assert(result == CopySpec.generateBranchingRecord) } it should "return expected result for 'hierarchyRequestModifier'" in { - val result = BranchingUtil.hierarchyRequestModifier(generateUpdateRequest(false, "afa2bef1-b5db-45d9-b0d7-aeea757906c3"), generateNodeBLRecord(), generateIdentifiers()) - val expectedResult = generateUpdateRequest(true, "do_11351201604857856013") + val result = BranchingUtil.hierarchyRequestModifier(CopySpec.generateUpdateRequest(false, "afa2bef1-b5db-45d9-b0d7-aeea757906c3"), CopySpec.generateBranchingRecord(), CopySpec.generateIdentifiers()) + val expectedResult = CopySpec.generateUpdateRequest(true, "do_11351201604857856013") assert(result.getRequest.get(AssessmentConstants.NODES_MODIFIED) == expectedResult.getRequest.get(AssessmentConstants.NODES_MODIFIED)) assert(result.getRequest.get(AssessmentConstants.HIERARCHY) == expectedResult.getRequest.get(AssessmentConstants.HIERARCHY)) } From 66b16598d655e745a85afe241b07f58c8550b199 Mon Sep 17 00:00:00 2001 From: Kumar Gauraw Date: Fri, 27 May 2022 01:30:28 +0530 Subject: [PATCH 064/490] Issue #SB-26400 fix: Code fix for lastPublishedBy --- .../src/main/scala/org/sunbird/actors/QuestionActor.scala | 3 +++ .../main/scala/org/sunbird/actors/QuestionSetActor.scala | 7 ++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/assessment-api/assessment-actors/src/main/scala/org/sunbird/actors/QuestionActor.scala b/assessment-api/assessment-actors/src/main/scala/org/sunbird/actors/QuestionActor.scala index 89d32184a..5e1eb12ca 100644 --- a/assessment-api/assessment-actors/src/main/scala/org/sunbird/actors/QuestionActor.scala +++ b/assessment-api/assessment-actors/src/main/scala/org/sunbird/actors/QuestionActor.scala @@ -58,8 +58,11 @@ class QuestionActor @Inject()(implicit oec: OntologyEngineContext) extends BaseA } def publish(request: Request): Future[Response] = { + val lastPublishedBy: String = request.getRequest.getOrDefault("lastPublishedBy", "").asInstanceOf[String] request.getRequest.put("identifier", request.getContext.get("identifier")) AssessmentManager.getValidatedNodeForPublish(request, "ERR_QUESTION_PUBLISH").map(node => { + if(StringUtils.isNotBlank(lastPublishedBy)) + node.getMetadata.put("lastPublishedBy", lastPublishedBy) AssessmentManager.pushInstructionEvent(node.getIdentifier, node) ResponseHandler.OK.putAll(Map[String, AnyRef]("identifier" -> node.getIdentifier.replace(".img", ""), "message" -> "Question is successfully sent for Publish").asJava) }) diff --git a/assessment-api/assessment-actors/src/main/scala/org/sunbird/actors/QuestionSetActor.scala b/assessment-api/assessment-actors/src/main/scala/org/sunbird/actors/QuestionSetActor.scala index b4a68317c..797b684d9 100644 --- a/assessment-api/assessment-actors/src/main/scala/org/sunbird/actors/QuestionSetActor.scala +++ b/assessment-api/assessment-actors/src/main/scala/org/sunbird/actors/QuestionSetActor.scala @@ -1,8 +1,10 @@ package org.sunbird.actors import java.util + import javax.inject.Inject import org.apache.commons.collections4.CollectionUtils +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 @@ -65,13 +67,16 @@ class QuestionSetActor @Inject()(implicit oec: OntologyEngineContext) extends Ba } def publish(request: Request): Future[Response] = { + val lastPublishedBy: String = request.getRequest.getOrDefault("lastPublishedBy", "").asInstanceOf[String] request.getRequest.put("identifier", request.getContext.get("identifier")) request.put("mode", "edit") AssessmentManager.getValidatedNodeForPublish(request, "ERR_QUESTION_SET_PUBLISH").flatMap(node => { AssessmentManager.getQuestionSetHierarchy(request, node).map(hierarchyString => { AssessmentManager.validateQuestionSetHierarchy(hierarchyString.asInstanceOf[String], node.getMetadata.getOrDefault("createdBy", "").asInstanceOf[String]) + if(StringUtils.isNotBlank(lastPublishedBy)) + node.getMetadata.put("lastPublishedBy", lastPublishedBy) AssessmentManager.pushInstructionEvent(node.getIdentifier, node) - ResponseHandler.OK.putAll(Map[String, AnyRef]("identifier" -> node.getIdentifier.replace(".img", ""), "message" -> "Question is successfully sent for Publish").asJava) + ResponseHandler.OK.putAll(Map[String, AnyRef]("identifier" -> node.getIdentifier.replace(".img", ""), "message" -> "QuestionSet is successfully sent for Publish").asJava) }) }) } From 3b928954166ba403cfb4a553f3633208b22803f0 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Wed, 15 Jun 2022 19:02:05 +0530 Subject: [PATCH 065/490] Issue #SB-30307 fix: relational_metadata setting as string null --- .../scala/org/sunbird/content/actors/ContentActor.scala | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/actors/ContentActor.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/actors/ContentActor.scala index 16ebcc64d..7ad4e0dee 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/actors/ContentActor.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/actors/ContentActor.scala @@ -243,6 +243,12 @@ class ContentActor @Inject() (implicit oec: OntologyEngineContext, ss: StorageSe "Yes".equalsIgnoreCase(node.getMetadata.getOrDefault("trackable", new java.util.HashMap[String, AnyRef]).asInstanceOf[java.util.Map[String, AnyRef]].getOrDefault("enabled", "").asInstanceOf[String])) { node.getMetadata.put("contentType", "Course") } + + //TODO: Below fix to be reviewed when the fix for null to Stringify in ExternalStore.scala is implemented + if(node.getExternalData != null && node.getExternalData.containsKey("relational_metadata") && node.getExternalData.get("relational_metadata") == null) { + node.getExternalData.put("relational_metadata", "{}") + } + node } From 89e77bc2931e3db9bd2ff58439147e8874a5bed0 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Wed, 15 Jun 2022 19:05:13 +0530 Subject: [PATCH 066/490] Issue #SB-30307 fix: relational_metadata setting as string null --- .../main/scala/org/sunbird/content/actors/ContentActor.scala | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/actors/ContentActor.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/actors/ContentActor.scala index 16ebcc64d..fabc95d43 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/actors/ContentActor.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/actors/ContentActor.scala @@ -243,6 +243,11 @@ class ContentActor @Inject() (implicit oec: OntologyEngineContext, ss: StorageSe "Yes".equalsIgnoreCase(node.getMetadata.getOrDefault("trackable", new java.util.HashMap[String, AnyRef]).asInstanceOf[java.util.Map[String, AnyRef]].getOrDefault("enabled", "").asInstanceOf[String])) { node.getMetadata.put("contentType", "Course") } + + //TODO: Below fix to be reviewed when the fix for null to Stringify in ExternalStore.scala is implemented + if(node.getExternalData != null && node.getExternalData.containsKey("relational_metadata") && node.getExternalData.get("relational_metadata") == null) { + node.getExternalData.put("relational_metadata", "{}") + } node } From 079c080ac13bc181401a2726b353508331487de0 Mon Sep 17 00:00:00 2001 From: Kumar Gauraw Date: Mon, 27 Jun 2022 14:16:34 +0530 Subject: [PATCH 067/490] Issue #SB-30221 feat: added profile to build core components --- pom.xml | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/pom.xml b/pom.xml index f3f14193a..2ce1e07bc 100644 --- a/pom.xml +++ b/pom.xml @@ -17,15 +17,34 @@ 3.0.8 2.9.8 - - platform-core - ontology-engine - content-api - assessment-api - taxonomy-api - platform-modules - search-api - + + + knowlg-platform + + true + + + platform-core + ontology-engine + content-api + assessment-api + taxonomy-api + platform-modules + search-api + + + + knowlg-core + + false + + + platform-core + ontology-engine + platform-modules/import-manager + + + From db528da02e6c842faeb1f7d5cc868096e51ab41b Mon Sep 17 00:00:00 2001 From: Kumar Gauraw Date: Tue, 28 Jun 2022 13:45:52 +0530 Subject: [PATCH 068/490] Issue #SB-26264 fix: Revert added dependency --- platform-modules/import-manager/pom.xml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/platform-modules/import-manager/pom.xml b/platform-modules/import-manager/pom.xml index 13c5fa70c..7914fcf5c 100644 --- a/platform-modules/import-manager/pom.xml +++ b/platform-modules/import-manager/pom.xml @@ -22,16 +22,6 @@ platform-telemetry 1.0-SNAPSHOT - - org.sunbird - mimetype-manager - 1.0-SNAPSHOT - - - org.sunbird - url-manager - 1.0-SNAPSHOT - org.sunbird graph-engine_2.11 From f4c9133a32cd08710a6e63893ed4972f35e1f823 Mon Sep 17 00:00:00 2001 From: Kumar Gauraw Date: Tue, 28 Jun 2022 16:47:44 +0530 Subject: [PATCH 069/490] Issue #SB-30221 fix: fixed build issue --- .../main/scala/org/sunbird/managers/CopyManager.scala | 11 ++++++----- .../main/scala/org/sunbird/utils/BranchingUtil.scala | 2 +- .../src/test/scala/org/sunbird/actors/CopySpec.scala | 4 ++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/assessment-api/assessment-actors/src/main/scala/org/sunbird/managers/CopyManager.scala b/assessment-api/assessment-actors/src/main/scala/org/sunbird/managers/CopyManager.scala index dc73939e9..22aefc8f0 100644 --- a/assessment-api/assessment-actors/src/main/scala/org/sunbird/managers/CopyManager.scala +++ b/assessment-api/assessment-actors/src/main/scala/org/sunbird/managers/CopyManager.scala @@ -1,8 +1,7 @@ package org.sunbird.managers -import org.apache.commons.collections.CollectionUtils +import org.apache.commons.collections4.CollectionUtils import org.apache.commons.collections4.MapUtils -import org.apache.commons.lang.StringUtils import org.sunbird.common.{JsonUtils, Platform} import org.sunbird.common.dto.{Request, Response, ResponseHandler} import org.sunbird.common.exception.{ClientException, ServerException} @@ -14,11 +13,13 @@ import org.sunbird.graph.schema.DefinitionNode import org.sunbird.graph.utils.{NodeUtil, ScalaJsonUtils} import org.sunbird.telemetry.logger.TelemetryManager import org.sunbird.utils.{AssessmentConstants, BranchingUtil, HierarchyConstants} - import java.util -import java.util.concurrent.{CompletionException} +import java.util.concurrent.CompletionException import java.util.{Optional, UUID} -import scala.collection.JavaConversions.{mapAsScalaMap} + +import org.apache.commons.lang3.StringUtils + +import scala.collection.JavaConversions.mapAsScalaMap import scala.collection.JavaConverters._ import scala.concurrent.{ExecutionContext, Future} diff --git a/assessment-api/assessment-actors/src/main/scala/org/sunbird/utils/BranchingUtil.scala b/assessment-api/assessment-actors/src/main/scala/org/sunbird/utils/BranchingUtil.scala index fa9098816..791079d42 100644 --- a/assessment-api/assessment-actors/src/main/scala/org/sunbird/utils/BranchingUtil.scala +++ b/assessment-api/assessment-actors/src/main/scala/org/sunbird/utils/BranchingUtil.scala @@ -1,6 +1,6 @@ package org.sunbird.utils -import org.apache.commons.lang.StringUtils +import org.apache.commons.lang3.StringUtils import org.sunbird.common.dto.Request import java.util diff --git a/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/CopySpec.scala b/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/CopySpec.scala index 772a23d23..b5cbd725e 100644 --- a/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/CopySpec.scala +++ b/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/CopySpec.scala @@ -1,6 +1,6 @@ package org.sunbird.actors -import org.mortbay.util.StringUtil +import org.apache.commons.lang3.StringUtils import org.sunbird.common.dto.{Request, Response, ResponseParams} import org.sunbird.graph.dac.model.Node import org.sunbird.utils.AssessmentConstants @@ -105,7 +105,7 @@ object CopySpec { { put("code", "xyz") put("mimeType", { - if (StringUtil.endsWithIgnoreCase(objectType, AssessmentConstants.QUESTIONSET_SCHEMA_NAME)) { + if (StringUtils.endsWithIgnoreCase(objectType, AssessmentConstants.QUESTIONSET_SCHEMA_NAME)) { AssessmentConstants.QUESTIONSET_MIME_TYPE } else { AssessmentConstants.QUESTION_MIME_TYPE From 57574afa31c31ae81d543b158097f28a614dcf71 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Wed, 29 Jun 2022 11:08:36 +0530 Subject: [PATCH 070/490] Update ContentActor.scala --- .../src/main/scala/org/sunbird/content/actors/ContentActor.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/actors/ContentActor.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/actors/ContentActor.scala index c9bb0187b..fabc95d43 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/actors/ContentActor.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/actors/ContentActor.scala @@ -248,7 +248,6 @@ class ContentActor @Inject() (implicit oec: OntologyEngineContext, ss: StorageSe if(node.getExternalData != null && node.getExternalData.containsKey("relational_metadata") && node.getExternalData.get("relational_metadata") == null) { node.getExternalData.put("relational_metadata", "{}") } - node } From 889040c424ec06f30a89aa42c42531287b09eed1 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Wed, 29 Jun 2022 11:09:34 +0530 Subject: [PATCH 071/490] Update LocalizeAssetProcessor.scala --- .../mimetype/ecml/processor/LocalizeAssetProcessor.scala | 3 --- 1 file changed, 3 deletions(-) diff --git a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/ecml/processor/LocalizeAssetProcessor.scala b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/ecml/processor/LocalizeAssetProcessor.scala index 432f88773..629953a1a 100644 --- a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/ecml/processor/LocalizeAssetProcessor.scala +++ b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/ecml/processor/LocalizeAssetProcessor.scala @@ -7,7 +7,6 @@ import org.apache.commons.lang3.StringUtils import org.sunbird.cloudstore.StorageService import org.sunbird.common.Platform import org.sunbird.common.exception.ClientException -import org.sunbird.telemetry.logger.TelemetryManager import scala.concurrent.{Await, ExecutionContext, Future} import scala.concurrent.duration.Duration @@ -62,8 +61,6 @@ trait LocalizeAssetProcessor extends IProcessor { } def downloadFile(downloadPath: String, fileUrl: String): File = try { - TelemetryManager.info("LocalizeAssetProcessor:: downloadFile:: downloadPath:: " + downloadPath) - TelemetryManager.info("LocalizeAssetProcessor:: downloadFile:: fileUrl:: " + fileUrl) createDirectory(downloadPath) val file = new File(downloadPath + File.separator + getFileNameFromURL(fileUrl)) FileUtils.copyURLToFile(new URL(fileUrl), file) From 88d3ca9b5ad6b714b824b55024d659d940748a5a Mon Sep 17 00:00:00 2001 From: Kumar Gauraw Date: Wed, 29 Jun 2022 16:32:27 +0530 Subject: [PATCH 072/490] Issue #SB-30221 feat: fixed build issue --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2ce1e07bc..0558992df 100644 --- a/pom.xml +++ b/pom.xml @@ -41,7 +41,7 @@ platform-core ontology-engine - platform-modules/import-manager + platform-modules From 65415159596b75e3eb7ae7f1027b79c6acb0c015 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Thu, 30 Jun 2022 14:57:40 +0530 Subject: [PATCH 073/490] Issue #SB-30154 feat: DIAL Link API refactoring debug. --- .../scala/org/sunbird/content/dial/DIALManager.scala | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala index a62dcfe28..6bd706632 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala @@ -10,6 +10,7 @@ import org.sunbird.graph.dac.model.Node import org.sunbird.graph.nodes.DataNode import org.sunbird.graph.utils.ScalaJsonUtils import org.sunbird.managers.HierarchyManager +import org.sunbird.telemetry.logger.TelemetryManager import java.util import scala.collection.JavaConverters._ @@ -27,8 +28,11 @@ object DIALManager { val linkType: String = request.getContext.getOrDefault(DIALConstants.LINK_TYPE, DIALConstants.CONTENT).asInstanceOf[String] val channelId: String = request.getContext.getOrDefault(DIALConstants.CHANNEL, "").asInstanceOf[String] val objectId: String = request.getContext.getOrDefault(DIALConstants.IDENTIFIER, "").asInstanceOf[String] + TelemetryManager.info("DIALManager::link:: linkType: " + linkType + " || channelId: " + channelId + " || objectId: " + objectId) val reqList: List[Map[String, List[String]]] = getRequestData(request) + TelemetryManager.info("DIALManager::link:: reqList: " + reqList) val requestMap: Map[String, List[String]] = validateAndGetRequestMap(channelId, reqList) + TelemetryManager.info("DIALManager::link:: requestMap: " + requestMap) linkType match { case DIALConstants.CONTENT => linkContent(requestMap, request.getContext) case DIALConstants.COLLECTION => linkCollection(objectId, requestMap, request.getContext) @@ -126,17 +130,19 @@ object DIALManager { req.setContext(reqContext) req.put(ContentConstants.IDENTIFIER, objectId) req.put(ContentConstants.MODE, ContentConstants.EDIT_MODE) + TelemetryManager.info("DIALManager::linkCollection:: req: " + req) DataNode.read(req).flatMap(rootNode => { req.getContext.put(ContentConstants.SCHEMA_NAME, ContentConstants.COLLECTION_SCHEMA_NAME) req.getContext.put(ContentConstants.VERSION, ContentConstants.SCHEMA_VERSION) req.put(ContentConstants.ROOT_ID, objectId) + TelemetryManager.info("DIALManager::linkCollection:: updated req: " + req) HierarchyManager.getHierarchy(req).flatMap(getHierarchyResponse => { val collectionHierarchy = getHierarchyResponse.getResult.getOrDefault(ContentConstants.CONTENT, new java.util.HashMap[String, AnyRef]()).asInstanceOf[java.util.Map[String, AnyRef]] val childrenHierarchy = collectionHierarchy.get(ContentConstants.CHILDREN).asInstanceOf[util.List[util.Map[String, AnyRef]]] val updatedChildrenHierarchy = updateChildrenHierarchy(childrenHierarchy, requestMap) val childrenDIALMap = getChildrenDIALMap(updatedChildrenHierarchy, requestMap) val consolidatedUnitDIALMap = if (!requestMap.contains(objectId)) childrenDIALMap else childrenDIALMap ++ Map(objectId -> requestMap(objectId)) - + TelemetryManager.info("DIALManager::linkCollection:: consolidatedUnitDIALMap: " + consolidatedUnitDIALMap) validateDuplicateDIALCodes(consolidatedUnitDIALMap.filter(rec => rec._2.asInstanceOf[List[String]].nonEmpty)) val updatedHierarchy = new java.util.HashMap[String, AnyRef]() @@ -146,6 +152,7 @@ object DIALManager { val hierarchyReq = new Request(req) hierarchyReq.put(ContentConstants.HIERARCHY, ScalaJsonUtils.serialize(updatedHierarchy)) hierarchyReq.put(ContentConstants.IDENTIFIER, rootNode.getIdentifier) + TelemetryManager.info("DIALManager::linkCollection:: hierarchyReq: " + hierarchyReq) oec.graphService.saveExternalProps(hierarchyReq).flatMap(rec => if(requestMap.contains(objectId)) { val updateReq = new Request(req) updateReq.put(ContentConstants.IDENTIFIER, rootNode.getIdentifier) From 5891ebf706504c0e571f84b15c0ce9538020f904 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Thu, 30 Jun 2022 16:07:10 +0530 Subject: [PATCH 074/490] Issue #SB-30154 feat: DIAL Link API refactoring debug. --- .../main/scala/org/sunbird/content/dial/DIALManager.scala | 6 +++++- .../content-actors/src/test/resources/application.conf | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala index 6bd706632..bf2b734f2 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala @@ -65,8 +65,10 @@ object DIALManager { validateReqStructure(dialcodes, contents) contents.foreach(id => reqMap += (id -> dialcodes)) }) + TelemetryManager.info("DIALManager::validateAndGetRequestMap:: requestList: " + requestList) if (Platform.getBoolean("content.link_dialcode.validation", true)) { val dials = requestList.collect { case m if m.contains(DIALConstants.DIALCODE) => m(DIALConstants.DIALCODE) }.flatten + TelemetryManager.info("DIALManager::validateAndGetRequestMap:: dials: " + dials) validateDialCodes(channelId, dials) } reqMap @@ -90,8 +92,10 @@ object DIALManager { }}) }} val headerParam = new util.HashMap[String, String]{put(DIALConstants.X_CHANNEL_ID, channelId); put(DIALConstants.AUTHORIZATION, DIAL_API_AUTH_KEY);} - + TelemetryManager.info("DIALManager::validateAndGetRequestMap:: DIAL_SEARCH_API_URL: " + DIAL_SEARCH_API_URL) + TelemetryManager.info("DIALManager::validateAndGetRequestMap:: reqMap: " + reqMap) val searchResponse = oec.httpUtil.post(DIAL_SEARCH_API_URL, reqMap, headerParam) + TelemetryManager.info("DIALManager::validateAndGetRequestMap:: searchResponse.getResponseCode: " + searchResponse.getResponseCode) if (searchResponse.getResponseCode.toString == "OK") { val result = searchResponse.getResult if (dialcodes.distinct.size == result.get(DIALConstants.COUNT).asInstanceOf[Integer]) { diff --git a/content-api/content-actors/src/test/resources/application.conf b/content-api/content-actors/src/test/resources/application.conf index aa9400e59..0428b1e0e 100644 --- a/content-api/content-actors/src/test/resources/application.conf +++ b/content-api/content-actors/src/test/resources/application.conf @@ -495,7 +495,7 @@ dial_service { api { base_url = "https://qa.ekstep.in/api" auth_key = "auth_key" - search = "/dialcode/v1/search" + search = "/dialcode/v3/search" } } content.link_dialcode.validation=true From 4f5a0e552e13519d67d808f9d672c98b491fba79 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Thu, 30 Jun 2022 17:19:20 +0530 Subject: [PATCH 075/490] Issue #SB-30154 feat: DIAL Link API refactoring. --- .../org/sunbird/content/dial/DIALErrors.scala | 2 ++ .../org/sunbird/content/dial/DIALManager.scala | 18 +++++++++++------- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALErrors.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALErrors.scala index 53fa49388..3d528dde4 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALErrors.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALErrors.scala @@ -6,6 +6,7 @@ object DIALErrors { val ERR_DIALCODE_LINK_REQUEST: String = "ERR_DIALCODE_LINK_REQUEST" val ERR_DIALCODE_LINK: String = "ERR_DIALCODE_LINK" val ERR_DUPLICATE_DIAL_CODES: String = "ERR_DUPLICATE_DIAL_CODES" + val ERR_DIALCODE_CONTENT_LINK_FIELDS_MISSING: String = "ERR_DIALCODE_CONTENT_LINK_FIELDS_MISSING" //Error Messages val ERR_INVALID_REQ_MSG: String = "Invalid Request! Please Provide Valid Request." @@ -15,4 +16,5 @@ object DIALErrors { val ERR_CONTENT_NOT_FOUND_MSG: String = "Content Not Found With Id(s): " val ERR_SERVER_ERROR_MSG: String = "Something Went Wrong While Processing Your Request. Please Try Again After Sometime!" val ERR_DUPLICATE_DIAL_CODES_MSG: String = "QR Code should not be linked to multiple contents. Please validate: " + val ERR_DIALCODE_CONTENT_LINK_FIELDS_MISSING_MSG: String = "Required fields for content link dialcode are missing." } diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala index bf2b734f2..8fecd0c05 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala @@ -59,12 +59,16 @@ object DIALManager { def validateAndGetRequestMap(channelId: String, requestList: List[Map[String, List[String]]])(implicit oec:OntologyEngineContext): Map[String, List[String]] = { var reqMap = HashMap[String, List[String]]() - requestList.foreach(req => { - val contents: List[String] = req(DIALConstants.IDENTIFIER) - val dialcodes: List[String] = req(DIALConstants.DIALCODE) - validateReqStructure(dialcodes, contents) - contents.foreach(id => reqMap += (id -> dialcodes)) - }) + try { + requestList.foreach(req => { + val contents: List[String] = req(DIALConstants.IDENTIFIER) + val dialcodes: List[String] = req(DIALConstants.DIALCODE) + validateReqStructure(dialcodes, contents) + contents.foreach(id => reqMap += (id -> dialcodes)) + }) + } catch { + case e: Exception => throw new ClientException(DIALErrors.ERR_DIALCODE_CONTENT_LINK_FIELDS_MISSING, DIALErrors.ERR_DIALCODE_CONTENT_LINK_FIELDS_MISSING_MSG) + } TelemetryManager.info("DIALManager::validateAndGetRequestMap:: requestList: " + requestList) if (Platform.getBoolean("content.link_dialcode.validation", true)) { val dials = requestList.collect { case m if m.contains(DIALConstants.DIALCODE) => m(DIALConstants.DIALCODE) }.flatten @@ -257,7 +261,7 @@ object DIALManager { val dupUnitsList: List[String] = unitDIALCodesMap.flatMap(loopMapRec => if(loopMapRec._1 != mapRec._1 && loopMapRec._2.asInstanceOf[List[String]].contains(listRec)) { List(loopMapRec._1, mapRec._1) } else List.empty[String]).filter(unitRec => unitRec.nonEmpty).toList - Map(listRec -> dupUnitsList) + Map(listRec -> dupUnitsList.toSet) })).filter(unitRec => unitRec._2.nonEmpty) if (duplicateDIALCodes.nonEmpty) From 53b6ee2b40ef392102b7967c0adeadfb4b3c2b8b Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Thu, 30 Jun 2022 17:40:06 +0530 Subject: [PATCH 076/490] Issue #SB-30154 feat: DIAL Link API refactoring. --- .../org/sunbird/content/dial/DIALErrors.scala | 2 +- .../org/sunbird/content/dial/DIALManager.scala | 16 ++++++---------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALErrors.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALErrors.scala index 3d528dde4..0dbff9d6f 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALErrors.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALErrors.scala @@ -16,5 +16,5 @@ object DIALErrors { val ERR_CONTENT_NOT_FOUND_MSG: String = "Content Not Found With Id(s): " val ERR_SERVER_ERROR_MSG: String = "Something Went Wrong While Processing Your Request. Please Try Again After Sometime!" val ERR_DUPLICATE_DIAL_CODES_MSG: String = "QR Code should not be linked to multiple contents. Please validate: " - val ERR_DIALCODE_CONTENT_LINK_FIELDS_MISSING_MSG: String = "Required fields for content link dialcode are missing." + val ERR_DIALCODE_CONTENT_LINK_FIELDS_MISSING_MSG: String = "Required fields for content link dialcode are missing: " } diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala index 8fecd0c05..a7c0b8c10 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala @@ -59,16 +59,12 @@ object DIALManager { def validateAndGetRequestMap(channelId: String, requestList: List[Map[String, List[String]]])(implicit oec:OntologyEngineContext): Map[String, List[String]] = { var reqMap = HashMap[String, List[String]]() - try { - requestList.foreach(req => { - val contents: List[String] = req(DIALConstants.IDENTIFIER) - val dialcodes: List[String] = req(DIALConstants.DIALCODE) - validateReqStructure(dialcodes, contents) - contents.foreach(id => reqMap += (id -> dialcodes)) - }) - } catch { - case e: Exception => throw new ClientException(DIALErrors.ERR_DIALCODE_CONTENT_LINK_FIELDS_MISSING, DIALErrors.ERR_DIALCODE_CONTENT_LINK_FIELDS_MISSING_MSG) - } + requestList.foreach(req => { + val contents: List[String] = if(req.contains(DIALConstants.IDENTIFIER)) req(DIALConstants.IDENTIFIER) else throw new ClientException(DIALErrors.ERR_DIALCODE_CONTENT_LINK_FIELDS_MISSING, DIALErrors.ERR_DIALCODE_CONTENT_LINK_FIELDS_MISSING_MSG + DIALConstants.IDENTIFIER) + val dialcodes: List[String] = if(req.contains(DIALConstants.DIALCODE)) req(DIALConstants.DIALCODE) else throw new ClientException(DIALErrors.ERR_DIALCODE_CONTENT_LINK_FIELDS_MISSING, DIALErrors.ERR_DIALCODE_CONTENT_LINK_FIELDS_MISSING_MSG + DIALConstants.DIALCODE) + validateReqStructure(dialcodes, contents) + contents.foreach(id => reqMap += (id -> dialcodes)) + }) TelemetryManager.info("DIALManager::validateAndGetRequestMap:: requestList: " + requestList) if (Platform.getBoolean("content.link_dialcode.validation", true)) { val dials = requestList.collect { case m if m.contains(DIALConstants.DIALCODE) => m(DIALConstants.DIALCODE) }.flatten From 8139d0d210d8b4af491336268cde0629532385b4 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Tue, 5 Jul 2022 12:50:47 +0530 Subject: [PATCH 077/490] Issue #SB-28316 feat: DIAL code reserve API refactor. --- .../sunbird/content/actors/ContentActor.scala | 3 + .../org/sunbird/content/dial/DIALErrors.scala | 15 +++ .../sunbird/content/dial/DIALManager.scala | 120 +++++++++++++++++- .../src/test/resources/application.conf | 1 + .../controllers/v3/ContentController.scala | 10 +- .../content-service/app/utils/ApiId.scala | 1 + .../content-service/conf/application.conf | 1 + 7 files changed, 141 insertions(+), 10 deletions(-) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/actors/ContentActor.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/actors/ContentActor.scala index fabc95d43..e3925113f 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/actors/ContentActor.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/actors/ContentActor.scala @@ -49,6 +49,7 @@ class ContentActor @Inject() (implicit oec: OntologyEngineContext, ss: StorageSe case "flagContent" => flag(request) case "acceptFlag" => acceptFlag(request) case "linkDIALCode" => linkDIALCode(request) + case "reserveDialCode" => reserveDialCode(request) case "importContent" => importContent(request) case "systemUpdate" => systemUpdate(request) case "reviewContent" => reviewContent(request) @@ -176,6 +177,8 @@ class ContentActor @Inject() (implicit oec: OntologyEngineContext, ss: StorageSe def linkDIALCode(request: Request): Future[Response] = DIALManager.link(request) + def reserveDialCode(request: Request): Future[Response] = DIALManager.reserve(request) + def importContent(request: Request): Future[Response] = importMgr.importObject(request) def reviewContent(request: Request): Future[Response] = { diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALErrors.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALErrors.scala index 0dbff9d6f..743cbe86a 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALErrors.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALErrors.scala @@ -5,6 +5,12 @@ object DIALErrors { //Error Codes val ERR_DIALCODE_LINK_REQUEST: String = "ERR_DIALCODE_LINK_REQUEST" val ERR_DIALCODE_LINK: String = "ERR_DIALCODE_LINK" + val ERR_INVALID_CHANNEL: String = "ERR_INVALID_CHANNEL" + val ERR_CONTENT_BLANK_OBJECT_ID: String = "ERR_CONTENT_BLANK_OBJECT_ID" + val ERR_CONTENT_MIMETYPE: String = "ERR_CONTENT_MIMETYPE" + val ERR_INVALID_COUNT: String = "ERR_INVALID_COUNT" + val ERR_INVALID_COUNT_RANGE: String = "ERR_INVALID_COUNT_RANGE" + val ERR_CONTENT_INVALID_OBJECT: String = "ERR_CONTENT_INVALID_OBJECT" val ERR_DUPLICATE_DIAL_CODES: String = "ERR_DUPLICATE_DIAL_CODES" val ERR_DIALCODE_CONTENT_LINK_FIELDS_MISSING: String = "ERR_DIALCODE_CONTENT_LINK_FIELDS_MISSING" @@ -15,6 +21,15 @@ object DIALErrors { val ERR_DIAL_NOT_FOUND_MSG: String = "DIAL Code Not Found With Id(s): " val ERR_CONTENT_NOT_FOUND_MSG: String = "Content Not Found With Id(s): " val ERR_SERVER_ERROR_MSG: String = "Something Went Wrong While Processing Your Request. Please Try Again After Sometime!" + val ERR_INVALID_CHANNEL_MSG: String = "Invalid Channel Id." + val ERR_CONTENT_BLANK_OBJECT_ID_MSG: String = "Content Id cannot be Blank." + val ERR_CONTENT_MIMETYPE_MSG: String = "Invalid mimeType." + val ERR_INVALID_COUNT_MSG: String = "Invalid dialcode count." + val ERR_INVALID_COUNT_RANGE_MSG: String = "Invalid dialcode count range. It should be between 1 to " + val ERR_DIAL_GEN_LIST_EMPTY_MSG: String = "Dialcode generated list is empty. Please Try Again After Sometime!" + val ERR_DIAL_GENERATION_MSG: String = "Error During generate Dialcode. Please Try Again After Sometime!" + val ERR_DIAL_INVALID_COUNT_RESPONSE = "No new DIAL Codes have been generated, as requested count is less or equal to existing reserved dialcode count." + val ERR_CONTENT_INVALID_OBJECT_MSG = "Invalid Request. Cannot update status of Live Node." val ERR_DUPLICATE_DIAL_CODES_MSG: String = "QR Code should not be linked to multiple contents. Please validate: " val ERR_DIALCODE_CONTENT_LINK_FIELDS_MISSING_MSG: String = "Required fields for content link dialcode are missing: " } diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala index a7c0b8c10..1d65ed1be 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala @@ -21,6 +21,7 @@ import scala.concurrent.{ExecutionContext, Future} object DIALManager { val DIAL_SEARCH_API_URL: String = Platform.config.getString("dial_service.api.base_url") + Platform.config.getString("dial_service.api.search") + val DIALCODE_GENERATE_URI: String = Platform.config.getString("dial_service.api.base_url") + Platform.config.getString("dial_service.api.generate_url") val DIAL_API_AUTH_KEY: String = "Bearer " + Platform.config.getString("dial_service.api.auth_key") val PASSPORT_KEY: String = Platform.config.getString("graph.passport.key.base") @@ -28,11 +29,8 @@ object DIALManager { val linkType: String = request.getContext.getOrDefault(DIALConstants.LINK_TYPE, DIALConstants.CONTENT).asInstanceOf[String] val channelId: String = request.getContext.getOrDefault(DIALConstants.CHANNEL, "").asInstanceOf[String] val objectId: String = request.getContext.getOrDefault(DIALConstants.IDENTIFIER, "").asInstanceOf[String] - TelemetryManager.info("DIALManager::link:: linkType: " + linkType + " || channelId: " + channelId + " || objectId: " + objectId) val reqList: List[Map[String, List[String]]] = getRequestData(request) - TelemetryManager.info("DIALManager::link:: reqList: " + reqList) val requestMap: Map[String, List[String]] = validateAndGetRequestMap(channelId, reqList) - TelemetryManager.info("DIALManager::link:: requestMap: " + requestMap) linkType match { case DIALConstants.CONTENT => linkContent(requestMap, request.getContext) case DIALConstants.COLLECTION => linkCollection(objectId, requestMap, request.getContext) @@ -65,10 +63,8 @@ object DIALManager { validateReqStructure(dialcodes, contents) contents.foreach(id => reqMap += (id -> dialcodes)) }) - TelemetryManager.info("DIALManager::validateAndGetRequestMap:: requestList: " + requestList) if (Platform.getBoolean("content.link_dialcode.validation", true)) { - val dials = requestList.collect { case m if m.contains(DIALConstants.DIALCODE) => m(DIALConstants.DIALCODE) }.flatten - TelemetryManager.info("DIALManager::validateAndGetRequestMap:: dials: " + dials) + val dials = requestList.collect { case m if m.get(DIALConstants.DIALCODE).nonEmpty => m.get(DIALConstants.DIALCODE).get }.flatten validateDialCodes(channelId, dials) } reqMap @@ -95,7 +91,6 @@ object DIALManager { TelemetryManager.info("DIALManager::validateAndGetRequestMap:: DIAL_SEARCH_API_URL: " + DIAL_SEARCH_API_URL) TelemetryManager.info("DIALManager::validateAndGetRequestMap:: reqMap: " + reqMap) val searchResponse = oec.httpUtil.post(DIAL_SEARCH_API_URL, reqMap, headerParam) - TelemetryManager.info("DIALManager::validateAndGetRequestMap:: searchResponse.getResponseCode: " + searchResponse.getResponseCode) if (searchResponse.getResponseCode.toString == "OK") { val result = searchResponse.getResult if (dialcodes.distinct.size == result.get(DIALConstants.COUNT).asInstanceOf[Integer]) { @@ -263,4 +258,115 @@ object DIALManager { if (duplicateDIALCodes.nonEmpty) throw new ClientException(DIALErrors.ERR_DUPLICATE_DIAL_CODES, DIALErrors.ERR_DUPLICATE_DIAL_CODES_MSG + duplicateDIALCodes) } + + def reserve(request: Request)(implicit oec: OntologyEngineContext, ec: ExecutionContext): Future[Response] = { + val channelId: String = request.getContext.getOrDefault(DIALConstants.CHANNEL, "").asInstanceOf[String] + val contentId: String = request.get(ContentConstants.IDENTIFIER).asInstanceOf[String] + + if (contentId == null || contentId.isEmpty) throw new ClientException(DIALErrors.ERR_CONTENT_BLANK_OBJECT_ID, DIALErrors.ERR_CONTENT_BLANK_OBJECT_ID_MSG) + + val req = new Request() + req.setContext(request.getContext) + req.put(DIALConstants.IDENTIFIER, contentId) + req.put(ContentConstants.ROOT_ID, contentId) + req.put(ContentConstants.MODE, "edit") + DataNode.read(req).flatMap(rootNode => { + val contentMetadata = rootNode.getMetadata + TelemetryManager.info("DialManager:: reserve:: contentMetadata: " + contentMetadata) + val contentChannel = contentMetadata.get("channel").asInstanceOf[String] + validateChannel(contentChannel, channelId) + validateContentForReservedDialcodes(contentMetadata) + validateCountForReservingDialCode(request.getRequest.get("dialcodes").asInstanceOf[util.Map[String, AnyRef]]) + + if (contentMetadata.get("status").asInstanceOf[String].equalsIgnoreCase("Live") || contentMetadata.get("status").asInstanceOf[String].equalsIgnoreCase("Unlisted")) + throw new ClientException(DIALErrors.ERR_CONTENT_INVALID_OBJECT, DIALErrors.ERR_CONTENT_INVALID_OBJECT_MSG) + + val reservedDialCodes = contentMetadata.getOrDefault("dialcodes", Map.empty[String, Integer]).asInstanceOf[Map[String, Integer]] + TelemetryManager.info("DialManager:: reserve:: reservedDialCodes: " + reservedDialCodes) + val maxIndex: Integer = if (reservedDialCodes.nonEmpty) reservedDialCodes.max._2 else -1 + val dialCodes = reservedDialCodes.keySet + val reqDialcodesCount = request.getRequest.get("dialcodes").asInstanceOf[util.Map[String, AnyRef]].get("count").asInstanceOf[Integer] + TelemetryManager.info("DialManager:: reserve:: reqDialcodesCount: " + reqDialcodesCount) + val updateDialCodes = if (dialCodes.size < reqDialcodesCount) { + val newDialcodes = generateDialCodes(channelId, contentId, reqDialcodesCount - dialCodes.size, request.get("publisher").asInstanceOf[String]) + val newDialCodesMap: Map[String, Integer] = newDialcodes.zipWithIndex.map { case (newDialCode, idx) => + (newDialCode -> (maxIndex + idx + 1).asInstanceOf[Integer]) + }.toMap + reservedDialCodes ++ newDialCodesMap + } else reservedDialCodes + TelemetryManager.info("DialManager:: reserve:: updateDialCodes: " + updateDialCodes) + if(updateDialCodes.size > reservedDialCodes.size) { + val updateReq = new Request(request) + updateReq.put("identifier", rootNode.getIdentifier) + val rootNodeMetadata = rootNode.getMetadata + rootNodeMetadata.remove("discussionForum") + rootNodeMetadata.remove("credentials") + rootNodeMetadata.remove("trackable") + + updateReq.put(DIALConstants.RESERVED_DIALCODES, updateDialCodes) + updateReq.getRequest.putAll(rootNodeMetadata) + DataNode.update(updateReq).map(updatedNode => { + val response = ResponseHandler.OK() + response.getResult.put("count", updateDialCodes.size.asInstanceOf[Integer]) + response.getResult.put("node_id", contentId) + response.getResult.put("processId", updatedNode.getMetadata.get("processId")) + response.getResult.put("reservedDialcodes", updatedNode.getMetadata.get("reservedDialcodes")) + response.getResult.put("versionKey", updatedNode.getMetadata.get("versionKey")) + TelemetryManager.info("DialManager:: reserve:: response: " + response) + response + }) + } else { + val errorResponse = ResponseHandler.ERROR(ResponseCode.CLIENT_ERROR, DIALErrors.ERR_INVALID_COUNT, DIALErrors.ERR_DIAL_INVALID_COUNT_RESPONSE) + errorResponse.getResult.put("count", reservedDialCodes.size.asInstanceOf[Integer]) + errorResponse.getResult.put("node_id", contentId) + errorResponse.getResult.put("processId", rootNode.getMetadata.get("processId")) + errorResponse.getResult.put("reservedDialcodes", rootNode.getMetadata.get("reservedDialcodes")) + TelemetryManager.info("DialManager:: reserve:: errorResponse: " + errorResponse) + Future(errorResponse) + } + }) + } + + def validateChannel(contentChannel: String, channelId: String): Unit = { + if(contentChannel == null || channelId == null || !contentChannel.equalsIgnoreCase(channelId)) + throw new ClientException(DIALErrors.ERR_INVALID_CHANNEL, DIALErrors.ERR_INVALID_CHANNEL_MSG) + } + + def validateContentForReservedDialcodes(metaData: util.Map[String, AnyRef]): Unit = { + val validMimeType = if (Platform.config.hasPath("reserve_dialcode.mimeType")) Platform.config.getStringList("reserve_dialcode.mimeType") else util.Arrays.asList("application/vnd.ekstep.content-collection") + if (!validMimeType.contains(metaData.get("mimeType"))) throw new ClientException(DIALErrors.ERR_CONTENT_MIMETYPE, DIALErrors.ERR_CONTENT_MIMETYPE_MSG) + } + + def validateCountForReservingDialCode(request: util.Map[String, AnyRef]): Unit = { + if (null == request.get("count") || !request.get("count").isInstanceOf[Integer]) throw new ClientException(DIALErrors.ERR_INVALID_COUNT, DIALErrors.ERR_INVALID_COUNT_MSG) + val count = request.get("count").asInstanceOf[Integer] + val maxCount = if (Platform.config.hasPath("reserve_dialcode.max_count")) Platform.config.getInt("reserve_dialcode.max_count") else 250 + if (count < 1 || count > maxCount) throw new ClientException(DIALErrors.ERR_INVALID_COUNT_RANGE, DIALErrors.ERR_INVALID_COUNT_RANGE_MSG + maxCount + ".") + } + + @throws[Exception] + private def generateDialCodes(channelId: String, contentId: String, dialcodeCount: Integer, publisher: String)(implicit oec: OntologyEngineContext): List[String] = { + val dialcodeMap = new util.HashMap[String, AnyRef] + dialcodeMap.put("count", dialcodeCount) + dialcodeMap.put("publisher", publisher) + dialcodeMap.put("batchCode", contentId) + val request = new util.HashMap[String, AnyRef] + request.put("dialcodes", dialcodeMap) + val requestMap = new util.HashMap[String, AnyRef] + requestMap.put("request", request) + val headerParam = new util.HashMap[String, String]{put(DIALConstants.X_CHANNEL_ID, channelId); put(DIALConstants.AUTHORIZATION, DIAL_API_AUTH_KEY);} + + val generateResponse = oec.httpUtil.post(DIALCODE_GENERATE_URI, requestMap, headerParam) + if ((generateResponse.getResponseCode == ResponseCode.OK) || (generateResponse.getResponseCode == ResponseCode.PARTIAL_SUCCESS)) { + val generatedDialCodes = generateResponse.getResult.get("dialcodes").asInstanceOf[util.ArrayList[String]].asScala.toList + if (generatedDialCodes.nonEmpty) generatedDialCodes + else throw new ServerException(ErrorCodes.ERR_SYSTEM_EXCEPTION.name, DIALErrors.ERR_DIAL_GEN_LIST_EMPTY_MSG) + } + else if (generateResponse.getResponseCode eq ResponseCode.CLIENT_ERROR) { + throw new ClientException(generateResponse.getParams.getErr, generateResponse.getParams.getErrmsg) + } + else { + throw new ServerException(ErrorCodes.ERR_SYSTEM_EXCEPTION.name, DIALErrors.ERR_DIAL_GENERATION_MSG) + } + } } diff --git a/content-api/content-actors/src/test/resources/application.conf b/content-api/content-actors/src/test/resources/application.conf index 0428b1e0e..45c1b629f 100644 --- a/content-api/content-actors/src/test/resources/application.conf +++ b/content-api/content-actors/src/test/resources/application.conf @@ -496,6 +496,7 @@ dial_service { base_url = "https://qa.ekstep.in/api" auth_key = "auth_key" search = "/dialcode/v3/search" + generate_url = "/dialcode/v1/generate" } } content.link_dialcode.validation=true diff --git a/content-api/content-service/app/controllers/v3/ContentController.scala b/content-api/content-service/app/controllers/v3/ContentController.scala index 05c5b2470..ab3538098 100644 --- a/content-api/content-service/app/controllers/v3/ContentController.scala +++ b/content-api/content-service/app/controllers/v3/ContentController.scala @@ -206,9 +206,13 @@ class ContentController @Inject()(@Named(ActorNames.CONTENT_ACTOR) contentActor: } def reserveDialCode(identifier: String) = Action.async { implicit request => - val result = ResponseHandler.OK() - val response = JavaJsonUtils.serialize(result) - Future(Ok(response).as("application/json")) + val headers = commonHeaders() + val body = requestBody() + body.putAll(headers) + body.putAll(Map("identifier" -> identifier).asJava) + val reserveDialCode = getRequest(body, headers, "reserveDialCode") + setRequestContext(reserveDialCode, version, objectType, schemaName) + getResult(ApiId.RESERVE_DIAL_CONTENT, contentActor, reserveDialCode) } def releaseDialcodes(identifier: String) = Action.async { implicit request => diff --git a/content-api/content-service/app/utils/ApiId.scala b/content-api/content-service/app/utils/ApiId.scala index 7aa95ac57..6bad01791 100644 --- a/content-api/content-service/app/utils/ApiId.scala +++ b/content-api/content-service/app/utils/ApiId.scala @@ -29,6 +29,7 @@ object ApiId { val UPDATE_HIERARCHY = "api.content.hierarchy.update" val GET_HIERARCHY = "api.content.hierarchy.get" val LINK_DIAL_COLLECTION = "api.collection.dialcode.link" + val RESERVE_DIAL_CONTENT = "api.content.dialcode.reserve" //License APIs val CREATE_LICENSE = "api.license.create" diff --git a/content-api/content-service/conf/application.conf b/content-api/content-service/conf/application.conf index f1e9714db..a4a6147e2 100644 --- a/content-api/content-service/conf/application.conf +++ b/content-api/content-service/conf/application.conf @@ -604,6 +604,7 @@ dial_service { base_url = "https://qa.ekstep.in/api" auth_key = "auth_key" search = "/dialcode/v1/search" + generate_url = "/dialcode/v1/generate" } } content.link_dialcode.validation=true From c7aee00e4c394454acea974c01eafde53e102a2b Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Tue, 5 Jul 2022 12:50:52 +0530 Subject: [PATCH 078/490] Issue #SB-28316 feat: DIAL code reserve API refactor. --- .../src/main/scala/org/sunbird/content/dial/DIALConstants.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALConstants.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALConstants.scala index 70d8f74da..4891f30e1 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALConstants.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALConstants.scala @@ -16,6 +16,7 @@ object DIALConstants { val AUTHORIZATION: String = "Authorization" val X_CHANNEL_ID: String = "X-Channel-Id" val VERSION_KEY: String = "versionKey" + val RESERVED_DIALCODES: String = "reservedDialcodes" val DISCUSSION_FORUM: String = "discussionForum" val CREDENTIALS: String = "credentials" val TRACKABLE: String = "trackable" From 7b9b5f3b242c0cb106ffad5f1d6d7faa1dfd5c2d Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Tue, 5 Jul 2022 12:52:55 +0530 Subject: [PATCH 079/490] Issue #SB-28316 feat: DIAL code reserve API refactor. --- content-api/content-actors/src/test/resources/application.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content-api/content-actors/src/test/resources/application.conf b/content-api/content-actors/src/test/resources/application.conf index 45c1b629f..39a6cadb9 100644 --- a/content-api/content-actors/src/test/resources/application.conf +++ b/content-api/content-actors/src/test/resources/application.conf @@ -495,7 +495,7 @@ dial_service { api { base_url = "https://qa.ekstep.in/api" auth_key = "auth_key" - search = "/dialcode/v3/search" + search = "/dialcode/v1/search" generate_url = "/dialcode/v1/generate" } } From e1b02c92bc17994bf8927717e127576df0ee2087 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Wed, 6 Jul 2022 16:32:19 +0530 Subject: [PATCH 080/490] Issue #SB-28316 feat: DIAL code reserve API refactor. --- .../sunbird/content/dial/DIALManager.scala | 15 ++-- .../src/test/resources/application.conf | 4 + .../content/dial/DIALManagerTest.scala | 85 +++++++++++++++++++ 3 files changed, 97 insertions(+), 7 deletions(-) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala index 1d65ed1be..9f4ca3b2a 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala @@ -296,12 +296,14 @@ object DIALManager { } else reservedDialCodes TelemetryManager.info("DialManager:: reserve:: updateDialCodes: " + updateDialCodes) if(updateDialCodes.size > reservedDialCodes.size) { - val updateReq = new Request(request) - updateReq.put("identifier", rootNode.getIdentifier) + val updateReq = new Request(req) + updateReq.setContext(request.getContext) + updateReq.getContext.put(ContentConstants.IDENTIFIER, rootNode.getIdentifier) + updateReq.put(ContentConstants.IDENTIFIER, rootNode.getIdentifier) val rootNodeMetadata = rootNode.getMetadata - rootNodeMetadata.remove("discussionForum") - rootNodeMetadata.remove("credentials") - rootNodeMetadata.remove("trackable") + rootNodeMetadata.remove(DIALConstants.DISCUSSION_FORUM) + rootNodeMetadata.remove(DIALConstants.CREDENTIALS) + rootNodeMetadata.remove(DIALConstants.TRACKABLE) updateReq.put(DIALConstants.RESERVED_DIALCODES, updateDialCodes) updateReq.getRequest.putAll(rootNodeMetadata) @@ -355,9 +357,8 @@ object DIALManager { val requestMap = new util.HashMap[String, AnyRef] requestMap.put("request", request) val headerParam = new util.HashMap[String, String]{put(DIALConstants.X_CHANNEL_ID, channelId); put(DIALConstants.AUTHORIZATION, DIAL_API_AUTH_KEY);} - val generateResponse = oec.httpUtil.post(DIALCODE_GENERATE_URI, requestMap, headerParam) - if ((generateResponse.getResponseCode == ResponseCode.OK) || (generateResponse.getResponseCode == ResponseCode.PARTIAL_SUCCESS)) { + if (generateResponse.getResponseCode == ResponseCode.OK || generateResponse.getResponseCode == ResponseCode.PARTIAL_SUCCESS) { val generatedDialCodes = generateResponse.getResult.get("dialcodes").asInstanceOf[util.ArrayList[String]].asScala.toList if (generatedDialCodes.nonEmpty) generatedDialCodes else throw new ServerException(ErrorCodes.ERR_SYSTEM_EXCEPTION.name, DIALErrors.ERR_DIAL_GEN_LIST_EMPTY_MSG) diff --git a/content-api/content-actors/src/test/resources/application.conf b/content-api/content-actors/src/test/resources/application.conf index 39a6cadb9..54ea3dcf7 100644 --- a/content-api/content-actors/src/test/resources/application.conf +++ b/content-api/content-actors/src/test/resources/application.conf @@ -501,6 +501,10 @@ dial_service { } content.link_dialcode.validation=true content.link_dialcode.max_limit=10 + + +reserve_dialcode.mimeType = ["application/vnd.ekstep.content-collection", "application/pdf"] + # This is added to handle large artifacts sizes differently content.artifact.size.for_online=209715200 diff --git a/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala b/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala index eac0e8686..a2d0d8ec7 100644 --- a/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala +++ b/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala @@ -7,9 +7,11 @@ import org.sunbird.common.dto.{Property, Request, Response} import org.sunbird.common.exception.{ClientException, ResourceNotFoundException, ResponseCode, ServerException} import org.sunbird.common.{HttpUtil, JsonUtils} import org.sunbird.graph.dac.model.{Node, SearchCriteria} +import org.sunbird.graph.utils.ScalaJsonUtils import org.sunbird.graph.{GraphService, OntologyEngineContext} import java.util +import scala.collection.JavaConversions.mapAsJavaMap import scala.concurrent.Future class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory { @@ -260,11 +262,51 @@ 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() + (httpUtil.post(_: String, _:java.util.Map[String, AnyRef], _:java.util.Map[String, String])).expects(*, *, *).returns(getGenerateDIALResponse) + + val nodes: util.List[Node] = getCategoryNode() + (graphDB.getNodeByUniqueIds(_: String, _: SearchCriteria)).expects(*, *).returns(Future(nodes)).anyNumberOfTimes() + + (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, *).returns(Future(new Response())) + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, "do_1111.img", *, *).returns(Future(getNode("do_1111"))).anyNumberOfTimes() + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, "do_1111", *, *).returns(Future(getNode("do_1111"))).anyNumberOfTimes() + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, *, *, *).returns(Future(getDefinitionNode())).anyNumberOfTimes() + (graphDB.upsertNode(_: String, _: Node, _: Request)).expects(*, *, *).returns(Future(getNode("do_1111"))) + (graphDB.saveExternalProps(_: Request)).expects(*).returns(Future(new Response())) + + val request = getReserveDIALRequest() + + val response = DIALManager.reserve(request) + response.map(result => { + assert(result.getResponseCode.toString=="OK") + }) + + } + + def getDefinitionNode(): Node = { + val node = new Node() + node.setIdentifier("obj-cat:learning-resource_content_in.ekstep") + node.setNodeType("DATA_NODE") + node.setObjectType("Content") + node.setGraphId("domain") + node.setMetadata(mapAsJavaMap( + ScalaJsonUtils.deserialize[Map[String,AnyRef]]("{\n \"objectCategoryDefinition\": {\n \"name\": \"Learning Resource\",\n \"description\": \"Content Playlist\",\n \"categoryId\": \"obj-cat:learning-resource\",\n \"targetObjectType\": \"Content\",\n \"objectMetadata\": {\n \"config\": {},\n \"schema\": {\n \"required\": [\n \"author\",\n \"copyright\",\n \"license\",\n \"audience\"\n ],\n \"properties\": {\n \"audience\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\",\n \"enum\": [\n \"Student\",\n \"Teacher\"\n ]\n },\n \"default\": [\n \"Student\"\n ]\n },\n \"mimeType\": {\n \"type\": \"string\",\n \"enum\": [\n \"application/pdf\"\n ]\n }\n }\n }\n }\n }\n }"))) + node + } + def getDIALSearchResponse:Response = { val resString = "{\n \"id\": \"sunbird.dialcode.search\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-04-21T19:39:14ZZ\",\n \"params\": {\n \"resmsgid\": \"1dfcc25b-6c37-49f8-a6c3-7185063e8752\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"dialcodes\": [\n {\n \"dialcode_index\": 7609876,\n \"identifier\": \"N4Z7D5\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.603+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n },\n {\n \"dialcode_index\": 7610113,\n \"identifier\": \"E8B7Z6\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.635+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n },\n {\n \"dialcode_index\": 7610117,\n \"identifier\": \"R4X2P2\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.637+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n },\n {\n \"dialcode_index\": 7610961,\n \"identifier\": \"L4A6W8\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.734+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n },\n {\n \"dialcode_index\": 7611164,\n \"identifier\": \"D2E1J9\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.759+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n }\n ],\n \"count\": 5\n }\n}"; JsonUtils.deserialize(resString, classOf[Response]) } + def getGenerateDIALResponse:Response = { + val resString = "{\"id\": \"api.dialcode.generate\",\"ver\": \"1.0\",\"ts\": \"2022-07-05T09:47:26.000Z\",\"params\": {\"resmsgid\": \"79eb8b00-fc47-11ec-af25-0f53946b16ec\",\"msgid\": \"79be1260-fc47-11ec-8c03-63ca5ce41074\",\"status\": \"successful\",\"err\": null,\"errmsg\": null},\"responseCode\": \"OK\",\"result\": {\"dialcodes\": [\"K2C3R6\",\"H2E8F9\"],\"count\": 2,\"batchcode\": \"do_11357423520695910411\",\"publisher\": null}}" + JsonUtils.deserialize(resString, classOf[Response]) + } + def getRequestData(): List[Map[String, List[String]]] = { val reqMap : java.util.Map[String, AnyRef] = new util.HashMap[String, AnyRef](){{ put("content", new util.ArrayList[util.Map[String, AnyRef]](){{ @@ -332,6 +374,28 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory request } + def getReserveDIALRequest(): Request = { + val request = new Request() + request.setObjectType("Content") + request.setContext(getContext()) + request.getContext.put("identifier","do_1111") + request.put("identifier","do_1111") + request.putAll(getReserveRequest()) + request + } + + def getReserveRequest():util.Map[String, AnyRef] = { + val reqMap : java.util.Map[String, AnyRef] = new util.HashMap[String, AnyRef](){ + put("dialcodes", new util.HashMap[String, AnyRef](){ + put("count", 2.asInstanceOf[Integer]) + put("qrCodeSpec", new util.HashMap[String, AnyRef](){ + put("errorCorrectionLevel", "H") + }) + }) + } + reqMap + } + def getContext():util.Map[String, AnyRef] = { val contextMap: java.util.Map[String, AnyRef] = new util.HashMap[String, AnyRef](){{ put("graph_id", "domain") @@ -393,6 +457,27 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory put("channel", "test") put("versionKey", "1234") put("primaryCategory", "Learning Resource") + put("framework", "NCF") + put("board", "CBSE") + put("medium", new java.util.ArrayList[String](){ + add("English"); + }) + put("gradeLevel", new java.util.ArrayList[String](){ + add("Class 1"); + }) + put("subject", new java.util.ArrayList[String](){ + add("English"); + }) + put("se_boards", "CBSE") + put("se_mediums", new java.util.ArrayList[String](){ + add("English"); + }) + put("se_gradeLevels", new java.util.ArrayList[String](){ + add("Class 1"); + }) + put("se_subjects", new java.util.ArrayList[String](){ + add("English"); + }) } }) node From a45ecae788a7c43afc0e7f780cd36c7cef5ba3ff Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Thu, 7 Jul 2022 11:19:31 +0530 Subject: [PATCH 081/490] Issue #SB-28316 feat: DIAL code reserve API refactor. --- .../content/dial/DIALManagerTest.scala | 107 ++++++------------ 1 file changed, 36 insertions(+), 71 deletions(-) diff --git a/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala b/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala index a2d0d8ec7..0b21bbb44 100644 --- a/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala +++ b/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala @@ -7,11 +7,9 @@ import org.sunbird.common.dto.{Property, Request, Response} import org.sunbird.common.exception.{ClientException, ResourceNotFoundException, ResponseCode, ServerException} import org.sunbird.common.{HttpUtil, JsonUtils} import org.sunbird.graph.dac.model.{Node, SearchCriteria} -import org.sunbird.graph.utils.ScalaJsonUtils import org.sunbird.graph.{GraphService, OntologyEngineContext} import java.util -import scala.collection.JavaConversions.mapAsJavaMap import scala.concurrent.Future class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory { @@ -19,7 +17,7 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory implicit val oec: OntologyEngineContext = mock[OntologyEngineContext] val graphDB: GraphService = mock[GraphService] val httpUtil: HttpUtil = mock[HttpUtil] - + "getRequestData with list input" should "return request data as list with scala types" in { val reqMap : java.util.Map[String, AnyRef] = new util.HashMap[String, AnyRef](){{ put("content", new util.ArrayList[util.Map[String, AnyRef]](){{ @@ -204,7 +202,7 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory (graphDB.upsertNode(_: String, _: Node, _: Request)).expects(*, *, *).returns(Future(getNode("do_1111"))) (graphDB.saveExternalProps(_: Request)).expects(*).returns(Future(new Response())) (graphDB.getNodeProperty(_: String, _: String, _: String)).expects(*, *, *).returns(Future(new Property("versionKey", new org.neo4j.driver.internal.value.StringValue("1234")))) - + val request = getCollectionDIALRequest() val response = DIALManager.link(request) @@ -267,17 +265,17 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory (oec.graphService _).expects().returns(graphDB).anyNumberOfTimes() (httpUtil.post(_: String, _:java.util.Map[String, AnyRef], _:java.util.Map[String, String])).expects(*, *, *).returns(getGenerateDIALResponse) + (graphDB.getNodeByUniqueIds(_: String, _: SearchCriteria)).expects(*, *).returns(Future(getNodes())).noMoreThanOnce() val nodes: util.List[Node] = getCategoryNode() - (graphDB.getNodeByUniqueIds(_: String, _: SearchCriteria)).expects(*, *).returns(Future(nodes)).anyNumberOfTimes() - + (graphDB.getNodeByUniqueIds(_: String, _: SearchCriteria)).expects(*, *).returns(Future(nodes)).noMoreThanOnce() + val contentId: String = "do_123456" (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, *).returns(Future(new Response())) - (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, "do_1111.img", *, *).returns(Future(getNode("do_1111"))).anyNumberOfTimes() - (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, "do_1111", *, *).returns(Future(getNode("do_1111"))).anyNumberOfTimes() - (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, *, *, *).returns(Future(getDefinitionNode())).anyNumberOfTimes() - (graphDB.upsertNode(_: String, _: Node, _: Request)).expects(*, *, *).returns(Future(getNode("do_1111"))) - (graphDB.saveExternalProps(_: Request)).expects(*).returns(Future(new Response())) + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, *, *, *).returns(Future(getNode(contentId))) + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, *, *, *).returns(Future(getNode(contentId+".img"))) + (graphDB.getNodeProperty(_: String, _: String, _: String)).expects(*, *, *).returns(Future(new Property("versionKey", new org.neo4j.driver.internal.value.StringValue("1234")))) + (graphDB.upsertNode(_: String, _: Node, _: Request)).expects(*, *, *).returns(Future(getNode(contentId))) - val request = getReserveDIALRequest() + val request = getReserveDIALRequest(contentId) val response = DIALManager.reserve(request) response.map(result => { @@ -286,27 +284,11 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory } - def getDefinitionNode(): Node = { - val node = new Node() - node.setIdentifier("obj-cat:learning-resource_content_in.ekstep") - node.setNodeType("DATA_NODE") - node.setObjectType("Content") - node.setGraphId("domain") - node.setMetadata(mapAsJavaMap( - ScalaJsonUtils.deserialize[Map[String,AnyRef]]("{\n \"objectCategoryDefinition\": {\n \"name\": \"Learning Resource\",\n \"description\": \"Content Playlist\",\n \"categoryId\": \"obj-cat:learning-resource\",\n \"targetObjectType\": \"Content\",\n \"objectMetadata\": {\n \"config\": {},\n \"schema\": {\n \"required\": [\n \"author\",\n \"copyright\",\n \"license\",\n \"audience\"\n ],\n \"properties\": {\n \"audience\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\",\n \"enum\": [\n \"Student\",\n \"Teacher\"\n ]\n },\n \"default\": [\n \"Student\"\n ]\n },\n \"mimeType\": {\n \"type\": \"string\",\n \"enum\": [\n \"application/pdf\"\n ]\n }\n }\n }\n }\n }\n }"))) - node - } - def getDIALSearchResponse:Response = { val resString = "{\n \"id\": \"sunbird.dialcode.search\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-04-21T19:39:14ZZ\",\n \"params\": {\n \"resmsgid\": \"1dfcc25b-6c37-49f8-a6c3-7185063e8752\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"dialcodes\": [\n {\n \"dialcode_index\": 7609876,\n \"identifier\": \"N4Z7D5\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.603+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n },\n {\n \"dialcode_index\": 7610113,\n \"identifier\": \"E8B7Z6\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.635+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n },\n {\n \"dialcode_index\": 7610117,\n \"identifier\": \"R4X2P2\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.637+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n },\n {\n \"dialcode_index\": 7610961,\n \"identifier\": \"L4A6W8\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.734+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n },\n {\n \"dialcode_index\": 7611164,\n \"identifier\": \"D2E1J9\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.759+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n }\n ],\n \"count\": 5\n }\n}"; JsonUtils.deserialize(resString, classOf[Response]) } - def getGenerateDIALResponse:Response = { - val resString = "{\"id\": \"api.dialcode.generate\",\"ver\": \"1.0\",\"ts\": \"2022-07-05T09:47:26.000Z\",\"params\": {\"resmsgid\": \"79eb8b00-fc47-11ec-af25-0f53946b16ec\",\"msgid\": \"79be1260-fc47-11ec-8c03-63ca5ce41074\",\"status\": \"successful\",\"err\": null,\"errmsg\": null},\"responseCode\": \"OK\",\"result\": {\"dialcodes\": [\"K2C3R6\",\"H2E8F9\"],\"count\": 2,\"batchcode\": \"do_11357423520695910411\",\"publisher\": null}}" - JsonUtils.deserialize(resString, classOf[Response]) - } - def getRequestData(): List[Map[String, List[String]]] = { val reqMap : java.util.Map[String, AnyRef] = new util.HashMap[String, AnyRef](){{ put("content", new util.ArrayList[util.Map[String, AnyRef]](){{ @@ -374,28 +356,6 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory request } - def getReserveDIALRequest(): Request = { - val request = new Request() - request.setObjectType("Content") - request.setContext(getContext()) - request.getContext.put("identifier","do_1111") - request.put("identifier","do_1111") - request.putAll(getReserveRequest()) - request - } - - def getReserveRequest():util.Map[String, AnyRef] = { - val reqMap : java.util.Map[String, AnyRef] = new util.HashMap[String, AnyRef](){ - put("dialcodes", new util.HashMap[String, AnyRef](){ - put("count", 2.asInstanceOf[Integer]) - put("qrCodeSpec", new util.HashMap[String, AnyRef](){ - put("errorCorrectionLevel", "H") - }) - }) - } - reqMap - } - def getContext():util.Map[String, AnyRef] = { val contextMap: java.util.Map[String, AnyRef] = new util.HashMap[String, AnyRef](){{ put("graph_id", "domain") @@ -457,27 +417,6 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory put("channel", "test") put("versionKey", "1234") put("primaryCategory", "Learning Resource") - put("framework", "NCF") - put("board", "CBSE") - put("medium", new java.util.ArrayList[String](){ - add("English"); - }) - put("gradeLevel", new java.util.ArrayList[String](){ - add("Class 1"); - }) - put("subject", new java.util.ArrayList[String](){ - add("English"); - }) - put("se_boards", "CBSE") - put("se_mediums", new java.util.ArrayList[String](){ - add("English"); - }) - put("se_gradeLevels", new java.util.ArrayList[String](){ - add("Class 1"); - }) - put("se_subjects", new java.util.ArrayList[String](){ - add("English"); - }) } }) node @@ -608,4 +547,30 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory response.put("hierarchy", hierarchyString) } + def getReserveDIALRequest(identifier: String): Request = { + val request = new Request() + request.setObjectType("Content") + request.setContext(getContext()) + request.getContext.put("identifier",identifier) + request.put("identifier",identifier) + request.putAll(getReserveRequest()) + request + } + + def getReserveRequest():util.Map[String, AnyRef] = { + val reqMap : java.util.Map[String, AnyRef] = new util.HashMap[String, AnyRef](){ + put("dialcodes", new util.HashMap[String, AnyRef](){ + put("count", 2.asInstanceOf[Integer]) + put("qrCodeSpec", new util.HashMap[String, AnyRef](){ + put("errorCorrectionLevel", "H") + }) + }) + } + reqMap + } + + def getGenerateDIALResponse:Response = { + val resString = "{\"id\": \"api.dialcode.generate\",\"ver\": \"1.0\",\"ts\": \"2022-07-05T09:47:26.000Z\",\"params\": {\"resmsgid\": \"79eb8b00-fc47-11ec-af25-0f53946b16ec\",\"msgid\": \"79be1260-fc47-11ec-8c03-63ca5ce41074\",\"status\": \"successful\",\"err\": null,\"errmsg\": null},\"responseCode\": \"OK\",\"result\": {\"dialcodes\": [\"K2C3R6\",\"H2E8F9\"],\"count\": 2,\"batchcode\": \"do_11357423520695910411\",\"publisher\": null}}" + JsonUtils.deserialize(resString, classOf[Response]) + } } \ No newline at end of file From 544b567e0e9d3017a00b0f9bf8a5826973acb852 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Thu, 7 Jul 2022 12:19:43 +0530 Subject: [PATCH 082/490] Issue #SB-28316 feat: DIAL code reserve API refactor. --- .../org/sunbird/content/dial/DIALManagerTest.scala | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala b/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala index 0b21bbb44..fe532977d 100644 --- a/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala +++ b/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala @@ -265,13 +265,13 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory (oec.graphService _).expects().returns(graphDB).anyNumberOfTimes() (httpUtil.post(_: String, _:java.util.Map[String, AnyRef], _:java.util.Map[String, String])).expects(*, *, *).returns(getGenerateDIALResponse) - (graphDB.getNodeByUniqueIds(_: String, _: SearchCriteria)).expects(*, *).returns(Future(getNodes())).noMoreThanOnce() val nodes: util.List[Node] = getCategoryNode() - (graphDB.getNodeByUniqueIds(_: String, _: SearchCriteria)).expects(*, *).returns(Future(nodes)).noMoreThanOnce() + (graphDB.getNodeByUniqueIds(_: String, _: SearchCriteria)).expects(*, *).returns(Future(nodes)).anyNumberOfTimes() + val contentId: String = "do_123456" - (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, *).returns(Future(new Response())) - (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, *, *, *).returns(Future(getNode(contentId))) - (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, *, *, *).returns(Future(getNode(contentId+".img"))) +// (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, *).returns(Future(new Response())).anyNumberOfTimes() + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, *, *, *).returns(Future(getNode(contentId))).anyNumberOfTimes() +// (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, *, *, *).returns(Future(getNode(contentId+".img"))).anyNumberOfTimes() (graphDB.getNodeProperty(_: String, _: String, _: String)).expects(*, *, *).returns(Future(new Property("versionKey", new org.neo4j.driver.internal.value.StringValue("1234")))) (graphDB.upsertNode(_: String, _: Node, _: Request)).expects(*, *, *).returns(Future(getNode(contentId))) @@ -279,6 +279,7 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory val response = DIALManager.reserve(request) response.map(result => { + println("Result:: " + result.getResult) assert(result.getResponseCode.toString=="OK") }) @@ -397,6 +398,7 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory add(getNode("do_3333")) add(getNode("do_4444")) add(getNode("do_5555")) + add(getNode("do_123456")) }} result } From 3e0923810f604c829d55ad26aa3119389d8f9543 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Thu, 7 Jul 2022 14:57:36 +0530 Subject: [PATCH 083/490] Issue #SB-28316 feat: DIAL code reserve API refactor. --- .../content/dial/DIALManagerTest.scala | 25 +++++++++++++++++-- .../controllers/v4/CollectionController.scala | 10 ++++++++ .../controllers/v4/ContentController.scala | 10 ++++++++ content-api/content-service/conf/routes | 4 ++- 4 files changed, 46 insertions(+), 3 deletions(-) diff --git a/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala b/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala index fe532977d..11e56e0ee 100644 --- a/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala +++ b/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala @@ -279,12 +279,11 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory val response = DIALManager.reserve(request) response.map(result => { - println("Result:: " + result.getResult) assert(result.getResponseCode.toString=="OK") }) - } + def getDIALSearchResponse:Response = { val resString = "{\n \"id\": \"sunbird.dialcode.search\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-04-21T19:39:14ZZ\",\n \"params\": {\n \"resmsgid\": \"1dfcc25b-6c37-49f8-a6c3-7185063e8752\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"dialcodes\": [\n {\n \"dialcode_index\": 7609876,\n \"identifier\": \"N4Z7D5\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.603+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n },\n {\n \"dialcode_index\": 7610113,\n \"identifier\": \"E8B7Z6\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.635+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n },\n {\n \"dialcode_index\": 7610117,\n \"identifier\": \"R4X2P2\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.637+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n },\n {\n \"dialcode_index\": 7610961,\n \"identifier\": \"L4A6W8\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.734+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n },\n {\n \"dialcode_index\": 7611164,\n \"identifier\": \"D2E1J9\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.759+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n }\n ],\n \"count\": 5\n }\n}"; JsonUtils.deserialize(resString, classOf[Response]) @@ -575,4 +574,26 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory val resString = "{\"id\": \"api.dialcode.generate\",\"ver\": \"1.0\",\"ts\": \"2022-07-05T09:47:26.000Z\",\"params\": {\"resmsgid\": \"79eb8b00-fc47-11ec-af25-0f53946b16ec\",\"msgid\": \"79be1260-fc47-11ec-8c03-63ca5ce41074\",\"status\": \"successful\",\"err\": null,\"errmsg\": null},\"responseCode\": \"OK\",\"result\": {\"dialcodes\": [\"K2C3R6\",\"H2E8F9\"],\"count\": 2,\"batchcode\": \"do_11357423520695910411\",\"publisher\": null}}" JsonUtils.deserialize(resString, classOf[Response]) } + + def getInvalidChannelReserveDIALRequest(identifier: String): Request = { + val request = new Request() + request.setObjectType("Content") + request.setContext(getInavlidChannelContext()) + request.getContext.put("identifier",identifier) + request.put("identifier",identifier) + request.putAll(getReserveRequest()) + request + } + + def getInavlidChannelContext():util.Map[String, AnyRef] = { + val contextMap: java.util.Map[String, AnyRef] = new util.HashMap[String, AnyRef](){{ + put("graph_id", "domain") + put("version" , "1.0") + put("objectType" , "Content") + put("schemaName", "content") + put("channel", "invalidChannel") + }} + contextMap + } + } \ No newline at end of file diff --git a/content-api/content-service/app/controllers/v4/CollectionController.scala b/content-api/content-service/app/controllers/v4/CollectionController.scala index 2c649a219..cd84c0579 100644 --- a/content-api/content-service/app/controllers/v4/CollectionController.scala +++ b/content-api/content-service/app/controllers/v4/CollectionController.scala @@ -186,6 +186,16 @@ class CollectionController @Inject()(@Named(ActorNames.CONTENT_ACTOR) contentAc getResult(ApiId.LINK_DIAL_COLLECTION, contentActor, contentRequest, version = apiVersion) } + def reserveDialCode(identifier: String) = Action.async { implicit request => + val headers = commonHeaders() + val body = requestBody() + body.putAll(headers) + body.putAll(Map("identifier" -> identifier).asJava) + val reserveDialCode = getRequest(body, headers, "reserveDialCode") + setRequestContext(reserveDialCode, version, objectType, schemaName) + getResult(ApiId.RESERVE_DIAL_CONTENT, contentActor, reserveDialCode) + } + def copy(identifier: String, mode: Option[String], copyType: String) = Action.async { implicit request => val headers = commonHeaders() val body = requestBody() diff --git a/content-api/content-service/app/controllers/v4/ContentController.scala b/content-api/content-service/app/controllers/v4/ContentController.scala index ddf2dc45c..f5a25d0fe 100644 --- a/content-api/content-service/app/controllers/v4/ContentController.scala +++ b/content-api/content-service/app/controllers/v4/ContentController.scala @@ -131,6 +131,16 @@ class ContentController @Inject()(@Named(ActorNames.CONTENT_ACTOR) contentActor: getResult(ApiId.LINK_DIAL_CONTENT, contentActor, contentRequest, version = apiVersion) } + def reserveDialCode(identifier: String) = Action.async { implicit request => + val headers = commonHeaders() + val body = requestBody() + body.putAll(headers) + body.putAll(Map("identifier" -> identifier).asJava) + val reserveDialCode = getRequest(body, headers, "reserveDialCode") + setRequestContext(reserveDialCode, version, objectType, schemaName) + getResult(ApiId.RESERVE_DIAL_CONTENT, contentActor, reserveDialCode) + } + def upload(identifier: String, fileFormat: Option[String], validation: Option[String]) = Action.async { implicit request => val headers = commonHeaders() val content = requestFormData(identifier) diff --git a/content-api/content-service/conf/routes b/content-api/content-service/conf/routes index fc4c8ad83..17b4e3059 100644 --- a/content-api/content-service/conf/routes +++ b/content-api/content-service/conf/routes @@ -99,6 +99,7 @@ DELETE /content/v4/retire/:identifier controllers.v4.ContentControl PATCH /content/v4/system/update/:identifier controllers.v4.ContentController.systemUpdate(identifier:String) POST /content/v4/review/:identifier controllers.v4.ContentController.review(identifier:String) POST /content/v4/reject/:identifier controllers.v4.ContentController.reviewReject(identifier:String) +POST /content/v4/dialcode/reserve/:identifier controllers.v4.ContentController.reserveDialCode(identifier:String) # App v4 APIs POST /app/v4/register controllers.v4.AppController.register @@ -129,4 +130,5 @@ GET /object/v4/read/:identifier controllers.v4.ObjectController # Collection V4 APIs POST /collection/v4/import/:collectionId controllers.v4.CollectionController.importCollection(collectionId:String) GET /collection/v4/export/:collectionId controllers.v4.CollectionController.exportCollection(collectionId:String, fileType:Option[String]) -POST /collection/v4/review/:identifier controllers.v4.CollectionController.review(identifier:String) \ No newline at end of file +POST /collection/v4/review/:identifier controllers.v4.CollectionController.review(identifier:String) +POST /collection/v4/dialcode/reserve/:identifier controllers.v4.CollectionController.reserveDialCode(identifier:String) \ No newline at end of file From 542ffcde42b99b972355f689130b1ae121f08e2d Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Thu, 7 Jul 2022 15:04:46 +0530 Subject: [PATCH 084/490] Issue #SB-28316 feat: DIAL code reserve API refactor. --- .../app/controllers/v4/CollectionController.scala | 2 +- content-api/content-service/app/utils/ApiId.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/content-api/content-service/app/controllers/v4/CollectionController.scala b/content-api/content-service/app/controllers/v4/CollectionController.scala index cd84c0579..4dfd17829 100644 --- a/content-api/content-service/app/controllers/v4/CollectionController.scala +++ b/content-api/content-service/app/controllers/v4/CollectionController.scala @@ -193,7 +193,7 @@ class CollectionController @Inject()(@Named(ActorNames.CONTENT_ACTOR) contentAc body.putAll(Map("identifier" -> identifier).asJava) val reserveDialCode = getRequest(body, headers, "reserveDialCode") setRequestContext(reserveDialCode, version, objectType, schemaName) - getResult(ApiId.RESERVE_DIAL_CONTENT, contentActor, reserveDialCode) + getResult(ApiId.RESERVE_DIAL_COLLECTION, contentActor, reserveDialCode) } def copy(identifier: String, mode: Option[String], copyType: String) = Action.async { implicit request => diff --git a/content-api/content-service/app/utils/ApiId.scala b/content-api/content-service/app/utils/ApiId.scala index 6bad01791..8b5afe44f 100644 --- a/content-api/content-service/app/utils/ApiId.scala +++ b/content-api/content-service/app/utils/ApiId.scala @@ -100,5 +100,5 @@ object ApiId { //Collection CSV APIs val IMPORT_CSV = "api.collection.import" val EXPORT_CSV = "api.collection.export" - + val RESERVE_DIAL_COLLECTION = "api.collection.dialcode.reserve" } From d2bf1945399cd3c4712530a54fbd789ceaaef774 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Thu, 7 Jul 2022 15:06:55 +0530 Subject: [PATCH 085/490] Issue #SB-28316 feat: DIAL code reserve API refactor. --- .../content/dial/DIALManagerTest.scala | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala b/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala index 11e56e0ee..9457a4173 100644 --- a/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala +++ b/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala @@ -574,26 +574,4 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory val resString = "{\"id\": \"api.dialcode.generate\",\"ver\": \"1.0\",\"ts\": \"2022-07-05T09:47:26.000Z\",\"params\": {\"resmsgid\": \"79eb8b00-fc47-11ec-af25-0f53946b16ec\",\"msgid\": \"79be1260-fc47-11ec-8c03-63ca5ce41074\",\"status\": \"successful\",\"err\": null,\"errmsg\": null},\"responseCode\": \"OK\",\"result\": {\"dialcodes\": [\"K2C3R6\",\"H2E8F9\"],\"count\": 2,\"batchcode\": \"do_11357423520695910411\",\"publisher\": null}}" JsonUtils.deserialize(resString, classOf[Response]) } - - def getInvalidChannelReserveDIALRequest(identifier: String): Request = { - val request = new Request() - request.setObjectType("Content") - request.setContext(getInavlidChannelContext()) - request.getContext.put("identifier",identifier) - request.put("identifier",identifier) - request.putAll(getReserveRequest()) - request - } - - def getInavlidChannelContext():util.Map[String, AnyRef] = { - val contextMap: java.util.Map[String, AnyRef] = new util.HashMap[String, AnyRef](){{ - put("graph_id", "domain") - put("version" , "1.0") - put("objectType" , "Content") - put("schemaName", "content") - put("channel", "invalidChannel") - }} - contextMap - } - } \ No newline at end of file From 34ab1982e82c2da6cd097dad8e6baf6af08f6516 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Tue, 12 Jul 2022 11:19:50 +0530 Subject: [PATCH 086/490] Issue #SB-28316 feat: DIAL code reserve API refactor. --- .../main/scala/org/sunbird/content/dial/DIALManager.scala | 2 +- .../content-actors/src/test/resources/application.conf | 7 +++++-- content-api/content-service/conf/application.conf | 8 +++++++- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala index 9f4ca3b2a..a3bd862d4 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala @@ -21,7 +21,7 @@ import scala.concurrent.{ExecutionContext, Future} object DIALManager { val DIAL_SEARCH_API_URL: String = Platform.config.getString("dial_service.api.base_url") + Platform.config.getString("dial_service.api.search") - val DIALCODE_GENERATE_URI: String = Platform.config.getString("dial_service.api.base_url") + Platform.config.getString("dial_service.api.generate_url") + 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 = "Bearer " + Platform.config.getString("dial_service.api.auth_key") val PASSPORT_KEY: String = Platform.config.getString("graph.passport.key.base") diff --git a/content-api/content-actors/src/test/resources/application.conf b/content-api/content-actors/src/test/resources/application.conf index 54ea3dcf7..837e732ba 100644 --- a/content-api/content-actors/src/test/resources/application.conf +++ b/content-api/content-actors/src/test/resources/application.conf @@ -496,14 +496,17 @@ dial_service { base_url = "https://qa.ekstep.in/api" auth_key = "auth_key" search = "/dialcode/v1/search" - generate_url = "/dialcode/v1/generate" + generate = "/dialcode/v1/generate" } } content.link_dialcode.validation=true content.link_dialcode.max_limit=10 -reserve_dialcode.mimeType = ["application/vnd.ekstep.content-collection", "application/pdf"] +reserve_dialcode { + mimeType = ["application/vnd.ekstep.content-collection", "application/pdf"] + max_count = 250 +} # This is added to handle large artifacts sizes differently content.artifact.size.for_online=209715200 diff --git a/content-api/content-service/conf/application.conf b/content-api/content-service/conf/application.conf index a4a6147e2..b969a73eb 100644 --- a/content-api/content-service/conf/application.conf +++ b/content-api/content-service/conf/application.conf @@ -604,9 +604,15 @@ dial_service { base_url = "https://qa.ekstep.in/api" auth_key = "auth_key" search = "/dialcode/v1/search" - generate_url = "/dialcode/v1/generate" + generate = "/api/dialcode/v1/generate" } } + +reserve_dialcode { + mimeType = ["application/vnd.ekstep.content-collection"] + max_count = 250 +} + content.link_dialcode.validation=true content.link_dialcode.max_limit=10 # This is added to handle large artifacts sizes differently From 9bd4a32dcaf49b9de4a4210496eef20fee1a70bb Mon Sep 17 00:00:00 2001 From: AmiableAnil Date: Tue, 12 Jul 2022 19:25:28 +0530 Subject: [PATCH 087/490] Issue #SB-30119 test: Added the postman tests for content v2 create, read and update API. --- ...ontent V2 API Test.postman_collection.json | 1165 +++++++++++++++++ .../Content V2.postman_collection.json | 661 ++++++++++ 2 files changed, 1826 insertions(+) create mode 100644 content-api/api-test/Content V2 API Test.postman_collection.json create mode 100644 content-api/api-test/Content V2.postman_collection.json diff --git a/content-api/api-test/Content V2 API Test.postman_collection.json b/content-api/api-test/Content V2 API Test.postman_collection.json new file mode 100644 index 000000000..64dc2f6cf --- /dev/null +++ b/content-api/api-test/Content V2 API Test.postman_collection.json @@ -0,0 +1,1165 @@ +{ + "info": { + "_postman_id": "58a713b4-b8bb-4885-9909-b23d253f8f31", + "name": "Content V2 API Test", + "description": "The Content Management APIs allow you to create, manage and process content on the Sunbird platform. Content is the basic building block of the Knowledge Service. Some examples would be Videos, Audio, Pdfs, Html, ECML etc. The basic operations for this API include Create, Update, Read, Review, Publish.\n\nThe URL for Content Management API(s) is /content/v1.", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "1419050" + }, + "item": [ + { + "name": "Positive", + "item": [ + { + "name": "Content Create ", + "item": [ + { + "name": "Content Create", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "pm.test(\"Content-Type header is present\", () => {", + " pm.response.to.have.header(\"Content-Type\");", + "});", + "pm.test(\"Content-Type header is application/json\", () => {", + " pm.expect(pm.response.headers.get('Content-Type')).to.eql('application/json');", + "});", + "pm.test(\"Content-Encoding header is present\", () => {", + " pm.response.to.have.header(\"Content-Encoding\");", + "});", + "pm.test(\"Content-Encoding header is gzip\", () => {", + " pm.expect(pm.response.headers.get('Content-Encoding')).to.eql('gzip');", + "});", + "", + "pm.test(\"Test data type of the response and keys\", () => {", + " pm.expect(jsonResponse).to.be.an(\"object\").that.has.all.keys('id', 'ver', 'ts', 'params', 'responseCode', 'result');", + " pm.expect(jsonResponse.id).to.be.a(\"string\");", + " pm.expect(jsonResponse.ver).to.be.a(\"string\");", + " pm.expect(jsonResponse.ts).to.be.a(\"string\");", + " pm.expect(jsonResponse.responseCode).to.be.a(\"string\");", + " pm.expect(jsonResponse.params).to.be.an(\"object\").that.has.all.keys('resmsgid', 'msgid', 'err', 'status', 'errmsg');", + " pm.expect(jsonResponse.params.resmsgid).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.msgid).to.be.null;", + " pm.expect(jsonResponse.params.err).to.be.null;", + " pm.expect(jsonResponse.params.status).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.errmsg).to.be.null;", + " pm.expect(jsonResponse.result).to.be.an(\"object\");", + " pm.expect(jsonResponse.result).to.have.all.keys('identifier', 'node_id', 'versionKey');", + " pm.expect(jsonResponse.result.identifier).to.be.a(\"string\");", + " pm.expect(jsonResponse.result.node_id).to.be.a(\"string\");", + " pm.expect(jsonResponse.result.versionKey).to.be.a(\"string\");", + "});", + "", + "//Validation", + "pm.test(\"id should be api.content.create\", function () {", + " pm.expect(jsonResponse.id).to.eql(\"api.content.create\");", + "});", + "pm.test(\"ver should be 4.0\", function () {", + " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", + "});", + "pm.test(\"params.status should be successful\", function () {", + " pm.expect(jsonResponse.params.status).to.eql(\"successful\");", + "});", + "pm.test(\"responseCode should be OK\", function () {", + " pm.expect(jsonResponse.responseCode).to.eql(\"OK\");", + "});", + "", + "// Set global variable", + "pm.globals.set(\"content_id\", jsonResponse.result.identifier);", + "pm.globals.set(\"versionKey\", jsonResponse.result.versionKey);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": { + "connection": true + } + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"createdBy\": \"530b19ea-dc8d-4cc7-a4b5-0c0214c8113a\",\n \"resourceType\": \"Learn\",\n \"framework\": \"ekstep_ncert_k-12\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + } + ] + }, + { + "name": "Content Read", + "item": [ + { + "name": "Content Read", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", () => {", + " pm.response.to.have.status(200)", + "});", + "", + "//code name contains a string", + "tests[\"Status code name has string OK\"] = responseCode.name.has(\"OK\");", + "", + "pm.test(\"Content-Type header is present\", () => {", + " pm.response.to.have.header(\"Content-Type\");", + "});", + "pm.test(\"Content-Type header is application/json\", () => {", + " pm.expect(pm.response.headers.get('Content-Type')).to.eql('application/json');", + "});", + "pm.test(\"Content-Encoding header is present\", () => {", + " pm.response.to.have.header(\"Content-Encoding\");", + "});", + "pm.test(\"Content-Encoding header is gzip\", () => {", + " pm.expect(pm.response.headers.get('Content-Encoding')).to.eql('gzip');", + "});", + "", + "pm.test(\"Test data type of the response and keys\", () => {", + " pm.expect(jsonResponse).to.be.an(\"object\").that.has.all.keys('id', 'ver', 'ts', 'params', 'responseCode', 'result');", + " pm.expect(jsonResponse.id).to.be.a(\"string\");", + " pm.expect(jsonResponse.ver).to.be.a(\"string\");", + " pm.expect(jsonResponse.ts).to.be.a(\"string\");", + " pm.expect(jsonResponse.responseCode).to.be.a(\"string\");", + " pm.expect(jsonResponse.params).to.be.an(\"object\").that.has.all.keys('resmsgid', 'msgid', 'err', 'status', 'errmsg');", + " pm.expect(jsonResponse.params.resmsgid).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.msgid).to.be.null;", + " pm.expect(jsonResponse.params.err).to.be.null;", + " pm.expect(jsonResponse.params.status).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.errmsg).to.be.null;", + " pm.expect(jsonResponse.result).to.be.an(\"object\").that.has.all.keys('content');", + " pm.expect(jsonResponse.result.content).to.be.an(\"object\")", + " pm.expect(jsonResponse.result.content).to.have.any.keys('identifier', 'versionKey', 'code', 'channel', 'mimeType', 'createdOn', 'primaryCategory', 'contentDisposition', 'contentEncoding', 'objectType', 'visibility', 'discussionForum', 'license', 'version', 'compatibilityLevel', 'name', 'status');", + " pm.expect(jsonResponse.result.content.identifier).to.be.a(\"string\");", + " pm.expect(jsonResponse.result.content.versionKey).to.be.a(\"string\");", + " pm.expect(jsonResponse.result.content.version).to.be.a(\"number\");", + " pm.expect(jsonResponse.result.content.channel).to.be.a(\"string\");", + " pm.expect(jsonResponse.result.content.compatibilityLevel).to.be.a(\"number\");", + " pm.expect(jsonResponse.result.content.ownershipType).to.be.an(\"array\");", + " pm.expect(jsonResponse.result.content.trackable).to.be.an(\"object\");", + "});", + "", + "//Validation", + "pm.test(\"id should be api.content.read\", function () {", + " pm.expect(jsonResponse.id).to.eql(\"api.content.read\");", + "});", + "pm.test(\"ver should be 4.0\", () => {", + " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", + "});", + "pm.test(\"params.status should be successful\", () => {", + " pm.expect(jsonResponse.params.status).to.eql(\"successful\");", + "});", + "pm.test(\"responseCode should be OK\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"OK\");", + "});", + "pm.test(\"identifier should be equal to \" + pm.globals.get(\"content_id\"), () => {", + " pm.expect(jsonResponse.result.content.identifier).to.eql(pm.globals.get(\"content_id\"));", + "});", + "pm.test(\"objectType should be Content\", () => {", + " pm.expect(jsonResponse.result.content.objectType).to.eql(\"Content\");", + "});", + "pm.test(\"visibility should be Default\", () => {", + " pm.expect(jsonResponse.result.content.visibility).to.eql(\"Default\");", + "});", + "pm.test(\"compatibilityLevel should be 1\", () => {", + " pm.expect(jsonResponse.result.content.compatibilityLevel).to.eql(1);", + "});", + "pm.test(\"status should be Draft\", () => {", + " pm.expect(jsonResponse.result.content.status).to.eql(\"Draft\");", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "type": "text" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + } + ], + "url": { + "raw": "{{host}}/content/v2/read/{{content_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v2", + "read", + "{{content_id}}" + ] + }, + "description": "Fetch a particular Batch\nThis API is associated with fetching a particular batch on the Sunbird Platform.\nThe endpoint for Fetch a particular Batch is /batch/read/{Batch_ID}\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + }, + { + "name": "Content Read - fields query params", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", () => {", + " pm.response.to.have.status(200)", + "});", + "", + "//code name contains a string", + "tests[\"Status code name has string OK\"] = responseCode.name.has(\"OK\");", + "", + "pm.test(\"Content-Type header is present\", () => {", + " pm.response.to.have.header(\"Content-Type\");", + "});", + "pm.test(\"Content-Type header is application/json\", () => {", + " pm.expect(pm.response.headers.get('Content-Type')).to.eql('application/json');", + "});", + "pm.test(\"Content-Encoding header is present\", () => {", + " pm.response.to.have.header(\"Content-Encoding\");", + "});", + "pm.test(\"Content-Encoding header is gzip\", () => {", + " pm.expect(pm.response.headers.get('Content-Encoding')).to.eql('gzip');", + "});", + "", + "pm.test(\"Test data type of the response and keys\", () => {", + " pm.expect(jsonResponse).to.be.an(\"object\").that.has.all.keys('id', 'ver', 'ts', 'params', 'responseCode', 'result');", + " pm.expect(jsonResponse.id).to.be.a(\"string\");", + " pm.expect(jsonResponse.ver).to.be.a(\"string\");", + " pm.expect(jsonResponse.ts).to.be.a(\"string\");", + " pm.expect(jsonResponse.responseCode).to.be.a(\"string\");", + " pm.expect(jsonResponse.params).to.be.an(\"object\").that.has.all.keys('resmsgid', 'msgid', 'err', 'status', 'errmsg');", + " pm.expect(jsonResponse.params.resmsgid).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.msgid).to.be.null;", + " pm.expect(jsonResponse.params.err).to.be.null;", + " pm.expect(jsonResponse.params.status).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.errmsg).to.be.null;", + " pm.expect(jsonResponse.result).to.be.an(\"object\").that.has.all.keys('content');", + " pm.expect(jsonResponse.result.content).to.be.an(\"object\")", + " pm.expect(jsonResponse.result.content).to.have.all.keys('identifier', 'primaryCategory', 'name', 'languageCode');", + " pm.expect(jsonResponse.result.content.identifier).to.be.a(\"string\");", + " pm.expect(jsonResponse.result.content.versionKey).to.be.undefined;", + " pm.expect(jsonResponse.result.content.version).to.be.undefined;", + " pm.expect(jsonResponse.result.content.channel).to.be.undefined;", + " pm.expect(jsonResponse.result.content.compatibilityLevel).to.be.undefined;", + " pm.expect(jsonResponse.result.content.languageCode).to.be.an(\"array\");", + " pm.expect(jsonResponse.result.content.trackable).to.be.undefined;", + "});", + "", + "//Validation", + "pm.test(\"id should be api.content.read\", function () {", + " pm.expect(jsonResponse.id).to.eql(\"api.content.read\");", + "});", + "pm.test(\"ver should be 4.0\", () => {", + " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", + "});", + "pm.test(\"params.status should be successful\", () => {", + " pm.expect(jsonResponse.params.status).to.eql(\"successful\");", + "});", + "pm.test(\"responseCode should be OK\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"OK\");", + "});", + "pm.test(\"identifier should be equal to \" + pm.globals.get(\"content_id\"), () => {", + " pm.expect(jsonResponse.result.content.identifier).to.eql(pm.globals.get(\"content_id\"));", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "type": "text" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + } + ], + "url": { + "raw": "{{host}}/content/v2/read/{{content_id}}?fields=name,primaryCategory", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v2", + "read", + "{{content_id}}" + ], + "query": [ + { + "key": "fields", + "value": "name,primaryCategory" + } + ] + }, + "description": "Fetch a particular Batch\nThis API is associated with fetching a particular batch on the Sunbird Platform.\nThe endpoint for Fetch a particular Batch is /batch/read/{Batch_ID}\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ] + }, + { + "name": "Content Update", + "item": [ + { + "name": "Content Update", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "pm.test(\"Content-Type header is present\", () => {", + " pm.response.to.have.header(\"Content-Type\");", + "});", + "pm.test(\"Content-Type header is application/json\", () => {", + " pm.expect(pm.response.headers.get('Content-Type')).to.eql('application/json');", + "});", + "pm.test(\"Content-Encoding header is present\", () => {", + " pm.response.to.have.header(\"Content-Encoding\");", + "});", + "pm.test(\"Content-Encoding header is gzip\", () => {", + " pm.expect(pm.response.headers.get('Content-Encoding')).to.eql('gzip');", + "});", + "", + "pm.test(\"Test data type of the response and keys\", () => {", + " pm.expect(jsonResponse).to.be.an(\"object\").that.has.all.keys('id', 'ver', 'ts', 'params', 'responseCode', 'result');", + " pm.expect(jsonResponse.id).to.be.a(\"string\");", + " pm.expect(jsonResponse.ver).to.be.a(\"string\");", + " pm.expect(jsonResponse.ts).to.be.a(\"string\");", + " pm.expect(jsonResponse.responseCode).to.be.a(\"string\");", + " pm.expect(jsonResponse.params).to.be.an(\"object\").that.has.all.keys('resmsgid', 'msgid', 'err', 'status', 'errmsg');", + " pm.expect(jsonResponse.params.resmsgid).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.msgid).to.be.null;", + " pm.expect(jsonResponse.params.err).to.be.null;", + " pm.expect(jsonResponse.params.status).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.errmsg).to.be.null;", + " pm.expect(jsonResponse.result).to.be.an(\"object\");", + " pm.expect(jsonResponse.result).to.have.all.keys('identifier', 'node_id', 'versionKey');", + " pm.expect(jsonResponse.result.identifier).to.be.a(\"string\");", + " pm.expect(jsonResponse.result.node_id).to.be.a(\"string\");", + " pm.expect(jsonResponse.result.versionKey).to.be.a(\"string\");", + "});", + "", + "//Validation", + "pm.test(\"id should be api.content.update\", function () {", + " pm.expect(jsonResponse.id).to.eql(\"api.content.update\");", + "});", + "pm.test(\"ver should be 4.0\", function () {", + " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", + "});", + "pm.test(\"params.status should be successful\", function () {", + " pm.expect(jsonResponse.params.status).to.eql(\"successful\");", + "});", + "pm.test(\"responseCode should be OK\", function () {", + " pm.expect(jsonResponse.responseCode).to.eql(\"OK\");", + "});", + "pm.test(\"identifier should be equal to \" + pm.globals.get(\"content_id\"), function () {", + " pm.expect(jsonResponse.result.identifier).to.eql(pm.globals.get(\"content_id\"));", + "});", + "pm.test(\"node_id should be equal to \" + pm.globals.get(\"content_id\"), function () {", + " pm.expect(jsonResponse.result.node_id).to.eql(pm.globals.get(\"content_id\"));", + "});", + "", + "// Set global variable", + "pm.globals.set(\"versionKey\", jsonResponse.result.versionKey);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "type": "text" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"{{versionKey}}\",\n \"lastUpdatedBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\",\n \"description\": \"dsd\",\n \"name\": \"API DOCUMENTATION CONTENT\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v2/update/{{content_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v2", + "update", + "{{content_id}}" + ] + }, + "description": "This API is associated with updating a batch on the Sunbird Platform.\nThe endpoint for Course Batch Update is /batch/update\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ] + } + ] + }, + { + "name": "Negative", + "item": [ + { + "name": "Content Create", + "item": [ + { + "name": "Content Create - 404 - Invalid primaryCategory", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 404\", function () {", + " pm.response.to.have.status(404)", + "});", + "//code name contains a string", + "tests[\"Status code name has string \\\"Not Found\\\"\"] = responseCode.name.has(\"Not Found\");", + "", + "pm.test(\"Content-Type header is present\", () => {", + " pm.response.to.have.header(\"Content-Type\");", + "});", + "pm.test(\"Content-Type header is application/json\", () => {", + " pm.expect(pm.response.headers.get('Content-Type')).to.eql('application/json');", + "});", + "pm.test(\"Content-Encoding header should not present\", () => {", + " pm.response.to.have.header(\"Content-Encoding\");", + "});", + "pm.test(\"Content-Encoding header is gzip\", () => {", + " pm.expect(pm.response.headers.get('Content-Encoding')).to.eql('gzip');", + "});", + "", + "pm.test(\"Test data type of the response and keys\", () => {", + " pm.expect(jsonResponse).to.be.an(\"object\").that.has.all.keys('id', 'ver', 'ts', 'params', 'responseCode', 'result');", + " pm.expect(jsonResponse.id).to.be.a(\"string\");", + " pm.expect(jsonResponse.ver).to.be.a(\"string\");", + " pm.expect(jsonResponse.ts).to.be.a(\"string\");", + " pm.expect(jsonResponse.responseCode).to.be.a(\"string\");", + " pm.expect(jsonResponse.params).to.be.an(\"object\").that.has.all.keys('resmsgid', 'msgid', 'err', 'status', 'errmsg');", + " pm.expect(jsonResponse.params.resmsgid).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.msgid).to.be.null;", + " pm.expect(jsonResponse.params.err).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.status).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.errmsg).to.be.a(\"string\");", + " pm.expect(jsonResponse.result).to.be.an(\"object\");", + " pm.expect(jsonResponse.result).to.have.all.keys('messages');", + " pm.expect(jsonResponse.result.messages).to.be.null;", + "});", + "", + "//Validation", + "pm.test(\"id should be api.content.create\", () => {", + " pm.expect(jsonResponse.id).to.eql(\"api.content.create\");", + "});", + "pm.test(\"ver should be 4.0\", () => {", + " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", + "});", + "pm.test(\"params.err should be \\\"404\\\"\", () => {", + " pm.expect(jsonResponse.params.err).to.eql(\"404\");", + "});", + "pm.test(\"params.status should be \\\"failed\\\"\", () => {", + " pm.expect(jsonResponse.params.status).to.eql(\"failed\");", + "});", + "pm.test(\"params.errmsg should be \\\"Entry is not found in cassandra for content with identifier: obj-cat:aaaa_content_all {}\\\"\", () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(\"Entry is not found in cassandra for content with identifier: obj-cat:aaaa_content_all {}\");", + "});", + "pm.test(\"responseCode should be RESOURCE_NOT_FOUND\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"RESOURCE_NOT_FOUND\");", + "});", + "pm.test(\"messages should be null\", () => {", + " pm.expect(jsonResponse.result.messages).to.eql(null);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": { + "connection": true + } + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"createdBy\": \"530b19ea-dc8d-4cc7-a4b5-0c0214c8113a\",\n \"resourceType\": \"Learn\",\n \"framework\": \"ekstep_ncert_k-12\",\n \"primaryCategory\": \"aaaa\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + } + ] + }, + { + "name": "Content Read", + "item": [ + { + "name": "Content Read", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 404\", function () {", + " pm.response.to.have.status(404)", + "});", + "//code name contains a string", + "tests[\"Status code name has string \\\"Not Found\\\"\"] = responseCode.name.has(\"Not Found\");", + "", + "pm.test(\"Content-Type header is present\", () => {", + " pm.response.to.have.header(\"Content-Type\");", + "});", + "pm.test(\"Content-Type header is application/json\", () => {", + " pm.expect(pm.response.headers.get('Content-Type')).to.eql('application/json');", + "});", + "pm.test(\"Content-Encoding header should not present\", () => {", + " pm.response.to.have.header(\"Content-Encoding\");", + "});", + "pm.test(\"Content-Encoding header is gzip\", () => {", + " pm.expect(pm.response.headers.get('Content-Encoding')).to.eql('gzip');", + "});", + "", + "pm.test(\"Test data type of the response and keys\", () => {", + " pm.expect(jsonResponse).to.be.an(\"object\").that.has.all.keys('id', 'ver', 'ts', 'params', 'responseCode', 'result');", + " pm.expect(jsonResponse.id).to.be.a(\"string\");", + " pm.expect(jsonResponse.ver).to.be.a(\"string\");", + " pm.expect(jsonResponse.ts).to.be.a(\"string\");", + " pm.expect(jsonResponse.responseCode).to.be.a(\"string\");", + " pm.expect(jsonResponse.params).to.be.an(\"object\").that.has.all.keys('resmsgid', 'msgid', 'err', 'status', 'errmsg');", + " pm.expect(jsonResponse.params.resmsgid).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.msgid).to.be.null;", + " pm.expect(jsonResponse.params.err).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.status).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.errmsg).to.be.a(\"string\");", + " pm.expect(jsonResponse.result).to.be.an(\"object\");", + " pm.expect(jsonResponse.result).to.have.all.keys('messages');", + " pm.expect(jsonResponse.result.messages).to.be.null;", + "});", + "", + "//Validation", + "pm.test(\"id should be api.content.read\", () => {", + " pm.expect(jsonResponse.id).to.eql(\"api.content.read\");", + "});", + "pm.test(\"ver should be 4.0\", () => {", + " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", + "});", + "pm.test(\"params.err should be \\\"NOT_FOUND\\\"\", () => {", + " pm.expect(jsonResponse.params.err).to.eql(\"NOT_FOUND\");", + "});", + "pm.test(\"params.status should be \\\"failed\\\"\", () => {", + " pm.expect(jsonResponse.params.status).to.eql(\"failed\");", + "});", + "pm.test(\"params.errmsg should be \\\"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: do_42353245436534\\\"\", () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(\"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: do_42353245436534\");", + "});", + "pm.test(\"responseCode should be RESOURCE_NOT_FOUND\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"RESOURCE_NOT_FOUND\");", + "});", + "pm.test(\"messages should be null\", () => {", + " pm.expect(jsonResponse.result.messages).to.eql(null);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "type": "text" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + } + ], + "url": { + "raw": "{{host}}/content/v2/read/do_42353245436534", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v2", + "read", + "do_42353245436534" + ] + }, + "description": "Fetch a particular Batch\nThis API is associated with fetching a particular batch on the Sunbird Platform.\nThe endpoint for Fetch a particular Batch is /batch/read/{Batch_ID}\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ] + }, + { + "name": "Content Update", + "item": [ + { + "name": "Content Update - 400 - Invalid versionKey", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 400\", function () {", + " pm.response.to.have.status(400)", + "});", + "//code name contains a string", + "tests[\"Status code name has string Bad Request\"] = responseCode.name.has(\"Bad Request\");", + "", + "pm.test(\"Content-Type header is present\", () => {", + " pm.response.to.have.header(\"Content-Type\");", + "});", + "pm.test(\"Content-Type header is application/json\", () => {", + " pm.expect(pm.response.headers.get('Content-Type')).to.eql('application/json');", + "});", + "pm.test(\"Content-Encoding header should not present\", () => {", + " pm.response.to.not.have.header(\"Content-Encoding\");", + "});", + "", + "pm.test(\"Test data type of the response and keys\", () => {", + " pm.expect(jsonResponse).to.be.an(\"object\").that.has.all.keys('id', 'ver', 'ts', 'params', 'responseCode', 'result');", + " pm.expect(jsonResponse.id).to.be.a(\"string\");", + " pm.expect(jsonResponse.ver).to.be.a(\"string\");", + " pm.expect(jsonResponse.ts).to.be.a(\"string\");", + " pm.expect(jsonResponse.responseCode).to.be.a(\"string\");", + " pm.expect(jsonResponse.params).to.be.an(\"object\").that.has.all.keys('resmsgid', 'msgid', 'err', 'status', 'errmsg');", + " pm.expect(jsonResponse.params.resmsgid).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.msgid).to.be.null;", + " pm.expect(jsonResponse.params.err).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.status).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.errmsg).to.be.a(\"string\");", + " pm.expect(jsonResponse.result).to.be.an(\"object\");", + " pm.expect(jsonResponse.result).to.have.all.keys('messages');", + " pm.expect(jsonResponse.result.messages).to.be.null;", + "});", + "", + "//Validation", + "pm.test(\"id should be api.content.update\", function () {", + " pm.expect(jsonResponse.id).to.eql(\"api.content.update\");", + "});", + "pm.test(\"ver should be 4.0\", function () {", + " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", + "});", + "pm.test(\"params.err should be CLIENT_ERROR\", function () {", + " pm.expect(jsonResponse.params.err).to.eql(\"CLIENT_ERROR\");", + "});", + "pm.test(\"params.status should be failed\", function () {", + " pm.expect(jsonResponse.params.status).to.eql(\"failed\");", + "});", + "pm.test(\"params.errmsg should be \\\"Invalid version Key\\\"\", function () {", + " pm.expect(jsonResponse.params.errmsg).to.eql(\"Invalid version Key\");", + "});", + "pm.test(\"responseCode should be CLIENT_ERROR\", function () {", + " pm.expect(jsonResponse.responseCode).to.eql(\"CLIENT_ERROR\");", + "});", + "pm.test(\"messages should be null\", function () {", + " pm.expect(jsonResponse.result.messages).to.eql(null);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "type": "text" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"3423423\",\n \"lastUpdatedBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\",\n \"description\": \"dsd\",\n \"name\": \"API DOCUMENTATION CONTENT\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v2/update/do_213579099726987264122", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v2", + "update", + "do_213579099726987264122" + ] + }, + "description": "This API is associated with updating a batch on the Sunbird Platform.\nThe endpoint for Course Batch Update is /batch/update\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + }, + { + "name": "Content Update - 404 - Invalid content id", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 404\", function () {", + " pm.response.to.have.status(404)", + "});", + "//code name contains a string", + "tests[\"Status code name has string \\\"Not Found\\\"\"] = responseCode.name.has(\"Not Found\");", + "", + "pm.test(\"Content-Type header is present\", () => {", + " pm.response.to.have.header(\"Content-Type\");", + "});", + "pm.test(\"Content-Type header is application/json\", () => {", + " pm.expect(pm.response.headers.get('Content-Type')).to.eql('application/json');", + "});", + "pm.test(\"Content-Encoding header should not present\", () => {", + " pm.response.to.have.header(\"Content-Encoding\");", + "});", + "pm.test(\"Content-Encoding header is gzip\", () => {", + " pm.expect(pm.response.headers.get('Content-Encoding')).to.eql('gzip');", + "});", + "", + "pm.test(\"Test data type of the response and keys\", () => {", + " pm.expect(jsonResponse).to.be.an(\"object\").that.has.all.keys('id', 'ver', 'ts', 'params', 'responseCode', 'result');", + " pm.expect(jsonResponse.id).to.be.a(\"string\");", + " pm.expect(jsonResponse.ver).to.be.a(\"string\");", + " pm.expect(jsonResponse.ts).to.be.a(\"string\");", + " pm.expect(jsonResponse.responseCode).to.be.a(\"string\");", + " pm.expect(jsonResponse.params).to.be.an(\"object\").that.has.all.keys('resmsgid', 'msgid', 'err', 'status', 'errmsg');", + " pm.expect(jsonResponse.params.resmsgid).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.msgid).to.be.null;", + " pm.expect(jsonResponse.params.err).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.status).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.errmsg).to.be.a(\"string\");", + " pm.expect(jsonResponse.result).to.be.an(\"object\");", + " pm.expect(jsonResponse.result).to.have.all.keys('messages');", + " pm.expect(jsonResponse.result.messages).to.be.null;", + "});", + "", + "//Validation", + "pm.test(\"id should be api.content.update\", function () {", + " pm.expect(jsonResponse.id).to.eql(\"api.content.update\");", + "});", + "pm.test(\"ver should be 4.0\", function () {", + " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", + "});", + "pm.test(\"params.err should be NOT_FOUND\", function () {", + " pm.expect(jsonResponse.params.err).to.eql(\"NOT_FOUND\");", + "});", + "pm.test(\"params.status should be failed\", function () {", + " pm.expect(jsonResponse.params.status).to.eql(\"failed\");", + "});", + "pm.test(\"params.errmsg should be \\\"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: do_21357909972698726412211\\\"\", function () {", + " pm.expect(jsonResponse.params.errmsg).to.eql(\"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: do_21357909972698726412211\");", + "});", + "pm.test(\"responseCode should be RESOURCE_NOT_FOUND\", function () {", + " pm.expect(jsonResponse.responseCode).to.eql(\"RESOURCE_NOT_FOUND\");", + "});", + "pm.test(\"messages should be null\", function () {", + " pm.expect(jsonResponse.result.messages).to.eql(null);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "type": "text" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"{{versionKey}}\",\n \"lastUpdatedBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\",\n \"description\": \"dsd\",\n \"name\": \"API DOCUMENTATION CONTENT\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v2/update/do_21357909972698726412211", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v2", + "update", + "do_21357909972698726412211" + ] + }, + "description": "This API is associated with updating a batch on the Sunbird Platform.\nThe endpoint for Course Batch Update is /batch/update\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + }, + { + "name": "Content Update - 404 - Invalid primaryCategory", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 404\", function () {", + " pm.response.to.have.status(404)", + "});", + "//code name contains a string", + "tests[\"Status code name has string \\\"Not Found\\\"\"] = responseCode.name.has(\"Not Found\");", + "", + "pm.test(\"Content-Type header is present\", () => {", + " pm.response.to.have.header(\"Content-Type\");", + "});", + "pm.test(\"Content-Type header is application/json\", () => {", + " pm.expect(pm.response.headers.get('Content-Type')).to.eql('application/json');", + "});", + "pm.test(\"Content-Encoding header should not present\", () => {", + " pm.response.to.have.header(\"Content-Encoding\");", + "});", + "pm.test(\"Content-Encoding header is gzip\", () => {", + " pm.expect(pm.response.headers.get('Content-Encoding')).to.eql('gzip');", + "});", + "", + "pm.test(\"Test data type of the response and keys\", () => {", + " pm.expect(jsonResponse).to.be.an(\"object\").that.has.all.keys('id', 'ver', 'ts', 'params', 'responseCode', 'result');", + " pm.expect(jsonResponse.id).to.be.a(\"string\");", + " pm.expect(jsonResponse.ver).to.be.a(\"string\");", + " pm.expect(jsonResponse.ts).to.be.a(\"string\");", + " pm.expect(jsonResponse.responseCode).to.be.a(\"string\");", + " pm.expect(jsonResponse.params).to.be.an(\"object\").that.has.all.keys('resmsgid', 'msgid', 'err', 'status', 'errmsg');", + " pm.expect(jsonResponse.params.resmsgid).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.msgid).to.be.null;", + " pm.expect(jsonResponse.params.err).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.status).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.errmsg).to.be.a(\"string\");", + " pm.expect(jsonResponse.result).to.be.an(\"object\");", + " pm.expect(jsonResponse.result).to.have.all.keys('messages');", + " pm.expect(jsonResponse.result.messages).to.be.null;", + "});", + "", + "//Validation", + "pm.test(\"id should be api.content.update\", () => {", + " pm.expect(jsonResponse.id).to.eql(\"api.content.update\");", + "});", + "pm.test(\"ver should be 4.0\", () => {", + " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", + "});", + "pm.test(\"params.err should be \\\"404\\\"\", () => {", + " pm.expect(jsonResponse.params.err).to.eql(\"404\");", + "});", + "pm.test(\"params.status should be \\\"failed\\\"\", () => {", + " pm.expect(jsonResponse.params.status).to.eql(\"failed\");", + "});", + "pm.test(\"params.errmsg should be \\\"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\\\"\", () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(\"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\");", + "});", + "pm.test(\"responseCode should be RESOURCE_NOT_FOUND\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"RESOURCE_NOT_FOUND\");", + "});", + "pm.test(\"messages should be null\", () => {", + " pm.expect(jsonResponse.result.messages).to.eql(null);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "type": "text" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"{{versionKey}}\",\n \"description\": \"dsd\",\n \"name\": \"API DOCUMENTATION CONTENT\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v2/update/{{content_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v2", + "update", + "{{content_id}}" + ] + }, + "description": "This API is associated with updating a batch on the Sunbird Platform.\nThe endpoint for Course Batch Update is /batch/update\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/content-api/api-test/Content V2.postman_collection.json b/content-api/api-test/Content V2.postman_collection.json new file mode 100644 index 000000000..b11d5a029 --- /dev/null +++ b/content-api/api-test/Content V2.postman_collection.json @@ -0,0 +1,661 @@ +{ + "info": { + "_postman_id": "c1421998-1fcb-44c7-9db1-9421f27b0a98", + "name": "Content V2", + "description": "The Content Management APIs allow you to create, manage and process content on the Sunbird platform. Content is the basic building block of the Knowledge Service. Some examples would be Videos, Audio, Pdfs, Html, ECML etc. The basic operations for this API include Create, Update, Read, Review, Publish.\n\nThe URL for Content Management API(s) is /content/v1.", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "1419050" + }, + "item": [ + { + "name": "Content Create ", + "item": [ + { + "name": "Content Create", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "pm.test(\"Content-Type header is present\", () => {", + " pm.response.to.have.header(\"Content-Type\");", + "});", + "pm.test(\"Content-Type header is application/json\", () => {", + " pm.expect(pm.response.headers.get('Content-Type')).to.eql('application/json');", + "});", + "pm.test(\"Content-Encoding header is present\", () => {", + " pm.response.to.have.header(\"Content-Encoding\");", + "});", + "pm.test(\"Content-Encoding header is gzip\", () => {", + " pm.expect(pm.response.headers.get('Content-Encoding')).to.eql('gzip');", + "});", + "", + "pm.test(\"Test data type of the response and keys\", () => {", + " pm.expect(jsonResponse).to.be.an(\"object\").that.has.all.keys('id', 'ver', 'ts', 'params', 'responseCode', 'result');", + " pm.expect(jsonResponse.id).to.be.a(\"string\");", + " pm.expect(jsonResponse.ver).to.be.a(\"string\");", + " pm.expect(jsonResponse.ts).to.be.a(\"string\");", + " pm.expect(jsonResponse.responseCode).to.be.a(\"string\");", + " pm.expect(jsonResponse.params).to.be.an(\"object\").that.has.all.keys('resmsgid', 'msgid', 'err', 'status', 'errmsg');", + " pm.expect(jsonResponse.params.resmsgid).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.msgid).to.be.null;", + " pm.expect(jsonResponse.params.err).to.be.null;", + " pm.expect(jsonResponse.params.status).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.errmsg).to.be.null;", + " pm.expect(jsonResponse.result).to.be.an(\"object\");", + " pm.expect(jsonResponse.result).to.have.all.keys('identifier', 'node_id', 'versionKey');", + " pm.expect(jsonResponse.result.identifier).to.be.a(\"string\");", + " pm.expect(jsonResponse.result.node_id).to.be.a(\"string\");", + " pm.expect(jsonResponse.result.versionKey).to.be.a(\"string\");", + "});", + "", + "//Validation", + "pm.test(\"id should be api.content.create\", function () {", + " pm.expect(jsonResponse.id).to.eql(\"api.content.create\");", + "});", + "pm.test(\"ver should be 4.0\", function () {", + " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", + "});", + "pm.test(\"params.status should be successful\", function () {", + " pm.expect(jsonResponse.params.status).to.eql(\"successful\");", + "});", + "pm.test(\"responseCode should be OK\", function () {", + " pm.expect(jsonResponse.responseCode).to.eql(\"OK\");", + "});", + "", + "// Set global variable", + "pm.globals.set(\"content_id\", jsonResponse.result.identifier);", + "pm.globals.set(\"versionKey\", jsonResponse.result.versionKey);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": { + "connection": true + } + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"createdBy\": \"530b19ea-dc8d-4cc7-a4b5-0c0214c8113a\",\n \"resourceType\": \"Learn\",\n \"framework\": \"ekstep_ncert_k-12\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "userId", + "value": "user", + "type": "text" + }, + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "type": "text" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "Cookie", + "value": "connect.sid=s%3AyRLPeHt6zZ93WjZ2BD_Cejc11xNN-isK.ANbL%2FZ1t88yXax%2BiXjGFVtgFUAKhoWdl%2FgXU4yBX9rU; 81f30cf708470b974874c8a96a6bcdb2=l99ob678op07nq0qs5i0dae0m3; AWSELB=83D53DFF08C363B9195F6717118E44E326DE55CB796FB96A69500BA54C477B7512D6342B64B0B494BBD6699FCFFFB308C0DC9247AA23DA73B4F62BA4C399A735F647C86F50", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"API DOCUMENTATION CONTENT\",\n \"mimeType\": \"application/pdf\",\n \"code\": \"api-docs\",\n \"primaryCategory\": \"Explanation Content\",\n \"createdBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Mon, 23 Nov 2020 06:42:39 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "30000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "29991" + }, + { + "key": "Referrer-Policy", + "value": "origin-when-cross-origin, strict-origin-when-cross-origin" + }, + { + "key": "X-Frame-Options", + "value": "DENY" + }, + { + "key": "X-XSS-Protection", + "value": "1; mode=block" + }, + { + "key": "x-response-length", + "value": "55" + }, + { + "key": "X-Content-Type-Options", + "value": "nosniff" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Permitted-Cross-Domain-Policies", + "value": "master-only" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "53" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "36" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-09T11:11:12.697Z\",\n \"params\": {\n \"resmsgid\": \"3f5de290-3a0f-11eb-be5b-f9d249ba9049\",\n \"msgid\": \"3f57a100-3a0f-11eb-be5b-f9d249ba9049\",\n \"status\": \"successful\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"content_id\": \"do_1131687405374504961738\",\n \"versionKey\": \"1607512272640\"\n }\n}" + } + ] + } + ] + }, + { + "name": "Content Read", + "item": [ + { + "name": "Content Read", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", () => {", + " pm.response.to.have.status(200)", + "});", + "", + "//code name contains a string", + "tests[\"Status code name has string OK\"] = responseCode.name.has(\"OK\");", + "", + "pm.test(\"Content-Type header is present\", () => {", + " pm.response.to.have.header(\"Content-Type\");", + "});", + "pm.test(\"Content-Type header is application/json\", () => {", + " pm.expect(pm.response.headers.get('Content-Type')).to.eql('application/json');", + "});", + "pm.test(\"Content-Encoding header is present\", () => {", + " pm.response.to.have.header(\"Content-Encoding\");", + "});", + "pm.test(\"Content-Encoding header is gzip\", () => {", + " pm.expect(pm.response.headers.get('Content-Encoding')).to.eql('gzip');", + "});", + "", + "pm.test(\"Test data type of the response and keys\", () => {", + " pm.expect(jsonResponse).to.be.an(\"object\").that.has.all.keys('id', 'ver', 'ts', 'params', 'responseCode', 'result');", + " pm.expect(jsonResponse.id).to.be.a(\"string\");", + " pm.expect(jsonResponse.ver).to.be.a(\"string\");", + " pm.expect(jsonResponse.ts).to.be.a(\"string\");", + " pm.expect(jsonResponse.responseCode).to.be.a(\"string\");", + " pm.expect(jsonResponse.params).to.be.an(\"object\").that.has.all.keys('resmsgid', 'msgid', 'err', 'status', 'errmsg');", + " pm.expect(jsonResponse.params.resmsgid).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.msgid).to.be.null;", + " pm.expect(jsonResponse.params.err).to.be.null;", + " pm.expect(jsonResponse.params.status).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.errmsg).to.be.null;", + " pm.expect(jsonResponse.result).to.be.an(\"object\").that.has.all.keys('content');", + " pm.expect(jsonResponse.result.content).to.be.an(\"object\")", + " pm.expect(jsonResponse.result.content).to.have.any.keys('identifier', 'versionKey', 'code', 'channel', 'mimeType', 'createdOn', 'primaryCategory', 'contentDisposition', 'contentEncoding', 'objectType', 'visibility', 'discussionForum', 'license', 'version', 'compatibilityLevel', 'name', 'status');", + " pm.expect(jsonResponse.result.content.identifier).to.be.a(\"string\");", + " pm.expect(jsonResponse.result.content.versionKey).to.be.a(\"string\");", + " pm.expect(jsonResponse.result.content.version).to.be.a(\"number\");", + " pm.expect(jsonResponse.result.content.channel).to.be.a(\"string\");", + " pm.expect(jsonResponse.result.content.compatibilityLevel).to.be.a(\"number\");", + " pm.expect(jsonResponse.result.content.ownershipType).to.be.an(\"array\");", + " pm.expect(jsonResponse.result.content.trackable).to.be.an(\"object\");", + "});", + "", + "//Validation", + "pm.test(\"id should be api.content.read\", function () {", + " pm.expect(jsonResponse.id).to.eql(\"api.content.read\");", + "});", + "pm.test(\"ver should be 4.0\", () => {", + " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", + "});", + "pm.test(\"params.status should be successful\", () => {", + " pm.expect(jsonResponse.params.status).to.eql(\"successful\");", + "});", + "pm.test(\"responseCode should be OK\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"OK\");", + "});", + "pm.test(\"identifier should be equal to \" + pm.globals.get(\"content_id\"), () => {", + " pm.expect(jsonResponse.result.content.identifier).to.eql(pm.globals.get(\"content_id\"));", + "});", + "pm.test(\"objectType should be Content\", () => {", + " pm.expect(jsonResponse.result.content.objectType).to.eql(\"Content\");", + "});", + "pm.test(\"visibility should be Default\", () => {", + " pm.expect(jsonResponse.result.content.visibility).to.eql(\"Default\");", + "});", + "pm.test(\"compatibilityLevel should be 1\", () => {", + " pm.expect(jsonResponse.result.content.compatibilityLevel).to.eql(1);", + "});", + "pm.test(\"status should be Draft\", () => {", + " pm.expect(jsonResponse.result.content.status).to.eql(\"Draft\");", + "});", + "", + "pm.globals.set(\"versionKey\", jsonResponse.result.content.versionKey);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "type": "text" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + } + ], + "url": { + "raw": "{{host}}/content/v2/read/{{content_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v2", + "read", + "{{content_id}}" + ] + }, + "description": "Fetch a particular Batch\nThis API is associated with fetching a particular batch on the Sunbird Platform.\nThe endpoint for Fetch a particular Batch is /batch/read/{Batch_ID}\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [ + { + "name": "Content Read - 200 OK", + "originalRequest": { + "method": "GET", + "header": [ + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "type": "text" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + } + ], + "url": { + "raw": "{{host}}/content/v2/read/{{content_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v2", + "read", + "{{content_id}}" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Mon, 23 Nov 2020 04:48:35 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "30000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "29999" + }, + { + "key": "Referrer-Policy", + "value": "origin-when-cross-origin, strict-origin-when-cross-origin" + }, + { + "key": "X-Frame-Options", + "value": "DENY" + }, + { + "key": "X-XSS-Protection", + "value": "1; mode=block" + }, + { + "key": "x-response-length", + "value": "2759" + }, + { + "key": "X-Content-Type-Options", + "value": "nosniff" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Permitted-Cross-Domain-Policies", + "value": "master-only" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "11" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "35" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "X-Proxy-Cache", + "value": "MISS" + }, + { + "key": "X-Proxy-Cache-Date", + "value": "Mon, 23 Nov 2020 04:48:35 GMT" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.read\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-09T12:09:04.202Z\",\n \"params\": {\n \"resmsgid\": \"548b82a0-3a17-11eb-be5b-f9d249ba9049\",\n \"msgid\": \"5488ea90-3a17-11eb-be5b-f9d249ba9049\",\n \"status\": \"successful\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"content\": {\n \"ownershipType\": [\n \"createdBy\"\n ],\n \"code\": \"org.sunbird.ccG6ru\",\n \"credentials\": {\n \"enabled\": \"No\"\n },\n \"channel\": \"in.ekstep\",\n \"language\": [\n \"English\"\n ],\n \"mimeType\": \"application/pdf\",\n \"idealScreenSize\": \"normal\",\n \"createdOn\": \"2020-12-09T12:08:54.913+0000\",\n \"objectType\": \"Content\",\n \"primaryCategory\": \"Explanation Content\",\n \"contentDisposition\": \"inline\",\n \"lastUpdatedOn\": \"2020-12-09T12:08:54.913+0000\",\n \"contentEncoding\": \"identity\",\n \"contentType\": \"Resource\",\n \"dialcodeRequired\": \"No\",\n \"identifier\": \"do_1131687689003827201864\",\n \"lastStatusChangedOn\": \"2020-12-09T12:08:54.913+0000\",\n \"audience\": [\n \"Student\"\n ],\n \"os\": [\n \"All\"\n ],\n \"visibility\": \"Default\",\n \"consumerId\": \"7411b6bd-89f3-40ec-98d1-229dc64ce77d\",\n \"mediaType\": \"content\",\n \"osId\": \"org.ekstep.quiz.app\",\n \"languageCode\": [\n \"en\"\n ],\n \"version\": 2,\n \"versionKey\": \"1607515734913\",\n \"license\": \"CC BY 4.0\",\n \"idealScreenDensity\": \"hdpi\",\n \"framework\": \"NCF\",\n \"createdBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\",\n \"compatibilityLevel\": 1,\n \"name\": \"API DOCUMENTATION CONTENT\",\n \"status\": \"Draft\"\n }\n }\n}" + } + ] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ] + }, + { + "name": "Content Update", + "item": [ + { + "name": "Content Update", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "pm.test(\"Content-Type header is present\", () => {", + " pm.response.to.have.header(\"Content-Type\");", + "});", + "pm.test(\"Content-Type header is application/json\", () => {", + " pm.expect(pm.response.headers.get('Content-Type')).to.eql('application/json');", + "});", + "pm.test(\"Content-Encoding header is present\", () => {", + " pm.response.to.have.header(\"Content-Encoding\");", + "});", + "pm.test(\"Content-Encoding header is gzip\", () => {", + " pm.expect(pm.response.headers.get('Content-Encoding')).to.eql('gzip');", + "});", + "", + "pm.test(\"Test data type of the response and keys\", () => {", + " pm.expect(jsonResponse).to.be.an(\"object\").that.has.all.keys('id', 'ver', 'ts', 'params', 'responseCode', 'result');", + " pm.expect(jsonResponse.id).to.be.a(\"string\");", + " pm.expect(jsonResponse.ver).to.be.a(\"string\");", + " pm.expect(jsonResponse.ts).to.be.a(\"string\");", + " pm.expect(jsonResponse.responseCode).to.be.a(\"string\");", + " pm.expect(jsonResponse.params).to.be.an(\"object\").that.has.all.keys('resmsgid', 'msgid', 'err', 'status', 'errmsg');", + " pm.expect(jsonResponse.params.resmsgid).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.msgid).to.be.null;", + " pm.expect(jsonResponse.params.err).to.be.null;", + " pm.expect(jsonResponse.params.status).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.errmsg).to.be.null;", + " pm.expect(jsonResponse.result).to.be.an(\"object\");", + " pm.expect(jsonResponse.result).to.have.all.keys('identifier', 'node_id', 'versionKey');", + " pm.expect(jsonResponse.result.identifier).to.be.a(\"string\");", + " pm.expect(jsonResponse.result.node_id).to.be.a(\"string\");", + " pm.expect(jsonResponse.result.versionKey).to.be.a(\"string\");", + "});", + "", + "//Validation", + "pm.test(\"id should be api.content.update\", function () {", + " pm.expect(jsonResponse.id).to.eql(\"api.content.update\");", + "});", + "pm.test(\"ver should be 4.0\", function () {", + " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", + "});", + "pm.test(\"params.status should be successful\", function () {", + " pm.expect(jsonResponse.params.status).to.eql(\"successful\");", + "});", + "pm.test(\"responseCode should be OK\", function () {", + " pm.expect(jsonResponse.responseCode).to.eql(\"OK\");", + "});", + "pm.test(\"identifier should be equal to \" + pm.globals.get(\"content_id\"), function () {", + " pm.expect(jsonResponse.result.identifier).to.eql(pm.globals.get(\"content_id\"));", + "});", + "pm.test(\"node_id should be equal to \" + pm.globals.get(\"content_id\"), function () {", + " pm.expect(jsonResponse.result.node_id).to.eql(pm.globals.get(\"content_id\"));", + "});", + "", + "// Set global variable", + "pm.globals.set(\"versionKey\", jsonResponse.result.versionKey);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "type": "text" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"{{versionKey}}\",\n \"lastUpdatedBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\",\n \"description\": \"dsd\",\n \"name\": \"API DOCUMENTATION CONTENT\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v2/update/{{content_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v2", + "update", + "{{content_id}}" + ] + }, + "description": "This API is associated with updating a batch on the Sunbird Platform.\nThe endpoint for Course Batch Update is /batch/update\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ] + } + ] +} \ No newline at end of file From c2604aa3f4649d8f402d206425b842eba455b00c Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Thu, 14 Jul 2022 12:11:16 +0530 Subject: [PATCH 088/490] Issue #SB-28316 feat: DIAL code reserve API refactor. --- .../sunbird/content/dial/DIALConstants.scala | 6 +- .../sunbird/content/dial/DIALManager.scala | 216 ++++++++++-------- .../content/util/ContentConstants.scala | 3 + 3 files changed, 128 insertions(+), 97 deletions(-) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALConstants.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALConstants.scala index 4891f30e1..6d6c99e25 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALConstants.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALConstants.scala @@ -20,5 +20,9 @@ object DIALConstants { val DISCUSSION_FORUM: String = "discussionForum" val CREDENTIALS: String = "credentials" val TRACKABLE: String = "trackable" - + val PROCESS_ID: String = "processId" + val PUBLISHER: String = "publisher" + val BATCH_CODE: String = "batchCode" + val LIVE_STATUS: String = "Live" + val UNLISTED_STATUS: String = "Unlisted" } \ No newline at end of file diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala index a3bd862d4..81087b514 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala @@ -10,7 +10,6 @@ import org.sunbird.graph.dac.model.Node import org.sunbird.graph.nodes.DataNode import org.sunbird.graph.utils.ScalaJsonUtils import org.sunbird.managers.HierarchyManager -import org.sunbird.telemetry.logger.TelemetryManager import java.util import scala.collection.JavaConverters._ @@ -22,7 +21,7 @@ object DIALManager { val DIAL_SEARCH_API_URL: String = Platform.config.getString("dial_service.api.base_url") + Platform.config.getString("dial_service.api.search") 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 = "Bearer " + Platform.config.getString("dial_service.api.auth_key") + 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") def link(request: Request)(implicit oec: OntologyEngineContext, ec: ExecutionContext): Future[Response] = { @@ -64,7 +63,7 @@ object DIALManager { contents.foreach(id => reqMap += (id -> dialcodes)) }) if (Platform.getBoolean("content.link_dialcode.validation", true)) { - val dials = requestList.collect { case m if m.get(DIALConstants.DIALCODE).nonEmpty => m.get(DIALConstants.DIALCODE).get }.flatten + val dials = requestList.collect { case m if m.contains(DIALConstants.DIALCODE) => m(DIALConstants.DIALCODE) }.flatten validateDialCodes(channelId, dials) } reqMap @@ -88,10 +87,8 @@ object DIALManager { }}) }} val headerParam = new util.HashMap[String, String]{put(DIALConstants.X_CHANNEL_ID, channelId); put(DIALConstants.AUTHORIZATION, DIAL_API_AUTH_KEY);} - TelemetryManager.info("DIALManager::validateAndGetRequestMap:: DIAL_SEARCH_API_URL: " + DIAL_SEARCH_API_URL) - TelemetryManager.info("DIALManager::validateAndGetRequestMap:: reqMap: " + reqMap) val searchResponse = oec.httpUtil.post(DIAL_SEARCH_API_URL, reqMap, headerParam) - if (searchResponse.getResponseCode.toString == "OK") { + if (searchResponse.getResponseCode.toString == ContentConstants.OK_RESPONSE_CODE) { val result = searchResponse.getResult if (dialcodes.distinct.size == result.get(DIALConstants.COUNT).asInstanceOf[Integer]) { return true @@ -129,46 +126,18 @@ object DIALManager { req.setContext(reqContext) req.put(ContentConstants.IDENTIFIER, objectId) req.put(ContentConstants.MODE, ContentConstants.EDIT_MODE) - TelemetryManager.info("DIALManager::linkCollection:: req: " + req) DataNode.read(req).flatMap(rootNode => { req.getContext.put(ContentConstants.SCHEMA_NAME, ContentConstants.COLLECTION_SCHEMA_NAME) req.getContext.put(ContentConstants.VERSION, ContentConstants.SCHEMA_VERSION) req.put(ContentConstants.ROOT_ID, objectId) - TelemetryManager.info("DIALManager::linkCollection:: updated req: " + req) HierarchyManager.getHierarchy(req).flatMap(getHierarchyResponse => { - val collectionHierarchy = getHierarchyResponse.getResult.getOrDefault(ContentConstants.CONTENT, new java.util.HashMap[String, AnyRef]()).asInstanceOf[java.util.Map[String, AnyRef]] - val childrenHierarchy = collectionHierarchy.get(ContentConstants.CHILDREN).asInstanceOf[util.List[util.Map[String, AnyRef]]] - val updatedChildrenHierarchy = updateChildrenHierarchy(childrenHierarchy, requestMap) - val childrenDIALMap = getChildrenDIALMap(updatedChildrenHierarchy, requestMap) - val consolidatedUnitDIALMap = if (!requestMap.contains(objectId)) childrenDIALMap else childrenDIALMap ++ Map(objectId -> requestMap(objectId)) - TelemetryManager.info("DIALManager::linkCollection:: consolidatedUnitDIALMap: " + consolidatedUnitDIALMap) + val updatedChildrenHierarchy = getUpdatedChildrenHierarchy(getHierarchyResponse, requestMap) + val consolidatedUnitDIALMap = getConsolidatedUnitDIALMap(updatedChildrenHierarchy, requestMap, objectId) validateDuplicateDIALCodes(consolidatedUnitDIALMap.filter(rec => rec._2.asInstanceOf[List[String]].nonEmpty)) - val updatedHierarchy = new java.util.HashMap[String, AnyRef]() - updatedHierarchy.put(ContentConstants.IDENTIFIER, objectId) - updatedHierarchy.put(ContentConstants.CHILDREN, updatedChildrenHierarchy.asJava) - - val hierarchyReq = new Request(req) - hierarchyReq.put(ContentConstants.HIERARCHY, ScalaJsonUtils.serialize(updatedHierarchy)) - hierarchyReq.put(ContentConstants.IDENTIFIER, rootNode.getIdentifier) - TelemetryManager.info("DIALManager::linkCollection:: hierarchyReq: " + hierarchyReq) + val hierarchyReq = getHierarchyRequest(req, objectId, updatedChildrenHierarchy, rootNode) oec.graphService.saveExternalProps(hierarchyReq).flatMap(rec => if(requestMap.contains(objectId)) { - val updateReq = new Request(req) - updateReq.put(ContentConstants.IDENTIFIER, rootNode.getIdentifier) - val rootNodeMetadata = rootNode.getMetadata - rootNodeMetadata.remove(DIALConstants.DISCUSSION_FORUM) - rootNodeMetadata.remove(DIALConstants.CREDENTIALS) - rootNodeMetadata.remove(DIALConstants.TRACKABLE) - - if(rootNodeMetadata.containsKey(DIALConstants.DIALCODES)) - rootNodeMetadata.remove(DIALConstants.DIALCODES) - - if(requestMap(objectId).isEmpty) - updateReq.put(DIALConstants.DIALCODES, null) - else - updateReq.put(DIALConstants.DIALCODES, requestMap(objectId).toArray[String]) - - updateReq.getRequest.putAll(rootNodeMetadata) + val updateReq = getLinkUpdateRequest(req, rootNode, requestMap, objectId) DataNode.update(updateReq).flatMap(response => { getResponseCollectionLink(requestMap, consolidatedUnitDIALMap.keySet.toList, requestMap.keySet.diff(consolidatedUnitDIALMap.keySet).toList) @@ -180,6 +149,49 @@ object DIALManager { }) } + def getUpdatedChildrenHierarchy(getHierarchyResponse: Response, requestMap: Map[String, List[String]]): List[util.Map[String, AnyRef]] = { + val collectionHierarchy = getHierarchyResponse.getResult.getOrDefault(ContentConstants.CONTENT, new java.util.HashMap[String, AnyRef]()).asInstanceOf[java.util.Map[String, AnyRef]] + val childrenHierarchy = collectionHierarchy.get(ContentConstants.CHILDREN).asInstanceOf[util.List[util.Map[String, AnyRef]]] + updateChildrenHierarchy(childrenHierarchy, requestMap) + } + + def getConsolidatedUnitDIALMap(updatedChildrenHierarchy: List[util.Map[String, AnyRef]], requestMap: Map[String, List[String]], objectId: String): Map[String, AnyRef] = { + val childrenDIALMap = getChildrenDIALMap(updatedChildrenHierarchy, requestMap) + if (!requestMap.contains(objectId)) childrenDIALMap else childrenDIALMap ++ Map(objectId -> requestMap(objectId)) + } + + def getHierarchyRequest(req: Request, objectId: String, updatedChildrenHierarchy: List[util.Map[String, AnyRef]], rootNode: Node): Request = { + val updatedHierarchy = new java.util.HashMap[String, AnyRef]() + updatedHierarchy.put(ContentConstants.IDENTIFIER, objectId) + updatedHierarchy.put(ContentConstants.CHILDREN, updatedChildrenHierarchy.asJava) + + val hierarchyReq = new Request(req) + hierarchyReq.put(ContentConstants.HIERARCHY, ScalaJsonUtils.serialize(updatedHierarchy)) + hierarchyReq.put(ContentConstants.IDENTIFIER, rootNode.getIdentifier) + + hierarchyReq + } + + def getLinkUpdateRequest(req: Request, rootNode: Node, requestMap: Map[String, List[String]], objectId: String): Request = { + val updateReq = new Request(req) + updateReq.put(ContentConstants.IDENTIFIER, rootNode.getIdentifier) + val rootNodeMetadata = rootNode.getMetadata + rootNodeMetadata.remove(DIALConstants.DISCUSSION_FORUM) + rootNodeMetadata.remove(DIALConstants.CREDENTIALS) + rootNodeMetadata.remove(DIALConstants.TRACKABLE) + + if(rootNodeMetadata.containsKey(DIALConstants.DIALCODES)) + rootNodeMetadata.remove(DIALConstants.DIALCODES) + + if(requestMap(objectId).isEmpty) + updateReq.put(DIALConstants.DIALCODES, null) + else + updateReq.put(DIALConstants.DIALCODES, requestMap(objectId).toArray[String]) + + updateReq.getRequest.putAll(rootNodeMetadata) + updateReq + } + def validateContents(requestMap: Map[String, List[String]], reqContext: util.Map[String, AnyRef])(implicit ec: ExecutionContext, oec:OntologyEngineContext): Future[List[String]] = { val request = new Request() request.setContext(reqContext) @@ -219,7 +231,7 @@ object DIALManager { def updateChildrenHierarchy(childrenHierarchy: util.List[util.Map[String, AnyRef]], requestMap: Map[String, List[String]]): List[util.Map[String, AnyRef]] = { childrenHierarchy.asScala.toList.map(child => { - if (requestMap.contains(child.get(ContentConstants.IDENTIFIER).toString) && StringUtils.equalsIgnoreCase("Parent", child.get(ContentConstants.VISIBILITY).toString)) { + if (requestMap.contains(child.get(ContentConstants.IDENTIFIER).toString) && StringUtils.equalsIgnoreCase(ContentConstants.PARENT, child.get(ContentConstants.VISIBILITY).toString)) { if (requestMap.getOrElse(child.get(ContentConstants.IDENTIFIER).toString, List.empty).nonEmpty && requestMap(child.get(ContentConstants.IDENTIFIER).toString).exists(rec => rec.trim.nonEmpty)) child.put(DIALConstants.DIALCODES, requestMap(child.get(ContentConstants.IDENTIFIER).toString)) else @@ -269,62 +281,30 @@ object DIALManager { req.setContext(request.getContext) req.put(DIALConstants.IDENTIFIER, contentId) req.put(ContentConstants.ROOT_ID, contentId) - req.put(ContentConstants.MODE, "edit") + req.put(ContentConstants.MODE, ContentConstants.EDIT_MODE) DataNode.read(req).flatMap(rootNode => { val contentMetadata = rootNode.getMetadata - TelemetryManager.info("DialManager:: reserve:: contentMetadata: " + contentMetadata) - val contentChannel = contentMetadata.get("channel").asInstanceOf[String] - validateChannel(contentChannel, channelId) + + validateChannel(contentMetadata.get(DIALConstants.CHANNEL).asInstanceOf[String], channelId) validateContentForReservedDialcodes(contentMetadata) - validateCountForReservingDialCode(request.getRequest.get("dialcodes").asInstanceOf[util.Map[String, AnyRef]]) - - if (contentMetadata.get("status").asInstanceOf[String].equalsIgnoreCase("Live") || contentMetadata.get("status").asInstanceOf[String].equalsIgnoreCase("Unlisted")) - throw new ClientException(DIALErrors.ERR_CONTENT_INVALID_OBJECT, DIALErrors.ERR_CONTENT_INVALID_OBJECT_MSG) - - val reservedDialCodes = contentMetadata.getOrDefault("dialcodes", Map.empty[String, Integer]).asInstanceOf[Map[String, Integer]] - TelemetryManager.info("DialManager:: reserve:: reservedDialCodes: " + reservedDialCodes) - val maxIndex: Integer = if (reservedDialCodes.nonEmpty) reservedDialCodes.max._2 else -1 - val dialCodes = reservedDialCodes.keySet - val reqDialcodesCount = request.getRequest.get("dialcodes").asInstanceOf[util.Map[String, AnyRef]].get("count").asInstanceOf[Integer] - TelemetryManager.info("DialManager:: reserve:: reqDialcodesCount: " + reqDialcodesCount) - val updateDialCodes = if (dialCodes.size < reqDialcodesCount) { - val newDialcodes = generateDialCodes(channelId, contentId, reqDialcodesCount - dialCodes.size, request.get("publisher").asInstanceOf[String]) - val newDialCodesMap: Map[String, Integer] = newDialcodes.zipWithIndex.map { case (newDialCode, idx) => - (newDialCode -> (maxIndex + idx + 1).asInstanceOf[Integer]) - }.toMap - reservedDialCodes ++ newDialCodesMap - } else reservedDialCodes - TelemetryManager.info("DialManager:: reserve:: updateDialCodes: " + updateDialCodes) + validateCountForReservingDialCode(request.getRequest.get(DIALConstants.DIALCODES).asInstanceOf[util.Map[String, AnyRef]]) + validateContentStatus(contentMetadata) + + val reservedDialCodes = contentMetadata.getOrDefault(DIALConstants.DIALCODES, Map.empty[String, Integer]).asInstanceOf[Map[String, Integer]] + val updateDialCodes = getUpdateDIALCodes(reservedDialCodes, request, channelId, contentId) + if(updateDialCodes.size > reservedDialCodes.size) { - val updateReq = new Request(req) - updateReq.setContext(request.getContext) - updateReq.getContext.put(ContentConstants.IDENTIFIER, rootNode.getIdentifier) - updateReq.put(ContentConstants.IDENTIFIER, rootNode.getIdentifier) - val rootNodeMetadata = rootNode.getMetadata - rootNodeMetadata.remove(DIALConstants.DISCUSSION_FORUM) - rootNodeMetadata.remove(DIALConstants.CREDENTIALS) - rootNodeMetadata.remove(DIALConstants.TRACKABLE) - - updateReq.put(DIALConstants.RESERVED_DIALCODES, updateDialCodes) - updateReq.getRequest.putAll(rootNodeMetadata) + val updateReq = getDIALReserveUpdateRequest(req, request, rootNode, updateDialCodes) DataNode.update(updateReq).map(updatedNode => { val response = ResponseHandler.OK() - response.getResult.put("count", updateDialCodes.size.asInstanceOf[Integer]) - response.getResult.put("node_id", contentId) - response.getResult.put("processId", updatedNode.getMetadata.get("processId")) - response.getResult.put("reservedDialcodes", updatedNode.getMetadata.get("reservedDialcodes")) - response.getResult.put("versionKey", updatedNode.getMetadata.get("versionKey")) - TelemetryManager.info("DialManager:: reserve:: response: " + response) - response + val updatedSuccessResponse = getDIALReserveUpdateResponse(response, updateDialCodes.size.asInstanceOf[Integer], contentId, updatedNode) + updatedSuccessResponse.getResult.put(DIALConstants.VERSION_KEY, updatedNode.getMetadata.get(DIALConstants.VERSION_KEY)) + updatedSuccessResponse }) } else { val errorResponse = ResponseHandler.ERROR(ResponseCode.CLIENT_ERROR, DIALErrors.ERR_INVALID_COUNT, DIALErrors.ERR_DIAL_INVALID_COUNT_RESPONSE) - errorResponse.getResult.put("count", reservedDialCodes.size.asInstanceOf[Integer]) - errorResponse.getResult.put("node_id", contentId) - errorResponse.getResult.put("processId", rootNode.getMetadata.get("processId")) - errorResponse.getResult.put("reservedDialcodes", rootNode.getMetadata.get("reservedDialcodes")) - TelemetryManager.info("DialManager:: reserve:: errorResponse: " + errorResponse) - Future(errorResponse) + val updatedErrorResponse = getDIALReserveUpdateResponse(errorResponse, reservedDialCodes.size.asInstanceOf[Integer], contentId, rootNode) + Future(updatedErrorResponse) } }) } @@ -335,31 +315,50 @@ object DIALManager { } def validateContentForReservedDialcodes(metaData: util.Map[String, AnyRef]): Unit = { - val validMimeType = if (Platform.config.hasPath("reserve_dialcode.mimeType")) Platform.config.getStringList("reserve_dialcode.mimeType") else util.Arrays.asList("application/vnd.ekstep.content-collection") - if (!validMimeType.contains(metaData.get("mimeType"))) throw new ClientException(DIALErrors.ERR_CONTENT_MIMETYPE, DIALErrors.ERR_CONTENT_MIMETYPE_MSG) + val validMimeType = if (Platform.config.hasPath("reserve_dialcode.mimeType")) Platform.config.getStringList("reserve_dialcode.mimeType") else util.Arrays.asList(ContentConstants.COLLECTION_MIME_TYPE) + if (!validMimeType.contains(metaData.get(ContentConstants.MIME_TYPE))) throw new ClientException(DIALErrors.ERR_CONTENT_MIMETYPE, DIALErrors.ERR_CONTENT_MIMETYPE_MSG) } def validateCountForReservingDialCode(request: util.Map[String, AnyRef]): Unit = { - if (null == request.get("count") || !request.get("count").isInstanceOf[Integer]) throw new ClientException(DIALErrors.ERR_INVALID_COUNT, DIALErrors.ERR_INVALID_COUNT_MSG) - val count = request.get("count").asInstanceOf[Integer] + if (null == request.get(DIALConstants.COUNT) || !request.get(DIALConstants.COUNT).isInstanceOf[Integer]) throw new ClientException(DIALErrors.ERR_INVALID_COUNT, DIALErrors.ERR_INVALID_COUNT_MSG) + val count = request.get(DIALConstants.COUNT).asInstanceOf[Integer] val maxCount = if (Platform.config.hasPath("reserve_dialcode.max_count")) Platform.config.getInt("reserve_dialcode.max_count") else 250 if (count < 1 || count > maxCount) throw new ClientException(DIALErrors.ERR_INVALID_COUNT_RANGE, DIALErrors.ERR_INVALID_COUNT_RANGE_MSG + maxCount + ".") } + def validateContentStatus(contentMetadata: util.Map[String, AnyRef]): Unit = { + if (contentMetadata.get(ContentConstants.STATUS).asInstanceOf[String].equalsIgnoreCase(DIALConstants.LIVE_STATUS) || contentMetadata.get(ContentConstants.STATUS).asInstanceOf[String].equalsIgnoreCase(DIALConstants.UNLISTED_STATUS)) + throw new ClientException(DIALErrors.ERR_CONTENT_INVALID_OBJECT, DIALErrors.ERR_CONTENT_INVALID_OBJECT_MSG) + } + + 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 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) => + (newDialCode -> (maxIndex + idx + 1).asInstanceOf[Integer]) + }.toMap + reservedDialCodes ++ newDialCodesMap + } else reservedDialCodes + } + @throws[Exception] private def generateDialCodes(channelId: String, contentId: String, dialcodeCount: Integer, publisher: String)(implicit oec: OntologyEngineContext): List[String] = { val dialcodeMap = new util.HashMap[String, AnyRef] - dialcodeMap.put("count", dialcodeCount) - dialcodeMap.put("publisher", publisher) - dialcodeMap.put("batchCode", contentId) + dialcodeMap.put(DIALConstants.COUNT, dialcodeCount) + dialcodeMap.put(DIALConstants.PUBLISHER, publisher) + dialcodeMap.put(DIALConstants.BATCH_CODE, contentId) val request = new util.HashMap[String, AnyRef] - request.put("dialcodes", dialcodeMap) + request.put(DIALConstants.DIALCODES, dialcodeMap) val requestMap = new util.HashMap[String, AnyRef] - requestMap.put("request", request) + requestMap.put(DIALConstants.REQUEST, request) val headerParam = new util.HashMap[String, String]{put(DIALConstants.X_CHANNEL_ID, channelId); put(DIALConstants.AUTHORIZATION, DIAL_API_AUTH_KEY);} val generateResponse = oec.httpUtil.post(DIALCODE_GENERATE_URI, requestMap, headerParam) if (generateResponse.getResponseCode == ResponseCode.OK || generateResponse.getResponseCode == ResponseCode.PARTIAL_SUCCESS) { - val generatedDialCodes = generateResponse.getResult.get("dialcodes").asInstanceOf[util.ArrayList[String]].asScala.toList + val generatedDialCodes = generateResponse.getResult.get(DIALConstants.DIALCODES).asInstanceOf[util.ArrayList[String]].asScala.toList if (generatedDialCodes.nonEmpty) generatedDialCodes else throw new ServerException(ErrorCodes.ERR_SYSTEM_EXCEPTION.name, DIALErrors.ERR_DIAL_GEN_LIST_EMPTY_MSG) } @@ -370,4 +369,29 @@ object DIALManager { throw new ServerException(ErrorCodes.ERR_SYSTEM_EXCEPTION.name, DIALErrors.ERR_DIAL_GENERATION_MSG) } } + + def getDIALReserveUpdateRequest(req: Request, request: Request, rootNode: Node, updateDialCodes: Map[String, Integer]): Request = { + val updateReq = new Request(req) + updateReq.setContext(request.getContext) + updateReq.getContext.put(ContentConstants.IDENTIFIER, rootNode.getIdentifier) + updateReq.put(ContentConstants.IDENTIFIER, rootNode.getIdentifier) + val rootNodeMetadata = rootNode.getMetadata + rootNodeMetadata.remove(DIALConstants.DISCUSSION_FORUM) + rootNodeMetadata.remove(DIALConstants.CREDENTIALS) + rootNodeMetadata.remove(DIALConstants.TRACKABLE) + + updateReq.put(DIALConstants.RESERVED_DIALCODES, updateDialCodes) + updateReq.getRequest.putAll(rootNodeMetadata) + + updateReq + } + + def getDIALReserveUpdateResponse(response: Response, count: Integer, contentId: String, node: Node): Response = { + response.getResult.put(DIALConstants.COUNT, count) + response.getResult.put(ContentConstants.NODE_ID, contentId) + response.getResult.put(DIALConstants.PROCESS_ID, node.getMetadata.get(DIALConstants.PROCESS_ID)) + response.getResult.put(DIALConstants.RESERVED_DIALCODES, node.getMetadata.get(DIALConstants.RESERVED_DIALCODES)) + + response + } } diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/util/ContentConstants.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/util/ContentConstants.scala index 29889d0eb..c0640131e 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/util/ContentConstants.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/util/ContentConstants.scala @@ -56,4 +56,7 @@ object ContentConstants { val LAST_UPDATED_ON:String = "lastUpdatedOn" val VERSION:String = "version" val COPY_SCHEME:String = "copyScheme" + val PARENT: String = "Parent" + val BEARER: String = "Bearer" + val OK_RESPONSE_CODE: String = "OK" } From 32a30bf03194dff2d6835a2d5ae46a1ffd3d2db0 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Fri, 15 Jul 2022 20:40:36 +0530 Subject: [PATCH 089/490] Issue #SB-28316 feat: DIAL code reserve API refactor. --- .../sunbird/content/dial/DIALManager.scala | 20 ++++++++++--------- .../content/util/ContentConstants.scala | 2 +- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala index 81087b514..2974228ce 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala @@ -347,14 +347,16 @@ object DIALManager { @throws[Exception] private def generateDialCodes(channelId: String, contentId: String, dialcodeCount: Integer, publisher: String)(implicit oec: OntologyEngineContext): List[String] = { - val dialcodeMap = new util.HashMap[String, AnyRef] - dialcodeMap.put(DIALConstants.COUNT, dialcodeCount) - dialcodeMap.put(DIALConstants.PUBLISHER, publisher) - dialcodeMap.put(DIALConstants.BATCH_CODE, contentId) - val request = new util.HashMap[String, AnyRef] - request.put(DIALConstants.DIALCODES, dialcodeMap) - val requestMap = new util.HashMap[String, AnyRef] - requestMap.put(DIALConstants.REQUEST, request) + val requestMap = new util.HashMap[String, AnyRef]() {{ + put(DIALConstants.REQUEST, new util.HashMap[String, AnyRef]() {{ + put(DIALConstants.DIALCODES, new util.HashMap[String, AnyRef]() {{ + put(DIALConstants.COUNT, dialcodeCount) + put(DIALConstants.PUBLISHER, publisher) + put(DIALConstants.BATCH_CODE, contentId) + }}) + }}) + }} + val headerParam = new util.HashMap[String, String]{put(DIALConstants.X_CHANNEL_ID, channelId); put(DIALConstants.AUTHORIZATION, DIAL_API_AUTH_KEY);} val generateResponse = oec.httpUtil.post(DIALCODE_GENERATE_URI, requestMap, headerParam) if (generateResponse.getResponseCode == ResponseCode.OK || generateResponse.getResponseCode == ResponseCode.PARTIAL_SUCCESS) { @@ -380,7 +382,7 @@ object DIALManager { rootNodeMetadata.remove(DIALConstants.CREDENTIALS) rootNodeMetadata.remove(DIALConstants.TRACKABLE) - updateReq.put(DIALConstants.RESERVED_DIALCODES, updateDialCodes) + updateReq.put(DIALConstants.RESERVED_DIALCODES, updateDialCodes.asJava) updateReq.getRequest.putAll(rootNodeMetadata) updateReq diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/util/ContentConstants.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/util/ContentConstants.scala index c0640131e..622011222 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/util/ContentConstants.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/util/ContentConstants.scala @@ -57,6 +57,6 @@ object ContentConstants { val VERSION:String = "version" val COPY_SCHEME:String = "copyScheme" val PARENT: String = "Parent" - val BEARER: String = "Bearer" + val BEARER: String = "Bearer " val OK_RESPONSE_CODE: String = "OK" } From 242664c35da3786d83f2505f4a76a6c9b7b4ce06 Mon Sep 17 00:00:00 2001 From: AmiableAnil Date: Mon, 18 Jul 2022 15:33:48 +0530 Subject: [PATCH 090/490] Issue #SB-30119 test: Added the postman tests for content v2 create, read and update API. --- ...ontent V2 API Test.postman_collection.json | 1395 ++++++++++++----- .../Content V2.postman_collection.json | 1066 ++++++------- 2 files changed, 1539 insertions(+), 922 deletions(-) diff --git a/content-api/api-test/Content V2 API Test.postman_collection.json b/content-api/api-test/Content V2 API Test.postman_collection.json index 64dc2f6cf..7093dbae2 100644 --- a/content-api/api-test/Content V2 API Test.postman_collection.json +++ b/content-api/api-test/Content V2 API Test.postman_collection.json @@ -14,44 +14,93 @@ "name": "Content Create ", "item": [ { - "name": "Content Create", + "name": "Content Create - with minimal required attribute", "event": [ { "listen": "test", "script": { "exec": [ - "let jsonResponse = pm.response.json()", + "let jsonResponse = pm.response.json();", "", - "//Verification", - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.have.status(200)", - "});", - "pm.test(\"Content-Type header is present\", () => {", - " pm.response.to.have.header(\"Content-Type\");", - "});", - "pm.test(\"Content-Type header is application/json\", () => {", - " pm.expect(pm.response.headers.get('Content-Type')).to.eql('application/json');", + "pm.test(\"Verify keys and data type of the response result\", () => {", + " pm.expect(jsonResponse.result).to.have.all.keys('identifier', 'node_id', 'versionKey');", + " pm.expect(jsonResponse.result.identifier).to.be.a(\"string\");", + " pm.expect(jsonResponse.result.node_id).to.be.a(\"string\");", + " pm.expect(jsonResponse.result.versionKey).to.be.a(\"string\");", "});", - "pm.test(\"Content-Encoding header is present\", () => {", - " pm.response.to.have.header(\"Content-Encoding\");", + "", + "//Validation", + "pm.test(\"id should be api.content.create\", function () {", + " pm.expect(jsonResponse.id).to.eql(\"api.content.create\");", "});", - "pm.test(\"Content-Encoding header is gzip\", () => {", - " pm.expect(pm.response.headers.get('Content-Encoding')).to.eql('gzip');", + "pm.test(\"ver should be 4.0\", function () {", + " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", "});", "", - "pm.test(\"Test data type of the response and keys\", () => {", - " pm.expect(jsonResponse).to.be.an(\"object\").that.has.all.keys('id', 'ver', 'ts', 'params', 'responseCode', 'result');", - " pm.expect(jsonResponse.id).to.be.a(\"string\");", - " pm.expect(jsonResponse.ver).to.be.a(\"string\");", - " pm.expect(jsonResponse.ts).to.be.a(\"string\");", - " pm.expect(jsonResponse.responseCode).to.be.a(\"string\");", - " pm.expect(jsonResponse.params).to.be.an(\"object\").that.has.all.keys('resmsgid', 'msgid', 'err', 'status', 'errmsg');", - " pm.expect(jsonResponse.params.resmsgid).to.be.a(\"string\");", - " pm.expect(jsonResponse.params.msgid).to.be.null;", - " pm.expect(jsonResponse.params.err).to.be.null;", - " pm.expect(jsonResponse.params.status).to.be.a(\"string\");", - " pm.expect(jsonResponse.params.errmsg).to.be.null;", - " pm.expect(jsonResponse.result).to.be.an(\"object\");", + "// Set global variable", + "pm.globals.set(\"content_id\", jsonResponse.result.identifier);", + "pm.globals.set(\"versionKey\", jsonResponse.result.versionKey);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": { + "connection": true + } + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + }, + { + "name": "Content Create - Special character in name", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json();", + "", + "pm.test(\"Verify keys and data type of the response result\", () => {", " pm.expect(jsonResponse.result).to.have.all.keys('identifier', 'node_id', 'versionKey');", " pm.expect(jsonResponse.result.identifier).to.be.a(\"string\");", " pm.expect(jsonResponse.result.node_id).to.be.a(\"string\");", @@ -65,12 +114,6 @@ "pm.test(\"ver should be 4.0\", function () {", " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", "});", - "pm.test(\"params.status should be successful\", function () {", - " pm.expect(jsonResponse.params.status).to.eql(\"successful\");", - "});", - "pm.test(\"responseCode should be OK\", function () {", - " pm.expect(jsonResponse.responseCode).to.eql(\"OK\");", - "});", "", "// Set global variable", "pm.globals.set(\"content_id\", jsonResponse.result.identifier);", @@ -109,7 +152,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"createdBy\": \"530b19ea-dc8d-4cc7-a4b5-0c0214c8113a\",\n \"resourceType\": \"Learn\",\n \"framework\": \"ekstep_ncert_k-12\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"~`S!@#$%^&*()_+=:;\\\"'<,>.?/\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" }, "url": { "raw": "{{host}}/content/v2/create", @@ -138,42 +181,10 @@ "listen": "test", "script": { "exec": [ - "let jsonResponse = pm.response.json()", - "", - "//Verification", - "pm.test(\"Status code is 200\", () => {", - " pm.response.to.have.status(200)", - "});", - "", - "//code name contains a string", - "tests[\"Status code name has string OK\"] = responseCode.name.has(\"OK\");", - "", - "pm.test(\"Content-Type header is present\", () => {", - " pm.response.to.have.header(\"Content-Type\");", - "});", - "pm.test(\"Content-Type header is application/json\", () => {", - " pm.expect(pm.response.headers.get('Content-Type')).to.eql('application/json');", - "});", - "pm.test(\"Content-Encoding header is present\", () => {", - " pm.response.to.have.header(\"Content-Encoding\");", - "});", - "pm.test(\"Content-Encoding header is gzip\", () => {", - " pm.expect(pm.response.headers.get('Content-Encoding')).to.eql('gzip');", - "});", + "let jsonResponse = pm.response.json();", "", - "pm.test(\"Test data type of the response and keys\", () => {", - " pm.expect(jsonResponse).to.be.an(\"object\").that.has.all.keys('id', 'ver', 'ts', 'params', 'responseCode', 'result');", - " pm.expect(jsonResponse.id).to.be.a(\"string\");", - " pm.expect(jsonResponse.ver).to.be.a(\"string\");", - " pm.expect(jsonResponse.ts).to.be.a(\"string\");", - " pm.expect(jsonResponse.responseCode).to.be.a(\"string\");", - " pm.expect(jsonResponse.params).to.be.an(\"object\").that.has.all.keys('resmsgid', 'msgid', 'err', 'status', 'errmsg');", - " pm.expect(jsonResponse.params.resmsgid).to.be.a(\"string\");", - " pm.expect(jsonResponse.params.msgid).to.be.null;", - " pm.expect(jsonResponse.params.err).to.be.null;", - " pm.expect(jsonResponse.params.status).to.be.a(\"string\");", - " pm.expect(jsonResponse.params.errmsg).to.be.null;", - " pm.expect(jsonResponse.result).to.be.an(\"object\").that.has.all.keys('content');", + "pm.test(\"Verify keys and data type of the response result\", () => {", + " pm.expect(jsonResponse.result).to.have.all.keys('content');", " pm.expect(jsonResponse.result.content).to.be.an(\"object\")", " pm.expect(jsonResponse.result.content).to.have.any.keys('identifier', 'versionKey', 'code', 'channel', 'mimeType', 'createdOn', 'primaryCategory', 'contentDisposition', 'contentEncoding', 'objectType', 'visibility', 'discussionForum', 'license', 'version', 'compatibilityLevel', 'name', 'status');", " pm.expect(jsonResponse.result.content.identifier).to.be.a(\"string\");", @@ -182,23 +193,17 @@ " pm.expect(jsonResponse.result.content.channel).to.be.a(\"string\");", " pm.expect(jsonResponse.result.content.compatibilityLevel).to.be.a(\"number\");", " pm.expect(jsonResponse.result.content.ownershipType).to.be.an(\"array\");", - " pm.expect(jsonResponse.result.content.trackable).to.be.an(\"object\");", "});", "", "//Validation", - "pm.test(\"id should be api.content.read\", function () {", + "pm.test(\"id should be api.content.read\", () => {", " pm.expect(jsonResponse.id).to.eql(\"api.content.read\");", "});", "pm.test(\"ver should be 4.0\", () => {", " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", "});", - "pm.test(\"params.status should be successful\", () => {", - " pm.expect(jsonResponse.params.status).to.eql(\"successful\");", - "});", - "pm.test(\"responseCode should be OK\", () => {", - " pm.expect(jsonResponse.responseCode).to.eql(\"OK\");", - "});", - "pm.test(\"identifier should be equal to \" + pm.globals.get(\"content_id\"), () => {", + "", + "pm.test(`identifier should be equal to ${pm.globals.get(\"content_id\")}`, () => {", " pm.expect(jsonResponse.result.content.identifier).to.eql(pm.globals.get(\"content_id\"));", "});", "pm.test(\"objectType should be Content\", () => {", @@ -217,6 +222,15 @@ ], "type": "text/javascript" } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "// pm.globals.set('content_id', '');" + ], + "type": "text/javascript" + } } ], "request": { @@ -256,42 +270,10 @@ "listen": "test", "script": { "exec": [ - "let jsonResponse = pm.response.json()", - "", - "//Verification", - "pm.test(\"Status code is 200\", () => {", - " pm.response.to.have.status(200)", - "});", - "", - "//code name contains a string", - "tests[\"Status code name has string OK\"] = responseCode.name.has(\"OK\");", - "", - "pm.test(\"Content-Type header is present\", () => {", - " pm.response.to.have.header(\"Content-Type\");", - "});", - "pm.test(\"Content-Type header is application/json\", () => {", - " pm.expect(pm.response.headers.get('Content-Type')).to.eql('application/json');", - "});", - "pm.test(\"Content-Encoding header is present\", () => {", - " pm.response.to.have.header(\"Content-Encoding\");", - "});", - "pm.test(\"Content-Encoding header is gzip\", () => {", - " pm.expect(pm.response.headers.get('Content-Encoding')).to.eql('gzip');", - "});", + "let jsonResponse = pm.response.json();", "", - "pm.test(\"Test data type of the response and keys\", () => {", - " pm.expect(jsonResponse).to.be.an(\"object\").that.has.all.keys('id', 'ver', 'ts', 'params', 'responseCode', 'result');", - " pm.expect(jsonResponse.id).to.be.a(\"string\");", - " pm.expect(jsonResponse.ver).to.be.a(\"string\");", - " pm.expect(jsonResponse.ts).to.be.a(\"string\");", - " pm.expect(jsonResponse.responseCode).to.be.a(\"string\");", - " pm.expect(jsonResponse.params).to.be.an(\"object\").that.has.all.keys('resmsgid', 'msgid', 'err', 'status', 'errmsg');", - " pm.expect(jsonResponse.params.resmsgid).to.be.a(\"string\");", - " pm.expect(jsonResponse.params.msgid).to.be.null;", - " pm.expect(jsonResponse.params.err).to.be.null;", - " pm.expect(jsonResponse.params.status).to.be.a(\"string\");", - " pm.expect(jsonResponse.params.errmsg).to.be.null;", - " pm.expect(jsonResponse.result).to.be.an(\"object\").that.has.all.keys('content');", + "pm.test(\"Verify keys and data type of the response result\", () => {", + " pm.expect(jsonResponse.result).to.have.all.keys('content');", " pm.expect(jsonResponse.result.content).to.be.an(\"object\")", " pm.expect(jsonResponse.result.content).to.have.all.keys('identifier', 'primaryCategory', 'name', 'languageCode');", " pm.expect(jsonResponse.result.content.identifier).to.be.a(\"string\");", @@ -300,7 +282,6 @@ " pm.expect(jsonResponse.result.content.channel).to.be.undefined;", " pm.expect(jsonResponse.result.content.compatibilityLevel).to.be.undefined;", " pm.expect(jsonResponse.result.content.languageCode).to.be.an(\"array\");", - " pm.expect(jsonResponse.result.content.trackable).to.be.undefined;", "});", "", "//Validation", @@ -310,19 +291,23 @@ "pm.test(\"ver should be 4.0\", () => {", " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", "});", - "pm.test(\"params.status should be successful\", () => {", - " pm.expect(jsonResponse.params.status).to.eql(\"successful\");", - "});", - "pm.test(\"responseCode should be OK\", () => {", - " pm.expect(jsonResponse.responseCode).to.eql(\"OK\");", - "});", - "pm.test(\"identifier should be equal to \" + pm.globals.get(\"content_id\"), () => {", + "", + "pm.test(`identifier should be equal to ${pm.globals.get(\"content_id\")}`, () => {", " pm.expect(jsonResponse.result.content.identifier).to.eql(pm.globals.get(\"content_id\"));", "});", "" ], "type": "text/javascript" } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "// pm.globals.set('content_id', '');" + ], + "type": "text/javascript" + } } ], "request": { @@ -393,38 +378,9 @@ "listen": "test", "script": { "exec": [ - "let jsonResponse = pm.response.json()", - "", - "//Verification", - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.have.status(200)", - "});", - "pm.test(\"Content-Type header is present\", () => {", - " pm.response.to.have.header(\"Content-Type\");", - "});", - "pm.test(\"Content-Type header is application/json\", () => {", - " pm.expect(pm.response.headers.get('Content-Type')).to.eql('application/json');", - "});", - "pm.test(\"Content-Encoding header is present\", () => {", - " pm.response.to.have.header(\"Content-Encoding\");", - "});", - "pm.test(\"Content-Encoding header is gzip\", () => {", - " pm.expect(pm.response.headers.get('Content-Encoding')).to.eql('gzip');", - "});", + "let jsonResponse = pm.response.json();", "", - "pm.test(\"Test data type of the response and keys\", () => {", - " pm.expect(jsonResponse).to.be.an(\"object\").that.has.all.keys('id', 'ver', 'ts', 'params', 'responseCode', 'result');", - " pm.expect(jsonResponse.id).to.be.a(\"string\");", - " pm.expect(jsonResponse.ver).to.be.a(\"string\");", - " pm.expect(jsonResponse.ts).to.be.a(\"string\");", - " pm.expect(jsonResponse.responseCode).to.be.a(\"string\");", - " pm.expect(jsonResponse.params).to.be.an(\"object\").that.has.all.keys('resmsgid', 'msgid', 'err', 'status', 'errmsg');", - " pm.expect(jsonResponse.params.resmsgid).to.be.a(\"string\");", - " pm.expect(jsonResponse.params.msgid).to.be.null;", - " pm.expect(jsonResponse.params.err).to.be.null;", - " pm.expect(jsonResponse.params.status).to.be.a(\"string\");", - " pm.expect(jsonResponse.params.errmsg).to.be.null;", - " pm.expect(jsonResponse.result).to.be.an(\"object\");", + "pm.test(\"Verify keys and data type of the response result\", () => {", " pm.expect(jsonResponse.result).to.have.all.keys('identifier', 'node_id', 'versionKey');", " pm.expect(jsonResponse.result.identifier).to.be.a(\"string\");", " pm.expect(jsonResponse.result.node_id).to.be.a(\"string\");", @@ -432,22 +388,17 @@ "});", "", "//Validation", - "pm.test(\"id should be api.content.update\", function () {", + "pm.test(\"id should be api.content.update\", () => {", " pm.expect(jsonResponse.id).to.eql(\"api.content.update\");", "});", - "pm.test(\"ver should be 4.0\", function () {", + "pm.test(\"ver should be 4.0\", () => {", " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", "});", - "pm.test(\"params.status should be successful\", function () {", - " pm.expect(jsonResponse.params.status).to.eql(\"successful\");", - "});", - "pm.test(\"responseCode should be OK\", function () {", - " pm.expect(jsonResponse.responseCode).to.eql(\"OK\");", - "});", - "pm.test(\"identifier should be equal to \" + pm.globals.get(\"content_id\"), function () {", + "", + "pm.test(\"identifier should be equal to \" + pm.globals.get(\"content_id\"), () => {", " pm.expect(jsonResponse.result.identifier).to.eql(pm.globals.get(\"content_id\"));", "});", - "pm.test(\"node_id should be equal to \" + pm.globals.get(\"content_id\"), function () {", + "pm.test(\"node_id should be equal to \" + pm.globals.get(\"content_id\"), () => {", " pm.expect(jsonResponse.result.node_id).to.eql(pm.globals.get(\"content_id\"));", "});", "", @@ -457,6 +408,15 @@ ], "type": "text/javascript" } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "// pm.globals.set('content_id');" + ], + "type": "text/javascript" + } } ], "request": { @@ -485,7 +445,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"{{versionKey}}\",\n \"lastUpdatedBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\",\n \"description\": \"dsd\",\n \"name\": \"API DOCUMENTATION CONTENT\"\n }\n }\n}" + "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"{{versionKey}}\",\n \"lastUpdatedBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\",\n \"description\": \"dsd\",\n \"createdBy\": \"530b19ea-dc8d-4cc7-a4b5-0c0214c8113a\",\n \"resourceType\": \"Learn\",\n \"framework\": \"ekstep_ncert_k-12\",\n \"name\": \"API DOCUMENTATION CONTENT\"\n }\n }\n}" }, "url": { "raw": "{{host}}/content/v2/update/{{content_id}}", @@ -502,39 +462,9 @@ "description": "This API is associated with updating a batch on the Sunbird Platform.\nThe endpoint for Course Batch Update is /batch/update\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." }, "response": [] - } - ], - "event": [ - { - "listen": "prerequest", - "script": { - "type": "text/javascript", - "exec": [ - "" - ] - } }, { - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "" - ] - } - } - ] - } - ] - }, - { - "name": "Negative", - "item": [ - { - "name": "Content Create", - "item": [ - { - "name": "Content Create - 404 - Invalid primaryCategory", + "name": "Content Update - Special character in name", "event": [ { "listen": "test", @@ -562,7 +492,7 @@ " pm.expect(pm.response.headers.get('Content-Encoding')).to.eql('gzip');", "});", "", - "pm.test(\"Test data type of the response and keys\", () => {", + "pm.test(\"Verify keys and data type of the response result\", () => {", " pm.expect(jsonResponse).to.be.an(\"object\").that.has.all.keys('id', 'ver', 'ts', 'params', 'responseCode', 'result');", " pm.expect(jsonResponse.id).to.be.a(\"string\");", " pm.expect(jsonResponse.ver).to.be.a(\"string\");", @@ -580,8 +510,9 @@ "});", "", "//Validation", - "pm.test(\"id should be api.content.create\", () => {", - " pm.expect(jsonResponse.id).to.eql(\"api.content.create\");", + "// Response body", + "pm.test(\"id should be api.content.update\", () => {", + " pm.expect(jsonResponse.id).to.eql(\"api.content.update\");", "});", "pm.test(\"ver should be 4.0\", () => {", " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", @@ -592,8 +523,8 @@ "pm.test(\"params.status should be \\\"failed\\\"\", () => {", " pm.expect(jsonResponse.params.status).to.eql(\"failed\");", "});", - "pm.test(\"params.errmsg should be \\\"Entry is not found in cassandra for content with identifier: obj-cat:aaaa_content_all {}\\\"\", () => {", - " pm.expect(jsonResponse.params.errmsg).to.eql(\"Entry is not found in cassandra for content with identifier: obj-cat:aaaa_content_all {}\");", + "pm.test(\"params.errmsg should be \\\"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\\\"\", () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(\"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\");", "});", "pm.test(\"responseCode should be RESOURCE_NOT_FOUND\", () => {", " pm.expect(jsonResponse.responseCode).to.eql(\"RESOURCE_NOT_FOUND\");", @@ -607,10 +538,636 @@ } } ], - "protocolProfileBehavior": { - "disabledSystemHeaders": { - "connection": true - } + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "type": "text" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"{{versionKey}}\",\n \"description\": \"dsd\",\n \"name\": \"~`S!@#$%^&*()_+=:;\\\"'<,>.?/\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v2/update/{{content_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v2", + "update", + "{{content_id}}" + ] + }, + "description": "This API is associated with updating a batch on the Sunbird Platform.\nThe endpoint for Course Batch Update is /batch/update\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "let jsonResponse = pm.response.json();", + "", + "//Verification", + "pm.test(\"Status code is 200\", () => {", + " pm.response.to.have.status(200)", + "});", + "//code name contains a string", + "tests[\"Status code name has string OK\"] = responseCode.name.has(\"OK\");", + "", + "// pm.test(\"Content-Encoding header is present\", () => {", + "// pm.response.to.have.header(\"Content-Encoding\");", + "// });", + "// pm.test(\"Content-Encoding header is gzip\", () => {", + "// pm.expect(pm.response.headers.get('Content-Encoding')).to.eql('gzip');", + "// });", + "", + "pm.test(\"Verify keys and data type of the params in positive scenario\", () => {", + " pm.expect(jsonResponse.params.resmsgid).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.msgid).to.be.null;", + " pm.expect(jsonResponse.params.err).to.be.null;", + " pm.expect(jsonResponse.params.status).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.errmsg).to.be.null;", + "});", + "", + "pm.test(\"params.status should be \\\"successful\\\"\", () => {", + " pm.expect(jsonResponse.params.status).to.eql(\"successful\");", + "});", + "pm.test(\"responseCode should be OK\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"OK\");", + "});" + ] + } + } + ] + }, + { + "name": "Negative", + "item": [ + { + "name": "Content Create", + "item": [ + { + "name": "Content Create - 404 - missing name", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 400\", () => {", + " pm.response.to.have.status(400)", + "});", + "//code name contains a string", + "tests[\"Status code name has string \\\"Bad Request\\\"\"] = responseCode.name.has(\"Bad Request\");", + "", + "pm.test(\"Verify keys and data type of the response result\", () => {", + " pm.expect(jsonResponse.result).to.be.an(\"object\");", + " pm.expect(jsonResponse.result).to.have.all.keys('messages');", + " pm.expect(jsonResponse.result.messages).to.not.be.empty;", + "});", + "", + "//Validation", + "// Response body", + "pm.test(\"responseCode should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"CLIENT_ERROR\");", + "});", + "", + "// Params", + "pm.test(\"params.err should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.params.err).to.eql(\"CLIENT_ERROR\");", + "});", + "pm.test(\"params.errmsg should be \\\"Validation Errors\\\"\", () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(\"Validation Errors\");", + "});", + "", + "// Result", + "pm.test(\"result body should include messages \\\"Required Metadata name not set\\\"\", () => {", + " pm.expect(jsonResponse.result.messages).to.have.length(1)", + " pm.expect(jsonResponse.result.messages).to.include('Required Metadata name not set');", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": { + "connection": true + } + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + }, + { + "name": "Content Create - 404 - name empty", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 400\", function () {", + " pm.response.to.have.status(400)", + "});", + "//code name contains a string", + "tests[\"Status code name has string \\\"Bad Request\\\"\"] = responseCode.name.has(\"Bad Request\");", + "", + "pm.test(\"Verify keys and data type of the response result\", () => {", + " pm.expect(jsonResponse.result).to.be.an(\"object\");", + " pm.expect(jsonResponse.result).to.have.all.keys('messages');", + " pm.expect(jsonResponse.result.messages).to.not.be.empty;", + "});", + "", + "//Validation", + "// Response body", + "pm.test(\"responseCode should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"CLIENT_ERROR\");", + "});", + "", + "// Params", + "pm.test(\"params.err should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.params.err).to.eql(\"CLIENT_ERROR\");", + "});", + "pm.test(\"params.errmsg should be \\\"Validation Errors\\\"\", () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(\"Validation Errors\");", + "});", + "", + "// Result", + "pm.test(\"result body should include messages \\\"Required Metadata name not set\\\"\", () => {", + " pm.expect(jsonResponse.result.messages).to.have.length(1)", + " pm.expect(jsonResponse.result.messages).to.include('Required Metadata name not set');", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": { + "connection": true + } + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + }, + { + "name": "Content Create - 404 - missing code", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 400\", function () {", + " pm.response.to.have.status(400)", + "});", + "//code name contains a string", + "tests[\"Status code name has string \\\"Bad Request\\\"\"] = responseCode.name.has(\"Bad Request\");", + "", + "pm.test(\"Verify keys and data type of the response result\", () => {", + " pm.expect(jsonResponse.result).to.be.an(\"object\");", + " pm.expect(jsonResponse.result).to.have.all.keys('messages');", + " pm.expect(jsonResponse.result.messages).to.not.be.empty;", + "});", + "", + "//Validation", + "// Response body", + "pm.test(\"responseCode should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"CLIENT_ERROR\");", + "});", + "", + "// Params", + "pm.test(\"params.err should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.params.err).to.eql(\"CLIENT_ERROR\");", + "});", + "pm.test(\"params.errmsg should be \\\"Validation Errors\\\"\", () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(\"Validation Errors\");", + "});", + "", + "// Result", + "pm.test(\"result body should include messages \\\"Required Metadata code not set\\\"\", () => {", + " pm.expect(jsonResponse.result.messages).to.have.length(1)", + " pm.expect(jsonResponse.result.messages).to.include('Required Metadata code not set');", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": { + "connection": true + } + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + }, + { + "name": "Content Create - 404 - missing mimeType", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 400\", function () {", + " pm.response.to.have.status(400)", + "});", + "//code name contains a string", + "tests[\"Status code name has string \\\"Bad Request\\\"\"] = responseCode.name.has(\"Bad Request\");", + "", + "pm.test(\"Verify keys and data type of the response result\", () => {", + " pm.expect(jsonResponse.result).to.be.an(\"object\");", + " pm.expect(jsonResponse.result).to.have.all.keys('messages');", + " pm.expect(jsonResponse.result.messages).to.not.be.empty;", + "});", + "", + "//Validation", + "// Response body", + "pm.test(\"responseCode should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"CLIENT_ERROR\");", + "});", + "", + "// Params", + "pm.test(\"params.err should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.params.err).to.eql(\"CLIENT_ERROR\");", + "});", + "pm.test(\"params.errmsg should be \\\"Validation Errors\\\"\", () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(\"Validation Errors\");", + "});", + "", + "// Result", + "pm.test(\"result body should include messages \\\"Required Metadata mimeType not set\\\"\", () => {", + " pm.expect(jsonResponse.result.messages).to.have.length(1)", + " pm.expect(jsonResponse.result.messages).to.include('Required Metadata mimeType not set');", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": { + "connection": true + } + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + }, + { + "name": "Content Create - 404 - missing primaryCategory", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 400\", () => {", + " pm.response.to.have.status(400)", + "});", + "//code name contains a string", + "tests[\"Status code name has string \\\"Bad Request\\\"\"] = responseCode.name.has(\"Bad Request\");", + "", + "pm.test(\"Verify keys and data type of the response result\", () => {", + " pm.expect(jsonResponse.result).to.be.an(\"object\");", + "});", + "", + "//Validation", + "// Response body", + "pm.test(\"responseCode should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"CLIENT_ERROR\");", + "});", + "", + "// Params", + "pm.test(\"params.err should be \\\"VALIDATION_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.params.err).to.eql(\"VALIDATION_ERROR\");", + "});", + "pm.test(\"params.errmsg should be \\\"primaryCategory is a mandatory parameter\\\"\", () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(\"primaryCategory is a mandatory parameter\");", + "});", + "", + "// Result", + "pm.test(\"result body should be empty\", () => {", + " pm.expect(jsonResponse.result).to.eql({});", + " pm.expect(jsonResponse.result).to.be.empty;", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": { + "connection": true + } + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + }, + { + "name": "Content Create - 404 - Invalid primaryCategory", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 404\", () => {", + " pm.response.to.have.status(404)", + "});", + "//code name contains a string", + "tests[\"Status code name has string \\\"Not Found\\\"\"] = responseCode.name.has(\"Not Found\");", + "", + "pm.test(\"Verify keys and data type of the response result\", () => {", + " pm.expect(jsonResponse.result).to.have.all.keys('messages');", + " pm.expect(jsonResponse.result.messages).to.be.null;", + "});", + "", + "//Validation", + "// Response body", + "pm.test(\"responseCode should be RESOURCE_NOT_FOUND\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"RESOURCE_NOT_FOUND\");", + "});", + "", + "// Params", + "pm.test(\"params.err should be \\\"404\\\"\", () => {", + " pm.expect(jsonResponse.params.err).to.eql(\"404\");", + "});", + "pm.test(`params.errmsg should be \\\"Entry is not found in cassandra for content with identifier: obj-cat:${JSON.parse(request.data).request.content.primaryCategory}_content_all {}\\\"`, () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(`Entry is not found in cassandra for content with identifier: obj-cat:${JSON.parse(request.data).request.content.primaryCategory}_content_all {}`);", + "});", + "", + "// Result", + "pm.test(\"messages should be null\", () => {", + " pm.expect(jsonResponse.result.messages).to.eql(null);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": { + "connection": true + } }, "request": { "method": "POST", @@ -635,7 +1192,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"createdBy\": \"530b19ea-dc8d-4cc7-a4b5-0c0214c8113a\",\n \"resourceType\": \"Learn\",\n \"framework\": \"ekstep_ncert_k-12\",\n \"primaryCategory\": \"aaaa\"\n }\n }\n}" + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"createdBy\": \"530b19ea-dc8d-4cc7-a4b5-0c0214c8113a\",\n \"resourceType\": \"Learn\",\n \"framework\": \"ekstep_ncert_k-12\",\n \"primaryCategory\": \"aabaa\"\n }\n }\n}" }, "url": { "raw": "{{host}}/content/v2/create", @@ -652,6 +1209,33 @@ }, "response": [] } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "let jsonResponse = pm.response.json();", + "", + "pm.test(\"id should be api.content.create\", () => {", + " pm.expect(jsonResponse.id).to.eql(\"api.content.create\");", + "});", + "pm.test(\"ver should be 4.0\", () => {", + " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", + "});" + ] + } + } ] }, { @@ -673,55 +1257,36 @@ "//code name contains a string", "tests[\"Status code name has string \\\"Not Found\\\"\"] = responseCode.name.has(\"Not Found\");", "", - "pm.test(\"Content-Type header is present\", () => {", - " pm.response.to.have.header(\"Content-Type\");", - "});", - "pm.test(\"Content-Type header is application/json\", () => {", - " pm.expect(pm.response.headers.get('Content-Type')).to.eql('application/json');", - "});", - "pm.test(\"Content-Encoding header should not present\", () => {", - " pm.response.to.have.header(\"Content-Encoding\");", - "});", - "pm.test(\"Content-Encoding header is gzip\", () => {", - " pm.expect(pm.response.headers.get('Content-Encoding')).to.eql('gzip');", - "});", - "", "pm.test(\"Test data type of the response and keys\", () => {", - " pm.expect(jsonResponse).to.be.an(\"object\").that.has.all.keys('id', 'ver', 'ts', 'params', 'responseCode', 'result');", - " pm.expect(jsonResponse.id).to.be.a(\"string\");", - " pm.expect(jsonResponse.ver).to.be.a(\"string\");", - " pm.expect(jsonResponse.ts).to.be.a(\"string\");", - " pm.expect(jsonResponse.responseCode).to.be.a(\"string\");", - " pm.expect(jsonResponse.params).to.be.an(\"object\").that.has.all.keys('resmsgid', 'msgid', 'err', 'status', 'errmsg');", - " pm.expect(jsonResponse.params.resmsgid).to.be.a(\"string\");", - " pm.expect(jsonResponse.params.msgid).to.be.null;", - " pm.expect(jsonResponse.params.err).to.be.a(\"string\");", - " pm.expect(jsonResponse.params.status).to.be.a(\"string\");", - " pm.expect(jsonResponse.params.errmsg).to.be.a(\"string\");", " pm.expect(jsonResponse.result).to.be.an(\"object\");", " pm.expect(jsonResponse.result).to.have.all.keys('messages');", " pm.expect(jsonResponse.result.messages).to.be.null;", "});", "", "//Validation", + "// Response body", "pm.test(\"id should be api.content.read\", () => {", " pm.expect(jsonResponse.id).to.eql(\"api.content.read\");", "});", "pm.test(\"ver should be 4.0\", () => {", " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", "});", + "pm.test(\"responseCode should be RESOURCE_NOT_FOUND\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"RESOURCE_NOT_FOUND\");", + "});", + "", + "// Params", "pm.test(\"params.err should be \\\"NOT_FOUND\\\"\", () => {", " pm.expect(jsonResponse.params.err).to.eql(\"NOT_FOUND\");", "});", "pm.test(\"params.status should be \\\"failed\\\"\", () => {", " pm.expect(jsonResponse.params.status).to.eql(\"failed\");", "});", - "pm.test(\"params.errmsg should be \\\"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: do_42353245436534\\\"\", () => {", - " pm.expect(jsonResponse.params.errmsg).to.eql(\"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: do_42353245436534\");", - "});", - "pm.test(\"responseCode should be RESOURCE_NOT_FOUND\", () => {", - " pm.expect(jsonResponse.responseCode).to.eql(\"RESOURCE_NOT_FOUND\");", + "pm.test(`params.errmsg should be \\\"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: ${pm.globals.get('invalid_content_id')}\\\"`, () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(`Error! Node(s) doesn't Exists. | [Invalid Node Id.]: ${pm.globals.get('invalid_content_id')}`);", "});", + "", + "// Result", "pm.test(\"messages should be null\", () => {", " pm.expect(jsonResponse.result.messages).to.eql(null);", "});", @@ -729,6 +1294,15 @@ ], "type": "text/javascript" } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "pm.globals.set('invalid_content_id', 'do_42353245436534');" + ], + "type": "text/javascript" + } } ], "request": { @@ -746,7 +1320,7 @@ } ], "url": { - "raw": "{{host}}/content/v2/read/do_42353245436534", + "raw": "{{host}}/content/v2/read/{{invalid_content_id}}", "host": [ "{{host}}" ], @@ -754,7 +1328,7 @@ "content", "v2", "read", - "do_42353245436534" + "{{invalid_content_id}}" ] }, "description": "Fetch a particular Batch\nThis API is associated with fetching a particular batch on the Sunbird Platform.\nThe endpoint for Fetch a particular Batch is /batch/read/{Batch_ID}\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." @@ -796,65 +1370,57 @@ "let jsonResponse = pm.response.json()", "", "//Verification", - "pm.test(\"Status code is 400\", function () {", + "pm.test(\"Status code is 400\", () => {", " pm.response.to.have.status(400)", "});", "//code name contains a string", "tests[\"Status code name has string Bad Request\"] = responseCode.name.has(\"Bad Request\");", "", - "pm.test(\"Content-Type header is present\", () => {", - " pm.response.to.have.header(\"Content-Type\");", - "});", - "pm.test(\"Content-Type header is application/json\", () => {", - " pm.expect(pm.response.headers.get('Content-Type')).to.eql('application/json');", - "});", "pm.test(\"Content-Encoding header should not present\", () => {", " pm.response.to.not.have.header(\"Content-Encoding\");", "});", "", - "pm.test(\"Test data type of the response and keys\", () => {", - " pm.expect(jsonResponse).to.be.an(\"object\").that.has.all.keys('id', 'ver', 'ts', 'params', 'responseCode', 'result');", - " pm.expect(jsonResponse.id).to.be.a(\"string\");", - " pm.expect(jsonResponse.ver).to.be.a(\"string\");", - " pm.expect(jsonResponse.ts).to.be.a(\"string\");", - " pm.expect(jsonResponse.responseCode).to.be.a(\"string\");", - " pm.expect(jsonResponse.params).to.be.an(\"object\").that.has.all.keys('resmsgid', 'msgid', 'err', 'status', 'errmsg');", - " pm.expect(jsonResponse.params.resmsgid).to.be.a(\"string\");", - " pm.expect(jsonResponse.params.msgid).to.be.null;", - " pm.expect(jsonResponse.params.err).to.be.a(\"string\");", - " pm.expect(jsonResponse.params.status).to.be.a(\"string\");", - " pm.expect(jsonResponse.params.errmsg).to.be.a(\"string\");", + "pm.test(\"Verify keys and data type of the response result\", () => {", " pm.expect(jsonResponse.result).to.be.an(\"object\");", " pm.expect(jsonResponse.result).to.have.all.keys('messages');", " pm.expect(jsonResponse.result.messages).to.be.null;", "});", "", "//Validation", - "pm.test(\"id should be api.content.update\", function () {", - " pm.expect(jsonResponse.id).to.eql(\"api.content.update\");", - "});", - "pm.test(\"ver should be 4.0\", function () {", - " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", + "// Response body", + "pm.test(\"responseCode should be CLIENT_ERROR\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"CLIENT_ERROR\");", "});", - "pm.test(\"params.err should be CLIENT_ERROR\", function () {", + "", + "// Params", + "pm.test(\"params.err should be CLIENT_ERROR\", () => {", " pm.expect(jsonResponse.params.err).to.eql(\"CLIENT_ERROR\");", "});", - "pm.test(\"params.status should be failed\", function () {", + "pm.test(\"params.status should be failed\", () => {", " pm.expect(jsonResponse.params.status).to.eql(\"failed\");", "});", - "pm.test(\"params.errmsg should be \\\"Invalid version Key\\\"\", function () {", + "pm.test(\"params.errmsg should be \\\"Invalid version Key\\\"\", () => {", " pm.expect(jsonResponse.params.errmsg).to.eql(\"Invalid version Key\");", "});", - "pm.test(\"responseCode should be CLIENT_ERROR\", function () {", - " pm.expect(jsonResponse.responseCode).to.eql(\"CLIENT_ERROR\");", - "});", - "pm.test(\"messages should be null\", function () {", + "", + "// Result", + "pm.test(\"messages should be null\", () => {", " pm.expect(jsonResponse.result.messages).to.eql(null);", "});", "" ], "type": "text/javascript" } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "pm.globals.set('versionKey', '3534534543');", + "// pm.globals.set(\"content_id\", '');" + ], + "type": "text/javascript" + } } ], "request": { @@ -883,10 +1449,10 @@ ], "body": { "mode": "raw", - "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"3423423\",\n \"lastUpdatedBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\",\n \"description\": \"dsd\",\n \"name\": \"API DOCUMENTATION CONTENT\"\n }\n }\n}" + "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"{{versionKey}}\",\n \"lastUpdatedBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\",\n \"description\": \"dsd\",\n \"name\": \"API DOCUMENTATION CONTENT\"\n }\n }\n}" }, "url": { - "raw": "{{host}}/content/v2/update/do_213579099726987264122", + "raw": "{{host}}/content/v2/update/{{content_id}}", "host": [ "{{host}}" ], @@ -894,7 +1460,7 @@ "content", "v2", "update", - "do_213579099726987264122" + "{{content_id}}" ] }, "description": "This API is associated with updating a batch on the Sunbird Platform.\nThe endpoint for Course Batch Update is /batch/update\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." @@ -911,68 +1477,49 @@ "let jsonResponse = pm.response.json()", "", "//Verification", - "pm.test(\"Status code is 404\", function () {", + "pm.test(\"Status code is 404\", () => {", " pm.response.to.have.status(404)", "});", "//code name contains a string", "tests[\"Status code name has string \\\"Not Found\\\"\"] = responseCode.name.has(\"Not Found\");", "", - "pm.test(\"Content-Type header is present\", () => {", - " pm.response.to.have.header(\"Content-Type\");", - "});", - "pm.test(\"Content-Type header is application/json\", () => {", - " pm.expect(pm.response.headers.get('Content-Type')).to.eql('application/json');", - "});", - "pm.test(\"Content-Encoding header should not present\", () => {", - " pm.response.to.have.header(\"Content-Encoding\");", - "});", - "pm.test(\"Content-Encoding header is gzip\", () => {", - " pm.expect(pm.response.headers.get('Content-Encoding')).to.eql('gzip');", - "});", - "", - "pm.test(\"Test data type of the response and keys\", () => {", - " pm.expect(jsonResponse).to.be.an(\"object\").that.has.all.keys('id', 'ver', 'ts', 'params', 'responseCode', 'result');", - " pm.expect(jsonResponse.id).to.be.a(\"string\");", - " pm.expect(jsonResponse.ver).to.be.a(\"string\");", - " pm.expect(jsonResponse.ts).to.be.a(\"string\");", - " pm.expect(jsonResponse.responseCode).to.be.a(\"string\");", - " pm.expect(jsonResponse.params).to.be.an(\"object\").that.has.all.keys('resmsgid', 'msgid', 'err', 'status', 'errmsg');", - " pm.expect(jsonResponse.params.resmsgid).to.be.a(\"string\");", - " pm.expect(jsonResponse.params.msgid).to.be.null;", - " pm.expect(jsonResponse.params.err).to.be.a(\"string\");", - " pm.expect(jsonResponse.params.status).to.be.a(\"string\");", - " pm.expect(jsonResponse.params.errmsg).to.be.a(\"string\");", + "pm.test(\"Verify keys and data type of the response result\", () => {", " pm.expect(jsonResponse.result).to.be.an(\"object\");", " pm.expect(jsonResponse.result).to.have.all.keys('messages');", " pm.expect(jsonResponse.result.messages).to.be.null;", "});", "", "//Validation", - "pm.test(\"id should be api.content.update\", function () {", - " pm.expect(jsonResponse.id).to.eql(\"api.content.update\");", - "});", - "pm.test(\"ver should be 4.0\", function () {", - " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", + "// Response body", + "pm.test(\"responseCode should be RESOURCE_NOT_FOUND\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"RESOURCE_NOT_FOUND\");", "});", - "pm.test(\"params.err should be NOT_FOUND\", function () {", + "", + "// Params", + "pm.test(\"params.err should be NOT_FOUND\", () => {", " pm.expect(jsonResponse.params.err).to.eql(\"NOT_FOUND\");", "});", - "pm.test(\"params.status should be failed\", function () {", - " pm.expect(jsonResponse.params.status).to.eql(\"failed\");", - "});", - "pm.test(\"params.errmsg should be \\\"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: do_21357909972698726412211\\\"\", function () {", - " pm.expect(jsonResponse.params.errmsg).to.eql(\"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: do_21357909972698726412211\");", - "});", - "pm.test(\"responseCode should be RESOURCE_NOT_FOUND\", function () {", - " pm.expect(jsonResponse.responseCode).to.eql(\"RESOURCE_NOT_FOUND\");", + "pm.test(`params.errmsg should be \\\"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: ${pm.globals.get(\"content_id\")}\\\"`, () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(`Error! Node(s) doesn't Exists. | [Invalid Node Id.]: ${pm.globals.get(\"content_id\")}`);", "});", - "pm.test(\"messages should be null\", function () {", + "", + "// Result", + "pm.test(\"messages should be null\", () => {", " pm.expect(jsonResponse.result.messages).to.eql(null);", "});", "" ], "type": "text/javascript" } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "pm.globals.set(\"content_id\", 'do_21357909972698726412211');" + ], + "type": "text/javascript" + } } ], "request": { @@ -1004,7 +1551,7 @@ "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"{{versionKey}}\",\n \"lastUpdatedBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\",\n \"description\": \"dsd\",\n \"name\": \"API DOCUMENTATION CONTENT\"\n }\n }\n}" }, "url": { - "raw": "{{host}}/content/v2/update/do_21357909972698726412211", + "raw": "{{host}}/content/v2/update/{{content_id}}", "host": [ "{{host}}" ], @@ -1012,7 +1559,7 @@ "content", "v2", "update", - "do_21357909972698726412211" + "{{content_id}}" ] }, "description": "This API is associated with updating a batch on the Sunbird Platform.\nThe endpoint for Course Batch Update is /batch/update\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." @@ -1029,61 +1576,35 @@ "let jsonResponse = pm.response.json()", "", "//Verification", - "pm.test(\"Status code is 404\", function () {", + "pm.test(\"Status code is 404\", () => {", " pm.response.to.have.status(404)", "});", "//code name contains a string", "tests[\"Status code name has string \\\"Not Found\\\"\"] = responseCode.name.has(\"Not Found\");", "", - "pm.test(\"Content-Type header is present\", () => {", - " pm.response.to.have.header(\"Content-Type\");", - "});", - "pm.test(\"Content-Type header is application/json\", () => {", - " pm.expect(pm.response.headers.get('Content-Type')).to.eql('application/json');", - "});", - "pm.test(\"Content-Encoding header should not present\", () => {", - " pm.response.to.have.header(\"Content-Encoding\");", - "});", - "pm.test(\"Content-Encoding header is gzip\", () => {", - " pm.expect(pm.response.headers.get('Content-Encoding')).to.eql('gzip');", - "});", - "", - "pm.test(\"Test data type of the response and keys\", () => {", - " pm.expect(jsonResponse).to.be.an(\"object\").that.has.all.keys('id', 'ver', 'ts', 'params', 'responseCode', 'result');", - " pm.expect(jsonResponse.id).to.be.a(\"string\");", - " pm.expect(jsonResponse.ver).to.be.a(\"string\");", - " pm.expect(jsonResponse.ts).to.be.a(\"string\");", - " pm.expect(jsonResponse.responseCode).to.be.a(\"string\");", - " pm.expect(jsonResponse.params).to.be.an(\"object\").that.has.all.keys('resmsgid', 'msgid', 'err', 'status', 'errmsg');", - " pm.expect(jsonResponse.params.resmsgid).to.be.a(\"string\");", - " pm.expect(jsonResponse.params.msgid).to.be.null;", - " pm.expect(jsonResponse.params.err).to.be.a(\"string\");", - " pm.expect(jsonResponse.params.status).to.be.a(\"string\");", - " pm.expect(jsonResponse.params.errmsg).to.be.a(\"string\");", + "pm.test(\"Verify keys and data type of the response result\", () => {", " pm.expect(jsonResponse.result).to.be.an(\"object\");", " pm.expect(jsonResponse.result).to.have.all.keys('messages');", " pm.expect(jsonResponse.result.messages).to.be.null;", "});", "", "//Validation", - "pm.test(\"id should be api.content.update\", () => {", - " pm.expect(jsonResponse.id).to.eql(\"api.content.update\");", - "});", - "pm.test(\"ver should be 4.0\", () => {", - " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", + "// Response body", + "pm.test(\"responseCode should be RESOURCE_NOT_FOUND\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"RESOURCE_NOT_FOUND\");", "});", + "", + "// Params", "pm.test(\"params.err should be \\\"404\\\"\", () => {", " pm.expect(jsonResponse.params.err).to.eql(\"404\");", "});", "pm.test(\"params.status should be \\\"failed\\\"\", () => {", " pm.expect(jsonResponse.params.status).to.eql(\"failed\");", "});", - "pm.test(\"params.errmsg should be \\\"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\\\"\", () => {", - " pm.expect(jsonResponse.params.errmsg).to.eql(\"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\");", - "});", - "pm.test(\"responseCode should be RESOURCE_NOT_FOUND\", () => {", - " pm.expect(jsonResponse.responseCode).to.eql(\"RESOURCE_NOT_FOUND\");", + "pm.test(`params.errmsg should be \\\"Entry is not found in cassandra for content with identifier: obj-cat:${JSON.parse(request.data).request.content.primaryCategory}_content_all {}\\\"`, () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(`Entry is not found in cassandra for content with identifier: obj-cat:${JSON.parse(request.data).request.content.primaryCategory}_content_all {}`);", "});", + "", "pm.test(\"messages should be null\", () => {", " pm.expect(jsonResponse.result.messages).to.eql(null);", "});", @@ -1136,6 +1657,97 @@ "description": "This API is associated with updating a batch on the Sunbird Platform.\nThe endpoint for Course Batch Update is /batch/update\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." }, "response": [] + }, + { + "name": "Content Update - 404 - Empty name", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 400\", () => {", + " pm.response.to.have.status(400)", + "});", + "//code name contains a string", + "tests[\"Status code name has string \\\"Bad Request\\\"\"] = responseCode.name.has(\"Bad Request\");", + "", + "pm.test(\"Verify keys and data type of the response result\", () => {", + " pm.expect(jsonResponse.result).to.be.an(\"object\");", + " pm.expect(jsonResponse.result).to.have.all.keys('messages');", + " pm.expect(jsonResponse.result.messages).to.not.be.empty;", + "});", + "", + "//Validation", + "// Response body", + "pm.test(\"responseCode should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"CLIENT_ERROR\");", + "});", + "", + "// Params", + "pm.test(\"params.err should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.params.err).to.eql(\"CLIENT_ERROR\");", + "});", + "pm.test(\"params.errmsg should be \\\"Validation Errors\\\"\", () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(\"Validation Errors\");", + "});", + "", + "// Result", + "pm.test(\"result body should include messages \\\"Required Metadata name not set\\\"\", () => {", + " pm.expect(jsonResponse.result.messages).to.have.length(1)", + " pm.expect(jsonResponse.result.messages).to.include('Required Metadata name not set');", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "type": "text" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"{{versionKey}}\",\n \"description\": \"dsd\",\n \"name\": \"\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v2/update/{{content_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v2", + "update", + "{{content_id}}" + ] + }, + "description": "This API is associated with updating a batch on the Sunbird Platform.\nThe endpoint for Course Batch Update is /batch/update\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] } ], "event": [ @@ -1144,7 +1756,7 @@ "script": { "type": "text/javascript", "exec": [ - "" + "// pm.globals.set(\"content_id\", '');" ] } }, @@ -1153,13 +1765,94 @@ "script": { "type": "text/javascript", "exec": [ - "" + "let jsonResponse = pm.response.json();", + "", + "pm.test(\"id should be api.content.update\", () => {", + " pm.expect(jsonResponse.id).to.eql(\"api.content.update\");", + "});", + "pm.test(\"ver should be 4.0\", () => {", + " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", + "});" ] } } ] } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "let jsonResponse = pm.response.json();", + "", + "//Verification", + "// pm.test(\"Content-Encoding header should not present\", () => {", + "// pm.response.to.not.have.header(\"Content-Encoding\");", + "// });", + "", + "pm.test(\"Verify keys and data type of the params in negative scenario\", () => {", + " pm.expect(jsonResponse.params.resmsgid).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.msgid).to.be.null;", + " pm.expect(jsonResponse.params.err).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.status).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.errmsg).to.be.a(\"string\");", + "});", + "", + "pm.test(\"params.status should be \\\"failed\\\"\", () => {", + " pm.expect(jsonResponse.params.status).to.eql(\"failed\");", + "});", + "" + ] + } + } ] } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "pm.test(\"Content-Type header is present\", () => {", + " pm.response.to.have.header(\"Content-Type\");", + "});", + "pm.test(\"Content-Type header is application/json\", () => {", + " pm.expect(pm.response.headers.get('Content-Type')).to.eql('application/json');", + "});", + "", + "pm.test(\"Validate keys and data type of the response body\", () => {", + " let jsonResponse = pm.response.json();", + " pm.expect(jsonResponse).to.be.an(\"object\").that.has.all.keys('id', 'ver', 'ts', 'params', 'responseCode', 'result');", + " pm.expect(jsonResponse.id).to.be.a(\"string\");", + " pm.expect(jsonResponse.ver).to.be.a(\"string\");", + " pm.expect(jsonResponse.ts).to.be.a(\"string\");", + " pm.expect(jsonResponse.params).to.be.an(\"object\").that.has.all.keys('resmsgid', 'msgid', 'err', 'status', 'errmsg');", + " pm.expect(jsonResponse.responseCode).to.be.a(\"string\");", + " pm.expect(jsonResponse.result).to.be.an(\"object\");", + "});" + ] + } + } ] } \ No newline at end of file diff --git a/content-api/api-test/Content V2.postman_collection.json b/content-api/api-test/Content V2.postman_collection.json index b11d5a029..d116d704d 100644 --- a/content-api/api-test/Content V2.postman_collection.json +++ b/content-api/api-test/Content V2.postman_collection.json @@ -1,6 +1,6 @@ { "info": { - "_postman_id": "c1421998-1fcb-44c7-9db1-9421f27b0a98", + "_postman_id": "95bee03d-d782-47da-89f9-cc70bb0a267e", "name": "Content V2", "description": "The Content Management APIs allow you to create, manage and process content on the Sunbird platform. Content is the basic building block of the Knowledge Service. Some examples would be Videos, Audio, Pdfs, Html, ECML etc. The basic operations for this API include Create, Update, Read, Review, Publish.\n\nThe URL for Content Management API(s) is /content/v1.", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", @@ -8,105 +8,143 @@ }, "item": [ { - "name": "Content Create ", - "item": [ + "name": "Content Create", + "event": [ { - "name": "Content Create", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "let jsonResponse = pm.response.json()", - "", - "//Verification", - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.have.status(200)", - "});", - "pm.test(\"Content-Type header is present\", () => {", - " pm.response.to.have.header(\"Content-Type\");", - "});", - "pm.test(\"Content-Type header is application/json\", () => {", - " pm.expect(pm.response.headers.get('Content-Type')).to.eql('application/json');", - "});", - "pm.test(\"Content-Encoding header is present\", () => {", - " pm.response.to.have.header(\"Content-Encoding\");", - "});", - "pm.test(\"Content-Encoding header is gzip\", () => {", - " pm.expect(pm.response.headers.get('Content-Encoding')).to.eql('gzip');", - "});", - "", - "pm.test(\"Test data type of the response and keys\", () => {", - " pm.expect(jsonResponse).to.be.an(\"object\").that.has.all.keys('id', 'ver', 'ts', 'params', 'responseCode', 'result');", - " pm.expect(jsonResponse.id).to.be.a(\"string\");", - " pm.expect(jsonResponse.ver).to.be.a(\"string\");", - " pm.expect(jsonResponse.ts).to.be.a(\"string\");", - " pm.expect(jsonResponse.responseCode).to.be.a(\"string\");", - " pm.expect(jsonResponse.params).to.be.an(\"object\").that.has.all.keys('resmsgid', 'msgid', 'err', 'status', 'errmsg');", - " pm.expect(jsonResponse.params.resmsgid).to.be.a(\"string\");", - " pm.expect(jsonResponse.params.msgid).to.be.null;", - " pm.expect(jsonResponse.params.err).to.be.null;", - " pm.expect(jsonResponse.params.status).to.be.a(\"string\");", - " pm.expect(jsonResponse.params.errmsg).to.be.null;", - " pm.expect(jsonResponse.result).to.be.an(\"object\");", - " pm.expect(jsonResponse.result).to.have.all.keys('identifier', 'node_id', 'versionKey');", - " pm.expect(jsonResponse.result.identifier).to.be.a(\"string\");", - " pm.expect(jsonResponse.result.node_id).to.be.a(\"string\");", - " pm.expect(jsonResponse.result.versionKey).to.be.a(\"string\");", - "});", - "", - "//Validation", - "pm.test(\"id should be api.content.create\", function () {", - " pm.expect(jsonResponse.id).to.eql(\"api.content.create\");", - "});", - "pm.test(\"ver should be 4.0\", function () {", - " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", - "});", - "pm.test(\"params.status should be successful\", function () {", - " pm.expect(jsonResponse.params.status).to.eql(\"successful\");", - "});", - "pm.test(\"responseCode should be OK\", function () {", - " pm.expect(jsonResponse.responseCode).to.eql(\"OK\");", - "});", - "", - "// Set global variable", - "pm.globals.set(\"content_id\", jsonResponse.result.identifier);", - "pm.globals.set(\"versionKey\", jsonResponse.result.versionKey);", - "" - ], - "type": "text/javascript" - } - } - ], - "protocolProfileBehavior": { - "disabledSystemHeaders": { - "connection": true - } + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "pm.test(\"Content-Type header is present\", () => {", + " pm.response.to.have.header(\"Content-Type\");", + "});", + "pm.test(\"Content-Type header is application/json\", () => {", + " pm.expect(pm.response.headers.get('Content-Type')).to.eql('application/json');", + "});", + "", + "pm.test(\"Test data type of the response and keys\", () => {", + " pm.expect(jsonResponse).to.be.an(\"object\").that.has.all.keys('id', 'ver', 'ts', 'params', 'responseCode', 'result');", + " pm.expect(jsonResponse.id).to.be.a(\"string\");", + " pm.expect(jsonResponse.ver).to.be.a(\"string\");", + " pm.expect(jsonResponse.ts).to.be.a(\"string\");", + " pm.expect(jsonResponse.responseCode).to.be.a(\"string\");", + " pm.expect(jsonResponse.params).to.be.an(\"object\").that.has.all.keys('resmsgid', 'msgid', 'err', 'status', 'errmsg');", + " pm.expect(jsonResponse.params.resmsgid).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.msgid).to.be.null;", + " pm.expect(jsonResponse.params.err).to.be.null;", + " pm.expect(jsonResponse.params.status).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.errmsg).to.be.null;", + " pm.expect(jsonResponse.result).to.be.an(\"object\");", + " pm.expect(jsonResponse.result).to.have.all.keys('identifier', 'node_id', 'versionKey');", + " pm.expect(jsonResponse.result.identifier).to.be.a(\"string\");", + " pm.expect(jsonResponse.result.node_id).to.be.a(\"string\");", + " pm.expect(jsonResponse.result.versionKey).to.be.a(\"string\");", + "});", + "", + "//Validation", + "pm.test(\"id should be api.content.create\", function () {", + " pm.expect(jsonResponse.id).to.eql(\"api.content.create\");", + "});", + "pm.test(\"ver should be 4.0\", function () {", + " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", + "});", + "pm.test(\"params.status should be successful\", function () {", + " pm.expect(jsonResponse.params.status).to.eql(\"successful\");", + "});", + "pm.test(\"responseCode should be OK\", function () {", + " pm.expect(jsonResponse.responseCode).to.eql(\"OK\");", + "});", + "", + "// Set global variable", + "pm.globals.set(\"content_id\", jsonResponse.result.identifier);", + "pm.globals.set(\"versionKey\", jsonResponse.result.versionKey);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" }, - "request": { + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"createdBy\": \"530b19ea-dc8d-4cc7-a4b5-0c0214c8113a\",\n \"resourceType\": \"Learn\",\n \"framework\": \"ekstep_ncert_k-12\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { "method": "POST", "header": [ + { + "key": "userId", + "value": "user", + "type": "text" + }, { "key": "Content-Type", - "value": "application/json" + "value": "application/json", + "type": "text" }, { "key": "X-Authenticated-User-token", - "value": "{{keycloak_access_token}}" + "value": "{{keycloak_access_token}}", + "type": "text" }, { "key": "Authorization", - "value": "{{kong_api_key}}" + "value": "{{kong_api_key}}", + "type": "text" }, { - "key": "X-Channel-Id", - "value": "{{channel_id}}", + "key": "Cookie", + "value": "connect.sid=s%3AyRLPeHt6zZ93WjZ2BD_Cejc11xNN-isK.ANbL%2FZ1t88yXax%2BiXjGFVtgFUAKhoWdl%2FgXU4yBX9rU; 81f30cf708470b974874c8a96a6bcdb2=l99ob678op07nq0qs5i0dae0m3; AWSELB=83D53DFF08C363B9195F6717118E44E326DE55CB796FB96A69500BA54C477B7512D6342B64B0B494BBD6699FCFFFB308C0DC9247AA23DA73B4F62BA4C399A735F647C86F50", "type": "text" } ], "body": { "mode": "raw", - "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"createdBy\": \"530b19ea-dc8d-4cc7-a4b5-0c0214c8113a\",\n \"resourceType\": \"Learn\",\n \"framework\": \"ekstep_ncert_k-12\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"API DOCUMENTATION CONTENT\",\n \"mimeType\": \"application/pdf\",\n \"code\": \"api-docs\",\n \"primaryCategory\": \"Explanation Content\",\n \"createdBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\"\n }\n }\n}" }, "url": { "raw": "{{host}}/content/v2/create", @@ -118,234 +156,204 @@ "v2", "create" ] - }, - "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + } }, - "response": [ - { - "name": "Content Create - 200 OK", - "originalRequest": { - "method": "POST", - "header": [ - { - "key": "userId", - "value": "user", - "type": "text" - }, - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - }, - { - "key": "X-Authenticated-User-token", - "value": "{{keycloak_access_token}}", - "type": "text" - }, - { - "key": "Authorization", - "value": "{{kong_api_key}}", - "type": "text" - }, - { - "key": "Cookie", - "value": "connect.sid=s%3AyRLPeHt6zZ93WjZ2BD_Cejc11xNN-isK.ANbL%2FZ1t88yXax%2BiXjGFVtgFUAKhoWdl%2FgXU4yBX9rU; 81f30cf708470b974874c8a96a6bcdb2=l99ob678op07nq0qs5i0dae0m3; AWSELB=83D53DFF08C363B9195F6717118E44E326DE55CB796FB96A69500BA54C477B7512D6342B64B0B494BBD6699FCFFFB308C0DC9247AA23DA73B4F62BA4C399A735F647C86F50", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"API DOCUMENTATION CONTENT\",\n \"mimeType\": \"application/pdf\",\n \"code\": \"api-docs\",\n \"primaryCategory\": \"Explanation Content\",\n \"createdBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\"\n }\n }\n}" - }, - "url": { - "raw": "{{host}}/content/v2/create", - "host": [ - "{{host}}" - ], - "path": [ - "content", - "v2", - "create" - ] - } - }, - "status": "OK", - "code": 200, - "_postman_previewlanguage": "json", - "header": [ - { - "key": "Date", - "value": "Mon, 23 Nov 2020 06:42:39 GMT" - }, - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "Transfer-Encoding", - "value": "chunked" - }, - { - "key": "Connection", - "value": "keep-alive" - }, - { - "key": "Vary", - "value": "Accept-Encoding" - }, - { - "key": "X-RateLimit-Limit-hour", - "value": "30000" - }, - { - "key": "X-RateLimit-Remaining-hour", - "value": "29991" - }, - { - "key": "Referrer-Policy", - "value": "origin-when-cross-origin, strict-origin-when-cross-origin" - }, - { - "key": "X-Frame-Options", - "value": "DENY" - }, - { - "key": "X-XSS-Protection", - "value": "1; mode=block" - }, - { - "key": "x-response-length", - "value": "55" - }, - { - "key": "X-Content-Type-Options", - "value": "nosniff" - }, - { - "key": "Access-Control-Allow-Origin", - "value": "*" - }, - { - "key": "X-Permitted-Cross-Domain-Policies", - "value": "master-only" - }, - { - "key": "X-Kong-Upstream-Latency", - "value": "53" - }, - { - "key": "X-Kong-Proxy-Latency", - "value": "36" - }, - { - "key": "Via", - "value": "kong/0.14.1" - }, - { - "key": "Content-Encoding", - "value": "gzip" - } - ], - "cookie": [], - "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-09T11:11:12.697Z\",\n \"params\": {\n \"resmsgid\": \"3f5de290-3a0f-11eb-be5b-f9d249ba9049\",\n \"msgid\": \"3f57a100-3a0f-11eb-be5b-f9d249ba9049\",\n \"status\": \"successful\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"content_id\": \"do_1131687405374504961738\",\n \"versionKey\": \"1607512272640\"\n }\n}" + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Mon, 23 Nov 2020 06:42:39 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "30000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "29991" + }, + { + "key": "Referrer-Policy", + "value": "origin-when-cross-origin, strict-origin-when-cross-origin" + }, + { + "key": "X-Frame-Options", + "value": "DENY" + }, + { + "key": "X-XSS-Protection", + "value": "1; mode=block" + }, + { + "key": "x-response-length", + "value": "55" + }, + { + "key": "X-Content-Type-Options", + "value": "nosniff" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Permitted-Cross-Domain-Policies", + "value": "master-only" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "53" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "36" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" } - ] + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-09T11:11:12.697Z\",\n \"params\": {\n \"resmsgid\": \"3f5de290-3a0f-11eb-be5b-f9d249ba9049\",\n \"msgid\": \"3f57a100-3a0f-11eb-be5b-f9d249ba9049\",\n \"status\": \"successful\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"content_id\": \"do_1131687405374504961738\",\n \"versionKey\": \"1607512272640\"\n }\n}" } ] }, { "name": "Content Read", - "item": [ + "event": [ { - "name": "Content Read", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "let jsonResponse = pm.response.json()", - "", - "//Verification", - "pm.test(\"Status code is 200\", () => {", - " pm.response.to.have.status(200)", - "});", - "", - "//code name contains a string", - "tests[\"Status code name has string OK\"] = responseCode.name.has(\"OK\");", - "", - "pm.test(\"Content-Type header is present\", () => {", - " pm.response.to.have.header(\"Content-Type\");", - "});", - "pm.test(\"Content-Type header is application/json\", () => {", - " pm.expect(pm.response.headers.get('Content-Type')).to.eql('application/json');", - "});", - "pm.test(\"Content-Encoding header is present\", () => {", - " pm.response.to.have.header(\"Content-Encoding\");", - "});", - "pm.test(\"Content-Encoding header is gzip\", () => {", - " pm.expect(pm.response.headers.get('Content-Encoding')).to.eql('gzip');", - "});", - "", - "pm.test(\"Test data type of the response and keys\", () => {", - " pm.expect(jsonResponse).to.be.an(\"object\").that.has.all.keys('id', 'ver', 'ts', 'params', 'responseCode', 'result');", - " pm.expect(jsonResponse.id).to.be.a(\"string\");", - " pm.expect(jsonResponse.ver).to.be.a(\"string\");", - " pm.expect(jsonResponse.ts).to.be.a(\"string\");", - " pm.expect(jsonResponse.responseCode).to.be.a(\"string\");", - " pm.expect(jsonResponse.params).to.be.an(\"object\").that.has.all.keys('resmsgid', 'msgid', 'err', 'status', 'errmsg');", - " pm.expect(jsonResponse.params.resmsgid).to.be.a(\"string\");", - " pm.expect(jsonResponse.params.msgid).to.be.null;", - " pm.expect(jsonResponse.params.err).to.be.null;", - " pm.expect(jsonResponse.params.status).to.be.a(\"string\");", - " pm.expect(jsonResponse.params.errmsg).to.be.null;", - " pm.expect(jsonResponse.result).to.be.an(\"object\").that.has.all.keys('content');", - " pm.expect(jsonResponse.result.content).to.be.an(\"object\")", - " pm.expect(jsonResponse.result.content).to.have.any.keys('identifier', 'versionKey', 'code', 'channel', 'mimeType', 'createdOn', 'primaryCategory', 'contentDisposition', 'contentEncoding', 'objectType', 'visibility', 'discussionForum', 'license', 'version', 'compatibilityLevel', 'name', 'status');", - " pm.expect(jsonResponse.result.content.identifier).to.be.a(\"string\");", - " pm.expect(jsonResponse.result.content.versionKey).to.be.a(\"string\");", - " pm.expect(jsonResponse.result.content.version).to.be.a(\"number\");", - " pm.expect(jsonResponse.result.content.channel).to.be.a(\"string\");", - " pm.expect(jsonResponse.result.content.compatibilityLevel).to.be.a(\"number\");", - " pm.expect(jsonResponse.result.content.ownershipType).to.be.an(\"array\");", - " pm.expect(jsonResponse.result.content.trackable).to.be.an(\"object\");", - "});", - "", - "//Validation", - "pm.test(\"id should be api.content.read\", function () {", - " pm.expect(jsonResponse.id).to.eql(\"api.content.read\");", - "});", - "pm.test(\"ver should be 4.0\", () => {", - " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", - "});", - "pm.test(\"params.status should be successful\", () => {", - " pm.expect(jsonResponse.params.status).to.eql(\"successful\");", - "});", - "pm.test(\"responseCode should be OK\", () => {", - " pm.expect(jsonResponse.responseCode).to.eql(\"OK\");", - "});", - "pm.test(\"identifier should be equal to \" + pm.globals.get(\"content_id\"), () => {", - " pm.expect(jsonResponse.result.content.identifier).to.eql(pm.globals.get(\"content_id\"));", - "});", - "pm.test(\"objectType should be Content\", () => {", - " pm.expect(jsonResponse.result.content.objectType).to.eql(\"Content\");", - "});", - "pm.test(\"visibility should be Default\", () => {", - " pm.expect(jsonResponse.result.content.visibility).to.eql(\"Default\");", - "});", - "pm.test(\"compatibilityLevel should be 1\", () => {", - " pm.expect(jsonResponse.result.content.compatibilityLevel).to.eql(1);", - "});", - "pm.test(\"status should be Draft\", () => {", - " pm.expect(jsonResponse.result.content.status).to.eql(\"Draft\");", - "});", - "", - "pm.globals.set(\"versionKey\", jsonResponse.result.content.versionKey);" - ], - "type": "text/javascript" - } - } + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", () => {", + " pm.response.to.have.status(200)", + "});", + "", + "//code name contains a string", + "tests[\"Status code name has string OK\"] = responseCode.name.has(\"OK\");", + "", + "pm.test(\"Content-Type header is present\", () => {", + " pm.response.to.have.header(\"Content-Type\");", + "});", + "pm.test(\"Content-Type header is application/json\", () => {", + " pm.expect(pm.response.headers.get('Content-Type')).to.eql('application/json');", + "});", + "", + "pm.test(\"Test data type of the response and keys\", () => {", + " pm.expect(jsonResponse).to.be.an(\"object\").that.has.all.keys('id', 'ver', 'ts', 'params', 'responseCode', 'result');", + " pm.expect(jsonResponse.id).to.be.a(\"string\");", + " pm.expect(jsonResponse.ver).to.be.a(\"string\");", + " pm.expect(jsonResponse.ts).to.be.a(\"string\");", + " pm.expect(jsonResponse.responseCode).to.be.a(\"string\");", + " pm.expect(jsonResponse.params).to.be.an(\"object\").that.has.all.keys('resmsgid', 'msgid', 'err', 'status', 'errmsg');", + " pm.expect(jsonResponse.params.resmsgid).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.msgid).to.be.null;", + " pm.expect(jsonResponse.params.err).to.be.null;", + " pm.expect(jsonResponse.params.status).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.errmsg).to.be.null;", + " pm.expect(jsonResponse.result).to.be.an(\"object\").that.has.all.keys('content');", + " pm.expect(jsonResponse.result.content).to.be.an(\"object\")", + " pm.expect(jsonResponse.result.content).to.have.any.keys('identifier', 'versionKey', 'code', 'channel', 'mimeType', 'createdOn', 'primaryCategory', 'contentDisposition', 'contentEncoding', 'objectType', 'visibility', 'discussionForum', 'license', 'version', 'compatibilityLevel', 'name', 'status');", + " pm.expect(jsonResponse.result.content.identifier).to.be.a(\"string\");", + " pm.expect(jsonResponse.result.content.versionKey).to.be.a(\"string\");", + " pm.expect(jsonResponse.result.content.version).to.be.a(\"number\");", + " pm.expect(jsonResponse.result.content.channel).to.be.a(\"string\");", + " pm.expect(jsonResponse.result.content.compatibilityLevel).to.be.a(\"number\");", + " pm.expect(jsonResponse.result.content.ownershipType).to.be.an(\"array\");", + "});", + "", + "//Validation", + "pm.test(\"id should be api.content.read\", function () {", + " pm.expect(jsonResponse.id).to.eql(\"api.content.read\");", + "});", + "pm.test(\"ver should be 4.0\", () => {", + " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", + "});", + "pm.test(\"params.status should be successful\", () => {", + " pm.expect(jsonResponse.params.status).to.eql(\"successful\");", + "});", + "pm.test(\"responseCode should be OK\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"OK\");", + "});", + "pm.test(\"identifier should be equal to \" + pm.globals.get(\"content_id\"), () => {", + " pm.expect(jsonResponse.result.content.identifier).to.eql(pm.globals.get(\"content_id\"));", + "});", + "pm.test(\"objectType should be Content\", () => {", + " pm.expect(jsonResponse.result.content.objectType).to.eql(\"Content\");", + "});", + "pm.test(\"visibility should be Default\", () => {", + " pm.expect(jsonResponse.result.content.visibility).to.eql(\"Default\");", + "});", + "pm.test(\"compatibilityLevel should be 1\", () => {", + " pm.expect(jsonResponse.result.content.compatibilityLevel).to.eql(1);", + "});", + "pm.test(\"status should be Draft\", () => {", + " pm.expect(jsonResponse.result.content.status).to.eql(\"Draft\");", + "});", + "", + "pm.globals.set(\"versionKey\", jsonResponse.result.content.versionKey);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "type": "text" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + } + ], + "url": { + "raw": "{{host}}/content/v2/read/{{content_id}}", + "host": [ + "{{host}}" ], - "request": { + "path": [ + "content", + "v2", + "read", + "{{content_id}}" + ] + }, + "description": "Fetch a particular Batch\nThis API is associated with fetching a particular batch on the Sunbird Platform.\nThe endpoint for Fetch a particular Batch is /batch/read/{Batch_ID}\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [ + { + "name": "Content Read - 200 OK", + "originalRequest": { "method": "GET", "header": [ { @@ -370,292 +378,208 @@ "read", "{{content_id}}" ] - }, - "description": "Fetch a particular Batch\nThis API is associated with fetching a particular batch on the Sunbird Platform.\nThe endpoint for Fetch a particular Batch is /batch/read/{Batch_ID}\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + } }, - "response": [ - { - "name": "Content Read - 200 OK", - "originalRequest": { - "method": "GET", - "header": [ - { - "key": "X-Authenticated-User-token", - "value": "{{keycloak_access_token}}", - "type": "text" - }, - { - "key": "Authorization", - "value": "{{kong_api_key}}", - "type": "text" - } - ], - "url": { - "raw": "{{host}}/content/v2/read/{{content_id}}", - "host": [ - "{{host}}" - ], - "path": [ - "content", - "v2", - "read", - "{{content_id}}" - ] - } - }, - "status": "OK", - "code": 200, - "_postman_previewlanguage": "json", - "header": [ - { - "key": "Date", - "value": "Mon, 23 Nov 2020 04:48:35 GMT" - }, - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "Transfer-Encoding", - "value": "chunked" - }, - { - "key": "Connection", - "value": "keep-alive" - }, - { - "key": "Vary", - "value": "Accept-Encoding" - }, - { - "key": "X-RateLimit-Limit-hour", - "value": "30000" - }, - { - "key": "X-RateLimit-Remaining-hour", - "value": "29999" - }, - { - "key": "Referrer-Policy", - "value": "origin-when-cross-origin, strict-origin-when-cross-origin" - }, - { - "key": "X-Frame-Options", - "value": "DENY" - }, - { - "key": "X-XSS-Protection", - "value": "1; mode=block" - }, - { - "key": "x-response-length", - "value": "2759" - }, - { - "key": "X-Content-Type-Options", - "value": "nosniff" - }, - { - "key": "Access-Control-Allow-Origin", - "value": "*" - }, - { - "key": "X-Permitted-Cross-Domain-Policies", - "value": "master-only" - }, - { - "key": "X-Kong-Upstream-Latency", - "value": "11" - }, - { - "key": "X-Kong-Proxy-Latency", - "value": "35" - }, - { - "key": "Via", - "value": "kong/0.14.1" - }, - { - "key": "X-Proxy-Cache", - "value": "MISS" - }, - { - "key": "X-Proxy-Cache-Date", - "value": "Mon, 23 Nov 2020 04:48:35 GMT" - }, - { - "key": "Content-Encoding", - "value": "gzip" - } - ], - "cookie": [], - "body": "{\n \"id\": \"api.content.read\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-09T12:09:04.202Z\",\n \"params\": {\n \"resmsgid\": \"548b82a0-3a17-11eb-be5b-f9d249ba9049\",\n \"msgid\": \"5488ea90-3a17-11eb-be5b-f9d249ba9049\",\n \"status\": \"successful\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"content\": {\n \"ownershipType\": [\n \"createdBy\"\n ],\n \"code\": \"org.sunbird.ccG6ru\",\n \"credentials\": {\n \"enabled\": \"No\"\n },\n \"channel\": \"in.ekstep\",\n \"language\": [\n \"English\"\n ],\n \"mimeType\": \"application/pdf\",\n \"idealScreenSize\": \"normal\",\n \"createdOn\": \"2020-12-09T12:08:54.913+0000\",\n \"objectType\": \"Content\",\n \"primaryCategory\": \"Explanation Content\",\n \"contentDisposition\": \"inline\",\n \"lastUpdatedOn\": \"2020-12-09T12:08:54.913+0000\",\n \"contentEncoding\": \"identity\",\n \"contentType\": \"Resource\",\n \"dialcodeRequired\": \"No\",\n \"identifier\": \"do_1131687689003827201864\",\n \"lastStatusChangedOn\": \"2020-12-09T12:08:54.913+0000\",\n \"audience\": [\n \"Student\"\n ],\n \"os\": [\n \"All\"\n ],\n \"visibility\": \"Default\",\n \"consumerId\": \"7411b6bd-89f3-40ec-98d1-229dc64ce77d\",\n \"mediaType\": \"content\",\n \"osId\": \"org.ekstep.quiz.app\",\n \"languageCode\": [\n \"en\"\n ],\n \"version\": 2,\n \"versionKey\": \"1607515734913\",\n \"license\": \"CC BY 4.0\",\n \"idealScreenDensity\": \"hdpi\",\n \"framework\": \"NCF\",\n \"createdBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\",\n \"compatibilityLevel\": 1,\n \"name\": \"API DOCUMENTATION CONTENT\",\n \"status\": \"Draft\"\n }\n }\n}" + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Mon, 23 Nov 2020 04:48:35 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "30000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "29999" + }, + { + "key": "Referrer-Policy", + "value": "origin-when-cross-origin, strict-origin-when-cross-origin" + }, + { + "key": "X-Frame-Options", + "value": "DENY" + }, + { + "key": "X-XSS-Protection", + "value": "1; mode=block" + }, + { + "key": "x-response-length", + "value": "2759" + }, + { + "key": "X-Content-Type-Options", + "value": "nosniff" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Permitted-Cross-Domain-Policies", + "value": "master-only" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "11" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "35" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "X-Proxy-Cache", + "value": "MISS" + }, + { + "key": "X-Proxy-Cache-Date", + "value": "Mon, 23 Nov 2020 04:48:35 GMT" + }, + { + "key": "Content-Encoding", + "value": "gzip" } - ] - } - ], - "event": [ - { - "listen": "prerequest", - "script": { - "type": "text/javascript", - "exec": [ - "" - ] - } - }, - { - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "" - ] - } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.read\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-09T12:09:04.202Z\",\n \"params\": {\n \"resmsgid\": \"548b82a0-3a17-11eb-be5b-f9d249ba9049\",\n \"msgid\": \"5488ea90-3a17-11eb-be5b-f9d249ba9049\",\n \"status\": \"successful\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"content\": {\n \"ownershipType\": [\n \"createdBy\"\n ],\n \"code\": \"org.sunbird.ccG6ru\",\n \"credentials\": {\n \"enabled\": \"No\"\n },\n \"channel\": \"in.ekstep\",\n \"language\": [\n \"English\"\n ],\n \"mimeType\": \"application/pdf\",\n \"idealScreenSize\": \"normal\",\n \"createdOn\": \"2020-12-09T12:08:54.913+0000\",\n \"objectType\": \"Content\",\n \"primaryCategory\": \"Explanation Content\",\n \"contentDisposition\": \"inline\",\n \"lastUpdatedOn\": \"2020-12-09T12:08:54.913+0000\",\n \"contentEncoding\": \"identity\",\n \"contentType\": \"Resource\",\n \"dialcodeRequired\": \"No\",\n \"identifier\": \"do_1131687689003827201864\",\n \"lastStatusChangedOn\": \"2020-12-09T12:08:54.913+0000\",\n \"audience\": [\n \"Student\"\n ],\n \"os\": [\n \"All\"\n ],\n \"visibility\": \"Default\",\n \"consumerId\": \"7411b6bd-89f3-40ec-98d1-229dc64ce77d\",\n \"mediaType\": \"content\",\n \"osId\": \"org.ekstep.quiz.app\",\n \"languageCode\": [\n \"en\"\n ],\n \"version\": 2,\n \"versionKey\": \"1607515734913\",\n \"license\": \"CC BY 4.0\",\n \"idealScreenDensity\": \"hdpi\",\n \"framework\": \"NCF\",\n \"createdBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\",\n \"compatibilityLevel\": 1,\n \"name\": \"API DOCUMENTATION CONTENT\",\n \"status\": \"Draft\"\n }\n }\n}" } ] }, { "name": "Content Update", - "item": [ - { - "name": "Content Update", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "let jsonResponse = pm.response.json()", - "", - "//Verification", - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.have.status(200)", - "});", - "pm.test(\"Content-Type header is present\", () => {", - " pm.response.to.have.header(\"Content-Type\");", - "});", - "pm.test(\"Content-Type header is application/json\", () => {", - " pm.expect(pm.response.headers.get('Content-Type')).to.eql('application/json');", - "});", - "pm.test(\"Content-Encoding header is present\", () => {", - " pm.response.to.have.header(\"Content-Encoding\");", - "});", - "pm.test(\"Content-Encoding header is gzip\", () => {", - " pm.expect(pm.response.headers.get('Content-Encoding')).to.eql('gzip');", - "});", - "", - "pm.test(\"Test data type of the response and keys\", () => {", - " pm.expect(jsonResponse).to.be.an(\"object\").that.has.all.keys('id', 'ver', 'ts', 'params', 'responseCode', 'result');", - " pm.expect(jsonResponse.id).to.be.a(\"string\");", - " pm.expect(jsonResponse.ver).to.be.a(\"string\");", - " pm.expect(jsonResponse.ts).to.be.a(\"string\");", - " pm.expect(jsonResponse.responseCode).to.be.a(\"string\");", - " pm.expect(jsonResponse.params).to.be.an(\"object\").that.has.all.keys('resmsgid', 'msgid', 'err', 'status', 'errmsg');", - " pm.expect(jsonResponse.params.resmsgid).to.be.a(\"string\");", - " pm.expect(jsonResponse.params.msgid).to.be.null;", - " pm.expect(jsonResponse.params.err).to.be.null;", - " pm.expect(jsonResponse.params.status).to.be.a(\"string\");", - " pm.expect(jsonResponse.params.errmsg).to.be.null;", - " pm.expect(jsonResponse.result).to.be.an(\"object\");", - " pm.expect(jsonResponse.result).to.have.all.keys('identifier', 'node_id', 'versionKey');", - " pm.expect(jsonResponse.result.identifier).to.be.a(\"string\");", - " pm.expect(jsonResponse.result.node_id).to.be.a(\"string\");", - " pm.expect(jsonResponse.result.versionKey).to.be.a(\"string\");", - "});", - "", - "//Validation", - "pm.test(\"id should be api.content.update\", function () {", - " pm.expect(jsonResponse.id).to.eql(\"api.content.update\");", - "});", - "pm.test(\"ver should be 4.0\", function () {", - " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", - "});", - "pm.test(\"params.status should be successful\", function () {", - " pm.expect(jsonResponse.params.status).to.eql(\"successful\");", - "});", - "pm.test(\"responseCode should be OK\", function () {", - " pm.expect(jsonResponse.responseCode).to.eql(\"OK\");", - "});", - "pm.test(\"identifier should be equal to \" + pm.globals.get(\"content_id\"), function () {", - " pm.expect(jsonResponse.result.identifier).to.eql(pm.globals.get(\"content_id\"));", - "});", - "pm.test(\"node_id should be equal to \" + pm.globals.get(\"content_id\"), function () {", - " pm.expect(jsonResponse.result.node_id).to.eql(pm.globals.get(\"content_id\"));", - "});", - "", - "// Set global variable", - "pm.globals.set(\"versionKey\", jsonResponse.result.versionKey);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PATCH", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - }, - { - "key": "X-Authenticated-User-token", - "value": "{{keycloak_access_token}}", - "type": "text" - }, - { - "key": "Authorization", - "value": "{{kong_api_key}}", - "type": "text" - }, - { - "key": "X-Channel-Id", - "value": "{{channel_id}}", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"{{versionKey}}\",\n \"lastUpdatedBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\",\n \"description\": \"dsd\",\n \"name\": \"API DOCUMENTATION CONTENT\"\n }\n }\n}" - }, - "url": { - "raw": "{{host}}/content/v2/update/{{content_id}}", - "host": [ - "{{host}}" - ], - "path": [ - "content", - "v2", - "update", - "{{content_id}}" - ] - }, - "description": "This API is associated with updating a batch on the Sunbird Platform.\nThe endpoint for Course Batch Update is /batch/update\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." - }, - "response": [] - } - ], "event": [ - { - "listen": "prerequest", - "script": { - "type": "text/javascript", - "exec": [ - "" - ] - } - }, { "listen": "test", "script": { - "type": "text/javascript", "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "pm.test(\"Content-Type header is present\", () => {", + " pm.response.to.have.header(\"Content-Type\");", + "});", + "pm.test(\"Content-Type header is application/json\", () => {", + " pm.expect(pm.response.headers.get('Content-Type')).to.eql('application/json');", + "});", + "", + "pm.test(\"Test data type of the response and keys\", () => {", + " pm.expect(jsonResponse).to.be.an(\"object\").that.has.all.keys('id', 'ver', 'ts', 'params', 'responseCode', 'result');", + " pm.expect(jsonResponse.id).to.be.a(\"string\");", + " pm.expect(jsonResponse.ver).to.be.a(\"string\");", + " pm.expect(jsonResponse.ts).to.be.a(\"string\");", + " pm.expect(jsonResponse.responseCode).to.be.a(\"string\");", + " pm.expect(jsonResponse.params).to.be.an(\"object\").that.has.all.keys('resmsgid', 'msgid', 'err', 'status', 'errmsg');", + " pm.expect(jsonResponse.params.resmsgid).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.msgid).to.be.null;", + " pm.expect(jsonResponse.params.err).to.be.null;", + " pm.expect(jsonResponse.params.status).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.errmsg).to.be.null;", + " pm.expect(jsonResponse.result).to.be.an(\"object\");", + " pm.expect(jsonResponse.result).to.have.all.keys('identifier', 'node_id', 'versionKey');", + " pm.expect(jsonResponse.result.identifier).to.be.a(\"string\");", + " pm.expect(jsonResponse.result.node_id).to.be.a(\"string\");", + " pm.expect(jsonResponse.result.versionKey).to.be.a(\"string\");", + "});", + "", + "//Validation", + "pm.test(\"id should be api.content.update\", function () {", + " pm.expect(jsonResponse.id).to.eql(\"api.content.update\");", + "});", + "pm.test(\"ver should be 4.0\", function () {", + " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", + "});", + "pm.test(\"params.status should be successful\", function () {", + " pm.expect(jsonResponse.params.status).to.eql(\"successful\");", + "});", + "pm.test(\"responseCode should be OK\", function () {", + " pm.expect(jsonResponse.responseCode).to.eql(\"OK\");", + "});", + "pm.test(\"identifier should be equal to \" + pm.globals.get(\"content_id\"), function () {", + " pm.expect(jsonResponse.result.identifier).to.eql(pm.globals.get(\"content_id\"));", + "});", + "pm.test(\"node_id should be equal to \" + pm.globals.get(\"content_id\"), function () {", + " pm.expect(jsonResponse.result.node_id).to.eql(pm.globals.get(\"content_id\"));", + "});", + "", + "// Set global variable", + "pm.globals.set(\"versionKey\", jsonResponse.result.versionKey);", "" - ] + ], + "type": "text/javascript" } } - ] + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "type": "text" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"{{versionKey}}\",\n \"lastUpdatedBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\",\n \"description\": \"dsd\",\n \"name\": \"API DOCUMENTATION CONTENT\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v2/update/{{content_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v2", + "update", + "{{content_id}}" + ] + }, + "description": "This API is associated with updating a batch on the Sunbird Platform.\nThe endpoint for Course Batch Update is /batch/update\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] } ] } \ No newline at end of file From e4644377426beba8ae4fdd3e18cd059359d0e8d8 Mon Sep 17 00:00:00 2001 From: AmiableAnil Date: Tue, 19 Jul 2022 09:23:35 +0530 Subject: [PATCH 091/490] Issue #SB-30119 test: Added the postman tests for content v2 create, read and update API. --- .../Content V2.postman_collection.json | 78 ++++++++++++------- 1 file changed, 52 insertions(+), 26 deletions(-) diff --git a/content-api/api-test/Content V2.postman_collection.json b/content-api/api-test/Content V2.postman_collection.json index d116d704d..c70084091 100644 --- a/content-api/api-test/Content V2.postman_collection.json +++ b/content-api/api-test/Content V2.postman_collection.json @@ -2,7 +2,7 @@ "info": { "_postman_id": "95bee03d-d782-47da-89f9-cc70bb0a267e", "name": "Content V2", - "description": "The Content Management APIs allow you to create, manage and process content on the Sunbird platform. Content is the basic building block of the Knowledge Service. Some examples would be Videos, Audio, Pdfs, Html, ECML etc. The basic operations for this API include Create, Update, Read, Review, Publish.\n\nThe URL for Content Management API(s) is /content/v1.", + "description": "The Content Management APIs allow you to create, manage and process content on the Sunbird platform. Content is the basic building block of the Knowledge Service. Some examples would be Videos, Audio, Pdfs, Html, ECML etc. The basic operations for this API include Create, Update, Read, Review, Publish.\n\n* Each of the endpoints serve a different purpose, so which one to use depends on what you want to do\n* The operations include CRUD (Create, Update, Read and Delete) operations and other operations such as upload, publish, flag, link DIAL code, etc.\n* The URL for Content Management API(s) is /content/v2.\n \n\nAPI tests", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", "_exporter_id": "1419050" }, @@ -77,25 +77,29 @@ "header": [ { "key": "Content-Type", - "value": "application/json" + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." }, { "key": "X-Authenticated-User-token", - "value": "{{keycloak_access_token}}" + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." }, { "key": "Authorization", - "value": "{{kong_api_key}}" + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." }, { "key": "X-Channel-Id", "value": "{{channel_id}}", - "type": "text" + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." } ], "body": { "mode": "raw", - "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"createdBy\": \"530b19ea-dc8d-4cc7-a4b5-0c0214c8113a\",\n \"resourceType\": \"Learn\",\n \"framework\": \"ekstep_ncert_k-12\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" }, "url": { "raw": "{{host}}/content/v2/create", @@ -108,7 +112,7 @@ "create" ] }, - "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" }, "response": [ { @@ -116,11 +120,6 @@ "originalRequest": { "method": "POST", "header": [ - { - "key": "userId", - "value": "user", - "type": "text" - }, { "key": "Content-Type", "value": "application/json", @@ -137,8 +136,8 @@ "type": "text" }, { - "key": "Cookie", - "value": "connect.sid=s%3AyRLPeHt6zZ93WjZ2BD_Cejc11xNN-isK.ANbL%2FZ1t88yXax%2BiXjGFVtgFUAKhoWdl%2FgXU4yBX9rU; 81f30cf708470b974874c8a96a6bcdb2=l99ob678op07nq0qs5i0dae0m3; AWSELB=83D53DFF08C363B9195F6717118E44E326DE55CB796FB96A69500BA54C477B7512D6342B64B0B494BBD6699FCFFFB308C0DC9247AA23DA73B4F62BA4C399A735F647C86F50", + "key": "X-Channel-Id", + "value": "{{channel_id}}", "type": "text" } ], @@ -147,11 +146,16 @@ "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"API DOCUMENTATION CONTENT\",\n \"mimeType\": \"application/pdf\",\n \"code\": \"api-docs\",\n \"primaryCategory\": \"Explanation Content\",\n \"createdBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\"\n }\n }\n}" }, "url": { - "raw": "{{host}}/content/v2/create", + "raw": "https://dev.knowlg.sunbird.org/api/content/v2/create", + "protocol": "https", "host": [ - "{{host}}" + "dev", + "knowlg", + "sunbird", + "org" ], "path": [ + "api", "content", "v2", "create" @@ -325,15 +329,23 @@ "request": { "method": "GET", "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, { "key": "X-Authenticated-User-token", "value": "{{keycloak_access_token}}", - "type": "text" + "type": "text", + "description": "Represents the authorized User ID to execute the API." }, { "key": "Authorization", "value": "{{kong_api_key}}", - "type": "text" + "type": "text", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." } ], "url": { @@ -348,7 +360,7 @@ "{{content_id}}" ] }, - "description": "Fetch a particular Batch\nThis API is associated with fetching a particular batch on the Sunbird Platform.\nThe endpoint for Fetch a particular Batch is /batch/read/{Batch_ID}\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + "description": "This API is associated with viewing and reading out the content on the Sunbird Platform. You need to provide a valid content Id value in {{content_id}} field of API URL." }, "response": [ { @@ -356,6 +368,11 @@ "originalRequest": { "method": "GET", "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, { "key": "X-Authenticated-User-token", "value": "{{keycloak_access_token}}", @@ -368,11 +385,16 @@ } ], "url": { - "raw": "{{host}}/content/v2/read/{{content_id}}", + "raw": "https://dev.knowlg.sunbird.org/api/content/v2/read/{{content_id}}", + "protocol": "https", "host": [ - "{{host}}" + "dev", + "knowlg", + "sunbird", + "org" ], "path": [ + "api", "content", "v2", "read", @@ -543,22 +565,26 @@ { "key": "Content-Type", "value": "application/json", - "type": "text" + "type": "text", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." }, { "key": "X-Authenticated-User-token", "value": "{{keycloak_access_token}}", - "type": "text" + "type": "text", + "description": "Represents the authorized User ID to execute the API." }, { "key": "Authorization", "value": "{{kong_api_key}}", - "type": "text" + "type": "text", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." }, { "key": "X-Channel-Id", "value": "{{channel_id}}", - "type": "text" + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." } ], "body": { @@ -577,7 +603,7 @@ "{{content_id}}" ] }, - "description": "This API is associated with updating a batch on the Sunbird Platform.\nThe endpoint for Course Batch Update is /batch/update\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + "description": "This API is associated with updating content on the Sunbird Platform. Mandatory fields cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" }, "response": [] } From a0909944e720b7a8e127c3d0421edc811931f573 Mon Sep 17 00:00:00 2001 From: AmiableAnil Date: Tue, 19 Jul 2022 12:20:24 +0530 Subject: [PATCH 092/490] Issue #SB-30119 test: Added the postman tests for content v2 create, read and update API. --- ...ontent V2 API Test.postman_collection.json | 159 ++-- .../Content V2.postman_collection.json | 757 ++++++++++++++++-- 2 files changed, 775 insertions(+), 141 deletions(-) diff --git a/content-api/api-test/Content V2 API Test.postman_collection.json b/content-api/api-test/Content V2 API Test.postman_collection.json index 7093dbae2..77efae5c8 100644 --- a/content-api/api-test/Content V2 API Test.postman_collection.json +++ b/content-api/api-test/Content V2 API Test.postman_collection.json @@ -77,11 +77,12 @@ "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" }, "url": { - "raw": "{{host}}/content/v2/create", + "raw": "{{host}}/api/content/v2/create", "host": [ "{{host}}" ], "path": [ + "api", "content", "v2", "create" @@ -155,11 +156,12 @@ "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"~`S!@#$%^&*()_+=:;\\\"'<,>.?/\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" }, "url": { - "raw": "{{host}}/content/v2/create", + "raw": "{{host}}/api/content/v2/create", "host": [ "{{host}}" ], "path": [ + "api", "content", "v2", "create" @@ -248,11 +250,12 @@ } ], "url": { - "raw": "{{host}}/content/v2/read/{{content_id}}", + "raw": "{{host}}/api/content/v2/read/{{content_id}}", "host": [ "{{host}}" ], "path": [ + "api", "content", "v2", "read", @@ -325,11 +328,12 @@ } ], "url": { - "raw": "{{host}}/content/v2/read/{{content_id}}?fields=name,primaryCategory", + "raw": "{{host}}/api/content/v2/read/{{content_id}}?fields=name,primaryCategory", "host": [ "{{host}}" ], "path": [ + "api", "content", "v2", "read", @@ -448,11 +452,12 @@ "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"{{versionKey}}\",\n \"lastUpdatedBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\",\n \"description\": \"dsd\",\n \"createdBy\": \"530b19ea-dc8d-4cc7-a4b5-0c0214c8113a\",\n \"resourceType\": \"Learn\",\n \"framework\": \"ekstep_ncert_k-12\",\n \"name\": \"API DOCUMENTATION CONTENT\"\n }\n }\n}" }, "url": { - "raw": "{{host}}/content/v2/update/{{content_id}}", + "raw": "{{host}}/api/content/v2/update/{{content_id}}", "host": [ "{{host}}" ], "path": [ + "api", "content", "v2", "update", @@ -567,11 +572,12 @@ "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"{{versionKey}}\",\n \"description\": \"dsd\",\n \"name\": \"~`S!@#$%^&*()_+=:;\\\"'<,>.?/\"\n }\n }\n}" }, "url": { - "raw": "{{host}}/content/v2/update/{{content_id}}", + "raw": "{{host}}/api/content/v2/update/{{content_id}}", "host": [ "{{host}}" ], "path": [ + "api", "content", "v2", "update", @@ -662,7 +668,7 @@ "name": "Content Create", "item": [ { - "name": "Content Create - 404 - missing name", + "name": "Content Create - 400 - missing name", "event": [ { "listen": "test", @@ -739,11 +745,12 @@ "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" }, "url": { - "raw": "{{host}}/content/v2/create", + "raw": "{{host}}/api/content/v2/create", "host": [ "{{host}}" ], "path": [ + "api", "content", "v2", "create" @@ -754,7 +761,7 @@ "response": [] }, { - "name": "Content Create - 404 - name empty", + "name": "Content Create - 400 - name empty", "event": [ { "listen": "test", @@ -831,11 +838,12 @@ "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" }, "url": { - "raw": "{{host}}/content/v2/create", + "raw": "{{host}}/api/content/v2/create", "host": [ "{{host}}" ], "path": [ + "api", "content", "v2", "create" @@ -846,7 +854,7 @@ "response": [] }, { - "name": "Content Create - 404 - missing code", + "name": "Content Create - 400 - missing code", "event": [ { "listen": "test", @@ -923,11 +931,12 @@ "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" }, "url": { - "raw": "{{host}}/content/v2/create", + "raw": "{{host}}/api/content/v2/create", "host": [ "{{host}}" ], "path": [ + "api", "content", "v2", "create" @@ -938,7 +947,7 @@ "response": [] }, { - "name": "Content Create - 404 - missing mimeType", + "name": "Content Create - 400 - missing mimeType", "event": [ { "listen": "test", @@ -1015,11 +1024,12 @@ "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" }, "url": { - "raw": "{{host}}/content/v2/create", + "raw": "{{host}}/api/content/v2/create", "host": [ "{{host}}" ], "path": [ + "api", "content", "v2", "create" @@ -1030,7 +1040,7 @@ "response": [] }, { - "name": "Content Create - 404 - missing primaryCategory", + "name": "Content Create - 400 - missing primaryCategory", "event": [ { "listen": "test", @@ -1105,11 +1115,12 @@ "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\"\n }\n }\n}" }, "url": { - "raw": "{{host}}/content/v2/create", + "raw": "{{host}}/api/content/v2/create", "host": [ "{{host}}" ], "path": [ + "api", "content", "v2", "create" @@ -1195,11 +1206,12 @@ "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"createdBy\": \"530b19ea-dc8d-4cc7-a4b5-0c0214c8113a\",\n \"resourceType\": \"Learn\",\n \"framework\": \"ekstep_ncert_k-12\",\n \"primaryCategory\": \"aabaa\"\n }\n }\n}" }, "url": { - "raw": "{{host}}/content/v2/create", + "raw": "{{host}}/api/content/v2/create", "host": [ "{{host}}" ], "path": [ + "api", "content", "v2", "create" @@ -1242,7 +1254,7 @@ "name": "Content Read", "item": [ { - "name": "Content Read", + "name": "Content Read - 404 - Not Found", "event": [ { "listen": "test", @@ -1320,11 +1332,12 @@ } ], "url": { - "raw": "{{host}}/content/v2/read/{{invalid_content_id}}", + "raw": "{{host}}/api/content/v2/read/{{invalid_content_id}}", "host": [ "{{host}}" ], "path": [ + "api", "content", "v2", "read", @@ -1452,11 +1465,12 @@ "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"{{versionKey}}\",\n \"lastUpdatedBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\",\n \"description\": \"dsd\",\n \"name\": \"API DOCUMENTATION CONTENT\"\n }\n }\n}" }, "url": { - "raw": "{{host}}/content/v2/update/{{content_id}}", + "raw": "{{host}}/api/content/v2/update/{{content_id}}", "host": [ "{{host}}" ], "path": [ + "api", "content", "v2", "update", @@ -1468,7 +1482,7 @@ "response": [] }, { - "name": "Content Update - 404 - Invalid content id", + "name": "Content Update - 400 - Empty name", "event": [ { "listen": "test", @@ -1477,49 +1491,41 @@ "let jsonResponse = pm.response.json()", "", "//Verification", - "pm.test(\"Status code is 404\", () => {", - " pm.response.to.have.status(404)", + "pm.test(\"Status code is 400\", () => {", + " pm.response.to.have.status(400)", "});", "//code name contains a string", - "tests[\"Status code name has string \\\"Not Found\\\"\"] = responseCode.name.has(\"Not Found\");", + "tests[\"Status code name has string \\\"Bad Request\\\"\"] = responseCode.name.has(\"Bad Request\");", "", "pm.test(\"Verify keys and data type of the response result\", () => {", " pm.expect(jsonResponse.result).to.be.an(\"object\");", " pm.expect(jsonResponse.result).to.have.all.keys('messages');", - " pm.expect(jsonResponse.result.messages).to.be.null;", + " pm.expect(jsonResponse.result.messages).to.not.be.empty;", "});", "", "//Validation", "// Response body", - "pm.test(\"responseCode should be RESOURCE_NOT_FOUND\", () => {", - " pm.expect(jsonResponse.responseCode).to.eql(\"RESOURCE_NOT_FOUND\");", + "pm.test(\"responseCode should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"CLIENT_ERROR\");", "});", "", "// Params", - "pm.test(\"params.err should be NOT_FOUND\", () => {", - " pm.expect(jsonResponse.params.err).to.eql(\"NOT_FOUND\");", + "pm.test(\"params.err should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.params.err).to.eql(\"CLIENT_ERROR\");", "});", - "pm.test(`params.errmsg should be \\\"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: ${pm.globals.get(\"content_id\")}\\\"`, () => {", - " pm.expect(jsonResponse.params.errmsg).to.eql(`Error! Node(s) doesn't Exists. | [Invalid Node Id.]: ${pm.globals.get(\"content_id\")}`);", + "pm.test(\"params.errmsg should be \\\"Validation Errors\\\"\", () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(\"Validation Errors\");", "});", "", "// Result", - "pm.test(\"messages should be null\", () => {", - " pm.expect(jsonResponse.result.messages).to.eql(null);", + "pm.test(\"result body should include messages \\\"Required Metadata name not set\\\"\", () => {", + " pm.expect(jsonResponse.result.messages).to.have.length(1)", + " pm.expect(jsonResponse.result.messages).to.include('Required Metadata name not set');", "});", "" ], "type": "text/javascript" } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "pm.globals.set(\"content_id\", 'do_21357909972698726412211');" - ], - "type": "text/javascript" - } } ], "request": { @@ -1548,14 +1554,15 @@ ], "body": { "mode": "raw", - "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"{{versionKey}}\",\n \"lastUpdatedBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\",\n \"description\": \"dsd\",\n \"name\": \"API DOCUMENTATION CONTENT\"\n }\n }\n}" + "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"{{versionKey}}\",\n \"description\": \"dsd\",\n \"name\": \"\"\n }\n }\n}" }, "url": { - "raw": "{{host}}/content/v2/update/{{content_id}}", + "raw": "{{host}}/api/content/v2/update/{{content_id}}", "host": [ "{{host}}" ], "path": [ + "api", "content", "v2", "update", @@ -1567,7 +1574,7 @@ "response": [] }, { - "name": "Content Update - 404 - Invalid primaryCategory", + "name": "Content Update - 404 - Invalid content id", "event": [ { "listen": "test", @@ -1595,16 +1602,14 @@ "});", "", "// Params", - "pm.test(\"params.err should be \\\"404\\\"\", () => {", - " pm.expect(jsonResponse.params.err).to.eql(\"404\");", - "});", - "pm.test(\"params.status should be \\\"failed\\\"\", () => {", - " pm.expect(jsonResponse.params.status).to.eql(\"failed\");", + "pm.test(\"params.err should be NOT_FOUND\", () => {", + " pm.expect(jsonResponse.params.err).to.eql(\"NOT_FOUND\");", "});", - "pm.test(`params.errmsg should be \\\"Entry is not found in cassandra for content with identifier: obj-cat:${JSON.parse(request.data).request.content.primaryCategory}_content_all {}\\\"`, () => {", - " pm.expect(jsonResponse.params.errmsg).to.eql(`Entry is not found in cassandra for content with identifier: obj-cat:${JSON.parse(request.data).request.content.primaryCategory}_content_all {}`);", + "pm.test(`params.errmsg should be \\\"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: ${pm.globals.get(\"content_id\")}\\\"`, () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(`Error! Node(s) doesn't Exists. | [Invalid Node Id.]: ${pm.globals.get(\"content_id\")}`);", "});", "", + "// Result", "pm.test(\"messages should be null\", () => {", " pm.expect(jsonResponse.result.messages).to.eql(null);", "});", @@ -1612,6 +1617,15 @@ ], "type": "text/javascript" } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "pm.globals.set(\"content_id\", 'do_21357909972698726412211');" + ], + "type": "text/javascript" + } } ], "request": { @@ -1640,14 +1654,15 @@ ], "body": { "mode": "raw", - "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"{{versionKey}}\",\n \"description\": \"dsd\",\n \"name\": \"API DOCUMENTATION CONTENT\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"{{versionKey}}\",\n \"lastUpdatedBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\",\n \"description\": \"dsd\",\n \"name\": \"API DOCUMENTATION CONTENT\"\n }\n }\n}" }, "url": { - "raw": "{{host}}/content/v2/update/{{content_id}}", + "raw": "{{host}}/api/content/v2/update/{{content_id}}", "host": [ "{{host}}" ], "path": [ + "api", "content", "v2", "update", @@ -1659,7 +1674,7 @@ "response": [] }, { - "name": "Content Update - 404 - Empty name", + "name": "Content Update - 404 - Invalid primaryCategory", "event": [ { "listen": "test", @@ -1668,36 +1683,37 @@ "let jsonResponse = pm.response.json()", "", "//Verification", - "pm.test(\"Status code is 400\", () => {", - " pm.response.to.have.status(400)", + "pm.test(\"Status code is 404\", () => {", + " pm.response.to.have.status(404)", "});", "//code name contains a string", - "tests[\"Status code name has string \\\"Bad Request\\\"\"] = responseCode.name.has(\"Bad Request\");", + "tests[\"Status code name has string \\\"Not Found\\\"\"] = responseCode.name.has(\"Not Found\");", "", "pm.test(\"Verify keys and data type of the response result\", () => {", " pm.expect(jsonResponse.result).to.be.an(\"object\");", " pm.expect(jsonResponse.result).to.have.all.keys('messages');", - " pm.expect(jsonResponse.result.messages).to.not.be.empty;", + " pm.expect(jsonResponse.result.messages).to.be.null;", "});", "", "//Validation", "// Response body", - "pm.test(\"responseCode should be \\\"CLIENT_ERROR\\\"\", () => {", - " pm.expect(jsonResponse.responseCode).to.eql(\"CLIENT_ERROR\");", + "pm.test(\"responseCode should be RESOURCE_NOT_FOUND\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"RESOURCE_NOT_FOUND\");", "});", "", "// Params", - "pm.test(\"params.err should be \\\"CLIENT_ERROR\\\"\", () => {", - " pm.expect(jsonResponse.params.err).to.eql(\"CLIENT_ERROR\");", + "pm.test(\"params.err should be \\\"404\\\"\", () => {", + " pm.expect(jsonResponse.params.err).to.eql(\"404\");", "});", - "pm.test(\"params.errmsg should be \\\"Validation Errors\\\"\", () => {", - " pm.expect(jsonResponse.params.errmsg).to.eql(\"Validation Errors\");", + "pm.test(\"params.status should be \\\"failed\\\"\", () => {", + " pm.expect(jsonResponse.params.status).to.eql(\"failed\");", + "});", + "pm.test(`params.errmsg should be \\\"Entry is not found in cassandra for content with identifier: obj-cat:${JSON.parse(request.data).request.content.primaryCategory}_content_all {}\\\"`, () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(`Entry is not found in cassandra for content with identifier: obj-cat:${JSON.parse(request.data).request.content.primaryCategory}_content_all {}`);", "});", "", - "// Result", - "pm.test(\"result body should include messages \\\"Required Metadata name not set\\\"\", () => {", - " pm.expect(jsonResponse.result.messages).to.have.length(1)", - " pm.expect(jsonResponse.result.messages).to.include('Required Metadata name not set');", + "pm.test(\"messages should be null\", () => {", + " pm.expect(jsonResponse.result.messages).to.eql(null);", "});", "" ], @@ -1731,14 +1747,15 @@ ], "body": { "mode": "raw", - "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"{{versionKey}}\",\n \"description\": \"dsd\",\n \"name\": \"\"\n }\n }\n}" + "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"{{versionKey}}\",\n \"description\": \"dsd\",\n \"name\": \"API DOCUMENTATION CONTENT\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" }, "url": { - "raw": "{{host}}/content/v2/update/{{content_id}}", + "raw": "{{host}}/api/content/v2/update/{{content_id}}", "host": [ "{{host}}" ], "path": [ + "api", "content", "v2", "update", diff --git a/content-api/api-test/Content V2.postman_collection.json b/content-api/api-test/Content V2.postman_collection.json index c70084091..db88bf77e 100644 --- a/content-api/api-test/Content V2.postman_collection.json +++ b/content-api/api-test/Content V2.postman_collection.json @@ -102,11 +102,12 @@ "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" }, "url": { - "raw": "{{host}}/content/v2/create", + "raw": "{{host}}/api/content/v2/create", "host": [ "{{host}}" ], "path": [ + "api", "content", "v2", "create" @@ -123,36 +124,33 @@ { "key": "Content-Type", "value": "application/json", - "type": "text" + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." }, { "key": "X-Authenticated-User-token", "value": "{{keycloak_access_token}}", - "type": "text" + "description": "Represents the authorized User ID to execute the API." }, { "key": "Authorization", "value": "{{kong_api_key}}", - "type": "text" + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." }, { "key": "X-Channel-Id", "value": "{{channel_id}}", - "type": "text" + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." } ], "body": { "mode": "raw", - "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"API DOCUMENTATION CONTENT\",\n \"mimeType\": \"application/pdf\",\n \"code\": \"api-docs\",\n \"primaryCategory\": \"Explanation Content\",\n \"createdBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\"\n }\n }\n}" + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" }, "url": { - "raw": "https://dev.knowlg.sunbird.org/api/content/v2/create", - "protocol": "https", + "raw": "{{host}}/api/content/v2/create", "host": [ - "dev", - "knowlg", - "sunbird", - "org" + "{{host}}" ], "path": [ "api", @@ -168,7 +166,7 @@ "header": [ { "key": "Date", - "value": "Mon, 23 Nov 2020 06:42:39 GMT" + "value": "Tue, 19 Jul 2022 06:42:53 GMT" }, { "key": "Content-Type", @@ -188,47 +186,239 @@ }, { "key": "X-RateLimit-Limit-hour", - "value": "30000" + "value": "1000000" }, { "key": "X-RateLimit-Remaining-hour", - "value": "29991" + "value": "999995" }, { - "key": "Referrer-Policy", - "value": "origin-when-cross-origin, strict-origin-when-cross-origin" + "key": "request-time", + "value": "34" }, { - "key": "X-Frame-Options", - "value": "DENY" + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" }, { - "key": "X-XSS-Protection", - "value": "1; mode=block" + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" }, { - "key": "x-response-length", - "value": "55" + "key": "request-time", + "value": "11" }, { - "key": "X-Content-Type-Options", - "value": "nosniff" + "key": "x-envoy-upstream-service-time", + "value": "13" }, { "key": "Access-Control-Allow-Origin", "value": "*" }, { - "key": "X-Permitted-Cross-Domain-Policies", - "value": "master-only" + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" }, { "key": "X-Kong-Upstream-Latency", - "value": "53" + "value": "23" }, { "key": "X-Kong-Proxy-Latency", - "value": "36" + "value": "115" }, { "key": "Via", @@ -240,7 +430,7 @@ } ], "cookie": [], - "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-09T11:11:12.697Z\",\n \"params\": {\n \"resmsgid\": \"3f5de290-3a0f-11eb-be5b-f9d249ba9049\",\n \"msgid\": \"3f57a100-3a0f-11eb-be5b-f9d249ba9049\",\n \"status\": \"successful\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"content_id\": \"do_1131687405374504961738\",\n \"versionKey\": \"1607512272640\"\n }\n}" + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" } ] }, @@ -324,6 +514,15 @@ ], "type": "text/javascript" } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "// pm.globals.set('content_id', 'do_213584064677732352111');" + ], + "type": "text/javascript" + } } ], "request": { @@ -349,11 +548,12 @@ } ], "url": { - "raw": "{{host}}/content/v2/read/{{content_id}}", + "raw": "{{host}}/api/content/v2/read/{{content_id}}", "host": [ "{{host}}" ], "path": [ + "api", "content", "v2", "read", @@ -371,27 +571,26 @@ { "key": "Content-Type", "value": "application/json", - "type": "text" + "type": "text", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." }, { "key": "X-Authenticated-User-token", "value": "{{keycloak_access_token}}", - "type": "text" + "type": "text", + "description": "Represents the authorized User ID to execute the API." }, { "key": "Authorization", "value": "{{kong_api_key}}", - "type": "text" + "type": "text", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." } ], "url": { - "raw": "https://dev.knowlg.sunbird.org/api/content/v2/read/{{content_id}}", - "protocol": "https", + "raw": "{{host}}/api/content/v2/read/{{content_id}}", "host": [ - "dev", - "knowlg", - "sunbird", - "org" + "{{host}}" ], "path": [ "api", @@ -408,7 +607,7 @@ "header": [ { "key": "Date", - "value": "Mon, 23 Nov 2020 04:48:35 GMT" + "value": "Tue, 19 Jul 2022 06:44:16 GMT" }, { "key": "Content-Type", @@ -428,59 +627,137 @@ }, { "key": "X-RateLimit-Limit-hour", - "value": "30000" + "value": "1000000" }, { "key": "X-RateLimit-Remaining-hour", - "value": "29999" + "value": "999996" }, { - "key": "Referrer-Policy", - "value": "origin-when-cross-origin, strict-origin-when-cross-origin" + "key": "request-time", + "value": "26" }, { - "key": "X-Frame-Options", - "value": "DENY" + "key": "x-envoy-upstream-service-time", + "value": "28" }, { - "key": "X-XSS-Protection", - "value": "1; mode=block" + "key": "Access-Control-Allow-Origin", + "value": "*" }, { - "key": "x-response-length", - "value": "2759" + "key": "X-Kong-Upstream-Latency", + "value": "32" }, { - "key": "X-Content-Type-Options", - "value": "nosniff" + "key": "X-Kong-Proxy-Latency", + "value": "14" }, { - "key": "Access-Control-Allow-Origin", - "value": "*" + "key": "Via", + "value": "kong/0.14.1" }, { - "key": "X-Permitted-Cross-Domain-Policies", - "value": "master-only" + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.read\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:44:16ZZ\",\n \"params\": {\n \"resmsgid\": \"2c23c136-d4eb-4c88-897b-7077bd2894cd\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"content\": {\n \"ownershipType\": [\n \"createdBy\"\n ],\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"interceptionPoints\": {},\n \"credentials\": {\n \"enabled\": \"No\"\n },\n \"channel\": \"channel_id\",\n \"language\": [\n \"English\"\n ],\n \"mimeType\": \"application/pdf\",\n \"idealScreenSize\": \"normal\",\n \"createdOn\": \"2022-07-19T06:42:53.248+0000\",\n \"objectType\": \"Content\",\n \"primaryCategory\": \"Explanation Content\",\n \"contentDisposition\": \"inline\",\n \"lastUpdatedOn\": \"2022-07-19T06:42:53.248+0000\",\n \"contentEncoding\": \"identity\",\n \"contentType\": \"ClassroomTeachingVideo\",\n \"dialcodeRequired\": \"No\",\n \"identifier\": \"do_21358408067678208014\",\n \"lastStatusChangedOn\": \"2022-07-19T06:42:53.248+0000\",\n \"audience\": [\n \"Student\"\n ],\n \"os\": [\n \"All\"\n ],\n \"visibility\": \"Default\",\n \"consumerId\": \"ab35de9d-2a45-410a-901f-82a25e05af6d\",\n \"discussionForum\": {\n \"enabled\": \"No\"\n },\n \"mediaType\": \"content\",\n \"osId\": \"org.ekstep.quiz.app\",\n \"languageCode\": [\n \"en\"\n ],\n \"version\": 2,\n \"versionKey\": \"1658212973248\",\n \"license\": \"CC BY 4.0\",\n \"idealScreenDensity\": \"hdpi\",\n \"framework\": \"NCF\",\n \"compatibilityLevel\": 1,\n \"name\": \"Untitled Content\",\n \"status\": \"Draft\"\n }\n }\n}" + }, + { + "name": "Content Read - 404 Not Found", + "originalRequest": { + "method": "GET", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "type": "text", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + } + ], + "url": { + "raw": "{{host}}/api/content/v2/read/{{content_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "read", + "{{content_id}}" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:36:38 GMT" }, { - "key": "X-Kong-Upstream-Latency", - "value": "11" + "key": "Content-Type", + "value": "application/json" }, { - "key": "X-Kong-Proxy-Latency", - "value": "35" + "key": "Transfer-Encoding", + "value": "chunked" }, { - "key": "Via", - "value": "kong/0.14.1" + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" }, { - "key": "X-Proxy-Cache", - "value": "MISS" + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "59" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "61" }, { - "key": "X-Proxy-Cache-Date", - "value": "Mon, 23 Nov 2020 04:48:35 GMT" + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "74" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "116" + }, + { + "key": "Via", + "value": "kong/0.14.1" }, { "key": "Content-Encoding", @@ -488,7 +765,7 @@ } ], "cookie": [], - "body": "{\n \"id\": \"api.content.read\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-09T12:09:04.202Z\",\n \"params\": {\n \"resmsgid\": \"548b82a0-3a17-11eb-be5b-f9d249ba9049\",\n \"msgid\": \"5488ea90-3a17-11eb-be5b-f9d249ba9049\",\n \"status\": \"successful\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"content\": {\n \"ownershipType\": [\n \"createdBy\"\n ],\n \"code\": \"org.sunbird.ccG6ru\",\n \"credentials\": {\n \"enabled\": \"No\"\n },\n \"channel\": \"in.ekstep\",\n \"language\": [\n \"English\"\n ],\n \"mimeType\": \"application/pdf\",\n \"idealScreenSize\": \"normal\",\n \"createdOn\": \"2020-12-09T12:08:54.913+0000\",\n \"objectType\": \"Content\",\n \"primaryCategory\": \"Explanation Content\",\n \"contentDisposition\": \"inline\",\n \"lastUpdatedOn\": \"2020-12-09T12:08:54.913+0000\",\n \"contentEncoding\": \"identity\",\n \"contentType\": \"Resource\",\n \"dialcodeRequired\": \"No\",\n \"identifier\": \"do_1131687689003827201864\",\n \"lastStatusChangedOn\": \"2020-12-09T12:08:54.913+0000\",\n \"audience\": [\n \"Student\"\n ],\n \"os\": [\n \"All\"\n ],\n \"visibility\": \"Default\",\n \"consumerId\": \"7411b6bd-89f3-40ec-98d1-229dc64ce77d\",\n \"mediaType\": \"content\",\n \"osId\": \"org.ekstep.quiz.app\",\n \"languageCode\": [\n \"en\"\n ],\n \"version\": 2,\n \"versionKey\": \"1607515734913\",\n \"license\": \"CC BY 4.0\",\n \"idealScreenDensity\": \"hdpi\",\n \"framework\": \"NCF\",\n \"createdBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\",\n \"compatibilityLevel\": 1,\n \"name\": \"API DOCUMENTATION CONTENT\",\n \"status\": \"Draft\"\n }\n }\n}" + "body": "{\n \"id\": \"api.content.read\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:36:38ZZ\",\n \"params\": {\n \"resmsgid\": \"e8f48807-bff1-45c0-8eeb-760dcf7f98cf\",\n \"msgid\": null,\n \"err\": \"NOT_FOUND\",\n \"status\": \"failed\",\n \"errmsg\": \"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: do_213584064677732352111\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" } ] }, @@ -557,6 +834,16 @@ ], "type": "text/javascript" } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "// pm.globals.set('versionKey', '3534534543');", + "// pm.globals.set(\"content_id\", 'do_213584064677732352111');" + ], + "type": "text/javascript" + } } ], "request": { @@ -592,11 +879,12 @@ "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"{{versionKey}}\",\n \"lastUpdatedBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\",\n \"description\": \"dsd\",\n \"name\": \"API DOCUMENTATION CONTENT\"\n }\n }\n}" }, "url": { - "raw": "{{host}}/content/v2/update/{{content_id}}", + "raw": "{{host}}/api/content/v2/update/{{content_id}}", "host": [ "{{host}}" ], "path": [ + "api", "content", "v2", "update", @@ -605,7 +893,336 @@ }, "description": "This API is associated with updating content on the Sunbird Platform. Mandatory fields cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" }, - "response": [] + "response": [ + { + "name": "Content Update - 400 Bad Request", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "type": "text", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"{{versionKey}}\",\n \"lastUpdatedBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\",\n \"description\": \"dsd\",\n \"name\": \"API DOCUMENTATION CONTENT\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/update/{{content_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "update", + "{{content_id}}" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:38:11 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "269" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "33" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "35" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "38" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "12" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.update\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:38:11ZZ\",\n \"params\": {\n \"resmsgid\": \"0237c6eb-9d37-474f-aed5-c83aee24116b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Invalid version Key\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": null\n }\n}" + }, + { + "name": "Content Update - 404 Not Found", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "type": "text", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"{{versionKey}}\",\n \"lastUpdatedBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\",\n \"description\": \"dsd\",\n \"name\": \"API DOCUMENTATION CONTENT\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/update/{{content_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "update", + "{{content_id}}" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:39:22 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999996" + }, + { + "key": "request-time", + "value": "12" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "14" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "116" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.update\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:39:22ZZ\",\n \"params\": {\n \"resmsgid\": \"e60a2a4d-c8f9-4f10-bc66-695e0f86d82c\",\n \"msgid\": null,\n \"err\": \"NOT_FOUND\",\n \"status\": \"failed\",\n \"errmsg\": \"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: do_213584064677732352111\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + }, + { + "name": "Content Update - 200 OK", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "type": "text", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"{{versionKey}}\",\n \"lastUpdatedBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\",\n \"description\": \"dsd\",\n \"name\": \"API DOCUMENTATION CONTENT\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/update/{{content_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "update", + "{{content_id}}" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:44:37 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "87" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "88" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "92" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.update\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:44:37ZZ\",\n \"params\": {\n \"resmsgid\": \"767eada1-6342-4503-ad0d-b18aea711aab\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658213077381\"\n }\n}" + } + ] } ] } \ No newline at end of file From d8a8bb816912b6fd4905608f3670f7a1f983457f Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Wed, 20 Jul 2022 12:05:11 +0530 Subject: [PATCH 093/490] Issue #SB-30154 feat: DIAL code Link API refactor. --- .../src/main/scala/org/sunbird/content/dial/DIALManager.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala index 2974228ce..acbda5f8a 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala @@ -179,6 +179,7 @@ object DIALManager { rootNodeMetadata.remove(DIALConstants.DISCUSSION_FORUM) rootNodeMetadata.remove(DIALConstants.CREDENTIALS) rootNodeMetadata.remove(DIALConstants.TRACKABLE) + rootNodeMetadata.remove(DIALConstants.RESERVED_DIALCODES) if(rootNodeMetadata.containsKey(DIALConstants.DIALCODES)) rootNodeMetadata.remove(DIALConstants.DIALCODES) From 7522a8fd83e98db36397fa4242b4883cc16ac190 Mon Sep 17 00:00:00 2001 From: AmiableAnil Date: Wed, 27 Jul 2022 08:58:24 +0530 Subject: [PATCH 094/490] Issue #SB-30119 test: Added the postman tests for content v2 create, read and update API. --- .../Content V2.postman_collection.json | 90 +++++++++---------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/content-api/api-test/Content V2.postman_collection.json b/content-api/api-test/Content V2.postman_collection.json index db88bf77e..baf4d7f39 100644 --- a/content-api/api-test/Content V2.postman_collection.json +++ b/content-api/api-test/Content V2.postman_collection.json @@ -2,7 +2,7 @@ "info": { "_postman_id": "95bee03d-d782-47da-89f9-cc70bb0a267e", "name": "Content V2", - "description": "The Content Management APIs allow you to create, manage and process content on the Sunbird platform. Content is the basic building block of the Knowledge Service. Some examples would be Videos, Audio, Pdfs, Html, ECML etc. The basic operations for this API include Create, Update, Read, Review, Publish.\n\n* Each of the endpoints serve a different purpose, so which one to use depends on what you want to do\n* The operations include CRUD (Create, Update, Read and Delete) operations and other operations such as upload, publish, flag, link DIAL code, etc.\n* The URL for Content Management API(s) is /content/v2.\n \n\nAPI tests", + "description": "The Content Management APIs allow you to create, manage and process content on the Sunbird platform. Content is the basic building block of the Knowledge Service. Some examples would be Videos, Audio, Pdfs, Html, ECML etc. The basic operations for this API include Create, Update, Read, Review, Publish.\n\n* Each of the endpoints serve a different purpose, so which one to use depends on what you want to do\n* The operations include CRUD (Create, Update, Read and Delete) operations and other operations such as upload, publish, flag, link DIAL code, etc.\n* The URL for Content Management API(s) is /content/v2.\n \n\nFor more details refer [here](https://knowlg.sunbird.org/learn/readme)\n\nAPI tests", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", "_exporter_id": "1419050" }, @@ -113,7 +113,7 @@ "create" ] }, - "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" }, "response": [ { @@ -891,11 +891,11 @@ "{{content_id}}" ] }, - "description": "This API is associated with updating content on the Sunbird Platform. Mandatory fields cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + "description": "This API is associated with updating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" }, "response": [ { - "name": "Content Update - 400 Bad Request", + "name": "Content Update - 200 OK", "originalRequest": { "method": "PATCH", "header": [ @@ -942,41 +942,45 @@ ] } }, - "status": "Bad Request", - "code": 400, + "status": "OK", + "code": 200, "_postman_previewlanguage": "json", "header": [ { "key": "Date", - "value": "Tue, 19 Jul 2022 06:38:11 GMT" + "value": "Tue, 19 Jul 2022 06:44:37 GMT" }, { "key": "Content-Type", "value": "application/json" }, { - "key": "Content-Length", - "value": "269" + "key": "Transfer-Encoding", + "value": "chunked" }, { "key": "Connection", "value": "keep-alive" }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, { "key": "X-RateLimit-Limit-hour", "value": "1000000" }, { "key": "X-RateLimit-Remaining-hour", - "value": "999997" + "value": "999995" }, { "key": "request-time", - "value": "33" + "value": "87" }, { "key": "x-envoy-upstream-service-time", - "value": "35" + "value": "88" }, { "key": "Access-Control-Allow-Origin", @@ -984,22 +988,26 @@ }, { "key": "X-Kong-Upstream-Latency", - "value": "38" + "value": "92" }, { "key": "X-Kong-Proxy-Latency", - "value": "12" + "value": "115" }, { "key": "Via", "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" } ], "cookie": [], - "body": "{\n \"id\": \"api.content.update\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:38:11ZZ\",\n \"params\": {\n \"resmsgid\": \"0237c6eb-9d37-474f-aed5-c83aee24116b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Invalid version Key\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": null\n }\n}" + "body": "{\n \"id\": \"api.content.update\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:44:37ZZ\",\n \"params\": {\n \"resmsgid\": \"767eada1-6342-4503-ad0d-b18aea711aab\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658213077381\"\n }\n}" }, { - "name": "Content Update - 404 Not Found", + "name": "Content Update - 400 Bad Request", "originalRequest": { "method": "PATCH", "header": [ @@ -1046,45 +1054,41 @@ ] } }, - "status": "Not Found", - "code": 404, + "status": "Bad Request", + "code": 400, "_postman_previewlanguage": "json", "header": [ { "key": "Date", - "value": "Tue, 19 Jul 2022 06:39:22 GMT" + "value": "Tue, 19 Jul 2022 06:38:11 GMT" }, { "key": "Content-Type", "value": "application/json" }, { - "key": "Transfer-Encoding", - "value": "chunked" + "key": "Content-Length", + "value": "269" }, { "key": "Connection", "value": "keep-alive" }, - { - "key": "Vary", - "value": "Accept-Encoding" - }, { "key": "X-RateLimit-Limit-hour", "value": "1000000" }, { "key": "X-RateLimit-Remaining-hour", - "value": "999996" + "value": "999997" }, { "key": "request-time", - "value": "12" + "value": "33" }, { "key": "x-envoy-upstream-service-time", - "value": "14" + "value": "35" }, { "key": "Access-Control-Allow-Origin", @@ -1092,26 +1096,22 @@ }, { "key": "X-Kong-Upstream-Latency", - "value": "18" + "value": "38" }, { "key": "X-Kong-Proxy-Latency", - "value": "116" + "value": "12" }, { "key": "Via", "value": "kong/0.14.1" - }, - { - "key": "Content-Encoding", - "value": "gzip" } ], "cookie": [], - "body": "{\n \"id\": \"api.content.update\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:39:22ZZ\",\n \"params\": {\n \"resmsgid\": \"e60a2a4d-c8f9-4f10-bc66-695e0f86d82c\",\n \"msgid\": null,\n \"err\": \"NOT_FOUND\",\n \"status\": \"failed\",\n \"errmsg\": \"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: do_213584064677732352111\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + "body": "{\n \"id\": \"api.content.update\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:38:11ZZ\",\n \"params\": {\n \"resmsgid\": \"0237c6eb-9d37-474f-aed5-c83aee24116b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Invalid version Key\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": null\n }\n}" }, { - "name": "Content Update - 200 OK", + "name": "Content Update - 404 Not Found", "originalRequest": { "method": "PATCH", "header": [ @@ -1158,13 +1158,13 @@ ] } }, - "status": "OK", - "code": 200, + "status": "Not Found", + "code": 404, "_postman_previewlanguage": "json", "header": [ { "key": "Date", - "value": "Tue, 19 Jul 2022 06:44:37 GMT" + "value": "Tue, 19 Jul 2022 06:39:22 GMT" }, { "key": "Content-Type", @@ -1188,15 +1188,15 @@ }, { "key": "X-RateLimit-Remaining-hour", - "value": "999995" + "value": "999996" }, { "key": "request-time", - "value": "87" + "value": "12" }, { "key": "x-envoy-upstream-service-time", - "value": "88" + "value": "14" }, { "key": "Access-Control-Allow-Origin", @@ -1204,11 +1204,11 @@ }, { "key": "X-Kong-Upstream-Latency", - "value": "92" + "value": "18" }, { "key": "X-Kong-Proxy-Latency", - "value": "115" + "value": "116" }, { "key": "Via", @@ -1220,7 +1220,7 @@ } ], "cookie": [], - "body": "{\n \"id\": \"api.content.update\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:44:37ZZ\",\n \"params\": {\n \"resmsgid\": \"767eada1-6342-4503-ad0d-b18aea711aab\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658213077381\"\n }\n}" + "body": "{\n \"id\": \"api.content.update\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:39:22ZZ\",\n \"params\": {\n \"resmsgid\": \"e60a2a4d-c8f9-4f10-bc66-695e0f86d82c\",\n \"msgid\": null,\n \"err\": \"NOT_FOUND\",\n \"status\": \"failed\",\n \"errmsg\": \"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: do_213584064677732352111\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" } ] } From 10c0026942c9572b131f2c16f20eccdfc0ab1546 Mon Sep 17 00:00:00 2001 From: AmiableAnil Date: Wed, 27 Jul 2022 09:01:18 +0530 Subject: [PATCH 095/490] Issue #SB-30119 test: Added the Collections and Reports folder. --- .../Collections}/Content V2 API Test.postman_collection.json | 0 .../Collections}/Content V2.postman_collection.json | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename content-api/{api-test => api-tests/Collections}/Content V2 API Test.postman_collection.json (100%) rename content-api/{api-test => api-tests/Collections}/Content V2.postman_collection.json (100%) diff --git a/content-api/api-test/Content V2 API Test.postman_collection.json b/content-api/api-tests/Collections/Content V2 API Test.postman_collection.json similarity index 100% rename from content-api/api-test/Content V2 API Test.postman_collection.json rename to content-api/api-tests/Collections/Content V2 API Test.postman_collection.json diff --git a/content-api/api-test/Content V2.postman_collection.json b/content-api/api-tests/Collections/Content V2.postman_collection.json similarity index 100% rename from content-api/api-test/Content V2.postman_collection.json rename to content-api/api-tests/Collections/Content V2.postman_collection.json From 5f89777b163b3a59809073759acf00371b084aeb Mon Sep 17 00:00:00 2001 From: AmiableAnil Date: Wed, 27 Jul 2022 11:08:15 +0530 Subject: [PATCH 096/490] Issue #SB-30119 test: Added the Collections and Reports for content v2 APIs. --- ...ontent V2 API Test.postman_collection.json | 1044 +++++-- .../Content V2 API Test.postman_test_run.json | 2574 +++++++++++++++++ 2 files changed, 3395 insertions(+), 223 deletions(-) create mode 100644 content-api/api-tests/Reports/Content V2 API Test.postman_test_run.json diff --git a/content-api/api-tests/Collections/Content V2 API Test.postman_collection.json b/content-api/api-tests/Collections/Content V2 API Test.postman_collection.json index 77efae5c8..583e76895 100644 --- a/content-api/api-tests/Collections/Content V2 API Test.postman_collection.json +++ b/content-api/api-tests/Collections/Content V2 API Test.postman_collection.json @@ -14,7 +14,7 @@ "name": "Content Create ", "item": [ { - "name": "Content Create - with minimal required attribute", + "name": "200 - With minimal required attribute", "event": [ { "listen": "test", @@ -93,7 +93,174 @@ "response": [] }, { - "name": "Content Create - Special character in name", + "name": "200 - With identifier", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json();", + "", + "pm.test(\"Verify keys and data type of the response result\", () => {", + " pm.expect(jsonResponse.result).to.have.all.keys('identifier', 'node_id', 'versionKey');", + " pm.expect(jsonResponse.result.identifier).to.be.a(\"string\");", + " pm.expect(jsonResponse.result.node_id).to.be.a(\"string\");", + " pm.expect(jsonResponse.result.versionKey).to.be.a(\"string\");", + "});", + "", + "//Validation", + "// Response body", + "pm.test(\"id should be api.content.create\", function () {", + " pm.expect(jsonResponse.id).to.eql(\"api.content.create\");", + "});", + "pm.test(\"ver should be 4.0\", function () {", + " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", + "});", + "", + "// Result", + "pm.test(`identifier should be equal to ${JSON.parse(request.data).request.content.identifier}`, () => {", + " pm.expect(jsonResponse.result.identifier).to.eql(`${JSON.parse(request.data).request.content.identifier}`);", + "});", + "pm.test(`node_id should be equal to ${JSON.parse(request.data).request.content.identifier}`, () => {", + " pm.expect(jsonResponse.result.node_id).to.eql(`${JSON.parse(request.data).request.content.identifier}`);", + "});", + "", + "// Set global variable", + "pm.globals.set(\"content_id\", jsonResponse.result.identifier);", + "pm.globals.set(\"versionKey\", jsonResponse.result.versionKey);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": { + "connection": true + } + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"identifier\": \"some_identifier\",\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + }, + { + "name": "200 - With external fields", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json();", + "", + "pm.test(\"Verify keys and data type of the response result\", () => {", + " pm.expect(jsonResponse.result).to.have.all.keys('identifier', 'node_id', 'versionKey');", + " pm.expect(jsonResponse.result.identifier).to.be.a(\"string\");", + " pm.expect(jsonResponse.result.node_id).to.be.a(\"string\");", + " pm.expect(jsonResponse.result.versionKey).to.be.a(\"string\");", + "});", + "", + "//Validation", + "pm.test(\"id should be api.content.create\", function () {", + " pm.expect(jsonResponse.id).to.eql(\"api.content.create\");", + "});", + "pm.test(\"ver should be 4.0\", function () {", + " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", + "});", + "", + "// Set global variable", + "pm.globals.set(\"content_id\", jsonResponse.result.identifier);", + "pm.globals.set(\"versionKey\", jsonResponse.result.versionKey);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": { + "connection": true + } + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\",\n \"body\": \"{\\\"key\\\":\\\"value\\\"}\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + }, + { + "name": "200 - Special character in name", "event": [ { "listen": "test", @@ -177,7 +344,7 @@ "name": "Content Read", "item": [ { - "name": "Content Read", + "name": "200 - Get all data except external data", "event": [ { "listen": "test", @@ -267,7 +434,7 @@ "response": [] }, { - "name": "Content Read - fields query params", + "name": "200 - Get specific attributes by sending fields query params", "event": [ { "listen": "test", @@ -328,7 +495,7 @@ } ], "url": { - "raw": "{{host}}/api/content/v2/read/{{content_id}}?fields=name,primaryCategory", + "raw": "{{host}}/api/content/v2/read/{{content_id}}?fields=name,primaryCategory,body", "host": [ "{{host}}" ], @@ -342,7 +509,8 @@ "query": [ { "key": "fields", - "value": "name,primaryCategory" + "value": "name,primaryCategory,body", + "description": "Fields param is used to get the external data in response, by default read API doesn't return external data. Also fields can be used to get the specific data only in response instead of getting all the data." } ] }, @@ -376,7 +544,100 @@ "name": "Content Update", "item": [ { - "name": "Content Update", + "name": "200 - Update existing data or add new data", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json();", + "", + "pm.test(\"Verify keys and data type of the response result\", () => {", + " pm.expect(jsonResponse.result).to.have.all.keys('identifier', 'node_id', 'versionKey');", + " pm.expect(jsonResponse.result.identifier).to.be.a(\"string\");", + " pm.expect(jsonResponse.result.node_id).to.be.a(\"string\");", + " pm.expect(jsonResponse.result.versionKey).to.be.a(\"string\");", + "});", + "", + "//Validation", + "pm.test(\"id should be api.content.update\", () => {", + " pm.expect(jsonResponse.id).to.eql(\"api.content.update\");", + "});", + "pm.test(\"ver should be 4.0\", () => {", + " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", + "});", + "", + "pm.test(\"identifier should be equal to \" + pm.globals.get(\"content_id\"), () => {", + " pm.expect(jsonResponse.result.identifier).to.eql(pm.globals.get(\"content_id\"));", + "});", + "pm.test(\"node_id should be equal to \" + pm.globals.get(\"content_id\"), () => {", + " pm.expect(jsonResponse.result.node_id).to.eql(pm.globals.get(\"content_id\"));", + "});", + "", + "// Set global variable", + "pm.globals.set(\"versionKey\", jsonResponse.result.versionKey);", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "// pm.globals.set('content_id');" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "type": "text" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"{{versionKey}}\",\n \"description\": \"dsd\",\n \"resourceType\": \"Learn\",\n \"framework\": \"ekstep_ncert_k-12\",\n \"name\": \"API DOCUMENTATION CONTENT\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/update/{{content_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "update", + "{{content_id}}" + ] + }, + "description": "This API is associated with updating a batch on the Sunbird Platform.\nThe endpoint for Course Batch Update is /batch/update\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + }, + { + "name": "200 - Special character in name", "event": [ { "listen": "test", @@ -392,54 +653,498 @@ "});", "", "//Validation", - "pm.test(\"id should be api.content.update\", () => {", - " pm.expect(jsonResponse.id).to.eql(\"api.content.update\");", - "});", - "pm.test(\"ver should be 4.0\", () => {", - " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", + "pm.test(\"id should be api.content.update\", () => {", + " pm.expect(jsonResponse.id).to.eql(\"api.content.update\");", + "});", + "pm.test(\"ver should be 4.0\", () => {", + " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", + "});", + "", + "pm.test(\"identifier should be equal to \" + pm.globals.get(\"content_id\"), () => {", + " pm.expect(jsonResponse.result.identifier).to.eql(pm.globals.get(\"content_id\"));", + "});", + "pm.test(\"node_id should be equal to \" + pm.globals.get(\"content_id\"), () => {", + " pm.expect(jsonResponse.result.node_id).to.eql(pm.globals.get(\"content_id\"));", + "});", + "", + "// Set global variable", + "pm.globals.set(\"versionKey\", jsonResponse.result.versionKey);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "type": "text" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"{{versionKey}}\",\n \"description\": \"dsd\",\n \"name\": \"~`S!@#$%^&*()_+=:;\\\"'<,>.?/\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/update/{{content_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "update", + "{{content_id}}" + ] + }, + "description": "This API is associated with updating a batch on the Sunbird Platform.\nThe endpoint for Course Batch Update is /batch/update\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "let jsonResponse = pm.response.json();", + "", + "//Verification", + "pm.test(\"Status code is 200\", () => {", + " pm.response.to.have.status(200)", + "});", + "//code name contains a string", + "tests[\"Status code name has string OK\"] = responseCode.name.has(\"OK\");", + "", + "// pm.test(\"Content-Encoding header is present\", () => {", + "// pm.response.to.have.header(\"Content-Encoding\");", + "// });", + "// pm.test(\"Content-Encoding header is gzip\", () => {", + "// pm.expect(pm.response.headers.get('Content-Encoding')).to.eql('gzip');", + "// });", + "", + "pm.test(\"Verify keys and data type of the params in positive scenario\", () => {", + " pm.expect(jsonResponse.params.resmsgid).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.msgid).to.be.null;", + " pm.expect(jsonResponse.params.err).to.be.null;", + " pm.expect(jsonResponse.params.status).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.errmsg).to.be.null;", + "});", + "", + "pm.test(\"params.status should be \\\"successful\\\"\", () => {", + " pm.expect(jsonResponse.params.status).to.eql(\"successful\");", + "});", + "pm.test(\"responseCode should be OK\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"OK\");", + "});" + ] + } + } + ] + }, + { + "name": "Negative", + "item": [ + { + "name": "Content Create", + "item": [ + { + "name": "400 - missing channel", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 400\", () => {", + " pm.response.to.have.status(400)", + "});", + "//code name contains a string", + "tests[\"Status code name has string \\\"Bad Request\\\"\"] = responseCode.name.has(\"Bad Request\");", + "", + "pm.test(\"Verify keys and data type of the response result\", () => {", + " pm.expect(jsonResponse.result).to.be.an(\"object\");", + " pm.expect(jsonResponse.result).to.have.all.keys('messages');", + " pm.expect(jsonResponse.result.messages).to.not.be.empty;", + "});", + "", + "//Validation", + "// Response body", + "pm.test(\"responseCode should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"CLIENT_ERROR\");", + "});", + "", + "// Params", + "pm.test(\"params.err should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.params.err).to.eql(\"CLIENT_ERROR\");", + "});", + "pm.test(\"params.errmsg should be \\\"Validation Errors\\\"\", () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(\"Validation Errors\");", + "});", + "", + "// Result", + "pm.test(\"result body should include messages \\\"Required Metadata channel not set\\\"\", () => {", + " pm.expect(jsonResponse.result.messages).to.have.length(1)", + " pm.expect(jsonResponse.result.messages).to.include('Required Metadata channel not set');", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": { + "connection": true + } + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"createdBy\": \"530b19ea-dc8d-4cc7-a4b5-0c0214c8113a\",\n \"resourceType\": \"Learn\",\n \"framework\": \"ekstep_ncert_k-12\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + }, + { + "name": "400 - empty channel", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 400\", () => {", + " pm.response.to.have.status(400)", + "});", + "//code name contains a string", + "tests[\"Status code name has string \\\"Bad Request\\\"\"] = responseCode.name.has(\"Bad Request\");", + "", + "pm.test(\"Verify keys and data type of the response result\", () => {", + " pm.expect(jsonResponse.result).to.be.an(\"object\");", + " pm.expect(jsonResponse.result).to.have.all.keys('messages');", + " pm.expect(jsonResponse.result.messages).to.not.be.empty;", + "});", + "", + "//Validation", + "// Response body", + "pm.test(\"responseCode should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"CLIENT_ERROR\");", + "});", + "", + "// Params", + "pm.test(\"params.err should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.params.err).to.eql(\"CLIENT_ERROR\");", + "});", + "pm.test(\"params.errmsg should be \\\"Validation Errors\\\"\", () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(\"Validation Errors\");", + "});", + "", + "// Result", + "pm.test(\"result body should include messages \\\"Required Metadata channel not set\\\"\", () => {", + " pm.expect(jsonResponse.result.messages).to.have.length(1)", + " pm.expect(jsonResponse.result.messages).to.include('Required Metadata channel not set');", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": { + "connection": true + } + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "X-Channel-Id", + "value": "", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"createdBy\": \"530b19ea-dc8d-4cc7-a4b5-0c0214c8113a\",\n \"resourceType\": \"Learn\",\n \"framework\": \"ekstep_ncert_k-12\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + }, + { + "name": "400 - missing name", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 400\", () => {", + " pm.response.to.have.status(400)", + "});", + "//code name contains a string", + "tests[\"Status code name has string \\\"Bad Request\\\"\"] = responseCode.name.has(\"Bad Request\");", + "", + "pm.test(\"Verify keys and data type of the response result\", () => {", + " pm.expect(jsonResponse.result).to.be.an(\"object\");", + " pm.expect(jsonResponse.result).to.have.all.keys('messages');", + " pm.expect(jsonResponse.result.messages).to.not.be.empty;", + "});", + "", + "//Validation", + "// Response body", + "pm.test(\"responseCode should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"CLIENT_ERROR\");", + "});", + "", + "// Params", + "pm.test(\"params.err should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.params.err).to.eql(\"CLIENT_ERROR\");", + "});", + "pm.test(\"params.errmsg should be \\\"Validation Errors\\\"\", () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(\"Validation Errors\");", + "});", + "", + "// Result", + "pm.test(\"result body should include messages \\\"Required Metadata name not set\\\"\", () => {", + " pm.expect(jsonResponse.result.messages).to.have.length(1)", + " pm.expect(jsonResponse.result.messages).to.include('Required Metadata name not set');", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": { + "connection": true + } + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + }, + { + "name": "400 - name: invalid datatype", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 400\", () => {", + " pm.response.to.have.status(400)", + "});", + "//code name contains a string", + "tests[\"Status code name has string \\\"Bad Request\\\"\"] = responseCode.name.has(\"Bad Request\");", + "", + "pm.test(\"Verify keys and data type of the response result\", () => {", + " pm.expect(jsonResponse.result).to.be.an(\"object\");", + " pm.expect(jsonResponse.result).to.have.all.keys('messages');", + " pm.expect(jsonResponse.result.messages).to.not.be.empty;", + "});", + "", + "//Validation", + "// Response body", + "pm.test(\"responseCode should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"CLIENT_ERROR\");", "});", "", - "pm.test(\"identifier should be equal to \" + pm.globals.get(\"content_id\"), () => {", - " pm.expect(jsonResponse.result.identifier).to.eql(pm.globals.get(\"content_id\"));", + "// Params", + "pm.test(\"params.err should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.params.err).to.eql(\"CLIENT_ERROR\");", "});", - "pm.test(\"node_id should be equal to \" + pm.globals.get(\"content_id\"), () => {", - " pm.expect(jsonResponse.result.node_id).to.eql(pm.globals.get(\"content_id\"));", + "pm.test(\"params.errmsg should be \\\"Validation Errors\\\"\", () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(\"Validation Errors\");", "});", "", - "// Set global variable", - "pm.globals.set(\"versionKey\", jsonResponse.result.versionKey);", + "// Result", + "pm.test(\"result body should include messages \\\"Metadata name should be a/an String value\\\"\", () => {", + " pm.expect(jsonResponse.result.messages).to.have.length(1)", + " pm.expect(jsonResponse.result.messages).to.include('Metadata name should be a/an String value');", + "});", "" ], "type": "text/javascript" } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "// pm.globals.set('content_id');" - ], - "type": "text/javascript" - } } ], + "protocolProfileBehavior": { + "disabledSystemHeaders": { + "connection": true + } + }, "request": { - "method": "PATCH", + "method": "POST", "header": [ { "key": "Content-Type", - "value": "application/json", - "type": "text" + "value": "application/json" }, { "key": "X-Authenticated-User-token", - "value": "{{keycloak_access_token}}", - "type": "text" + "value": "{{keycloak_access_token}}" }, { "key": "Authorization", - "value": "{{kong_api_key}}", - "type": "text" + "value": "{{kong_api_key}}" }, { "key": "X-Channel-Id", @@ -449,10 +1154,10 @@ ], "body": { "mode": "raw", - "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"{{versionKey}}\",\n \"lastUpdatedBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\",\n \"description\": \"dsd\",\n \"createdBy\": \"530b19ea-dc8d-4cc7-a4b5-0c0214c8113a\",\n \"resourceType\": \"Learn\",\n \"framework\": \"ekstep_ncert_k-12\",\n \"name\": \"API DOCUMENTATION CONTENT\"\n }\n }\n}" + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": 423432,\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" }, "url": { - "raw": "{{host}}/api/content/v2/update/{{content_id}}", + "raw": "{{host}}/api/content/v2/create", "host": [ "{{host}}" ], @@ -460,16 +1165,15 @@ "api", "content", "v2", - "update", - "{{content_id}}" + "create" ] }, - "description": "This API is associated with updating a batch on the Sunbird Platform.\nThe endpoint for Course Batch Update is /batch/update\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." }, "response": [] }, { - "name": "Content Update - Special character in name", + "name": "400 - name empty", "event": [ { "listen": "test", @@ -478,64 +1182,36 @@ "let jsonResponse = pm.response.json()", "", "//Verification", - "pm.test(\"Status code is 404\", function () {", - " pm.response.to.have.status(404)", + "pm.test(\"Status code is 400\", function () {", + " pm.response.to.have.status(400)", "});", "//code name contains a string", - "tests[\"Status code name has string \\\"Not Found\\\"\"] = responseCode.name.has(\"Not Found\");", - "", - "pm.test(\"Content-Type header is present\", () => {", - " pm.response.to.have.header(\"Content-Type\");", - "});", - "pm.test(\"Content-Type header is application/json\", () => {", - " pm.expect(pm.response.headers.get('Content-Type')).to.eql('application/json');", - "});", - "pm.test(\"Content-Encoding header should not present\", () => {", - " pm.response.to.have.header(\"Content-Encoding\");", - "});", - "pm.test(\"Content-Encoding header is gzip\", () => {", - " pm.expect(pm.response.headers.get('Content-Encoding')).to.eql('gzip');", - "});", + "tests[\"Status code name has string \\\"Bad Request\\\"\"] = responseCode.name.has(\"Bad Request\");", "", "pm.test(\"Verify keys and data type of the response result\", () => {", - " pm.expect(jsonResponse).to.be.an(\"object\").that.has.all.keys('id', 'ver', 'ts', 'params', 'responseCode', 'result');", - " pm.expect(jsonResponse.id).to.be.a(\"string\");", - " pm.expect(jsonResponse.ver).to.be.a(\"string\");", - " pm.expect(jsonResponse.ts).to.be.a(\"string\");", - " pm.expect(jsonResponse.responseCode).to.be.a(\"string\");", - " pm.expect(jsonResponse.params).to.be.an(\"object\").that.has.all.keys('resmsgid', 'msgid', 'err', 'status', 'errmsg');", - " pm.expect(jsonResponse.params.resmsgid).to.be.a(\"string\");", - " pm.expect(jsonResponse.params.msgid).to.be.null;", - " pm.expect(jsonResponse.params.err).to.be.a(\"string\");", - " pm.expect(jsonResponse.params.status).to.be.a(\"string\");", - " pm.expect(jsonResponse.params.errmsg).to.be.a(\"string\");", " pm.expect(jsonResponse.result).to.be.an(\"object\");", " pm.expect(jsonResponse.result).to.have.all.keys('messages');", - " pm.expect(jsonResponse.result.messages).to.be.null;", + " pm.expect(jsonResponse.result.messages).to.not.be.empty;", "});", "", "//Validation", "// Response body", - "pm.test(\"id should be api.content.update\", () => {", - " pm.expect(jsonResponse.id).to.eql(\"api.content.update\");", - "});", - "pm.test(\"ver should be 4.0\", () => {", - " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", - "});", - "pm.test(\"params.err should be \\\"404\\\"\", () => {", - " pm.expect(jsonResponse.params.err).to.eql(\"404\");", - "});", - "pm.test(\"params.status should be \\\"failed\\\"\", () => {", - " pm.expect(jsonResponse.params.status).to.eql(\"failed\");", + "pm.test(\"responseCode should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"CLIENT_ERROR\");", "});", - "pm.test(\"params.errmsg should be \\\"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\\\"\", () => {", - " pm.expect(jsonResponse.params.errmsg).to.eql(\"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\");", + "", + "// Params", + "pm.test(\"params.err should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.params.err).to.eql(\"CLIENT_ERROR\");", "});", - "pm.test(\"responseCode should be RESOURCE_NOT_FOUND\", () => {", - " pm.expect(jsonResponse.responseCode).to.eql(\"RESOURCE_NOT_FOUND\");", + "pm.test(\"params.errmsg should be \\\"Validation Errors\\\"\", () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(\"Validation Errors\");", "});", - "pm.test(\"messages should be null\", () => {", - " pm.expect(jsonResponse.result.messages).to.eql(null);", + "", + "// Result", + "pm.test(\"result body should include messages \\\"Required Metadata name not set\\\"\", () => {", + " pm.expect(jsonResponse.result.messages).to.have.length(1)", + " pm.expect(jsonResponse.result.messages).to.include('Required Metadata name not set');", "});", "" ], @@ -543,23 +1219,25 @@ } } ], + "protocolProfileBehavior": { + "disabledSystemHeaders": { + "connection": true + } + }, "request": { - "method": "PATCH", + "method": "POST", "header": [ { "key": "Content-Type", - "value": "application/json", - "type": "text" + "value": "application/json" }, { "key": "X-Authenticated-User-token", - "value": "{{keycloak_access_token}}", - "type": "text" + "value": "{{keycloak_access_token}}" }, { "key": "Authorization", - "value": "{{kong_api_key}}", - "type": "text" + "value": "{{kong_api_key}}" }, { "key": "X-Channel-Id", @@ -569,10 +1247,10 @@ ], "body": { "mode": "raw", - "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"{{versionKey}}\",\n \"description\": \"dsd\",\n \"name\": \"~`S!@#$%^&*()_+=:;\\\"'<,>.?/\"\n }\n }\n}" + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" }, "url": { - "raw": "{{host}}/api/content/v2/update/{{content_id}}", + "raw": "{{host}}/api/content/v2/create", "host": [ "{{host}}" ], @@ -580,95 +1258,15 @@ "api", "content", "v2", - "update", - "{{content_id}}" + "create" ] }, - "description": "This API is associated with updating a batch on the Sunbird Platform.\nThe endpoint for Course Batch Update is /batch/update\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." }, "response": [] - } - ], - "event": [ - { - "listen": "prerequest", - "script": { - "type": "text/javascript", - "exec": [ - "" - ] - } }, { - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "" - ] - } - } - ] - } - ], - "event": [ - { - "listen": "prerequest", - "script": { - "type": "text/javascript", - "exec": [ - "" - ] - } - }, - { - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "let jsonResponse = pm.response.json();", - "", - "//Verification", - "pm.test(\"Status code is 200\", () => {", - " pm.response.to.have.status(200)", - "});", - "//code name contains a string", - "tests[\"Status code name has string OK\"] = responseCode.name.has(\"OK\");", - "", - "// pm.test(\"Content-Encoding header is present\", () => {", - "// pm.response.to.have.header(\"Content-Encoding\");", - "// });", - "// pm.test(\"Content-Encoding header is gzip\", () => {", - "// pm.expect(pm.response.headers.get('Content-Encoding')).to.eql('gzip');", - "// });", - "", - "pm.test(\"Verify keys and data type of the params in positive scenario\", () => {", - " pm.expect(jsonResponse.params.resmsgid).to.be.a(\"string\");", - " pm.expect(jsonResponse.params.msgid).to.be.null;", - " pm.expect(jsonResponse.params.err).to.be.null;", - " pm.expect(jsonResponse.params.status).to.be.a(\"string\");", - " pm.expect(jsonResponse.params.errmsg).to.be.null;", - "});", - "", - "pm.test(\"params.status should be \\\"successful\\\"\", () => {", - " pm.expect(jsonResponse.params.status).to.eql(\"successful\");", - "});", - "pm.test(\"responseCode should be OK\", () => {", - " pm.expect(jsonResponse.responseCode).to.eql(\"OK\");", - "});" - ] - } - } - ] - }, - { - "name": "Negative", - "item": [ - { - "name": "Content Create", - "item": [ - { - "name": "Content Create - 400 - missing name", + "name": "400 - missing code", "event": [ { "listen": "test", @@ -677,7 +1275,7 @@ "let jsonResponse = pm.response.json()", "", "//Verification", - "pm.test(\"Status code is 400\", () => {", + "pm.test(\"Status code is 400\", function () {", " pm.response.to.have.status(400)", "});", "//code name contains a string", @@ -704,9 +1302,9 @@ "});", "", "// Result", - "pm.test(\"result body should include messages \\\"Required Metadata name not set\\\"\", () => {", + "pm.test(\"result body should include messages \\\"Required Metadata code not set\\\"\", () => {", " pm.expect(jsonResponse.result.messages).to.have.length(1)", - " pm.expect(jsonResponse.result.messages).to.include('Required Metadata name not set');", + " pm.expect(jsonResponse.result.messages).to.include('Required Metadata code not set');", "});", "" ], @@ -742,7 +1340,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" }, "url": { "raw": "{{host}}/api/content/v2/create", @@ -761,7 +1359,7 @@ "response": [] }, { - "name": "Content Create - 400 - name empty", + "name": "400 - missing mimeType", "event": [ { "listen": "test", @@ -797,9 +1395,9 @@ "});", "", "// Result", - "pm.test(\"result body should include messages \\\"Required Metadata name not set\\\"\", () => {", + "pm.test(\"result body should include messages \\\"Required Metadata mimeType not set\\\"\", () => {", " pm.expect(jsonResponse.result.messages).to.have.length(1)", - " pm.expect(jsonResponse.result.messages).to.include('Required Metadata name not set');", + " pm.expect(jsonResponse.result.messages).to.include('Required Metadata mimeType not set');", "});", "" ], @@ -835,7 +1433,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" }, "url": { "raw": "{{host}}/api/content/v2/create", @@ -854,7 +1452,7 @@ "response": [] }, { - "name": "Content Create - 400 - missing code", + "name": "400 - invalid mimeType", "event": [ { "listen": "test", @@ -890,9 +1488,9 @@ "});", "", "// Result", - "pm.test(\"result body should include messages \\\"Required Metadata code not set\\\"\", () => {", + "pm.test(\"result body should include messages \\\"Metadata mimeType should be one of: [application/vnd.ekstep.ecml-archive, application/vnd.ekstep.html-archive, application/vnd.android.package-archive, application/vnd.ekstep.content-archive, application/vnd.ekstep.content-collection, application/vnd.ekstep.plugin-archive, application/vnd.ekstep.h5p-archive, application/epub, text/x-url, video/x-youtube, application/octet-stream, application/msword, application/pdf, image/jpeg, image/jpg, image/png, image/tiff, image/bmp, image/gif, image/svg+xml, video/avi, video/mpeg, video/quicktime, video/3gpp, video/mp4, video/ogg, video/webm, audio/mp3, audio/mp4, audio/mpeg, audio/ogg, audio/webm, audio/x-wav, audio/wav, application/json, application/quiz]\\\"\", () => {", " pm.expect(jsonResponse.result.messages).to.have.length(1)", - " pm.expect(jsonResponse.result.messages).to.include('Required Metadata code not set');", + " pm.expect(jsonResponse.result.messages).to.include('Metadata mimeType should be one of: [application/vnd.ekstep.ecml-archive, application/vnd.ekstep.html-archive, application/vnd.android.package-archive, application/vnd.ekstep.content-archive, application/vnd.ekstep.content-collection, application/vnd.ekstep.plugin-archive, application/vnd.ekstep.h5p-archive, application/epub, text/x-url, video/x-youtube, application/octet-stream, application/msword, application/pdf, image/jpeg, image/jpg, image/png, image/tiff, image/bmp, image/gif, image/svg+xml, video/avi, video/mpeg, video/quicktime, video/3gpp, video/mp4, video/ogg, video/webm, audio/mp3, audio/mp4, audio/mpeg, audio/ogg, audio/webm, audio/x-wav, audio/wav, application/json, application/quiz]');", "});", "" ], @@ -928,7 +1526,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"some-mimeType\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" }, "url": { "raw": "{{host}}/api/content/v2/create", @@ -947,7 +1545,7 @@ "response": [] }, { - "name": "Content Create - 400 - missing mimeType", + "name": "400 - missing primaryCategory", "event": [ { "listen": "test", @@ -956,7 +1554,7 @@ "let jsonResponse = pm.response.json()", "", "//Verification", - "pm.test(\"Status code is 400\", function () {", + "pm.test(\"Status code is 400\", () => {", " pm.response.to.have.status(400)", "});", "//code name contains a string", @@ -964,8 +1562,6 @@ "", "pm.test(\"Verify keys and data type of the response result\", () => {", " pm.expect(jsonResponse.result).to.be.an(\"object\");", - " pm.expect(jsonResponse.result).to.have.all.keys('messages');", - " pm.expect(jsonResponse.result.messages).to.not.be.empty;", "});", "", "//Validation", @@ -975,17 +1571,17 @@ "});", "", "// Params", - "pm.test(\"params.err should be \\\"CLIENT_ERROR\\\"\", () => {", - " pm.expect(jsonResponse.params.err).to.eql(\"CLIENT_ERROR\");", + "pm.test(\"params.err should be \\\"VALIDATION_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.params.err).to.eql(\"VALIDATION_ERROR\");", "});", - "pm.test(\"params.errmsg should be \\\"Validation Errors\\\"\", () => {", - " pm.expect(jsonResponse.params.errmsg).to.eql(\"Validation Errors\");", + "pm.test(\"params.errmsg should be \\\"primaryCategory is a mandatory parameter\\\"\", () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(\"primaryCategory is a mandatory parameter\");", "});", "", "// Result", - "pm.test(\"result body should include messages \\\"Required Metadata mimeType not set\\\"\", () => {", - " pm.expect(jsonResponse.result.messages).to.have.length(1)", - " pm.expect(jsonResponse.result.messages).to.include('Required Metadata mimeType not set');", + "pm.test(\"result body should be empty\", () => {", + " pm.expect(jsonResponse.result).to.eql({});", + " pm.expect(jsonResponse.result).to.be.empty;", "});", "" ], @@ -1021,7 +1617,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\"\n }\n }\n}" }, "url": { "raw": "{{host}}/api/content/v2/create", @@ -1040,7 +1636,7 @@ "response": [] }, { - "name": "Content Create - 400 - missing primaryCategory", + "name": "400 - Invalid subject", "event": [ { "listen": "test", @@ -1057,6 +1653,8 @@ "", "pm.test(\"Verify keys and data type of the response result\", () => {", " pm.expect(jsonResponse.result).to.be.an(\"object\");", + " pm.expect(jsonResponse.result).to.have.all.keys('messages');", + " pm.expect(jsonResponse.result.messages).to.not.be.empty;", "});", "", "//Validation", @@ -1066,17 +1664,17 @@ "});", "", "// Params", - "pm.test(\"params.err should be \\\"VALIDATION_ERROR\\\"\", () => {", - " pm.expect(jsonResponse.params.err).to.eql(\"VALIDATION_ERROR\");", + "pm.test(\"params.err should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.params.err).to.eql(\"CLIENT_ERROR\");", "});", - "pm.test(\"params.errmsg should be \\\"primaryCategory is a mandatory parameter\\\"\", () => {", - " pm.expect(jsonResponse.params.errmsg).to.eql(\"primaryCategory is a mandatory parameter\");", + "pm.test(\"params.errmsg should be \\\"Validation Errors.\\\"\", () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(\"Validation Errors.\");", "});", "", "// Result", - "pm.test(\"result body should be empty\", () => {", - " pm.expect(jsonResponse.result).to.eql({});", - " pm.expect(jsonResponse.result).to.be.empty;", + "pm.test(\"result body should include messages \\\"subject range data is empty from the given framework.\\\"\", () => {", + " pm.expect(jsonResponse.result.messages).to.have.length(1)", + " pm.expect(jsonResponse.result.messages).to.include('subject range data is empty from the given framework.');", "});", "" ], @@ -1112,7 +1710,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\"\n }\n }\n}" + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"createdBy\": \"530b19ea-dc8d-4cc7-a4b5-0c0214c8113a\",\n \"resourceType\": \"Learn\",\n \"framework\": \"ekstep_ncert_k-12-erwe\",\n \"subject\": [\"test\"],\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" }, "url": { "raw": "{{host}}/api/content/v2/create", @@ -1131,7 +1729,7 @@ "response": [] }, { - "name": "Content Create - 404 - Invalid primaryCategory", + "name": "404 - Invalid primaryCategory", "event": [ { "listen": "test", @@ -1254,7 +1852,7 @@ "name": "Content Read", "item": [ { - "name": "Content Read - 404 - Not Found", + "name": "404 - Not Found", "event": [ { "listen": "test", @@ -1374,7 +1972,7 @@ "name": "Content Update", "item": [ { - "name": "Content Update - 400 - Invalid versionKey", + "name": "400 - Invalid versionKey", "event": [ { "listen": "test", @@ -1429,7 +2027,7 @@ "listen": "prerequest", "script": { "exec": [ - "pm.globals.set('versionKey', '3534534543');", + "pm.globals.set('invalid-versionKey', '3534534543');", "// pm.globals.set(\"content_id\", '');" ], "type": "text/javascript" @@ -1462,7 +2060,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"{{versionKey}}\",\n \"lastUpdatedBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\",\n \"description\": \"dsd\",\n \"name\": \"API DOCUMENTATION CONTENT\"\n }\n }\n}" + "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"{{invalid-versionKey}}\",\n \"lastUpdatedBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\",\n \"description\": \"dsd\",\n \"name\": \"API DOCUMENTATION CONTENT\"\n }\n }\n}" }, "url": { "raw": "{{host}}/api/content/v2/update/{{content_id}}", @@ -1482,7 +2080,7 @@ "response": [] }, { - "name": "Content Update - 400 - Empty name", + "name": "400 - Empty name", "event": [ { "listen": "test", @@ -1574,7 +2172,7 @@ "response": [] }, { - "name": "Content Update - 404 - Invalid content id", + "name": "404 - Invalid content id", "event": [ { "listen": "test", @@ -1605,8 +2203,8 @@ "pm.test(\"params.err should be NOT_FOUND\", () => {", " pm.expect(jsonResponse.params.err).to.eql(\"NOT_FOUND\");", "});", - "pm.test(`params.errmsg should be \\\"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: ${pm.globals.get(\"content_id\")}\\\"`, () => {", - " pm.expect(jsonResponse.params.errmsg).to.eql(`Error! Node(s) doesn't Exists. | [Invalid Node Id.]: ${pm.globals.get(\"content_id\")}`);", + "pm.test(`params.errmsg should be \\\"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: ${pm.globals.get(\"invalid_content_id\")}\\\"`, () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(`Error! Node(s) doesn't Exists. | [Invalid Node Id.]: ${pm.globals.get(\"invalid_content_id\")}`);", "});", "", "// Result", @@ -1622,7 +2220,7 @@ "listen": "prerequest", "script": { "exec": [ - "pm.globals.set(\"content_id\", 'do_21357909972698726412211');" + "pm.globals.set(\"invalid_content_id\", 'do_21357909972698726412211');" ], "type": "text/javascript" } @@ -1657,7 +2255,7 @@ "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"{{versionKey}}\",\n \"lastUpdatedBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\",\n \"description\": \"dsd\",\n \"name\": \"API DOCUMENTATION CONTENT\"\n }\n }\n}" }, "url": { - "raw": "{{host}}/api/content/v2/update/{{content_id}}", + "raw": "{{host}}/api/content/v2/update/{{invalid_content_id}}", "host": [ "{{host}}" ], @@ -1666,7 +2264,7 @@ "content", "v2", "update", - "{{content_id}}" + "{{invalid_content_id}}" ] }, "description": "This API is associated with updating a batch on the Sunbird Platform.\nThe endpoint for Course Batch Update is /batch/update\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." @@ -1674,7 +2272,7 @@ "response": [] }, { - "name": "Content Update - 404 - Invalid primaryCategory", + "name": "404 - Invalid primaryCategory", "event": [ { "listen": "test", diff --git a/content-api/api-tests/Reports/Content V2 API Test.postman_test_run.json b/content-api/api-tests/Reports/Content V2 API Test.postman_test_run.json new file mode 100644 index 000000000..aff0883c2 --- /dev/null +++ b/content-api/api-tests/Reports/Content V2 API Test.postman_test_run.json @@ -0,0 +1,2574 @@ +{ + "id": "489956f4-e5fd-4c92-8408-6a4598e10940", + "name": "Content V2 API Test", + "timestamp": "2022-07-27T05:30:40.864Z", + "collection_id": "1419050-58a713b4-b8bb-4885-9909-b23d253f8f31", + "folder_id": 0, + "environment_id": "1419050-b1be6ead-50b9-49a1-b0e2-95f479990dad", + "totalPass": 328, + "totalFail": 0, + "results": [ + { + "id": "2383265f-a48f-47dc-9538-e3c7a16b1d98", + "name": "200 - With minimal required attribute", + "url": "https://dev.knowlg.sunbird.org/api/content/v2/create", + "time": 229, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true, + "Verify keys and data type of the params in positive scenario": true, + "params.status should be \"successful\"": true, + "responseCode should be OK": true, + "Status code name has string OK": true, + "Verify keys and data type of the response result": true, + "id should be api.content.create": true, + "ver should be 4.0": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Status code is 200": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in positive scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"successful\"": { + "pass": 1, + "fail": 0 + }, + "responseCode should be OK": { + "pass": 1, + "fail": 0 + }, + "Status code name has string OK": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the response result": { + "pass": 1, + "fail": 0 + }, + "id should be api.content.create": { + "pass": 1, + "fail": 0 + }, + "ver should be 4.0": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 229 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true, + "Verify keys and data type of the params in positive scenario": true, + "params.status should be \"successful\"": true, + "responseCode should be OK": true, + "Status code name has string OK": true, + "Verify keys and data type of the response result": true, + "id should be api.content.create": true, + "ver should be 4.0": true + } + ] + }, + { + "id": "fdb1f5bc-f5b3-4c3c-b622-bd86a8f92b40", + "name": "200 - With identifier", + "url": "https://dev.knowlg.sunbird.org/api/content/v2/create", + "time": 61, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true, + "Verify keys and data type of the params in positive scenario": true, + "params.status should be \"successful\"": true, + "responseCode should be OK": true, + "Status code name has string OK": true, + "Verify keys and data type of the response result": true, + "id should be api.content.create": true, + "ver should be 4.0": true, + "identifier should be equal to some_identifier": true, + "node_id should be equal to some_identifier": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Status code is 200": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in positive scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"successful\"": { + "pass": 1, + "fail": 0 + }, + "responseCode should be OK": { + "pass": 1, + "fail": 0 + }, + "Status code name has string OK": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the response result": { + "pass": 1, + "fail": 0 + }, + "id should be api.content.create": { + "pass": 1, + "fail": 0 + }, + "ver should be 4.0": { + "pass": 1, + "fail": 0 + }, + "identifier should be equal to some_identifier": { + "pass": 1, + "fail": 0 + }, + "node_id should be equal to some_identifier": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 61 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true, + "Verify keys and data type of the params in positive scenario": true, + "params.status should be \"successful\"": true, + "responseCode should be OK": true, + "Status code name has string OK": true, + "Verify keys and data type of the response result": true, + "id should be api.content.create": true, + "ver should be 4.0": true, + "identifier should be equal to some_identifier": true, + "node_id should be equal to some_identifier": true + } + ] + }, + { + "id": "67644d61-7797-4f54-82cf-d4b7711b5282", + "name": "200 - With external fields", + "url": "https://dev.knowlg.sunbird.org/api/content/v2/create", + "time": 84, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true, + "Verify keys and data type of the params in positive scenario": true, + "params.status should be \"successful\"": true, + "responseCode should be OK": true, + "Status code name has string OK": true, + "Verify keys and data type of the response result": true, + "id should be api.content.create": true, + "ver should be 4.0": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Status code is 200": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in positive scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"successful\"": { + "pass": 1, + "fail": 0 + }, + "responseCode should be OK": { + "pass": 1, + "fail": 0 + }, + "Status code name has string OK": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the response result": { + "pass": 1, + "fail": 0 + }, + "id should be api.content.create": { + "pass": 1, + "fail": 0 + }, + "ver should be 4.0": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 84 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true, + "Verify keys and data type of the params in positive scenario": true, + "params.status should be \"successful\"": true, + "responseCode should be OK": true, + "Status code name has string OK": true, + "Verify keys and data type of the response result": true, + "id should be api.content.create": true, + "ver should be 4.0": true + } + ] + }, + { + "id": "4fa811d0-1e33-493c-95fe-39bac77bae42", + "name": "200 - Special character in name", + "url": "https://dev.knowlg.sunbird.org/api/content/v2/create", + "time": 67, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true, + "Verify keys and data type of the params in positive scenario": true, + "params.status should be \"successful\"": true, + "responseCode should be OK": true, + "Status code name has string OK": true, + "Verify keys and data type of the response result": true, + "id should be api.content.create": true, + "ver should be 4.0": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Status code is 200": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in positive scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"successful\"": { + "pass": 1, + "fail": 0 + }, + "responseCode should be OK": { + "pass": 1, + "fail": 0 + }, + "Status code name has string OK": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the response result": { + "pass": 1, + "fail": 0 + }, + "id should be api.content.create": { + "pass": 1, + "fail": 0 + }, + "ver should be 4.0": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 67 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true, + "Verify keys and data type of the params in positive scenario": true, + "params.status should be \"successful\"": true, + "responseCode should be OK": true, + "Status code name has string OK": true, + "Verify keys and data type of the response result": true, + "id should be api.content.create": true, + "ver should be 4.0": true + } + ] + }, + { + "id": "5f38fbf3-1dd6-4f5a-8796-6ca07d837d25", + "name": "200 - Get all data ecxept external data", + "url": "https://dev.knowlg.sunbird.org/api/content/v2/read/do_2135897074718965761132", + "time": 111, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true, + "Verify keys and data type of the params in positive scenario": true, + "params.status should be \"successful\"": true, + "responseCode should be OK": true, + "Status code name has string OK": true, + "Verify keys and data type of the response result": true, + "id should be api.content.read": true, + "ver should be 4.0": true, + "identifier should be equal to do_2135897074718965761132": true, + "objectType should be Content": true, + "visibility should be Default": true, + "compatibilityLevel should be 1": true, + "status should be Draft": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Status code is 200": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in positive scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"successful\"": { + "pass": 1, + "fail": 0 + }, + "responseCode should be OK": { + "pass": 1, + "fail": 0 + }, + "Status code name has string OK": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the response result": { + "pass": 1, + "fail": 0 + }, + "id should be api.content.read": { + "pass": 1, + "fail": 0 + }, + "ver should be 4.0": { + "pass": 1, + "fail": 0 + }, + "identifier should be equal to do_2135897074718965761132": { + "pass": 1, + "fail": 0 + }, + "objectType should be Content": { + "pass": 1, + "fail": 0 + }, + "visibility should be Default": { + "pass": 1, + "fail": 0 + }, + "compatibilityLevel should be 1": { + "pass": 1, + "fail": 0 + }, + "status should be Draft": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 111 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true, + "Verify keys and data type of the params in positive scenario": true, + "params.status should be \"successful\"": true, + "responseCode should be OK": true, + "Status code name has string OK": true, + "Verify keys and data type of the response result": true, + "id should be api.content.read": true, + "ver should be 4.0": true, + "identifier should be equal to do_2135897074718965761132": true, + "objectType should be Content": true, + "visibility should be Default": true, + "compatibilityLevel should be 1": true, + "status should be Draft": true + } + ] + }, + { + "id": "6cad6c08-f73b-4d00-9395-b8cbcb3cafe0", + "name": "200 - Get specific attributes by sending fields query params", + "url": "https://dev.knowlg.sunbird.org/api/content/v2/read/do_2135897074718965761132?fields=name,primaryCategory,body", + "time": 69, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true, + "Verify keys and data type of the params in positive scenario": true, + "params.status should be \"successful\"": true, + "responseCode should be OK": true, + "Status code name has string OK": true, + "Verify keys and data type of the response result": true, + "id should be api.content.read": true, + "ver should be 4.0": true, + "identifier should be equal to do_2135897074718965761132": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Status code is 200": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in positive scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"successful\"": { + "pass": 1, + "fail": 0 + }, + "responseCode should be OK": { + "pass": 1, + "fail": 0 + }, + "Status code name has string OK": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the response result": { + "pass": 1, + "fail": 0 + }, + "id should be api.content.read": { + "pass": 1, + "fail": 0 + }, + "ver should be 4.0": { + "pass": 1, + "fail": 0 + }, + "identifier should be equal to do_2135897074718965761132": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 69 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true, + "Verify keys and data type of the params in positive scenario": true, + "params.status should be \"successful\"": true, + "responseCode should be OK": true, + "Status code name has string OK": true, + "Verify keys and data type of the response result": true, + "id should be api.content.read": true, + "ver should be 4.0": true, + "identifier should be equal to do_2135897074718965761132": true + } + ] + }, + { + "id": "bcd0f26d-ca6b-4eb7-815a-e85c4044a829", + "name": "200 - Update existing data or add new data", + "url": "https://dev.knowlg.sunbird.org/api/content/v2/update/do_2135897074718965761132", + "time": 558, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true, + "Verify keys and data type of the params in positive scenario": true, + "params.status should be \"successful\"": true, + "responseCode should be OK": true, + "Status code name has string OK": true, + "Verify keys and data type of the response result": true, + "id should be api.content.update": true, + "ver should be 4.0": true, + "identifier should be equal to do_2135897074718965761132": true, + "node_id should be equal to do_2135897074718965761132": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Status code is 200": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in positive scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"successful\"": { + "pass": 1, + "fail": 0 + }, + "responseCode should be OK": { + "pass": 1, + "fail": 0 + }, + "Status code name has string OK": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the response result": { + "pass": 1, + "fail": 0 + }, + "id should be api.content.update": { + "pass": 1, + "fail": 0 + }, + "ver should be 4.0": { + "pass": 1, + "fail": 0 + }, + "identifier should be equal to do_2135897074718965761132": { + "pass": 1, + "fail": 0 + }, + "node_id should be equal to do_2135897074718965761132": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 558 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true, + "Verify keys and data type of the params in positive scenario": true, + "params.status should be \"successful\"": true, + "responseCode should be OK": true, + "Status code name has string OK": true, + "Verify keys and data type of the response result": true, + "id should be api.content.update": true, + "ver should be 4.0": true, + "identifier should be equal to do_2135897074718965761132": true, + "node_id should be equal to do_2135897074718965761132": true + } + ] + }, + { + "id": "1d43c4ab-ea8b-449c-8dca-aa7eda247bfa", + "name": "200 - Special character in name", + "url": "https://dev.knowlg.sunbird.org/api/content/v2/update/do_2135897074718965761132", + "time": 575, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true, + "Verify keys and data type of the params in positive scenario": true, + "params.status should be \"successful\"": true, + "responseCode should be OK": true, + "Status code name has string OK": true, + "Verify keys and data type of the response result": true, + "id should be api.content.update": true, + "ver should be 4.0": true, + "identifier should be equal to do_2135897074718965761132": true, + "node_id should be equal to do_2135897074718965761132": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Status code is 200": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in positive scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"successful\"": { + "pass": 1, + "fail": 0 + }, + "responseCode should be OK": { + "pass": 1, + "fail": 0 + }, + "Status code name has string OK": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the response result": { + "pass": 1, + "fail": 0 + }, + "id should be api.content.update": { + "pass": 1, + "fail": 0 + }, + "ver should be 4.0": { + "pass": 1, + "fail": 0 + }, + "identifier should be equal to do_2135897074718965761132": { + "pass": 1, + "fail": 0 + }, + "node_id should be equal to do_2135897074718965761132": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 575 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true, + "Verify keys and data type of the params in positive scenario": true, + "params.status should be \"successful\"": true, + "responseCode should be OK": true, + "Status code name has string OK": true, + "Verify keys and data type of the response result": true, + "id should be api.content.update": true, + "ver should be 4.0": true, + "identifier should be equal to do_2135897074718965761132": true, + "node_id should be equal to do_2135897074718965761132": true + } + ] + }, + { + "id": "6d2a8074-8afe-40ba-9e29-2376fc3db053", + "name": "400 - missing channel", + "url": "https://dev.knowlg.sunbird.org/api/content/v2/create", + "time": 21, + "responseCode": { + "code": 400, + "name": "Bad Request" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "id should be api.content.create": true, + "ver should be 4.0": true, + "Status code is 400": true, + "Verify keys and data type of the response result": true, + "responseCode should be \"CLIENT_ERROR\"": true, + "params.err should be \"CLIENT_ERROR\"": true, + "params.errmsg should be \"Validation Errors\"": true, + "result body should include messages \"Required Metadata channel not set\"": true, + "Status code name has string \"Bad Request\"": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in negative scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"failed\"": { + "pass": 1, + "fail": 0 + }, + "id should be api.content.create": { + "pass": 1, + "fail": 0 + }, + "ver should be 4.0": { + "pass": 1, + "fail": 0 + }, + "Status code is 400": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the response result": { + "pass": 1, + "fail": 0 + }, + "responseCode should be \"CLIENT_ERROR\"": { + "pass": 1, + "fail": 0 + }, + "params.err should be \"CLIENT_ERROR\"": { + "pass": 1, + "fail": 0 + }, + "params.errmsg should be \"Validation Errors\"": { + "pass": 1, + "fail": 0 + }, + "result body should include messages \"Required Metadata channel not set\"": { + "pass": 1, + "fail": 0 + }, + "Status code name has string \"Bad Request\"": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 21 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "id should be api.content.create": true, + "ver should be 4.0": true, + "Status code is 400": true, + "Verify keys and data type of the response result": true, + "responseCode should be \"CLIENT_ERROR\"": true, + "params.err should be \"CLIENT_ERROR\"": true, + "params.errmsg should be \"Validation Errors\"": true, + "result body should include messages \"Required Metadata channel not set\"": true, + "Status code name has string \"Bad Request\"": true + } + ] + }, + { + "id": "59690e87-ff4d-4d1d-b549-0156afa7611b", + "name": "400 - empty channel", + "url": "https://dev.knowlg.sunbird.org/api/content/v2/create", + "time": 71, + "responseCode": { + "code": 400, + "name": "Bad Request" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "id should be api.content.create": true, + "ver should be 4.0": true, + "Status code is 400": true, + "Verify keys and data type of the response result": true, + "responseCode should be \"CLIENT_ERROR\"": true, + "params.err should be \"CLIENT_ERROR\"": true, + "params.errmsg should be \"Validation Errors\"": true, + "result body should include messages \"Required Metadata channel not set\"": true, + "Status code name has string \"Bad Request\"": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in negative scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"failed\"": { + "pass": 1, + "fail": 0 + }, + "id should be api.content.create": { + "pass": 1, + "fail": 0 + }, + "ver should be 4.0": { + "pass": 1, + "fail": 0 + }, + "Status code is 400": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the response result": { + "pass": 1, + "fail": 0 + }, + "responseCode should be \"CLIENT_ERROR\"": { + "pass": 1, + "fail": 0 + }, + "params.err should be \"CLIENT_ERROR\"": { + "pass": 1, + "fail": 0 + }, + "params.errmsg should be \"Validation Errors\"": { + "pass": 1, + "fail": 0 + }, + "result body should include messages \"Required Metadata channel not set\"": { + "pass": 1, + "fail": 0 + }, + "Status code name has string \"Bad Request\"": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 71 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "id should be api.content.create": true, + "ver should be 4.0": true, + "Status code is 400": true, + "Verify keys and data type of the response result": true, + "responseCode should be \"CLIENT_ERROR\"": true, + "params.err should be \"CLIENT_ERROR\"": true, + "params.errmsg should be \"Validation Errors\"": true, + "result body should include messages \"Required Metadata channel not set\"": true, + "Status code name has string \"Bad Request\"": true + } + ] + }, + { + "id": "dd956c1e-35b6-4525-baaf-0b2a8ab5ab57", + "name": "400 - missing name", + "url": "https://dev.knowlg.sunbird.org/api/content/v2/create", + "time": 69, + "responseCode": { + "code": 400, + "name": "Bad Request" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "id should be api.content.create": true, + "ver should be 4.0": true, + "Status code is 400": true, + "Verify keys and data type of the response result": true, + "responseCode should be \"CLIENT_ERROR\"": true, + "params.err should be \"CLIENT_ERROR\"": true, + "params.errmsg should be \"Validation Errors\"": true, + "result body should include messages \"Required Metadata name not set\"": true, + "Status code name has string \"Bad Request\"": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in negative scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"failed\"": { + "pass": 1, + "fail": 0 + }, + "id should be api.content.create": { + "pass": 1, + "fail": 0 + }, + "ver should be 4.0": { + "pass": 1, + "fail": 0 + }, + "Status code is 400": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the response result": { + "pass": 1, + "fail": 0 + }, + "responseCode should be \"CLIENT_ERROR\"": { + "pass": 1, + "fail": 0 + }, + "params.err should be \"CLIENT_ERROR\"": { + "pass": 1, + "fail": 0 + }, + "params.errmsg should be \"Validation Errors\"": { + "pass": 1, + "fail": 0 + }, + "result body should include messages \"Required Metadata name not set\"": { + "pass": 1, + "fail": 0 + }, + "Status code name has string \"Bad Request\"": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 69 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "id should be api.content.create": true, + "ver should be 4.0": true, + "Status code is 400": true, + "Verify keys and data type of the response result": true, + "responseCode should be \"CLIENT_ERROR\"": true, + "params.err should be \"CLIENT_ERROR\"": true, + "params.errmsg should be \"Validation Errors\"": true, + "result body should include messages \"Required Metadata name not set\"": true, + "Status code name has string \"Bad Request\"": true + } + ] + }, + { + "id": "c6c1b7fd-a278-4ef3-9d2e-6c76a53d1c33", + "name": "400 - name: invalid datatype", + "url": "https://dev.knowlg.sunbird.org/api/content/v2/create", + "time": 49, + "responseCode": { + "code": 400, + "name": "Bad Request" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "id should be api.content.create": true, + "ver should be 4.0": true, + "Status code is 400": true, + "Verify keys and data type of the response result": true, + "responseCode should be \"CLIENT_ERROR\"": true, + "params.err should be \"CLIENT_ERROR\"": true, + "params.errmsg should be \"Validation Errors\"": true, + "result body should include messages \"Metadata name should be a/an String value\"": true, + "Status code name has string \"Bad Request\"": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in negative scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"failed\"": { + "pass": 1, + "fail": 0 + }, + "id should be api.content.create": { + "pass": 1, + "fail": 0 + }, + "ver should be 4.0": { + "pass": 1, + "fail": 0 + }, + "Status code is 400": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the response result": { + "pass": 1, + "fail": 0 + }, + "responseCode should be \"CLIENT_ERROR\"": { + "pass": 1, + "fail": 0 + }, + "params.err should be \"CLIENT_ERROR\"": { + "pass": 1, + "fail": 0 + }, + "params.errmsg should be \"Validation Errors\"": { + "pass": 1, + "fail": 0 + }, + "result body should include messages \"Metadata name should be a/an String value\"": { + "pass": 1, + "fail": 0 + }, + "Status code name has string \"Bad Request\"": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 49 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "id should be api.content.create": true, + "ver should be 4.0": true, + "Status code is 400": true, + "Verify keys and data type of the response result": true, + "responseCode should be \"CLIENT_ERROR\"": true, + "params.err should be \"CLIENT_ERROR\"": true, + "params.errmsg should be \"Validation Errors\"": true, + "result body should include messages \"Metadata name should be a/an String value\"": true, + "Status code name has string \"Bad Request\"": true + } + ] + }, + { + "id": "a1ac9342-84c0-4395-aa52-b8248007722d", + "name": "400 - name empty", + "url": "https://dev.knowlg.sunbird.org/api/content/v2/create", + "time": 48, + "responseCode": { + "code": 400, + "name": "Bad Request" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "id should be api.content.create": true, + "ver should be 4.0": true, + "Status code is 400": true, + "Verify keys and data type of the response result": true, + "responseCode should be \"CLIENT_ERROR\"": true, + "params.err should be \"CLIENT_ERROR\"": true, + "params.errmsg should be \"Validation Errors\"": true, + "result body should include messages \"Required Metadata name not set\"": true, + "Status code name has string \"Bad Request\"": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in negative scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"failed\"": { + "pass": 1, + "fail": 0 + }, + "id should be api.content.create": { + "pass": 1, + "fail": 0 + }, + "ver should be 4.0": { + "pass": 1, + "fail": 0 + }, + "Status code is 400": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the response result": { + "pass": 1, + "fail": 0 + }, + "responseCode should be \"CLIENT_ERROR\"": { + "pass": 1, + "fail": 0 + }, + "params.err should be \"CLIENT_ERROR\"": { + "pass": 1, + "fail": 0 + }, + "params.errmsg should be \"Validation Errors\"": { + "pass": 1, + "fail": 0 + }, + "result body should include messages \"Required Metadata name not set\"": { + "pass": 1, + "fail": 0 + }, + "Status code name has string \"Bad Request\"": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 48 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "id should be api.content.create": true, + "ver should be 4.0": true, + "Status code is 400": true, + "Verify keys and data type of the response result": true, + "responseCode should be \"CLIENT_ERROR\"": true, + "params.err should be \"CLIENT_ERROR\"": true, + "params.errmsg should be \"Validation Errors\"": true, + "result body should include messages \"Required Metadata name not set\"": true, + "Status code name has string \"Bad Request\"": true + } + ] + }, + { + "id": "37e04040-1676-4dca-a674-54210b5990e2", + "name": "400 - missing code", + "url": "https://dev.knowlg.sunbird.org/api/content/v2/create", + "time": 67, + "responseCode": { + "code": 400, + "name": "Bad Request" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "id should be api.content.create": true, + "ver should be 4.0": true, + "Status code is 400": true, + "Verify keys and data type of the response result": true, + "responseCode should be \"CLIENT_ERROR\"": true, + "params.err should be \"CLIENT_ERROR\"": true, + "params.errmsg should be \"Validation Errors\"": true, + "result body should include messages \"Required Metadata code not set\"": true, + "Status code name has string \"Bad Request\"": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in negative scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"failed\"": { + "pass": 1, + "fail": 0 + }, + "id should be api.content.create": { + "pass": 1, + "fail": 0 + }, + "ver should be 4.0": { + "pass": 1, + "fail": 0 + }, + "Status code is 400": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the response result": { + "pass": 1, + "fail": 0 + }, + "responseCode should be \"CLIENT_ERROR\"": { + "pass": 1, + "fail": 0 + }, + "params.err should be \"CLIENT_ERROR\"": { + "pass": 1, + "fail": 0 + }, + "params.errmsg should be \"Validation Errors\"": { + "pass": 1, + "fail": 0 + }, + "result body should include messages \"Required Metadata code not set\"": { + "pass": 1, + "fail": 0 + }, + "Status code name has string \"Bad Request\"": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 67 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "id should be api.content.create": true, + "ver should be 4.0": true, + "Status code is 400": true, + "Verify keys and data type of the response result": true, + "responseCode should be \"CLIENT_ERROR\"": true, + "params.err should be \"CLIENT_ERROR\"": true, + "params.errmsg should be \"Validation Errors\"": true, + "result body should include messages \"Required Metadata code not set\"": true, + "Status code name has string \"Bad Request\"": true + } + ] + }, + { + "id": "077b803b-7fbb-4264-823c-c41c0bac597d", + "name": "400 - missing mimeType", + "url": "https://dev.knowlg.sunbird.org/api/content/v2/create", + "time": 45, + "responseCode": { + "code": 400, + "name": "Bad Request" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "id should be api.content.create": true, + "ver should be 4.0": true, + "Status code is 400": true, + "Verify keys and data type of the response result": true, + "responseCode should be \"CLIENT_ERROR\"": true, + "params.err should be \"CLIENT_ERROR\"": true, + "params.errmsg should be \"Validation Errors\"": true, + "result body should include messages \"Required Metadata mimeType not set\"": true, + "Status code name has string \"Bad Request\"": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in negative scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"failed\"": { + "pass": 1, + "fail": 0 + }, + "id should be api.content.create": { + "pass": 1, + "fail": 0 + }, + "ver should be 4.0": { + "pass": 1, + "fail": 0 + }, + "Status code is 400": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the response result": { + "pass": 1, + "fail": 0 + }, + "responseCode should be \"CLIENT_ERROR\"": { + "pass": 1, + "fail": 0 + }, + "params.err should be \"CLIENT_ERROR\"": { + "pass": 1, + "fail": 0 + }, + "params.errmsg should be \"Validation Errors\"": { + "pass": 1, + "fail": 0 + }, + "result body should include messages \"Required Metadata mimeType not set\"": { + "pass": 1, + "fail": 0 + }, + "Status code name has string \"Bad Request\"": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 45 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "id should be api.content.create": true, + "ver should be 4.0": true, + "Status code is 400": true, + "Verify keys and data type of the response result": true, + "responseCode should be \"CLIENT_ERROR\"": true, + "params.err should be \"CLIENT_ERROR\"": true, + "params.errmsg should be \"Validation Errors\"": true, + "result body should include messages \"Required Metadata mimeType not set\"": true, + "Status code name has string \"Bad Request\"": true + } + ] + }, + { + "id": "6a365695-00de-4d90-ab6f-4726e59b1898", + "name": "400 - invalid mimeType", + "url": "https://dev.knowlg.sunbird.org/api/content/v2/create", + "time": 69, + "responseCode": { + "code": 400, + "name": "Bad Request" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "id should be api.content.create": true, + "ver should be 4.0": true, + "Status code is 400": true, + "Verify keys and data type of the response result": true, + "responseCode should be \"CLIENT_ERROR\"": true, + "params.err should be \"CLIENT_ERROR\"": true, + "params.errmsg should be \"Validation Errors\"": true, + "result body should include messages \"Metadata mimeType should be one of: [application/vnd.ekstep.ecml-archive, application/vnd.ekstep.html-archive, application/vnd.android.package-archive, application/vnd.ekstep.content-archive, application/vnd.ekstep.content-collection, application/vnd.ekstep.plugin-archive, application/vnd.ekstep.h5p-archive, application/epub, text/x-url, video/x-youtube, application/octet-stream, application/msword, application/pdf, image/jpeg, image/jpg, image/png, image/tiff, image/bmp, image/gif, image/svg+xml, video/avi, video/mpeg, video/quicktime, video/3gpp, video/mp4, video/ogg, video/webm, audio/mp3, audio/mp4, audio/mpeg, audio/ogg, audio/webm, audio/x-wav, audio/wav, application/json, application/quiz]\"": true, + "Status code name has string \"Bad Request\"": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in negative scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"failed\"": { + "pass": 1, + "fail": 0 + }, + "id should be api.content.create": { + "pass": 1, + "fail": 0 + }, + "ver should be 4.0": { + "pass": 1, + "fail": 0 + }, + "Status code is 400": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the response result": { + "pass": 1, + "fail": 0 + }, + "responseCode should be \"CLIENT_ERROR\"": { + "pass": 1, + "fail": 0 + }, + "params.err should be \"CLIENT_ERROR\"": { + "pass": 1, + "fail": 0 + }, + "params.errmsg should be \"Validation Errors\"": { + "pass": 1, + "fail": 0 + }, + "result body should include messages \"Metadata mimeType should be one of: [application/vnd.ekstep.ecml-archive, application/vnd.ekstep.html-archive, application/vnd.android.package-archive, application/vnd.ekstep.content-archive, application/vnd.ekstep.content-collection, application/vnd.ekstep.plugin-archive, application/vnd.ekstep.h5p-archive, application/epub, text/x-url, video/x-youtube, application/octet-stream, application/msword, application/pdf, image/jpeg, image/jpg, image/png, image/tiff, image/bmp, image/gif, image/svg+xml, video/avi, video/mpeg, video/quicktime, video/3gpp, video/mp4, video/ogg, video/webm, audio/mp3, audio/mp4, audio/mpeg, audio/ogg, audio/webm, audio/x-wav, audio/wav, application/json, application/quiz]\"": { + "pass": 1, + "fail": 0 + }, + "Status code name has string \"Bad Request\"": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 69 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "id should be api.content.create": true, + "ver should be 4.0": true, + "Status code is 400": true, + "Verify keys and data type of the response result": true, + "responseCode should be \"CLIENT_ERROR\"": true, + "params.err should be \"CLIENT_ERROR\"": true, + "params.errmsg should be \"Validation Errors\"": true, + "result body should include messages \"Metadata mimeType should be one of: [application/vnd.ekstep.ecml-archive, application/vnd.ekstep.html-archive, application/vnd.android.package-archive, application/vnd.ekstep.content-archive, application/vnd.ekstep.content-collection, application/vnd.ekstep.plugin-archive, application/vnd.ekstep.h5p-archive, application/epub, text/x-url, video/x-youtube, application/octet-stream, application/msword, application/pdf, image/jpeg, image/jpg, image/png, image/tiff, image/bmp, image/gif, image/svg+xml, video/avi, video/mpeg, video/quicktime, video/3gpp, video/mp4, video/ogg, video/webm, audio/mp3, audio/mp4, audio/mpeg, audio/ogg, audio/webm, audio/x-wav, audio/wav, application/json, application/quiz]\"": true, + "Status code name has string \"Bad Request\"": true + } + ] + }, + { + "id": "174eb09b-0d2d-4ef4-9109-e74e75472d28", + "name": "400 - missing primaryCategory", + "url": "https://dev.knowlg.sunbird.org/api/content/v2/create", + "time": 48, + "responseCode": { + "code": 400, + "name": "Bad Request" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "id should be api.content.create": true, + "ver should be 4.0": true, + "Status code is 400": true, + "Verify keys and data type of the response result": true, + "responseCode should be \"CLIENT_ERROR\"": true, + "params.err should be \"VALIDATION_ERROR\"": true, + "params.errmsg should be \"primaryCategory is a mandatory parameter\"": true, + "result body should be empty": true, + "Status code name has string \"Bad Request\"": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in negative scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"failed\"": { + "pass": 1, + "fail": 0 + }, + "id should be api.content.create": { + "pass": 1, + "fail": 0 + }, + "ver should be 4.0": { + "pass": 1, + "fail": 0 + }, + "Status code is 400": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the response result": { + "pass": 1, + "fail": 0 + }, + "responseCode should be \"CLIENT_ERROR\"": { + "pass": 1, + "fail": 0 + }, + "params.err should be \"VALIDATION_ERROR\"": { + "pass": 1, + "fail": 0 + }, + "params.errmsg should be \"primaryCategory is a mandatory parameter\"": { + "pass": 1, + "fail": 0 + }, + "result body should be empty": { + "pass": 1, + "fail": 0 + }, + "Status code name has string \"Bad Request\"": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 48 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "id should be api.content.create": true, + "ver should be 4.0": true, + "Status code is 400": true, + "Verify keys and data type of the response result": true, + "responseCode should be \"CLIENT_ERROR\"": true, + "params.err should be \"VALIDATION_ERROR\"": true, + "params.errmsg should be \"primaryCategory is a mandatory parameter\"": true, + "result body should be empty": true, + "Status code name has string \"Bad Request\"": true + } + ] + }, + { + "id": "136c3218-e05b-418e-886a-d10129c996ba", + "name": "400 - Invalid subject", + "url": "https://dev.knowlg.sunbird.org/api/content/v2/create", + "time": 50, + "responseCode": { + "code": 400, + "name": "Bad Request" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "id should be api.content.create": true, + "ver should be 4.0": true, + "Status code is 400": true, + "Verify keys and data type of the response result": true, + "responseCode should be \"CLIENT_ERROR\"": true, + "params.err should be \"CLIENT_ERROR\"": true, + "params.errmsg should be \"Validation Errors.\"": true, + "result body should include messages \"subject range data is empty from the given framework.\"": true, + "Status code name has string \"Bad Request\"": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in negative scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"failed\"": { + "pass": 1, + "fail": 0 + }, + "id should be api.content.create": { + "pass": 1, + "fail": 0 + }, + "ver should be 4.0": { + "pass": 1, + "fail": 0 + }, + "Status code is 400": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the response result": { + "pass": 1, + "fail": 0 + }, + "responseCode should be \"CLIENT_ERROR\"": { + "pass": 1, + "fail": 0 + }, + "params.err should be \"CLIENT_ERROR\"": { + "pass": 1, + "fail": 0 + }, + "params.errmsg should be \"Validation Errors.\"": { + "pass": 1, + "fail": 0 + }, + "result body should include messages \"subject range data is empty from the given framework.\"": { + "pass": 1, + "fail": 0 + }, + "Status code name has string \"Bad Request\"": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 50 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "id should be api.content.create": true, + "ver should be 4.0": true, + "Status code is 400": true, + "Verify keys and data type of the response result": true, + "responseCode should be \"CLIENT_ERROR\"": true, + "params.err should be \"CLIENT_ERROR\"": true, + "params.errmsg should be \"Validation Errors.\"": true, + "result body should include messages \"subject range data is empty from the given framework.\"": true, + "Status code name has string \"Bad Request\"": true + } + ] + }, + { + "id": "c4a3ef78-c5f3-4a82-ac3e-88ba8e2cc4b3", + "name": "404 - Invalid primaryCategory", + "url": "https://dev.knowlg.sunbird.org/api/content/v2/create", + "time": 75, + "responseCode": { + "code": 404, + "name": "Not Found" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "id should be api.content.create": true, + "ver should be 4.0": true, + "Status code is 404": true, + "Verify keys and data type of the response result": true, + "responseCode should be RESOURCE_NOT_FOUND": true, + "params.err should be \"404\"": true, + "params.errmsg should be \"Entry is not found in cassandra for content with identifier: obj-cat:aabaa_content_all {}\"": true, + "messages should be null": true, + "Status code name has string \"Not Found\"": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in negative scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"failed\"": { + "pass": 1, + "fail": 0 + }, + "id should be api.content.create": { + "pass": 1, + "fail": 0 + }, + "ver should be 4.0": { + "pass": 1, + "fail": 0 + }, + "Status code is 404": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the response result": { + "pass": 1, + "fail": 0 + }, + "responseCode should be RESOURCE_NOT_FOUND": { + "pass": 1, + "fail": 0 + }, + "params.err should be \"404\"": { + "pass": 1, + "fail": 0 + }, + "params.errmsg should be \"Entry is not found in cassandra for content with identifier: obj-cat:aabaa_content_all {}\"": { + "pass": 1, + "fail": 0 + }, + "messages should be null": { + "pass": 1, + "fail": 0 + }, + "Status code name has string \"Not Found\"": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 75 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "id should be api.content.create": true, + "ver should be 4.0": true, + "Status code is 404": true, + "Verify keys and data type of the response result": true, + "responseCode should be RESOURCE_NOT_FOUND": true, + "params.err should be \"404\"": true, + "params.errmsg should be \"Entry is not found in cassandra for content with identifier: obj-cat:aabaa_content_all {}\"": true, + "messages should be null": true, + "Status code name has string \"Not Found\"": true + } + ] + }, + { + "id": "59f831d2-df67-4297-9641-2304cea74637", + "name": "404 - Not Found", + "url": "https://dev.knowlg.sunbird.org/api/content/v2/read/do_42353245436534", + "time": 65, + "responseCode": { + "code": 404, + "name": "Not Found" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "Status code is 404": true, + "Test data type of the response and keys": true, + "id should be api.content.read": true, + "ver should be 4.0": true, + "responseCode should be RESOURCE_NOT_FOUND": true, + "params.err should be \"NOT_FOUND\"": true, + "params.errmsg should be \"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: do_42353245436534\"": true, + "messages should be null": true, + "Status code name has string \"Not Found\"": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in negative scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"failed\"": { + "pass": 1, + "fail": 0 + }, + "Status code is 404": { + "pass": 1, + "fail": 0 + }, + "Test data type of the response and keys": { + "pass": 1, + "fail": 0 + }, + "id should be api.content.read": { + "pass": 1, + "fail": 0 + }, + "ver should be 4.0": { + "pass": 1, + "fail": 0 + }, + "responseCode should be RESOURCE_NOT_FOUND": { + "pass": 1, + "fail": 0 + }, + "params.err should be \"NOT_FOUND\"": { + "pass": 1, + "fail": 0 + }, + "params.errmsg should be \"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: do_42353245436534\"": { + "pass": 1, + "fail": 0 + }, + "messages should be null": { + "pass": 1, + "fail": 0 + }, + "Status code name has string \"Not Found\"": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 65 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "Status code is 404": true, + "Test data type of the response and keys": true, + "id should be api.content.read": true, + "ver should be 4.0": true, + "responseCode should be RESOURCE_NOT_FOUND": true, + "params.err should be \"NOT_FOUND\"": true, + "params.errmsg should be \"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: do_42353245436534\"": true, + "messages should be null": true, + "Status code name has string \"Not Found\"": true + } + ] + }, + { + "id": "8be3bf77-e65e-4dab-9053-cf7400fd5aa5", + "name": "400 - Invalid versionKey", + "url": "https://dev.knowlg.sunbird.org/api/content/v2/update/do_2135897074718965761132", + "time": 77, + "responseCode": { + "code": 400, + "name": "Bad Request" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "id should be api.content.update": true, + "ver should be 4.0": true, + "Status code is 400": true, + "Content-Encoding header should not present": true, + "Verify keys and data type of the response result": true, + "responseCode should be CLIENT_ERROR": true, + "params.err should be CLIENT_ERROR": true, + "params.status should be failed": true, + "params.errmsg should be \"Invalid version Key\"": true, + "messages should be null": true, + "Status code name has string Bad Request": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in negative scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"failed\"": { + "pass": 1, + "fail": 0 + }, + "id should be api.content.update": { + "pass": 1, + "fail": 0 + }, + "ver should be 4.0": { + "pass": 1, + "fail": 0 + }, + "Status code is 400": { + "pass": 1, + "fail": 0 + }, + "Content-Encoding header should not present": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the response result": { + "pass": 1, + "fail": 0 + }, + "responseCode should be CLIENT_ERROR": { + "pass": 1, + "fail": 0 + }, + "params.err should be CLIENT_ERROR": { + "pass": 1, + "fail": 0 + }, + "params.status should be failed": { + "pass": 1, + "fail": 0 + }, + "params.errmsg should be \"Invalid version Key\"": { + "pass": 1, + "fail": 0 + }, + "messages should be null": { + "pass": 1, + "fail": 0 + }, + "Status code name has string Bad Request": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 77 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "id should be api.content.update": true, + "ver should be 4.0": true, + "Status code is 400": true, + "Content-Encoding header should not present": true, + "Verify keys and data type of the response result": true, + "responseCode should be CLIENT_ERROR": true, + "params.err should be CLIENT_ERROR": true, + "params.status should be failed": true, + "params.errmsg should be \"Invalid version Key\"": true, + "messages should be null": true, + "Status code name has string Bad Request": true + } + ] + }, + { + "id": "d705cd83-e917-4be6-a755-e9c7d377c9c6", + "name": "400 - Empty name", + "url": "https://dev.knowlg.sunbird.org/api/content/v2/update/do_2135897074718965761132", + "time": 42, + "responseCode": { + "code": 400, + "name": "Bad Request" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "id should be api.content.update": true, + "ver should be 4.0": true, + "Status code is 400": true, + "Verify keys and data type of the response result": true, + "responseCode should be \"CLIENT_ERROR\"": true, + "params.err should be \"CLIENT_ERROR\"": true, + "params.errmsg should be \"Validation Errors\"": true, + "result body should include messages \"Required Metadata name not set\"": true, + "Status code name has string \"Bad Request\"": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in negative scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"failed\"": { + "pass": 1, + "fail": 0 + }, + "id should be api.content.update": { + "pass": 1, + "fail": 0 + }, + "ver should be 4.0": { + "pass": 1, + "fail": 0 + }, + "Status code is 400": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the response result": { + "pass": 1, + "fail": 0 + }, + "responseCode should be \"CLIENT_ERROR\"": { + "pass": 1, + "fail": 0 + }, + "params.err should be \"CLIENT_ERROR\"": { + "pass": 1, + "fail": 0 + }, + "params.errmsg should be \"Validation Errors\"": { + "pass": 1, + "fail": 0 + }, + "result body should include messages \"Required Metadata name not set\"": { + "pass": 1, + "fail": 0 + }, + "Status code name has string \"Bad Request\"": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 42 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "id should be api.content.update": true, + "ver should be 4.0": true, + "Status code is 400": true, + "Verify keys and data type of the response result": true, + "responseCode should be \"CLIENT_ERROR\"": true, + "params.err should be \"CLIENT_ERROR\"": true, + "params.errmsg should be \"Validation Errors\"": true, + "result body should include messages \"Required Metadata name not set\"": true, + "Status code name has string \"Bad Request\"": true + } + ] + }, + { + "id": "13158c62-e311-458c-a9ef-fabe24c065f6", + "name": "404 - Invalid content id", + "url": "https://dev.knowlg.sunbird.org/api/content/v2/update/do_21357909972698726412211", + "time": 36, + "responseCode": { + "code": 404, + "name": "Not Found" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "id should be api.content.update": true, + "ver should be 4.0": true, + "Status code is 404": true, + "Verify keys and data type of the response result": true, + "responseCode should be RESOURCE_NOT_FOUND": true, + "params.err should be NOT_FOUND": true, + "params.errmsg should be \"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: do_21357909972698726412211\"": true, + "messages should be null": true, + "Status code name has string \"Not Found\"": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in negative scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"failed\"": { + "pass": 1, + "fail": 0 + }, + "id should be api.content.update": { + "pass": 1, + "fail": 0 + }, + "ver should be 4.0": { + "pass": 1, + "fail": 0 + }, + "Status code is 404": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the response result": { + "pass": 1, + "fail": 0 + }, + "responseCode should be RESOURCE_NOT_FOUND": { + "pass": 1, + "fail": 0 + }, + "params.err should be NOT_FOUND": { + "pass": 1, + "fail": 0 + }, + "params.errmsg should be \"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: do_21357909972698726412211\"": { + "pass": 1, + "fail": 0 + }, + "messages should be null": { + "pass": 1, + "fail": 0 + }, + "Status code name has string \"Not Found\"": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 36 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "id should be api.content.update": true, + "ver should be 4.0": true, + "Status code is 404": true, + "Verify keys and data type of the response result": true, + "responseCode should be RESOURCE_NOT_FOUND": true, + "params.err should be NOT_FOUND": true, + "params.errmsg should be \"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: do_21357909972698726412211\"": true, + "messages should be null": true, + "Status code name has string \"Not Found\"": true + } + ] + }, + { + "id": "d05ba7e2-041f-4a54-9370-e5ecfd286a62", + "name": "404 - Invalid primaryCategory", + "url": "https://dev.knowlg.sunbird.org/api/content/v2/update/do_2135897074718965761132", + "time": 109, + "responseCode": { + "code": 404, + "name": "Not Found" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "id should be api.content.update": true, + "ver should be 4.0": true, + "Status code is 404": true, + "Verify keys and data type of the response result": true, + "responseCode should be RESOURCE_NOT_FOUND": true, + "params.err should be \"404\"": true, + "params.errmsg should be \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"": true, + "messages should be null": true, + "Status code name has string \"Not Found\"": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in negative scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"failed\"": { + "pass": 1, + "fail": 0 + }, + "id should be api.content.update": { + "pass": 1, + "fail": 0 + }, + "ver should be 4.0": { + "pass": 1, + "fail": 0 + }, + "Status code is 404": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the response result": { + "pass": 1, + "fail": 0 + }, + "responseCode should be RESOURCE_NOT_FOUND": { + "pass": 1, + "fail": 0 + }, + "params.err should be \"404\"": { + "pass": 1, + "fail": 0 + }, + "params.errmsg should be \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"": { + "pass": 1, + "fail": 0 + }, + "messages should be null": { + "pass": 1, + "fail": 0 + }, + "Status code name has string \"Not Found\"": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 109 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "id should be api.content.update": true, + "ver should be 4.0": true, + "Status code is 404": true, + "Verify keys and data type of the response result": true, + "responseCode should be RESOURCE_NOT_FOUND": true, + "params.err should be \"404\"": true, + "params.errmsg should be \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"": true, + "messages should be null": true, + "Status code name has string \"Not Found\"": true + } + ] + } + ], + "count": 1, + "totalTime": 2695, + "collection": { + "requests": [ + { + "id": "2383265f-a48f-47dc-9538-e3c7a16b1d98", + "method": "POST" + }, + { + "id": "fdb1f5bc-f5b3-4c3c-b622-bd86a8f92b40", + "method": "POST" + }, + { + "id": "67644d61-7797-4f54-82cf-d4b7711b5282", + "method": "POST" + }, + { + "id": "4fa811d0-1e33-493c-95fe-39bac77bae42", + "method": "POST" + }, + { + "id": "5f38fbf3-1dd6-4f5a-8796-6ca07d837d25", + "method": "GET" + }, + { + "id": "6cad6c08-f73b-4d00-9395-b8cbcb3cafe0", + "method": "GET" + }, + { + "id": "bcd0f26d-ca6b-4eb7-815a-e85c4044a829", + "method": "PATCH" + }, + { + "id": "1d43c4ab-ea8b-449c-8dca-aa7eda247bfa", + "method": "PATCH" + }, + { + "id": "6d2a8074-8afe-40ba-9e29-2376fc3db053", + "method": "POST" + }, + { + "id": "59690e87-ff4d-4d1d-b549-0156afa7611b", + "method": "POST" + }, + { + "id": "dd956c1e-35b6-4525-baaf-0b2a8ab5ab57", + "method": "POST" + }, + { + "id": "c6c1b7fd-a278-4ef3-9d2e-6c76a53d1c33", + "method": "POST" + }, + { + "id": "a1ac9342-84c0-4395-aa52-b8248007722d", + "method": "POST" + }, + { + "id": "37e04040-1676-4dca-a674-54210b5990e2", + "method": "POST" + }, + { + "id": "077b803b-7fbb-4264-823c-c41c0bac597d", + "method": "POST" + }, + { + "id": "6a365695-00de-4d90-ab6f-4726e59b1898", + "method": "POST" + }, + { + "id": "174eb09b-0d2d-4ef4-9109-e74e75472d28", + "method": "POST" + }, + { + "id": "136c3218-e05b-418e-886a-d10129c996ba", + "method": "POST" + }, + { + "id": "c4a3ef78-c5f3-4a82-ac3e-88ba8e2cc4b3", + "method": "POST" + }, + { + "id": "59f831d2-df67-4297-9641-2304cea74637", + "method": "GET" + }, + { + "id": "8be3bf77-e65e-4dab-9053-cf7400fd5aa5", + "method": "PATCH" + }, + { + "id": "d705cd83-e917-4be6-a755-e9c7d377c9c6", + "method": "PATCH" + }, + { + "id": "13158c62-e311-458c-a9ef-fabe24c065f6", + "method": "PATCH" + }, + { + "id": "d05ba7e2-041f-4a54-9370-e5ecfd286a62", + "method": "PATCH" + } + ] + } +} \ No newline at end of file From a047461eae5f4ebf0396e9165842cc5e87ec3321 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Wed, 27 Jul 2022 12:19:50 +0530 Subject: [PATCH 097/490] Issue #SB-28316 feat: DIAL code reserve API refactor. --- .../src/main/scala/org/sunbird/content/dial/DIALManager.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala index acbda5f8a..6af548049 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala @@ -392,7 +392,6 @@ object DIALManager { def getDIALReserveUpdateResponse(response: Response, count: Integer, contentId: String, node: Node): Response = { response.getResult.put(DIALConstants.COUNT, count) response.getResult.put(ContentConstants.NODE_ID, contentId) - response.getResult.put(DIALConstants.PROCESS_ID, node.getMetadata.get(DIALConstants.PROCESS_ID)) response.getResult.put(DIALConstants.RESERVED_DIALCODES, node.getMetadata.get(DIALConstants.RESERVED_DIALCODES)) response From 06aeee1c9951dec783f85daf98fc3b35b5ddafb0 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Fri, 29 Jul 2022 11:19:37 +0530 Subject: [PATCH 098/490] Issue #SB-28316 feat: DIAL code reserve API refactor. --- .../org/sunbird/content/dial/DIALManager.scala | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala index 6af548049..ab2b7405b 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala @@ -10,6 +10,7 @@ import org.sunbird.graph.dac.model.Node import org.sunbird.graph.nodes.DataNode import org.sunbird.graph.utils.ScalaJsonUtils import org.sunbird.managers.HierarchyManager +import org.sunbird.telemetry.logger.TelemetryManager import java.util import scala.collection.JavaConverters._ @@ -273,6 +274,7 @@ object DIALManager { } def reserve(request: Request)(implicit oec: OntologyEngineContext, ec: ExecutionContext): Future[Response] = { + TelemetryManager.log("DIALManager:: reserve") val channelId: String = request.getContext.getOrDefault(DIALConstants.CHANNEL, "").asInstanceOf[String] val contentId: String = request.get(ContentConstants.IDENTIFIER).asInstanceOf[String] @@ -285,15 +287,15 @@ object DIALManager { req.put(ContentConstants.MODE, ContentConstants.EDIT_MODE) DataNode.read(req).flatMap(rootNode => { val contentMetadata = rootNode.getMetadata - + TelemetryManager.log("DIALManager:: reserve:: contentMetadata: " + contentMetadata) validateChannel(contentMetadata.get(DIALConstants.CHANNEL).asInstanceOf[String], channelId) validateContentForReservedDialcodes(contentMetadata) validateCountForReservingDialCode(request.getRequest.get(DIALConstants.DIALCODES).asInstanceOf[util.Map[String, AnyRef]]) validateContentStatus(contentMetadata) - + TelemetryManager.log("DIALManager:: reserve:: post validation: ") val reservedDialCodes = contentMetadata.getOrDefault(DIALConstants.DIALCODES, Map.empty[String, Integer]).asInstanceOf[Map[String, Integer]] val updateDialCodes = getUpdateDIALCodes(reservedDialCodes, request, channelId, contentId) - + TelemetryManager.log("DIALManager:: reserve:: updateDialCodes: " + updateDialCodes) if(updateDialCodes.size > reservedDialCodes.size) { val updateReq = getDIALReserveUpdateRequest(req, request, rootNode, updateDialCodes) DataNode.update(updateReq).map(updatedNode => { @@ -378,14 +380,8 @@ object DIALManager { updateReq.setContext(request.getContext) updateReq.getContext.put(ContentConstants.IDENTIFIER, rootNode.getIdentifier) updateReq.put(ContentConstants.IDENTIFIER, rootNode.getIdentifier) - val rootNodeMetadata = rootNode.getMetadata - rootNodeMetadata.remove(DIALConstants.DISCUSSION_FORUM) - rootNodeMetadata.remove(DIALConstants.CREDENTIALS) - rootNodeMetadata.remove(DIALConstants.TRACKABLE) - + updateReq.put(DIALConstants.VERSION_KEY,rootNode.getMetadata.get("versionKey")) updateReq.put(DIALConstants.RESERVED_DIALCODES, updateDialCodes.asJava) - updateReq.getRequest.putAll(rootNodeMetadata) - updateReq } From 4d615690b0df4e2701a4d3ea3d0b438203a72c43 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Fri, 29 Jul 2022 11:22:57 +0530 Subject: [PATCH 099/490] Issue #SB-28316 feat: DIAL code reserve API refactor. --- .../main/scala/org/sunbird/content/dial/DIALManager.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala index ab2b7405b..2bc4073fd 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala @@ -287,15 +287,15 @@ object DIALManager { req.put(ContentConstants.MODE, ContentConstants.EDIT_MODE) DataNode.read(req).flatMap(rootNode => { val contentMetadata = rootNode.getMetadata - TelemetryManager.log("DIALManager:: reserve:: contentMetadata: " + contentMetadata) + validateChannel(contentMetadata.get(DIALConstants.CHANNEL).asInstanceOf[String], channelId) validateContentForReservedDialcodes(contentMetadata) validateCountForReservingDialCode(request.getRequest.get(DIALConstants.DIALCODES).asInstanceOf[util.Map[String, AnyRef]]) validateContentStatus(contentMetadata) - TelemetryManager.log("DIALManager:: reserve:: post validation: ") + val reservedDialCodes = contentMetadata.getOrDefault(DIALConstants.DIALCODES, Map.empty[String, Integer]).asInstanceOf[Map[String, Integer]] val updateDialCodes = getUpdateDIALCodes(reservedDialCodes, request, channelId, contentId) - TelemetryManager.log("DIALManager:: reserve:: updateDialCodes: " + updateDialCodes) + if(updateDialCodes.size > reservedDialCodes.size) { val updateReq = getDIALReserveUpdateRequest(req, request, rootNode, updateDialCodes) DataNode.update(updateReq).map(updatedNode => { From ed4c4f6772e2328d7cbd9156ac023e80489c8384 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Fri, 29 Jul 2022 11:26:08 +0530 Subject: [PATCH 100/490] Issue #SB-28316 feat: DIAL code reserve API refactor. --- .../src/main/scala/org/sunbird/content/dial/DIALManager.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala index 2bc4073fd..430a91fd4 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala @@ -274,7 +274,6 @@ object DIALManager { } def reserve(request: Request)(implicit oec: OntologyEngineContext, ec: ExecutionContext): Future[Response] = { - TelemetryManager.log("DIALManager:: reserve") val channelId: String = request.getContext.getOrDefault(DIALConstants.CHANNEL, "").asInstanceOf[String] val contentId: String = request.get(ContentConstants.IDENTIFIER).asInstanceOf[String] From 2d23d69931a6b8f63a6095abd1e344575c829e64 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Fri, 29 Jul 2022 11:26:49 +0530 Subject: [PATCH 101/490] Issue #SB-28316 feat: DIAL code reserve API refactor. --- .../src/main/scala/org/sunbird/content/dial/DIALManager.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala index 430a91fd4..9179f10bf 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala @@ -10,7 +10,6 @@ import org.sunbird.graph.dac.model.Node import org.sunbird.graph.nodes.DataNode import org.sunbird.graph.utils.ScalaJsonUtils import org.sunbird.managers.HierarchyManager -import org.sunbird.telemetry.logger.TelemetryManager import java.util import scala.collection.JavaConverters._ From 08133c31f22a0359335c2a87f08fda849f7aa252 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Fri, 29 Jul 2022 17:15:48 +0530 Subject: [PATCH 102/490] Issue #SB-28316 feat: DIAL code reserve API refactor. --- .../src/main/scala/org/sunbird/content/dial/DIALManager.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala index 9179f10bf..8fc4cb598 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala @@ -291,7 +291,7 @@ object DIALManager { validateCountForReservingDialCode(request.getRequest.get(DIALConstants.DIALCODES).asInstanceOf[util.Map[String, AnyRef]]) validateContentStatus(contentMetadata) - val reservedDialCodes = contentMetadata.getOrDefault(DIALConstants.DIALCODES, Map.empty[String, Integer]).asInstanceOf[Map[String, Integer]] + 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) if(updateDialCodes.size > reservedDialCodes.size) { From bf15edcd5f6a49ff0d0e622aeee7b1b45b145ca4 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Tue, 2 Aug 2022 16:10:04 +0530 Subject: [PATCH 103/490] Issue #SB-30154 feat: DIAL code link API refactor. --- .../scala/org/sunbird/content/dial/DIALManager.scala | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala index 8fc4cb598..fa7385d17 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala @@ -175,21 +175,13 @@ object DIALManager { def getLinkUpdateRequest(req: Request, rootNode: Node, requestMap: Map[String, List[String]], objectId: String): Request = { val updateReq = new Request(req) updateReq.put(ContentConstants.IDENTIFIER, rootNode.getIdentifier) - val rootNodeMetadata = rootNode.getMetadata - rootNodeMetadata.remove(DIALConstants.DISCUSSION_FORUM) - rootNodeMetadata.remove(DIALConstants.CREDENTIALS) - rootNodeMetadata.remove(DIALConstants.TRACKABLE) - rootNodeMetadata.remove(DIALConstants.RESERVED_DIALCODES) - - if(rootNodeMetadata.containsKey(DIALConstants.DIALCODES)) - rootNodeMetadata.remove(DIALConstants.DIALCODES) + updateReq.put(DIALConstants.VERSION_KEY,rootNode.getMetadata.get("versionKey")) if(requestMap(objectId).isEmpty) updateReq.put(DIALConstants.DIALCODES, null) else updateReq.put(DIALConstants.DIALCODES, requestMap(objectId).toArray[String]) - updateReq.getRequest.putAll(rootNodeMetadata) updateReq } From c1371c10b4a9ddce1d60325309e8149bef768719 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Fri, 5 Aug 2022 17:42:08 +0530 Subject: [PATCH 104/490] Issue #KN-9 feat: Content Publish API refactor. --- .../sunbird/content/actors/ContentActor.scala | 22 +++++ .../content/publish/mgr/PublishManager.scala | 91 +++++++++++++++++++ .../content/util/ContentConstants.scala | 15 +++ .../src/test/resources/application.conf | 1 + .../controllers/v3/ContentController.scala | 30 ++++-- .../controllers/v4/CollectionController.scala | 24 +++++ .../controllers/v4/ContentController.scala | 24 +++++ .../app/controllers/v4/EventController.scala | 2 +- .../controllers/v4/EventSetController.scala | 2 +- .../content-service/app/utils/ApiId.scala | 1 + .../content-service/conf/application.conf | 1 + content-api/content-service/conf/routes | 7 +- .../mimetype/mgr/BaseMimeTypeManager.scala | 4 + .../mimetype/mgr/MimeTypeManager.scala | 3 + .../mgr/impl/ApkMimeTypeMgrImpl.scala | 4 + .../mgr/impl/AssetMimeTypeMgrImpl.scala | 3 + .../mgr/impl/CollectionMimeTypeMgrImpl.scala | 4 + .../mgr/impl/DefaultMimeTypeMgrImpl.scala | 5 + .../mgr/impl/DocumentMimeTypeMgrImpl.scala | 4 + .../mgr/impl/EcmlMimeTypeMgrImpl.scala | 8 ++ .../mgr/impl/H5PMimeTypeMgrImpl.scala | 5 + .../mgr/impl/HtmlMimeTypeMgrImpl.scala | 6 ++ .../mgr/impl/PluginMimeTypeMgrImpl.scala | 6 ++ .../mgr/impl/YouTubeMimeTypeMgrImpl.scala | 8 ++ 24 files changed, 267 insertions(+), 13 deletions(-) create mode 100644 content-api/content-actors/src/main/scala/org/sunbird/content/publish/mgr/PublishManager.scala diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/actors/ContentActor.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/actors/ContentActor.scala index e3925113f..c6e5d2fa4 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/actors/ContentActor.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/actors/ContentActor.scala @@ -4,6 +4,7 @@ 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} @@ -15,6 +16,7 @@ import org.sunbird.common.{ContentParams, Platform, Slug} import org.sunbird.common.dto.{Request, Response, ResponseHandler} import org.sunbird.common.exception.ClientException 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 @@ -54,6 +56,7 @@ class ContentActor @Inject() (implicit oec: OntologyEngineContext, ss: StorageSe case "systemUpdate" => systemUpdate(request) case "reviewContent" => reviewContent(request) case "rejectContent" => rejectContent(request) + case "publishContent" => publishContent(request) case _ => ERROR(request.getOperation) } } @@ -195,6 +198,25 @@ class ContentActor @Inject() (implicit oec: OntologyEngineContext, ss: StorageSe }).flatMap(f => f) } + def publishContent(request: Request): Future[Response] = { + val identifier: String = request.getContext.getOrDefault("identifier", "").asInstanceOf[String] + val publisher: String = request.getRequest.getOrDefault("lastPublishedBy", "").asInstanceOf[String] + + if(publisher.isBlank) throw new ClientException("ERR_CONTENT_BLANK_PUBLISHER", "Publisher User Id is blank") + + val readReq = new Request(request) + readReq.put("identifier", identifier) + readReq.put("mode", "edit") + DataNode.read(readReq).map(node => { + if (null != node & StringUtils.isNotBlank(node.getObjectType)) + request.getContext.put("schemaName", node.getObjectType.toLowerCase()) + if (StringUtils.equalsAnyIgnoreCase("Processing", node.getMetadata.getOrDefault("status", "").asInstanceOf[String])) + throw new ClientException("ERR_NODE_ACCESS_DENIED", "Publish Operation Can't Be Applied On Node Under Processing State") + node.getMetadata.put("lastPublishedBy", publisher) + PublishManager.publish(request, node) + }).flatMap(f => f) + } + def populateDefaultersForCreation(request: Request) = { setDefaultsBasedOnMimeType(request, ContentParams.create.name) setDefaultLicense(request) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/publish/mgr/PublishManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/publish/mgr/PublishManager.scala new file mode 100644 index 000000000..e365bb885 --- /dev/null +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/publish/mgr/PublishManager.scala @@ -0,0 +1,91 @@ +package org.sunbird.content.publish.mgr + +import org.apache.commons.lang3.StringUtils +import org.sunbird.common.Platform +import org.sunbird.common.dto.ResponseParams.StatusType +import org.sunbird.common.dto.{Request, Response, ResponseParams} +import org.sunbird.common.exception.ClientException +import org.sunbird.content.util.ContentConstants +import org.sunbird.graph.OntologyEngineContext +import org.sunbird.graph.dac.model.Node +import org.sunbird.kafka.client.KafkaClient +import org.sunbird.mimetype.factory.MimeTypeManagerFactory +import org.sunbird.telemetry.util.LogTelemetryEventUtil + +import java.util +import scala.concurrent.{ExecutionContext, Future} + +object PublishManager { + + private val kfClient = new KafkaClient + + def publish(request: Request, node: Node)(implicit oec: OntologyEngineContext, ec: ExecutionContext): Future[Response] = { + val identifier: String = node.getIdentifier + val mimeType = node.getMetadata.getOrDefault(ContentConstants.MIME_TYPE, "").asInstanceOf[String] + val mgr = MimeTypeManagerFactory.getManager(node.getObjectType, mimeType) + + val publishCheckList = request.getContext.getOrDefault(ContentConstants.PUBLIC_CHECK_LIST, List.empty[String]).asInstanceOf[List[String]] + if (publishCheckList.isEmpty) node.getMetadata.put(ContentConstants.PUBLIC_CHECK_LIST, null) + + val publishType = request.getContext.getOrDefault(ContentConstants.PUBLISH_TYPE, "").asInstanceOf[String] + node.getMetadata.put(ContentConstants.PUBLISH_TYPE, publishType) + + val publishFuture: Future[scala.collection.Map[String, AnyRef]] = mgr.publish(identifier, node) + publishFuture.map(result => { + // Push Instruction Event - Learning code has logic to send publish instruction to different topics based on mimeTypes. That logic is not implemented here due to deprecation of samza jobs. + pushInstructionEvent(identifier, node) + + val response = new Response + val param = new ResponseParams + param.setStatus(StatusType.successful.name) + response.setParams(param) + response.put(ContentConstants.PUBLISH_STATUS, "Publish Event for Content Id '" + node.getIdentifier + "' is pushed Successfully!") + response.put(ContentConstants.NODE_ID, node.getIdentifier) + + Future(response) + }).flatMap(f => f) + } + + @throws[Exception] + private def pushInstructionEvent(identifier: String, node: Node): Unit = { + val actor: util.Map[String, AnyRef] = new util.HashMap[String, AnyRef] + val context: util.Map[String, AnyRef] = new util.HashMap[String, AnyRef] + val objectData: util.Map[String, AnyRef] = new util.HashMap[String, AnyRef] + val edata: util.Map[String, AnyRef] = new util.HashMap[String, AnyRef] + generateInstructionEventMetadata(actor, context, objectData, edata, node, identifier) + val beJobRequestEvent: String = LogTelemetryEventUtil.logInstructionEvent(actor, context, objectData, edata) + val topic: String = Platform.getString(ContentConstants.KAFKA_PUBLISH_TOPIC,"sunbirddev.publish.job.request") + if (StringUtils.isBlank(beJobRequestEvent)) throw new ClientException("BE_JOB_REQUEST_EXCEPTION", "Event is not generated properly.") + kfClient.send(beJobRequestEvent, topic) + } + + private def generateInstructionEventMetadata(actor: util.Map[String, AnyRef], context: util.Map[String, AnyRef], objectData: util.Map[String, AnyRef], edata: util.Map[String, AnyRef], node: Node, identifier: String): Unit = { + val metadata: util.Map[String, AnyRef] = node.getMetadata + val instructionEventMetadata = new util.HashMap[String, AnyRef] + + actor.put(ContentConstants.ID, node.getObjectType.toLowerCase() + "-publish") + actor.put(ContentConstants.TYPE, ContentConstants.SYSTEM) + context.put(ContentConstants.CHANNEL, metadata.get(ContentConstants.CHANNEL)) + context.put(ContentConstants.P_DATA, new util.HashMap[String, AnyRef]() {{ + put(ContentConstants.ID, ContentConstants.SUNBIRD_PLATFORM) + put(ContentConstants.VER, ContentConstants.SCHEMA_VERSION) + }}) + if (Platform.config.hasPath("cloud_storage.env")) { + val env: String = Platform.getString("cloud_storage.env", "dev") + context.put(ContentConstants.ENV, env) + } + objectData.put(ContentConstants.ID, identifier) + objectData.put(ContentConstants.VER, metadata.get(ContentConstants.VERSION_KEY)) + edata.put(ContentConstants.PUBLISH_TYPE, metadata.get(ContentConstants.PUBLISH_TYPE)) + instructionEventMetadata.put(ContentConstants.PACKAGE_VERSION, metadata.getOrDefault(ContentConstants.PACKAGE_VERSION,0.asInstanceOf[AnyRef])) + instructionEventMetadata.put(ContentConstants.MIME_TYPE, metadata.get(ContentConstants.MIME_TYPE)) + instructionEventMetadata.put(ContentConstants.LAST_PUBLISHED_BY, metadata.get(ContentConstants.LAST_PUBLISHED_BY)) + instructionEventMetadata.put(ContentConstants.IDENTIFIER, identifier) + instructionEventMetadata.put(ContentConstants.OBJECT_TYPE, node.getObjectType) + edata.put(ContentConstants.METADATA, instructionEventMetadata) + edata.put(ContentConstants.ACTION, ContentConstants.PUBLISH) + edata.put(ContentConstants.CONTENT_TYPE, metadata.get(ContentConstants.CONTENT_TYPE)) + } +} + + diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/util/ContentConstants.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/util/ContentConstants.scala index 622011222..070fb692e 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/util/ContentConstants.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/util/ContentConstants.scala @@ -59,4 +59,19 @@ object ContentConstants { val PARENT: String = "Parent" val BEARER: String = "Bearer " val OK_RESPONSE_CODE: String = "OK" + val PUBLIC_CHECK_LIST: String = "publishChecklist" + val PUBLISH_TYPE: String = "publish_type" + val PUBLISH_STATUS: String = "publishStatus" + val ID: String = "id" + val TYPE: String = "type" + val SYSTEM: String = "System" + val P_DATA: String = "pdata" + val SUNBIRD_PLATFORM: String = "org.sunbird.platform" + val VER: String = "ver" + val LAST_PUBLISHED_BY: String = "lastPublishedBy" + val OBJECT_TYPE: String = "objectType" + val ACTION: String = "action" + val PUBLISH: String = "publish" + val ENV: String = "env" + val KAFKA_PUBLISH_TOPIC = "kafka.publish.request.topic" } diff --git a/content-api/content-actors/src/test/resources/application.conf b/content-api/content-actors/src/test/resources/application.conf index 837e732ba..bacdabc13 100644 --- a/content-api/content-actors/src/test/resources/application.conf +++ b/content-api/content-actors/src/test/resources/application.conf @@ -394,6 +394,7 @@ channel.fetch.suggested_frameworks=true # Kafka configuration details kafka.topics.instruction="local.learning.job.request" +kafka.publish.request.topic = "local.publish.job.request" kafka.urls="localhost:9092" #Youtube Standard Licence Validation diff --git a/content-api/content-service/app/controllers/v3/ContentController.scala b/content-api/content-service/app/controllers/v3/ContentController.scala index ab3538098..ee6c425f5 100644 --- a/content-api/content-service/app/controllers/v3/ContentController.scala +++ b/content-api/content-service/app/controllers/v3/ContentController.scala @@ -148,9 +148,27 @@ class ContentController @Inject()(@Named(ActorNames.CONTENT_ACTOR) contentActor: } def publish(identifier: String) = Action.async { implicit request => - val result = ResponseHandler.OK() - val response = JavaJsonUtils.serialize(result) - Future(Ok(response).as("application/json")) + val headers = commonHeaders() + val body = requestBody() + val content = body.getOrDefault("content", new java.util.HashMap()).asInstanceOf[java.util.Map[String, Object]]; + content.putAll(headers) + val contentRequest = getRequest(content, headers, "publishContent") + setRequestContext(contentRequest, version, objectType, schemaName) + contentRequest.getContext.put("identifier", identifier); + contentRequest.getContext.put("publish_type", "public"); + getResult(ApiId.PUBLISH_CONTENT, contentActor, contentRequest) + } + + def publishUnlisted(identifier: String) = Action.async { implicit request => + val headers = commonHeaders() + val body = requestBody() + val content = body.getOrDefault("content", new java.util.HashMap()).asInstanceOf[java.util.Map[String, Object]]; + content.putAll(headers) + val contentRequest = getRequest(content, headers, "publishContent") + setRequestContext(contentRequest, version, objectType, schemaName) + contentRequest.getContext.put("identifier", identifier); + contentRequest.getContext.put("publish_type", "unlisted"); + getResult(ApiId.PUBLISH_CONTENT, contentActor, contentRequest) } def review(identifier: String) = Action.async { implicit request => @@ -221,12 +239,6 @@ class ContentController @Inject()(@Named(ActorNames.CONTENT_ACTOR) contentActor: Future(Ok(response).as("application/json")) } - def publishUnlisted(identifier: String) = Action.async { implicit request => - val result = ResponseHandler.OK() - val response = JavaJsonUtils.serialize(result) - Future(Ok(response).as("application/json")) - } - def upload(identifier: String, fileFormat: Option[String], validation: Option[String]) = Action.async { implicit request => val headers = commonHeaders() val content = requestFormData(identifier) diff --git a/content-api/content-service/app/controllers/v4/CollectionController.scala b/content-api/content-service/app/controllers/v4/CollectionController.scala index 4dfd17829..34d5ba35a 100644 --- a/content-api/content-service/app/controllers/v4/CollectionController.scala +++ b/content-api/content-service/app/controllers/v4/CollectionController.scala @@ -264,5 +264,29 @@ class CollectionController @Inject()(@Named(ActorNames.CONTENT_ACTOR) contentAc getResult(ApiId.REVIEW_COLLECTION, contentActor, contentRequest, version = apiVersion) } + def publish(identifier: String) = Action.async { implicit request => + val headers = commonHeaders() + val body = requestBody() + val content = body.getOrDefault("content", new java.util.HashMap()).asInstanceOf[java.util.Map[String, Object]]; + content.putAll(headers) + val contentRequest = getRequest(content, headers, "publishContent") + setRequestContext(contentRequest, version, objectType, schemaName) + contentRequest.getContext.put("identifier", identifier); + contentRequest.getContext.put("publish_type", "public"); + getResult(ApiId.PUBLISH_CONTENT, contentActor, contentRequest) + } + + def publishUnlisted(identifier: String) = Action.async { implicit request => + val headers = commonHeaders() + val body = requestBody() + val content = body.getOrDefault("content", new java.util.HashMap()).asInstanceOf[java.util.Map[String, Object]]; + content.putAll(headers) + val contentRequest = getRequest(content, headers, "publishContent") + setRequestContext(contentRequest, version, objectType, schemaName) + contentRequest.getContext.put("identifier", identifier); + contentRequest.getContext.put("publish_type", "unlisted"); + getResult(ApiId.PUBLISH_CONTENT, contentActor, contentRequest) + } + } diff --git a/content-api/content-service/app/controllers/v4/ContentController.scala b/content-api/content-service/app/controllers/v4/ContentController.scala index f5a25d0fe..98594df34 100644 --- a/content-api/content-service/app/controllers/v4/ContentController.scala +++ b/content-api/content-service/app/controllers/v4/ContentController.scala @@ -218,4 +218,28 @@ class ContentController @Inject()(@Named(ActorNames.CONTENT_ACTOR) contentActor: getResult(ApiId.REJECT_CONTENT, contentActor, contentRequest, version = apiVersion) } + def publish(identifier: String) = Action.async { implicit request => + val headers = commonHeaders() + val body = requestBody() + val content = body.getOrDefault("content", new java.util.HashMap()).asInstanceOf[java.util.Map[String, Object]]; + content.putAll(headers) + val contentRequest = getRequest(content, headers, "publishContent") + setRequestContext(contentRequest, version, objectType, schemaName) + contentRequest.getContext.put("identifier", identifier); + contentRequest.getContext.put("publish_type", "public"); + getResult(ApiId.PUBLISH_CONTENT, contentActor, contentRequest) + } + + def publishUnlisted(identifier: String) = Action.async { implicit request => + val headers = commonHeaders() + val body = requestBody() + val content = body.getOrDefault("content", new java.util.HashMap()).asInstanceOf[java.util.Map[String, Object]]; + content.putAll(headers) + val contentRequest = getRequest(content, headers, "publishContent") + setRequestContext(contentRequest, version, objectType, schemaName) + contentRequest.getContext.put("identifier", identifier); + contentRequest.getContext.put("publish_type", "unlisted"); + getResult(ApiId.PUBLISH_CONTENT, contentActor, contentRequest) + } + } diff --git a/content-api/content-service/app/controllers/v4/EventController.scala b/content-api/content-service/app/controllers/v4/EventController.scala index 3b609d32b..1c5f2cc86 100644 --- a/content-api/content-service/app/controllers/v4/EventController.scala +++ b/content-api/content-service/app/controllers/v4/EventController.scala @@ -55,7 +55,7 @@ class EventController @Inject()(@Named(ActorNames.EVENT_ACTOR) eventActor: Actor } } - def publish(identifier: String): Action[AnyContent] = Action.async { implicit request => + override def publish(identifier: String): Action[AnyContent] = Action.async { implicit request => val headers = commonHeaders() val content = new java.util.HashMap[String, Object]() content.put("status", "Live") diff --git a/content-api/content-service/app/controllers/v4/EventSetController.scala b/content-api/content-service/app/controllers/v4/EventSetController.scala index e6729fae4..c1dd509c3 100644 --- a/content-api/content-service/app/controllers/v4/EventSetController.scala +++ b/content-api/content-service/app/controllers/v4/EventSetController.scala @@ -64,7 +64,7 @@ class EventSetController @Inject()(@Named(ActorNames.EVENT_SET_ACTOR) eventSetAc } } - def publish(identifier: String): Action[AnyContent] = Action.async { implicit request => + override def publish(identifier: String): Action[AnyContent] = Action.async { implicit request => val headers = commonHeaders() val content = new java.util.HashMap[String, Object]() content.put("identifier", identifier) diff --git a/content-api/content-service/app/utils/ApiId.scala b/content-api/content-service/app/utils/ApiId.scala index 8b5afe44f..804aa87c6 100644 --- a/content-api/content-service/app/utils/ApiId.scala +++ b/content-api/content-service/app/utils/ApiId.scala @@ -22,6 +22,7 @@ object ApiId { val SYSTEM_UPDATE_CONTENT = "api.content.system.update" val REVIEW_CONTENT = "api.content.review" val REJECT_CONTENT = "api.content.review.reject" + val PUBLISH_CONTENT = "api.content.publish" // Collection APIs val ADD_HIERARCHY = "api.content.hierarchy.add" diff --git a/content-api/content-service/conf/application.conf b/content-api/content-service/conf/application.conf index b969a73eb..910dd9406 100644 --- a/content-api/content-service/conf/application.conf +++ b/content-api/content-service/conf/application.conf @@ -595,6 +595,7 @@ channel.fetch.suggested_frameworks=true content.h5p.library.path="https://s3.ap-south-1.amazonaws.com/ekstep-public-prod/content/templates/h5p-library-v2.zip" kafka.topics.graph.event="sunbirddev.learning.graph.events" +kafka.publish.request.topic = "sunbirddev.publish.job.request" content.discard.status=["Draft","FlagDraft"] content.discard.remove_publish_data=["compatibilityLevel", "lastPublishedOn", "pkgVersion", "leafNodesCount", "downloadUrl", "variants"] diff --git a/content-api/content-service/conf/routes b/content-api/content-service/conf/routes index 17b4e3059..baad416e0 100644 --- a/content-api/content-service/conf/routes +++ b/content-api/content-service/conf/routes @@ -20,7 +20,6 @@ POST /content/v3/bundle controllers.v3.ContentController POST /content/v3/flag/accept/:identifier controllers.v3.ContentController.acceptFlag(identifier:String) POST /content/v3/flag/reject/:identifier controllers.v3.ContentController.rejectFlag(identifier:String) POST /content/v3/publish/:identifier controllers.v3.ContentController.publish(identifier:String) -POST /content/v3/public/publish/:identifier controllers.v3.ContentController.publish(identifier:String) POST /content/v3/review/:identifier controllers.v3.ContentController.review(identifier:String) DELETE /content/v3/discard/:identifier controllers.v3.ContentController.discard(identifier:String) DELETE /content/v3/retire/:identifier controllers.v3.ContentController.retire(identifier:String) @@ -80,7 +79,9 @@ GET /collection/v4/hierarchy/:identifier/:bookmarkId controllers.v4.Coll POST /collection/v4/dialcode/link/:identifier controllers.v4.CollectionController.collectionLinkDialCode(identifier:String) POST /collection/v4/copy/:identifier controllers.v4.CollectionController.copy(identifier:String, mode:Option[String], type:String ?= "deep") PATCH /collection/v4/system/update/:identifier controllers.v4.CollectionController.systemUpdate(identifier:String) -POST /collection/v4/reject/:identifier controllers.v4.CollectionController.reviewReject(identifier:String) +POST /collection/v4/reject/:identifier controllers.v4.CollectionController.reviewReject(identifier:String) +POST /collection/v4/publish/:identifier controllers.v4.CollectionController.publish(identifier:String) +POST /collection/v4/unlisted/publish/:identifier controllers.v4.CollectionController.publishUnlisted(identifier:String) # Content v4 APIs POST /content/v4/create controllers.v4.ContentController.create @@ -100,6 +101,8 @@ PATCH /content/v4/system/update/:identifier controllers.v4.ContentControl POST /content/v4/review/:identifier controllers.v4.ContentController.review(identifier:String) POST /content/v4/reject/:identifier controllers.v4.ContentController.reviewReject(identifier:String) POST /content/v4/dialcode/reserve/:identifier controllers.v4.ContentController.reserveDialCode(identifier:String) +POST /content/v4/publish/:identifier controllers.v4.ContentController.publish(identifier:String) +POST /content/v4/unlisted/publish/:identifier controllers.v4.ContentController.publishUnlisted(identifier:String) # App v4 APIs POST /app/v4/register controllers.v4.AppController.register diff --git a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/BaseMimeTypeManager.scala b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/BaseMimeTypeManager.scala index 0d53fdbf1..94f9d2368 100644 --- a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/BaseMimeTypeManager.scala +++ b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/BaseMimeTypeManager.scala @@ -289,5 +289,9 @@ class BaseMimeTypeManager(implicit ss: StorageService) { Map("lastSubmittedOn"-> DateUtils.formatCurrentDate(), "reviewError" -> null, "status" -> newStatus) } + def getEnrichedPublishMetadata(status: String): Map[String, AnyRef] = { + Map("lastPublishedOn"-> DateUtils.formatCurrentDate(), "rejectReasons" -> null, "rejectComment" -> null, "publishError" -> null, "importError" -> null) + } + } diff --git a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/MimeTypeManager.scala b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/MimeTypeManager.scala index 3f3345074..5324fbe84 100644 --- a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/MimeTypeManager.scala +++ b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/MimeTypeManager.scala @@ -18,4 +18,7 @@ trait MimeTypeManager { @throws[Exception] def review(objectId: String, node: Node)(implicit ec: ExecutionContext, ontologyEngineContext: OntologyEngineContext): Future[Map[String, AnyRef]] + + @throws[Exception] + def publish(objectId: String, node: Node)(implicit ec: ExecutionContext, ontologyEngineContext: OntologyEngineContext): Future[Map[String, AnyRef]] } diff --git a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/ApkMimeTypeMgrImpl.scala b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/ApkMimeTypeMgrImpl.scala index ff19edd12..67f39cbfe 100644 --- a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/ApkMimeTypeMgrImpl.scala +++ b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/ApkMimeTypeMgrImpl.scala @@ -34,4 +34,8 @@ class ApkMimeTypeMgrImpl(implicit ss: StorageService) extends BaseMimeTypeManage Future(getEnrichedMetadata(node.getMetadata.getOrDefault("status", "").asInstanceOf[String])) } + override def publish(objectId: String, node: Node)(implicit ec: ExecutionContext, ontologyEngineContext: OntologyEngineContext): Future[Map[String, AnyRef]] = { + validate(node, " |[APK file should be uploaded for further processing!]") + Future(getEnrichedPublishMetadata(node.getMetadata.getOrDefault("status", "").asInstanceOf[String])) + } } diff --git a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/AssetMimeTypeMgrImpl.scala b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/AssetMimeTypeMgrImpl.scala index ab1f9055c..aebf4c9c8 100644 --- a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/AssetMimeTypeMgrImpl.scala +++ b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/AssetMimeTypeMgrImpl.scala @@ -38,4 +38,7 @@ class AssetMimeTypeMgrImpl(implicit ss: StorageService) extends BaseMimeTypeMana Future(getEnrichedMetadata(node.getMetadata.getOrDefault("status", "").asInstanceOf[String])) } + override def publish(objectId: String, node: Node)(implicit ec: ExecutionContext, ontologyEngineContext: OntologyEngineContext): Future[Map[String, AnyRef]] = { + Future(getEnrichedPublishMetadata(node.getMetadata.getOrDefault("status", "").asInstanceOf[String])) + } } diff --git a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/CollectionMimeTypeMgrImpl.scala b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/CollectionMimeTypeMgrImpl.scala index 597e4201b..0f833977d 100644 --- a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/CollectionMimeTypeMgrImpl.scala +++ b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/CollectionMimeTypeMgrImpl.scala @@ -34,6 +34,10 @@ class CollectionMimeTypeMgrImpl(implicit ss: StorageService) extends BaseMimeTyp validate(node).map(res => getEnrichedMetadata(node.getMetadata.getOrDefault("status", "").asInstanceOf[String])) } + override def publish(objectId: String, node: Node)(implicit ec: ExecutionContext, ontologyEngineContext: OntologyEngineContext): Future[Map[String, AnyRef]] = { + validate(node).map(res => getEnrichedPublishMetadata(node.getMetadata.getOrDefault("status", "").asInstanceOf[String])) + } + def validate(node: Node)(implicit ec: ExecutionContext, ontologyEngineContext: OntologyEngineContext) = { val req = new Request() req.setContext(Map[String, AnyRef]("schemaName" -> node.getObjectType.toLowerCase.replaceAll("image", ""), "version"->"1.0").asJava) diff --git a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/DefaultMimeTypeMgrImpl.scala b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/DefaultMimeTypeMgrImpl.scala index c9f47e256..3e2cc01cf 100644 --- a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/DefaultMimeTypeMgrImpl.scala +++ b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/DefaultMimeTypeMgrImpl.scala @@ -40,4 +40,9 @@ class DefaultMimeTypeMgrImpl(implicit ss: StorageService) extends BaseMimeTypeMa validate(node, " | [Either artifactUrl is missing or invalid!]") Future(getEnrichedMetadata(node.getMetadata.getOrDefault("status", "").asInstanceOf[String])) } + + override def publish(objectId: String, node: Node)(implicit ec: ExecutionContext, ontologyEngineContext: OntologyEngineContext): Future[Map[String, AnyRef]] = { + validate(node, " | [Either artifactUrl is missing or invalid!]") + Future(getEnrichedPublishMetadata(node.getMetadata.getOrDefault("status", "").asInstanceOf[String])) + } } diff --git a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/DocumentMimeTypeMgrImpl.scala b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/DocumentMimeTypeMgrImpl.scala index 30442b670..e2ffe0612 100644 --- a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/DocumentMimeTypeMgrImpl.scala +++ b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/DocumentMimeTypeMgrImpl.scala @@ -92,4 +92,8 @@ class DocumentMimeTypeMgrImpl(implicit ss: StorageService) extends BaseMimeTypeM Future(getEnrichedMetadata(node.getMetadata.getOrDefault("status", "").asInstanceOf[String])) } + override def publish(objectId: String, node: Node)(implicit ec: ExecutionContext, ontologyEngineContext: OntologyEngineContext): Future[Map[String, AnyRef]] = { + validate(node, " | [Either artifactUrl is missing or invalid!]") + Future(getEnrichedPublishMetadata(node.getMetadata.getOrDefault("status", "").asInstanceOf[String])) + } } diff --git a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/EcmlMimeTypeMgrImpl.scala b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/EcmlMimeTypeMgrImpl.scala index f44c3e0e5..773ca2842 100644 --- a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/EcmlMimeTypeMgrImpl.scala +++ b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/EcmlMimeTypeMgrImpl.scala @@ -108,6 +108,14 @@ class EcmlMimeTypeMgrImpl(implicit ss: StorageService) extends BaseMimeTypeManag }) } + override def publish(objectId: String, node: Node)(implicit ec: ExecutionContext, ontologyEngineContext: OntologyEngineContext): Future[Map[String, AnyRef]] = { + validate(node).map(result => { + if(result) { + getEnrichedPublishMetadata(node.getMetadata.getOrDefault("status", "").asInstanceOf[String]) + } else throw new ServerException("ERR_NODE_REVIEW", "Something Went Wrong While Applying Review On Node Having Identifier : "+objectId) + }) + } + def validate(node: Node)(implicit ec: ExecutionContext, oec: OntologyEngineContext): Future[Boolean] = { val artifactUrl = node.getMetadata.getOrDefault("artifactUrl", "").asInstanceOf[String] val req = new Request() diff --git a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/H5PMimeTypeMgrImpl.scala b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/H5PMimeTypeMgrImpl.scala index bea9318f2..5cb004f48 100644 --- a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/H5PMimeTypeMgrImpl.scala +++ b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/H5PMimeTypeMgrImpl.scala @@ -91,4 +91,9 @@ class H5PMimeTypeMgrImpl(implicit ss: StorageService) extends BaseMimeTypeManage validate(node, "| [H5P file should be uploaded for further processing!]") Future(getEnrichedMetadata(node.getMetadata.getOrDefault("status", "").asInstanceOf[String])) } + + override def publish(objectId: String, node: Node)(implicit ec: ExecutionContext, ontologyEngineContext: OntologyEngineContext): Future[Map[String, AnyRef]] = { + validate(node, "| [H5P file should be uploaded for further processing!]") + Future(getEnrichedPublishMetadata(node.getMetadata.getOrDefault("status", "").asInstanceOf[String])) + } } diff --git a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/HtmlMimeTypeMgrImpl.scala b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/HtmlMimeTypeMgrImpl.scala index 34014ebf3..4e3e35b57 100644 --- a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/HtmlMimeTypeMgrImpl.scala +++ b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/HtmlMimeTypeMgrImpl.scala @@ -42,4 +42,10 @@ class HtmlMimeTypeMgrImpl(implicit ss: StorageService) extends BaseMimeTypeManag validate(node, " | [HTML archive should be uploaded for further processing!]") Future(getEnrichedMetadata(node.getMetadata.getOrDefault("status", "").asInstanceOf[String])) } + + override def publish(objectId: String, node: Node)(implicit ec: ExecutionContext, ontologyEngineContext: OntologyEngineContext): Future[Map[String, AnyRef]] = { + validate(node, " | [HTML archive should be uploaded for further processing!]") + Future(getEnrichedPublishMetadata(node.getMetadata.getOrDefault("status", "").asInstanceOf[String])) + } + } diff --git a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/PluginMimeTypeMgrImpl.scala b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/PluginMimeTypeMgrImpl.scala index ccda298b9..2fb9215e9 100644 --- a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/PluginMimeTypeMgrImpl.scala +++ b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/PluginMimeTypeMgrImpl.scala @@ -65,4 +65,10 @@ class PluginMimeTypeMgrImpl(implicit ss: StorageService) extends BaseMimeTypeMan throw new ClientException("VALIDATOR_ERROR", MISSING_REQUIRED_FIELDS + " | [Either artifactUrl is missing or invalid!]") Future(getEnrichedMetadata(node.getMetadata.getOrDefault("status", "").asInstanceOf[String])) } + + override def publish(objectId: String, node: Node)(implicit ec: ExecutionContext, ontologyEngineContext: OntologyEngineContext): Future[Map[String, AnyRef]] = { + if(!isValidArtifact(node)) + throw new ClientException("VALIDATOR_ERROR", MISSING_REQUIRED_FIELDS + " | [Either artifactUrl is missing or invalid!]") + Future(getEnrichedPublishMetadata(node.getMetadata.getOrDefault("status", "").asInstanceOf[String])) + } } diff --git a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/YouTubeMimeTypeMgrImpl.scala b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/YouTubeMimeTypeMgrImpl.scala index 36c0e0015..82576823c 100644 --- a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/YouTubeMimeTypeMgrImpl.scala +++ b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/YouTubeMimeTypeMgrImpl.scala @@ -36,6 +36,14 @@ class YouTubeMimeTypeMgrImpl(implicit ss: StorageService) extends BaseMimeTypeMa Future(data ++ licenseData) } + override def publish(objectId: String, node: Node)(implicit ec: ExecutionContext, ontologyEngineContext: OntologyEngineContext): Future[Map[String, AnyRef]] = { + validate(node) + val license = node.getMetadata.getOrDefault("license", "").asInstanceOf[String] + val licenseData = if(StringUtils.isBlank(license)) validateAndGetLicense(node) else Map() + val data = getEnrichedPublishMetadata(node.getMetadata.getOrDefault("status", "").asInstanceOf[String]) + Future(data ++ licenseData) + } + def validate(node: Node): Unit = { if(isValidArtifact(node)) { val isValidYouTubeUrl = Pattern.matches(YOUTUBE_REGEX, node.getMetadata.get("artifactUrl").toString) From 71eb4585e0f1daf4976af55b6cebd600ac79ca98 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Mon, 8 Aug 2022 13:23:27 +0530 Subject: [PATCH 105/490] Issue #KN-9 feat: Content Publish API refactor. --- .../content/actors/TestContentActor.scala | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/content-api/content-actors/src/test/scala/org/sunbird/content/actors/TestContentActor.scala b/content-api/content-actors/src/test/scala/org/sunbird/content/actors/TestContentActor.scala index 40778e99f..1c3535917 100644 --- a/content-api/content-actors/src/test/scala/org/sunbird/content/actors/TestContentActor.scala +++ b/content-api/content-actors/src/test/scala/org/sunbird/content/actors/TestContentActor.scala @@ -626,6 +626,66 @@ class TestContentActor extends BaseSpec with MockFactory { node } + it should "return success response for 'publishContent' for document mimeType" in { + implicit val oec: OntologyEngineContext = mock[OntologyEngineContext] + val graphDB = mock[GraphService] + implicit val ss = mock[StorageService] + (oec.graphService _).expects().returns(graphDB).anyNumberOfTimes() + val node = getNodeForReview("do_123", "application/pdf", "LearningResource", "Content", "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/do_11316726916397465612760/artifact/sample.pdf") + node.getMetadata.put("contentType", "Resource") + node.getMetadata.put("organisationBoardIds", new util.ArrayList[String](){{add("ncf_board_cbse")}}) + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, *, *, *).returns(Future(node)).anyNumberOfTimes() + (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, *).returns(Future(new Response())).anyNumberOfTimes() + val nodes: util.List[Node] = getCategoryNode() + (graphDB.getNodeByUniqueIds(_: String, _: SearchCriteria)).expects(*, *).returns(Future(nodes)).anyNumberOfTimes() + val request = getContentRequest() + request.getContext.put("identifier", "do_123") + request.getRequest.put("lastPublishedBy", "Support Team") + request.setOperation("publishContent") + val response = callActor(request, Props(new ContentActor())) + assert("successful".equals(response.getParams.getStatus)) + } + + it should "through client exception for publish operation if node is under processing" in { + implicit val ss = mock[StorageService] + implicit val oec: OntologyEngineContext = mock[OntologyEngineContext] + val graphDB = mock[GraphService] + (oec.graphService _).expects().returns(graphDB).anyNumberOfTimes() + val node = getNodeForReview("do_123", "application/pdf", "LearningResource", "Content", "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/do_11316726916397465612760/artifact/sample.pdf") + node.getMetadata.put("contentType", "Resource") + node.getMetadata.put("status", "Processing") + node.getMetadata.put("organisationBoardIds", new util.ArrayList[String](){{add("ncf_board_cbse")}}) + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, *, *, *).returns(Future(node)).anyNumberOfTimes() + (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, *).returns(Future(new Response())).anyNumberOfTimes() + val request = getContentRequest() + request.getContext.put("identifier", "do_123") + request.getRequest.put("lastPublishedBy", "Support Team") + request.setOperation("publishContent") + val response = callActor(request, Props(new ContentActor())) + assert(response.getResponseCode == ResponseCode.CLIENT_ERROR) + assert(response.getParams.getErr == "ERR_NODE_ACCESS_DENIED") + assert(response.getParams.getErrmsg == "Publish Operation Can't Be Applied On Node Under Processing State") + } + + it should "through client exception for publish operation if published by is not passed" in { + implicit val ss = mock[StorageService] + implicit val oec: OntologyEngineContext = mock[OntologyEngineContext] + val graphDB = mock[GraphService] + (oec.graphService _).expects().returns(graphDB).anyNumberOfTimes() + val node = getNodeForReview("do_123", "application/pdf", "LearningResource", "Content", "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/do_11316726916397465612760/artifact/sample.pdf") + node.getMetadata.put("contentType", "Resource") + node.getMetadata.put("status", "Review") + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, *, *, *).returns(Future(node)).anyNumberOfTimes() + (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, *).returns(Future(new Response())).anyNumberOfTimes() + val request = getContentRequest() + request.getContext.put("identifier", "do_123") + request.setOperation("publishContent") + val response = callActor(request, Props(new ContentActor())) + assert(response.getResponseCode == ResponseCode.CLIENT_ERROR) + assert(response.getParams.getErr == "ERR_CONTENT_BLANK_PUBLISHER") + assert(response.getParams.getErrmsg == "Publisher User Id is blank") + } + private def getNode(): Node = { val node = new Node() node.setIdentifier("do_1234") From a3da6afd8db2014ff1aef5cdbf59ad9d8db56cea Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Mon, 8 Aug 2022 14:54:24 +0530 Subject: [PATCH 106/490] Issue #KN-9 feat: Content Publish API refactor. --- .../content/actors/TestContentActor.scala | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/content-api/content-actors/src/test/scala/org/sunbird/content/actors/TestContentActor.scala b/content-api/content-actors/src/test/scala/org/sunbird/content/actors/TestContentActor.scala index 1c3535917..9449943cf 100644 --- a/content-api/content-actors/src/test/scala/org/sunbird/content/actors/TestContentActor.scala +++ b/content-api/content-actors/src/test/scala/org/sunbird/content/actors/TestContentActor.scala @@ -646,6 +646,106 @@ class TestContentActor extends BaseSpec with MockFactory { assert("successful".equals(response.getParams.getStatus)) } + it should "return success response for 'publishContent' for youtube mimeType" in { + implicit val oec: OntologyEngineContext = mock[OntologyEngineContext] + val graphDB = mock[GraphService] + implicit val ss = mock[StorageService] + (oec.graphService _).expects().returns(graphDB).anyNumberOfTimes() + val node = getNodeForReview("do_123", "video/x-youtube", "LearningResource", "Content", "https://www.youtube.com/watch?v=EtYU11qNciQ&t=612s") + node.getMetadata.put("contentType", "Resource") + node.getMetadata.put("organisationBoardIds", new util.ArrayList[String](){{add("ncf_board_cbse")}}) + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, *, *, *).returns(Future(node)).anyNumberOfTimes() + (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, *).returns(Future(new Response())).anyNumberOfTimes() + val nodes: util.List[Node] = getCategoryNode() + (graphDB.getNodeByUniqueIds(_: String, _: SearchCriteria)).expects(*, *).returns(Future(nodes)).anyNumberOfTimes() + val request = getContentRequest() + request.getContext.put("identifier", "do_123") + request.getRequest.put("lastPublishedBy", "Support Team") + request.setOperation("publishContent") + val response = callActor(request, Props(new ContentActor())) + assert("successful".equals(response.getParams.getStatus)) + } + + it should "return success response for 'publishContent' for video mimeType" in { + implicit val oec: OntologyEngineContext = mock[OntologyEngineContext] + val graphDB = mock[GraphService] + implicit val ss = mock[StorageService] + (oec.graphService _).expects().returns(graphDB).anyNumberOfTimes() + val node = getNodeForReview("do_123", "video/mp4", "LearningResource", "Content", "https://ekstep-public-prod.s3-ap-south-1.amazonaws.com/content/do_312293067955650560231/artifact/itemupload_1500618666256.mp4") + node.getMetadata.put("contentType", "Resource") + node.getMetadata.put("organisationBoardIds", new util.ArrayList[String](){{add("ncf_board_cbse")}}) + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, *, *, *).returns(Future(node)).anyNumberOfTimes() + (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, *).returns(Future(new Response())).anyNumberOfTimes() + val nodes: util.List[Node] = getCategoryNode() + (graphDB.getNodeByUniqueIds(_: String, _: SearchCriteria)).expects(*, *).returns(Future(nodes)).anyNumberOfTimes() + val request = getContentRequest() + request.getContext.put("identifier", "do_123") + request.getRequest.put("lastPublishedBy", "Support Team") + request.setOperation("publishContent") + val response = callActor(request, Props(new ContentActor())) + assert("successful".equals(response.getParams.getStatus)) + } + + it should "return success response for 'publishContent' for HTML mimeType" in { + implicit val oec: OntologyEngineContext = mock[OntologyEngineContext] + val graphDB = mock[GraphService] + implicit val ss = mock[StorageService] + (oec.graphService _).expects().returns(graphDB).anyNumberOfTimes() + val node = getNodeForReview("do_123", "application/vnd.ekstep.html-archive", "LearningResource", "Content", "https://sunbirdstagingpublic.blob.core.windows.net/sunbird-content-staging/content/do_21357841995316428811795/artifact/2.-ekiikrnn-kii-prkrti-output-3_1657521986480_1657522093104.zip") + node.getMetadata.put("contentType", "Resource") + node.getMetadata.put("organisationBoardIds", new util.ArrayList[String](){{add("ncf_board_cbse")}}) + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, *, *, *).returns(Future(node)).anyNumberOfTimes() + (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, *).returns(Future(new Response())).anyNumberOfTimes() + val nodes: util.List[Node] = getCategoryNode() + (graphDB.getNodeByUniqueIds(_: String, _: SearchCriteria)).expects(*, *).returns(Future(nodes)).anyNumberOfTimes() + val request = getContentRequest() + request.getContext.put("identifier", "do_123") + request.getRequest.put("lastPublishedBy", "Support Team") + request.setOperation("publishContent") + val response = callActor(request, Props(new ContentActor())) + assert("successful".equals(response.getParams.getStatus)) + } + + it should "return success response for 'publishContent' for H5P mimeType" in { + implicit val oec: OntologyEngineContext = mock[OntologyEngineContext] + val graphDB = mock[GraphService] + implicit val ss = mock[StorageService] + (oec.graphService _).expects().returns(graphDB).anyNumberOfTimes() + val node = getNodeForReview("do_123", "application/vnd.ekstep.h5p-archive", "LearningResource", "Content", "https://sunbirdstagingpublic.blob.core.windows.net/sunbird-content-staging/content/do_2135971978057482241406/artifact/1659814186163_do_2135971978057482241406.zip") + node.getMetadata.put("contentType", "Resource") + node.getMetadata.put("organisationBoardIds", new util.ArrayList[String](){{add("ncf_board_cbse")}}) + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, *, *, *).returns(Future(node)).anyNumberOfTimes() + (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, *).returns(Future(new Response())).anyNumberOfTimes() + val nodes: util.List[Node] = getCategoryNode() + (graphDB.getNodeByUniqueIds(_: String, _: SearchCriteria)).expects(*, *).returns(Future(nodes)).anyNumberOfTimes() + val request = getContentRequest() + request.getContext.put("identifier", "do_123") + request.getRequest.put("lastPublishedBy", "Support Team") + request.setOperation("publishContent") + val response = callActor(request, Props(new ContentActor())) + assert("successful".equals(response.getParams.getStatus)) + } + + it should "return success response for 'publishContent' for Plugin mimeType" in { + implicit val oec: OntologyEngineContext = mock[OntologyEngineContext] + val graphDB = mock[GraphService] + implicit val ss = mock[StorageService] + (oec.graphService _).expects().returns(graphDB).anyNumberOfTimes() + val node = getNodeForReview("do_123", "application/vnd.ekstep.plugin-archive", "LearningResource", "Content", "https://ntpproductionall.blob.core.windows.net/ntp-content-production/content/org.ekstep.summary/artifact/org.ekstep.summary-1.0-1_1574060894628.zip") + node.getMetadata.put("contentType", "Resource") + node.getMetadata.put("organisationBoardIds", new util.ArrayList[String](){{add("ncf_board_cbse")}}) + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, *, *, *).returns(Future(node)).anyNumberOfTimes() + (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, *).returns(Future(new Response())).anyNumberOfTimes() + val nodes: util.List[Node] = getCategoryNode() + (graphDB.getNodeByUniqueIds(_: String, _: SearchCriteria)).expects(*, *).returns(Future(nodes)).anyNumberOfTimes() + val request = getContentRequest() + request.getContext.put("identifier", "do_123") + request.getRequest.put("lastPublishedBy", "Support Team") + request.setOperation("publishContent") + val response = callActor(request, Props(new ContentActor())) + assert("successful".equals(response.getParams.getStatus)) + } + it should "through client exception for publish operation if node is under processing" in { implicit val ss = mock[StorageService] implicit val oec: OntologyEngineContext = mock[OntologyEngineContext] From a26987cb2d27c3e12a152683dec1468a2d7db422 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Tue, 9 Aug 2022 11:15:36 +0530 Subject: [PATCH 107/490] Issue #KN-9 feat: Content Publish API refactor. --- .../content/actors/TestContentActor.scala | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/content-api/content-actors/src/test/scala/org/sunbird/content/actors/TestContentActor.scala b/content-api/content-actors/src/test/scala/org/sunbird/content/actors/TestContentActor.scala index 9449943cf..6cb3582c7 100644 --- a/content-api/content-actors/src/test/scala/org/sunbird/content/actors/TestContentActor.scala +++ b/content-api/content-actors/src/test/scala/org/sunbird/content/actors/TestContentActor.scala @@ -746,6 +746,24 @@ class TestContentActor extends BaseSpec with MockFactory { assert("successful".equals(response.getParams.getStatus)) } + it should "return success response for 'publishContent' for Collection mimeType" in { + implicit val oec: OntologyEngineContext = mock[OntologyEngineContext] + val graphDB = mock[GraphService] + implicit val ss = mock[StorageService] + (oec.graphService _).expects().returns(graphDB).anyNumberOfTimes() + val node = getValidNode() + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, *, *, *).returns(Future(node)).anyNumberOfTimes() + (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, *).returns(Future(new Response())).anyNumberOfTimes() + val nodes: util.List[Node] = getCategoryNode() + (graphDB.getNodeByUniqueIds(_: String, _: SearchCriteria)).expects(*, *).returns(Future(nodes)).anyNumberOfTimes() + val request = getContentRequest() + request.getContext.put("identifier", "do_1234") + request.getRequest.put("lastPublishedBy", "Support Team") + request.setOperation("publishContent") + val response = callActor(request, Props(new ContentActor())) + assert("successful".equals(response.getParams.getStatus)) + } + it should "through client exception for publish operation if node is under processing" in { implicit val ss = mock[StorageService] implicit val oec: OntologyEngineContext = mock[OntologyEngineContext] From f1b59977b39a7db925e4b7187236820dd0ccaf02 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Fri, 12 Aug 2022 11:38:25 +0530 Subject: [PATCH 108/490] Issue #KN-9 feat: Content Publish API refactor. --- .../sunbird/content/actors/ContentActor.scala | 54 +++++++++---------- .../content/publish/mgr/PublishManager.scala | 2 +- .../content/util/ContentConstants.scala | 3 +- .../controllers/v3/ContentController.scala | 4 +- .../controllers/v4/CollectionController.scala | 4 +- .../controllers/v4/ContentController.scala | 4 +- .../content-service/app/utils/ApiId.scala | 3 +- 7 files changed, 38 insertions(+), 36 deletions(-) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/actors/ContentActor.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/actors/ContentActor.scala index c6e5d2fa4..4f2fd92ac 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/actors/ContentActor.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/actors/ContentActor.scala @@ -65,7 +65,7 @@ class ContentActor @Inject() (implicit oec: OntologyEngineContext, ss: StorageSe populateDefaultersForCreation(request) RequestUtil.restrictProperties(request) DataNode.create(request, dataModifier).map(node => { - ResponseHandler.OK.put("identifier", node.getIdentifier).put("node_id", node.getIdentifier) + ResponseHandler.OK.put(ContentConstants.IDENTIFIER, node.getIdentifier).put("node_id", node.getIdentifier) .put("versionKey", node.getMetadata.get("versionKey")) }) } @@ -76,7 +76,7 @@ class ContentActor @Inject() (implicit oec: OntologyEngineContext, ss: StorageSe request.getRequest.put("fields", fields) DataNode.read(request).map(node => { val metadata: util.Map[String, AnyRef] = NodeUtil.serialize(node, fields, node.getObjectType.toLowerCase.replace("image", ""), request.getContext.get("version").asInstanceOf[String]) - metadata.put("identifier", node.getIdentifier.replace(".img", "")) + metadata.put(ContentConstants.IDENTIFIER, node.getIdentifier.replace(".img", "")) val response: Response = ResponseHandler.OK if (responseSchemaName.isEmpty) { response.put("content", metadata) @@ -100,7 +100,7 @@ class ContentActor @Inject() (implicit oec: OntologyEngineContext, ss: StorageSe if (StringUtils.isBlank(request.getRequest.getOrDefault("channel", "").asInstanceOf[String])) throw new ClientException("ERR_INVALID_CHANNEL", "Please Provide Channel!") DataNode.read(request).map(node => { val metadata: util.Map[String, AnyRef] = NodeUtil.serialize(node, fields, node.getObjectType.toLowerCase.replace("image", ""), request.getContext.get("version").asInstanceOf[String]) - metadata.put("identifier", node.getIdentifier.replace(".img", "")) + metadata.put(ContentConstants.IDENTIFIER, node.getIdentifier.replace(".img", "")) val response: Response = ResponseHandler.OK if (StringUtils.equalsIgnoreCase(metadata.getOrDefault("channel", "").asInstanceOf[String],request.getRequest.getOrDefault("channel", "").asInstanceOf[String])) { if (responseSchemaName.isEmpty) { @@ -123,19 +123,19 @@ class ContentActor @Inject() (implicit oec: OntologyEngineContext, ss: StorageSe RequestUtil.restrictProperties(request) DataNode.update(request, dataModifier).map(node => { val identifier: String = node.getIdentifier.replace(".img", "") - ResponseHandler.OK.put("node_id", identifier).put("identifier", identifier) + ResponseHandler.OK.put("node_id", identifier).put(ContentConstants.IDENTIFIER, identifier) .put("versionKey", node.getMetadata.get("versionKey")) }) } def upload(request: Request): Future[Response] = { - val identifier: String = request.getContext.getOrDefault("identifier", "").asInstanceOf[String] + val identifier: String = request.getContext.getOrDefault(ContentConstants.IDENTIFIER, "").asInstanceOf[String] val readReq = new Request(request) - readReq.put("identifier", identifier) + readReq.put(ContentConstants.IDENTIFIER, identifier) readReq.put("fields", new util.ArrayList[String]) DataNode.read(readReq).map(node => { if (null != node & StringUtils.isNotBlank(node.getObjectType)) - request.getContext.put("schemaName", node.getObjectType.toLowerCase()) + request.getContext.put(ContentConstants.SCHEMA_NAME, node.getObjectType.toLowerCase()) UploadManager.upload(request, node) }).flatMap(f => f) } @@ -150,14 +150,14 @@ class ContentActor @Inject() (implicit oec: OntologyEngineContext, ss: StorageSe val fileName: String = request.get("fileName").asInstanceOf[String] val filePath: String = request.getRequest.getOrDefault("filePath","").asInstanceOf[String] .replaceAll("^/+|/+$", "") - val identifier: String = request.get("identifier").asInstanceOf[String] + val identifier: String = request.get(ContentConstants.IDENTIFIER).asInstanceOf[String] validatePreSignedUrlRequest(`type`, fileName, filePath) DataNode.read(request).map(node => { val objectKey = if (StringUtils.isEmpty(filePath)) "content" + File.separator + `type` + File.separator + identifier + File.separator + Slug.makeSlug(fileName, true) else filePath + File.separator + "content" + File.separator + `type` + File.separator + identifier + File.separator + Slug.makeSlug(fileName, true) val expiry = Platform.config.getString("cloud_storage.upload.url.ttl") val preSignedURL = ss.getSignedURL(objectKey, Option.apply(expiry.toInt), Option.apply("w")) - ResponseHandler.OK().put("identifier", identifier).put("pre_signed_url", preSignedURL) + ResponseHandler.OK().put(ContentConstants.IDENTIFIER, identifier).put("pre_signed_url", preSignedURL) .put("url_expiry", expiry) }) recoverWith { case e: CompletionException => throw e.getCause } } @@ -185,34 +185,34 @@ class ContentActor @Inject() (implicit oec: OntologyEngineContext, ss: StorageSe def importContent(request: Request): Future[Response] = importMgr.importObject(request) def reviewContent(request: Request): Future[Response] = { - val identifier: String = request.getContext.getOrDefault("identifier", "").asInstanceOf[String] + val identifier: String = request.getContext.getOrDefault(ContentConstants.IDENTIFIER, "").asInstanceOf[String] val readReq = new Request(request) - readReq.put("identifier", identifier) - readReq.put("mode", "edit") + readReq.put(ContentConstants.IDENTIFIER, identifier) + readReq.put(ContentConstants.MODE, ContentConstants.EDIT_MODE) DataNode.read(readReq).map(node => { if (null != node & StringUtils.isNotBlank(node.getObjectType)) - request.getContext.put("schemaName", node.getObjectType.toLowerCase()) - if (StringUtils.equalsAnyIgnoreCase("Processing", node.getMetadata.getOrDefault("status", "").asInstanceOf[String])) + request.getContext.put(ContentConstants.SCHEMA_NAME, node.getObjectType.toLowerCase()) + if (StringUtils.equalsAnyIgnoreCase(ContentConstants.PROCESSING, node.getMetadata.getOrDefault(ContentConstants.STATUS, "").asInstanceOf[String])) throw new ClientException("ERR_NODE_ACCESS_DENIED", "Review Operation Can't Be Applied On Node Under Processing State") else ReviewManager.review(request, node) }).flatMap(f => f) } def publishContent(request: Request): Future[Response] = { - val identifier: String = request.getContext.getOrDefault("identifier", "").asInstanceOf[String] - val publisher: String = request.getRequest.getOrDefault("lastPublishedBy", "").asInstanceOf[String] + val identifier: String = request.getContext.getOrDefault(ContentConstants.IDENTIFIER, "").asInstanceOf[String] + val publisher: String = request.getRequest.getOrDefault(ContentConstants.LAST_PUBLISHED_BY, "").asInstanceOf[String] if(publisher.isBlank) throw new ClientException("ERR_CONTENT_BLANK_PUBLISHER", "Publisher User Id is blank") val readReq = new Request(request) - readReq.put("identifier", identifier) - readReq.put("mode", "edit") + readReq.put(ContentConstants.IDENTIFIER, identifier) + readReq.put(ContentConstants.MODE, ContentConstants.EDIT_MODE) DataNode.read(readReq).map(node => { if (null != node & StringUtils.isNotBlank(node.getObjectType)) - request.getContext.put("schemaName", node.getObjectType.toLowerCase()) - if (StringUtils.equalsAnyIgnoreCase("Processing", node.getMetadata.getOrDefault("status", "").asInstanceOf[String])) + request.getContext.put(ContentConstants.SCHEMA_NAME, node.getObjectType.toLowerCase()) + if (StringUtils.equalsAnyIgnoreCase(ContentConstants.PROCESSING, node.getMetadata.getOrDefault(ContentConstants.STATUS, "").asInstanceOf[String])) throw new ClientException("ERR_NODE_ACCESS_DENIED", "Publish Operation Can't Be Applied On Node Under Processing State") - node.getMetadata.put("lastPublishedBy", publisher) + node.getMetadata.put(ContentConstants.LAST_PUBLISHED_BY, publisher) PublishManager.publish(request, node) }).flatMap(f => f) } @@ -287,7 +287,7 @@ class ContentActor @Inject() (implicit oec: OntologyEngineContext, ss: StorageSe } def systemUpdate(request: Request): Future[Response] = { - val identifier = request.getContext.get("identifier").asInstanceOf[String] + val identifier = request.getContext.get(ContentConstants.IDENTIFIER).asInstanceOf[String] RequestUtil.validateRequest(request) RedisCache.delete(hierarchyPrefix + request.get("rootId")) @@ -305,21 +305,21 @@ class ContentActor @Inject() (implicit oec: OntologyEngineContext, ss: StorageSe else DataNode.systemUpdate(request, response,"", None) }).map(node => { - ResponseHandler.OK.put("identifier", identifier).put("status", "success") + ResponseHandler.OK.put(ContentConstants.IDENTIFIER, identifier).put(ContentConstants.STATUS, "success") }) } def rejectContent(request: Request): Future[Response] = { RequestUtil.validateRequest(request) DataNode.read(request).map(node => { - val status = node.getMetadata.get("status").asInstanceOf[String] + val status = node.getMetadata.get(ContentConstants.STATUS).asInstanceOf[String] if (StringUtils.isBlank(status)) throw new ClientException("ERR_METADATA_ISSUE", "Content metadata error, status is blank for identifier:" + node.getIdentifier) if (StringUtils.equals("Review", status)) { - request.getRequest.put("status", "Draft") + request.getRequest.put(ContentConstants.STATUS, "Draft") request.getRequest.put("prevStatus", "Review") } else if (StringUtils.equals("FlagReview", status)) { - request.getRequest.put("status", "FlagDraft") + request.getRequest.put(ContentConstants.STATUS, "FlagDraft") request.getRequest.put("prevStatus", "FlagReview") } else new ClientException("ERR_INVALID_REQUEST", "Content not in Review status.") @@ -330,7 +330,7 @@ class ContentActor @Inject() (implicit oec: OntologyEngineContext, ss: StorageSe RequestUtil.restrictProperties(request) DataNode.update(request).map(node => { val identifier: String = node.getIdentifier.replace(".img", "") - ResponseHandler.OK.put("node_id", identifier).put("identifier", identifier) + ResponseHandler.OK.put("node_id", identifier).put(ContentConstants.IDENTIFIER, identifier) }) }).flatMap(f => f) } diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/publish/mgr/PublishManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/publish/mgr/PublishManager.scala index e365bb885..ed25c8a85 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/publish/mgr/PublishManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/publish/mgr/PublishManager.scala @@ -39,7 +39,7 @@ object PublishManager { val param = new ResponseParams param.setStatus(StatusType.successful.name) response.setParams(param) - response.put(ContentConstants.PUBLISH_STATUS, "Publish Event for Content Id '" + node.getIdentifier + "' is pushed Successfully!") + response.put(ContentConstants.PUBLISH_STATUS, s"Publish Event for Content Id '${node.getIdentifier}' is pushed Successfully!") response.put(ContentConstants.NODE_ID, node.getIdentifier) Future(response) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/util/ContentConstants.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/util/ContentConstants.scala index 070fb692e..ffc31a8d2 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/util/ContentConstants.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/util/ContentConstants.scala @@ -73,5 +73,6 @@ object ContentConstants { val ACTION: String = "action" val PUBLISH: String = "publish" val ENV: String = "env" - val KAFKA_PUBLISH_TOPIC = "kafka.publish.request.topic" + val KAFKA_PUBLISH_TOPIC: String = "kafka.publish.request.topic" + val PROCESSING: String = "Processing" } diff --git a/content-api/content-service/app/controllers/v3/ContentController.scala b/content-api/content-service/app/controllers/v3/ContentController.scala index ee6c425f5..f366793da 100644 --- a/content-api/content-service/app/controllers/v3/ContentController.scala +++ b/content-api/content-service/app/controllers/v3/ContentController.scala @@ -156,7 +156,7 @@ class ContentController @Inject()(@Named(ActorNames.CONTENT_ACTOR) contentActor: setRequestContext(contentRequest, version, objectType, schemaName) contentRequest.getContext.put("identifier", identifier); contentRequest.getContext.put("publish_type", "public"); - getResult(ApiId.PUBLISH_CONTENT, contentActor, contentRequest) + getResult(ApiId.PUBLISH_CONTENT_PUBLIC, contentActor, contentRequest) } def publishUnlisted(identifier: String) = Action.async { implicit request => @@ -168,7 +168,7 @@ class ContentController @Inject()(@Named(ActorNames.CONTENT_ACTOR) contentActor: setRequestContext(contentRequest, version, objectType, schemaName) contentRequest.getContext.put("identifier", identifier); contentRequest.getContext.put("publish_type", "unlisted"); - getResult(ApiId.PUBLISH_CONTENT, contentActor, contentRequest) + getResult(ApiId.PUBLISH_CONTENT_UNLSTED, contentActor, contentRequest) } def review(identifier: String) = Action.async { implicit request => diff --git a/content-api/content-service/app/controllers/v4/CollectionController.scala b/content-api/content-service/app/controllers/v4/CollectionController.scala index 34d5ba35a..7b4647f61 100644 --- a/content-api/content-service/app/controllers/v4/CollectionController.scala +++ b/content-api/content-service/app/controllers/v4/CollectionController.scala @@ -273,7 +273,7 @@ class CollectionController @Inject()(@Named(ActorNames.CONTENT_ACTOR) contentAc setRequestContext(contentRequest, version, objectType, schemaName) contentRequest.getContext.put("identifier", identifier); contentRequest.getContext.put("publish_type", "public"); - getResult(ApiId.PUBLISH_CONTENT, contentActor, contentRequest) + getResult(ApiId.PUBLISH_CONTENT_PUBLIC, contentActor, contentRequest, version = apiVersion) } def publishUnlisted(identifier: String) = Action.async { implicit request => @@ -285,7 +285,7 @@ class CollectionController @Inject()(@Named(ActorNames.CONTENT_ACTOR) contentAc setRequestContext(contentRequest, version, objectType, schemaName) contentRequest.getContext.put("identifier", identifier); contentRequest.getContext.put("publish_type", "unlisted"); - getResult(ApiId.PUBLISH_CONTENT, contentActor, contentRequest) + getResult(ApiId.PUBLISH_CONTENT_UNLSTED, contentActor, contentRequest, version = apiVersion) } diff --git a/content-api/content-service/app/controllers/v4/ContentController.scala b/content-api/content-service/app/controllers/v4/ContentController.scala index 98594df34..78d717f13 100644 --- a/content-api/content-service/app/controllers/v4/ContentController.scala +++ b/content-api/content-service/app/controllers/v4/ContentController.scala @@ -227,7 +227,7 @@ class ContentController @Inject()(@Named(ActorNames.CONTENT_ACTOR) contentActor: setRequestContext(contentRequest, version, objectType, schemaName) contentRequest.getContext.put("identifier", identifier); contentRequest.getContext.put("publish_type", "public"); - getResult(ApiId.PUBLISH_CONTENT, contentActor, contentRequest) + getResult(ApiId.PUBLISH_CONTENT_PUBLIC, contentActor, contentRequest, version = apiVersion) } def publishUnlisted(identifier: String) = Action.async { implicit request => @@ -239,7 +239,7 @@ class ContentController @Inject()(@Named(ActorNames.CONTENT_ACTOR) contentActor: setRequestContext(contentRequest, version, objectType, schemaName) contentRequest.getContext.put("identifier", identifier); contentRequest.getContext.put("publish_type", "unlisted"); - getResult(ApiId.PUBLISH_CONTENT, contentActor, contentRequest) + getResult(ApiId.PUBLISH_CONTENT_UNLSTED, contentActor, contentRequest, version = apiVersion) } } diff --git a/content-api/content-service/app/utils/ApiId.scala b/content-api/content-service/app/utils/ApiId.scala index 804aa87c6..f69ae6c49 100644 --- a/content-api/content-service/app/utils/ApiId.scala +++ b/content-api/content-service/app/utils/ApiId.scala @@ -22,7 +22,8 @@ object ApiId { val SYSTEM_UPDATE_CONTENT = "api.content.system.update" val REVIEW_CONTENT = "api.content.review" val REJECT_CONTENT = "api.content.review.reject" - val PUBLISH_CONTENT = "api.content.publish" + val PUBLISH_CONTENT_PUBLIC = "api.content.publish.public" + val PUBLISH_CONTENT_UNLSTED = "api.content.publish.unlisted" // Collection APIs val ADD_HIERARCHY = "api.content.hierarchy.add" From a28e205108b426aa6663d7349902e7752aa3d812 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Fri, 12 Aug 2022 11:42:40 +0530 Subject: [PATCH 109/490] Issue #KN-9 feat: Content Publish API refactor. --- .../org/sunbird/content/publish/mgr/PublishManager.scala | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/publish/mgr/PublishManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/publish/mgr/PublishManager.scala index ed25c8a85..8786883c5 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/publish/mgr/PublishManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/publish/mgr/PublishManager.scala @@ -61,7 +61,6 @@ object PublishManager { private def generateInstructionEventMetadata(actor: util.Map[String, AnyRef], context: util.Map[String, AnyRef], objectData: util.Map[String, AnyRef], edata: util.Map[String, AnyRef], node: Node, identifier: String): Unit = { val metadata: util.Map[String, AnyRef] = node.getMetadata - val instructionEventMetadata = new util.HashMap[String, AnyRef] actor.put(ContentConstants.ID, node.getObjectType.toLowerCase() + "-publish") actor.put(ContentConstants.TYPE, ContentConstants.SYSTEM) @@ -76,12 +75,17 @@ object PublishManager { } objectData.put(ContentConstants.ID, identifier) objectData.put(ContentConstants.VER, metadata.get(ContentConstants.VERSION_KEY)) + getEData(metadata, edata, identifier, node.getObjectType) + } + + private def getEData(metadata: util.Map[String, AnyRef], edata: util.Map[String, AnyRef], identifier: String, objectType: String): Unit = { + val instructionEventMetadata = new util.HashMap[String, AnyRef] edata.put(ContentConstants.PUBLISH_TYPE, metadata.get(ContentConstants.PUBLISH_TYPE)) instructionEventMetadata.put(ContentConstants.PACKAGE_VERSION, metadata.getOrDefault(ContentConstants.PACKAGE_VERSION,0.asInstanceOf[AnyRef])) instructionEventMetadata.put(ContentConstants.MIME_TYPE, metadata.get(ContentConstants.MIME_TYPE)) instructionEventMetadata.put(ContentConstants.LAST_PUBLISHED_BY, metadata.get(ContentConstants.LAST_PUBLISHED_BY)) instructionEventMetadata.put(ContentConstants.IDENTIFIER, identifier) - instructionEventMetadata.put(ContentConstants.OBJECT_TYPE, node.getObjectType) + instructionEventMetadata.put(ContentConstants.OBJECT_TYPE, objectType) edata.put(ContentConstants.METADATA, instructionEventMetadata) edata.put(ContentConstants.ACTION, ContentConstants.PUBLISH) edata.put(ContentConstants.CONTENT_TYPE, metadata.get(ContentConstants.CONTENT_TYPE)) From c461aa2679003276b9777b9559a81865508e3085 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Fri, 12 Aug 2022 11:43:42 +0530 Subject: [PATCH 110/490] Issue #KN-9 feat: Content Publish API refactor. --- .../org/sunbird/content/publish/mgr/PublishManager.scala | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/publish/mgr/PublishManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/publish/mgr/PublishManager.scala index 8786883c5..16a4e2f71 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/publish/mgr/PublishManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/publish/mgr/PublishManager.scala @@ -62,8 +62,11 @@ object PublishManager { private def generateInstructionEventMetadata(actor: util.Map[String, AnyRef], context: util.Map[String, AnyRef], objectData: util.Map[String, AnyRef], edata: util.Map[String, AnyRef], node: Node, identifier: String): Unit = { val metadata: util.Map[String, AnyRef] = node.getMetadata + // actor actor.put(ContentConstants.ID, node.getObjectType.toLowerCase() + "-publish") actor.put(ContentConstants.TYPE, ContentConstants.SYSTEM) + + //context context.put(ContentConstants.CHANNEL, metadata.get(ContentConstants.CHANNEL)) context.put(ContentConstants.P_DATA, new util.HashMap[String, AnyRef]() {{ put(ContentConstants.ID, ContentConstants.SUNBIRD_PLATFORM) @@ -73,8 +76,12 @@ object PublishManager { val env: String = Platform.getString("cloud_storage.env", "dev") context.put(ContentConstants.ENV, env) } + + //objectData objectData.put(ContentConstants.ID, identifier) objectData.put(ContentConstants.VER, metadata.get(ContentConstants.VERSION_KEY)) + + //edata getEData(metadata, edata, identifier, node.getObjectType) } From b66079d36e26d6eff09c4c9369b7e4169b23d022 Mon Sep 17 00:00:00 2001 From: AmiableAnil Date: Fri, 19 Aug 2022 13:32:51 +0530 Subject: [PATCH 111/490] Issue #KN-231 feat: Added the support for google-cloud. --- platform-modules/mimetype-manager/pom.xml | 2 +- .../sunbird/cloudstore/StorageService.scala | 19 ++++++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/platform-modules/mimetype-manager/pom.xml b/platform-modules/mimetype-manager/pom.xml index eb25c58f1..c97b9bbd2 100644 --- a/platform-modules/mimetype-manager/pom.xml +++ b/platform-modules/mimetype-manager/pom.xml @@ -30,7 +30,7 @@ org.sunbird cloud-store-sdk - 1.3.0 + 1.4.0 org.scala-lang diff --git a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala index 83fabffc8..0045caf82 100644 --- a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala +++ b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala @@ -28,14 +28,17 @@ class StorageService { val storageKey = Platform.config.getString("aws_storage_key") val storageSecret = Platform.config.getString("aws_storage_secret") storageService = StorageServiceFactory.getStorageService(new StorageConfig(storageType, storageKey, storageSecret)) + } else if (StringUtils.equalsIgnoreCase(storageType, "gcloud")) { + val storageKey = Platform.config.getString("gcloud_client_key") + val storageSecret = Platform.config.getString("gcloud_private_secret") + storageService = StorageServiceFactory.getStorageService(new StorageConfig(storageType, storageKey, storageSecret)) } - else if (StringUtils.equalsIgnoreCase(storageType, "cephs3")) { - val storageKey = Platform.config.getString("cephs3_storage_key") - val storageSecret = Platform.config.getString("cephs3_storage_secret") - val endpoint = Platform.config.getString("cephs3_storage_endpoint") - storageService = StorageServiceFactory.getStorageService(new StorageConfig(storageType, storageKey, storageSecret, Option(endpoint))) - - } +// else if (StringUtils.equalsIgnoreCase(storageType, "cephs3")) { +// val storageKey = Platform.config.getString("cephs3_storage_key") +// val storageSecret = Platform.config.getString("cephs3_storage_secret") +// val endpoint = Platform.config.getString("cephs3_storage_endpoint") +// storageService = StorageServiceFactory.getStorageService(new StorageConfig(storageType, storageKey, storageSecret, Option(endpoint))) +// } else throw new ServerException("ERR_INVALID_CLOUD_STORAGE", "Error while initialising cloud storage") } storageService @@ -46,6 +49,8 @@ class StorageService { Platform.config.getString("azure_storage_container") else if (StringUtils.equalsIgnoreCase(storageType, "aws")) Platform.config.getString("aws_storage_container") + else if (StringUtils.equalsIgnoreCase(storageType, "gcloud")) + Platform.config.getString("gcloud_storage_bucket") else if (StringUtils.equalsIgnoreCase(storageType, "cephs3")) Platform.config.getString("cephs3_storage_container") else From 64fdb653f98303cac87fa757c47b6e082ee7116a Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Tue, 23 Aug 2022 14:25:33 +0530 Subject: [PATCH 112/490] Issue #KN-9 feat: Content Publish API refactor. --- .../Publish API.postman_collection.json | 9744 +++++++++++++++++ .../Reports/Publish API.postman_test_run.json | 2679 +++++ 2 files changed, 12423 insertions(+) create mode 100644 content-api/api-tests/Collections/Publish API.postman_collection.json create mode 100644 content-api/api-tests/Reports/Publish API.postman_test_run.json diff --git a/content-api/api-tests/Collections/Publish API.postman_collection.json b/content-api/api-tests/Collections/Publish API.postman_collection.json new file mode 100644 index 000000000..75a526868 --- /dev/null +++ b/content-api/api-tests/Collections/Publish API.postman_collection.json @@ -0,0 +1,9744 @@ +{ + "info": { + "_postman_id": "607f186e-f5e0-4d51-a4c6-02c534d1689c", + "name": "Publish API", + "description": "The Content/Collection Publish APIs allow you to publish content/collection on the Sunbird platform. Publish API was previously available as part of learning-service which has now been refactored into content-service.\n\nThe URL for Content Management API(s) is /content/v2/publish and /collection/v2/publish.", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "Pre-requisite", + "item": [ + { + "name": "Collection Create", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.environment.set(\"collection_id\", jsonResponse.result.identifier);", + "pm.globals.set(\"versionKey\", jsonResponse.result.versionKey);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Sample Collection\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/vnd.ekstep.content-collection\",\n \"primaryCategory\": \"Digital Textbook\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v4/create", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v4", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] + }, + { + "name": "TOC Upload", + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "fileUrl", + "value": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/hierarchy/do_113316820811522048194/createtoc.csv", + "type": "text" + } + ] + }, + "url": { + "raw": "{{host}}/collection/v4/import/{{collection_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "collection", + "v4", + "import", + "{{collection_id}}" + ] + } + }, + "response": [] + }, + { + "name": "Collection Create wo hierarchy", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.environment.set(\"collection_id_wo_hierarchy\", jsonResponse.result.identifier);", + "pm.globals.set(\"collection_id_wo_hierarchy\", jsonResponse.result.identifier);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Sample Collection\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/vnd.ekstep.content-collection\",\n \"primaryCategory\": \"Digital Textbook\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v4/create", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v4", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] + }, + { + "name": "PDF Content Create", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.globals.set(\"pdf_content_id\", jsonResponse.result.identifier);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\",\n \"artifactUrl\": \"https://sunbirdstagingpublic.blob.core.windows.net/sunbird-content-staging/content/assets/do_2136088747812372481510/samplepdf.pdf\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v4/create", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v4", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] + }, + { + "name": "PDF Content wo artifactUrl", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.globals.set(\"pdf_content_id_wo_artifact\", jsonResponse.result.identifier);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v4/create", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v4", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] + }, + { + "name": "MP4 Content Create", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.globals.set(\"mp4_content_id\", jsonResponse.result.identifier);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"video/mp4\",\n \"primaryCategory\": \"Explanation Content\",\n \"artifactUrl\": \"https://sunbirdstagingpublic.blob.core.windows.net/sunbird-content-staging/content/assets/do_21360643694649344011752/draftversion.mp4\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v4/create", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v4", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] + }, + { + "name": "MP4 Content Create wo artifactUrl", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.globals.set(\"mp4_content_id_wo_artifact\", jsonResponse.result.identifier);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"video/mp4\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v4/create", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v4", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] + }, + { + "name": "WEBM Content Create", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.globals.set(\"webm_content_id\", jsonResponse.result.identifier);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"video/webm\",\n \"primaryCategory\": \"Explanation Content\",\n \"artifactUrl\": \"https://sunbirdstagingpublic.blob.core.windows.net/sunbird-content-staging/content/assets/do_21360604894303027211507/big-buck-bunny_trailer.webm\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v4/create", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v4", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] + }, + { + "name": "WEBM Content Create wo artifactUrl", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.globals.set(\"webm_content_id_wo_artifact\", jsonResponse.result.identifier);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"video/webm\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v4/create", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v4", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] + }, + { + "name": "HTML Content Create", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.globals.set(\"html_content_id\", jsonResponse.result.identifier);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/vnd.ekstep.html-archive\",\n \"primaryCategory\": \"Explanation Content\",\n \"artifactUrl\": \"https://sunbirdstagingpublic.blob.core.windows.net/sunbird-content-staging/content/do_21357841995316428811795/artifact/2.-ekiikrnn-kii-prkrti-output-3_1657521986480_1657522093104.zip\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v4/create", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v4", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] + }, + { + "name": "HTML Content Create wo artifactUrl", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.globals.set(\"html_content_id_wo_artifact\", jsonResponse.result.identifier);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/vnd.ekstep.html-archive\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v4/create", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v4", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] + }, + { + "name": "H5P Content Create", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.globals.set(\"h5p_content_id\", jsonResponse.result.identifier);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/vnd.ekstep.h5p-archive\",\n \"primaryCategory\": \"Explanation Content\",\n \"artifactUrl\": \"https://sunbirdstagingpublic.blob.core.windows.net/sunbird-content-staging/content/do_2136088263163084801476/artifact/1661233685237_do_2136088263163084801476.zip\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v4/create", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v4", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] + }, + { + "name": "H5P Content Create wo artifactUrl", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.globals.set(\"h5p_content_id_wo_artifact\", jsonResponse.result.identifier);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/vnd.ekstep.h5p-archive\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v4/create", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v4", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] + }, + { + "name": "ECML Content Create", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.globals.set(\"ecml_content_id\", jsonResponse.result.identifier);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/vnd.ekstep.ecml-archive\",\n \"primaryCategory\": \"Explanation Content\",\n \"body\": \"{\\\"theme\\\":{\\\"id\\\":\\\"theme\\\",\\\"version\\\":\\\"1.0\\\",\\\"startStage\\\":\\\"463caa09-b78e-412c-ae20-a2776c76b4a0\\\",\\\"stage\\\":[{\\\"x\\\":0,\\\"y\\\":0,\\\"w\\\":100,\\\"h\\\":100,\\\"id\\\":\\\"463caa09-b78e-412c-ae20-a2776c76b4a0\\\",\\\"rotate\\\":null,\\\"config\\\":{\\\"__cdata\\\":\\\"{\\\\\\\"opacity\\\\\\\":100,\\\\\\\"strokeWidth\\\\\\\":1,\\\\\\\"stroke\\\\\\\":\\\\\\\"rgba(255, 255, 255, 0)\\\\\\\",\\\\\\\"autoplay\\\\\\\":false,\\\\\\\"visible\\\\\\\":true,\\\\\\\"color\\\\\\\":\\\\\\\"#FFFFFF\\\\\\\",\\\\\\\"genieControls\\\\\\\":false,\\\\\\\"instructions\\\\\\\":\\\\\\\"\\\\\\\"}\\\"},\\\"manifest\\\":{\\\"media\\\":[]},\\\"shape\\\":[{\\\"type\\\":\\\"ellipse\\\",\\\"x\\\":15,\\\"y\\\":15,\\\"fill\\\":\\\"#00FF00\\\",\\\"w\\\":18,\\\"h\\\":32,\\\"stroke\\\":\\\"rgba(255, 255, 255, 0)\\\",\\\"strokeWidth\\\":1,\\\"opacity\\\":1,\\\"rotate\\\":0,\\\"r\\\":64.8,\\\"z-index\\\":0,\\\"id\\\":\\\"7e35536a-1e50-4ada-872e-416b04da61a0\\\",\\\"config\\\":{\\\"__cdata\\\":\\\"{\\\\\\\"opacity\\\\\\\":100,\\\\\\\"strokeWidth\\\\\\\":1,\\\\\\\"stroke\\\\\\\":\\\\\\\"rgba(255, 255, 255, 0)\\\\\\\",\\\\\\\"autoplay\\\\\\\":false,\\\\\\\"visible\\\\\\\":true,\\\\\\\"color\\\\\\\":\\\\\\\"#00FF00\\\\\\\",\\\\\\\"radius\\\\\\\":64.8}\\\"}}]}],\\\"manifest\\\":{\\\"media\\\":[{\\\"id\\\":\\\"d91cc898-8885-4642-9e55-d16bd502d946\\\",\\\"plugin\\\":\\\"org.ekstep.navigation\\\",\\\"ver\\\":\\\"1.0\\\",\\\"src\\\":\\\"/content-plugins/org.ekstep.navigation-1.0/renderer/controller/navigation_ctrl.js\\\",\\\"type\\\":\\\"js\\\"},{\\\"id\\\":\\\"e02a792e-b75e-4eea-bae0-58666a4ee5c2\\\",\\\"plugin\\\":\\\"org.ekstep.navigation\\\",\\\"ver\\\":\\\"1.0\\\",\\\"src\\\":\\\"/content-plugins/org.ekstep.navigation-1.0/renderer/templates/navigation.html\\\",\\\"type\\\":\\\"js\\\"},{\\\"id\\\":\\\"org.ekstep.navigation\\\",\\\"plugin\\\":\\\"org.ekstep.navigation\\\",\\\"ver\\\":\\\"1.0\\\",\\\"src\\\":\\\"/content-plugins/org.ekstep.navigation-1.0/renderer/plugin.js\\\",\\\"type\\\":\\\"plugin\\\"},{\\\"id\\\":\\\"org.ekstep.navigation_manifest\\\",\\\"plugin\\\":\\\"org.ekstep.navigation\\\",\\\"ver\\\":\\\"1.0\\\",\\\"src\\\":\\\"/content-plugins/org.ekstep.navigation-1.0/manifest.json\\\",\\\"type\\\":\\\"json\\\"}]},\\\"plugin-manifest\\\":{\\\"plugin\\\":[{\\\"id\\\":\\\"org.ekstep.navigation\\\",\\\"ver\\\":\\\"1.0\\\",\\\"type\\\":\\\"plugin\\\",\\\"depends\\\":\\\"\\\"}]},\\\"compatibilityVersion\\\":2}}\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v4/create", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v4", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] + }, + { + "name": "ECML Content Create invalid body", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.globals.set(\"ecml_content_id_invalid_body\", jsonResponse.result.identifier);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/vnd.ekstep.ecml-archive\",\n \"primaryCategory\": \"Explanation Content\",\n \"body\": \"dummy\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v4/create", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v4", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] + }, + { + "name": "ECML Content Create wo body", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.globals.set(\"ecml_content_id_wo_body\", jsonResponse.result.identifier);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/vnd.ekstep.ecml-archive\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v4/create", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v4", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] + }, + { + "name": "EPUB Content Create", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.globals.set(\"epub_content_id\", jsonResponse.result.identifier);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/epub\",\n \"primaryCategory\": \"Explanation Content\",\n \"artifactUrl\": \"https://sunbirdstagingpublic.blob.core.windows.net/sunbird-content-staging/content/assets/do_2136085231556526081377/faulkner-sound-and-the-fury.epub\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v4/create", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v4", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] + }, + { + "name": "EPUB Content Create wo artifactUrl", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.globals.set(\"epub_content_id_wo_artifact\", jsonResponse.result.identifier);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/epub\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v4/create", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v4", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] + }, + { + "name": "Youtube Content Create", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.globals.set(\"yt_content_id\", jsonResponse.result.identifier);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"video/x-youtube\",\n \"primaryCategory\": \"Explanation Content\",\n \"artifactUrl\": \"https://www.youtube.com/watch?v=fkLcI4R7unE\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v4/create", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v4", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] + }, + { + "name": "Youtube Content Create wo artifactUrl", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.globals.set(\"yt_content_id_wo_artifact\", jsonResponse.result.identifier);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"video/x-youtube\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v4/create", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v4", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] + }, + { + "name": "Plugin Create", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.globals.set(\"plugin_id\", jsonResponse.result.identifier);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"org.ekstep.summary-test\",\n \"mimeType\": \"application/vnd.ekstep.plugin-archive\",\n \"primaryCategory\": \"Explanation Content\",\n \"artifactUrl\": \"https://ntpproductionall.blob.core.windows.net/ntp-content-production/content/org.ekstep.summary/artifact/org.ekstep.summary-1.0-1_1574060894628.zip\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v4/create", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v4", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] + }, + { + "name": "Plugin Create wo artifact", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.globals.set(\"plugin_id_wo_artifact\", jsonResponse.result.identifier);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"test-plugin\",\n \"mimeType\": \"application/vnd.ekstep.plugin-archive\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v4/create", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v4", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] + } + ] + }, + { + "name": "Positive", + "item": [ + { + "name": "Content Publish API", + "item": [ + { + "name": "Collection Publish", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/collection/v4/publish/{{collection_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "collection", + "v4", + "publish", + "{{collection_id}}" + ] + } + }, + "response": [] + }, + { + "name": "PDF Content Publish", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v4/publish/{{pdf_content_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v4", + "publish", + "{{pdf_content_id}}" + ] + } + }, + "response": [] + }, + { + "name": "MP4 Content Publish", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v4/publish/{{mp4_content_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v4", + "publish", + "{{mp4_content_id}}" + ] + } + }, + "response": [] + }, + { + "name": "WEBM Content Publish", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v4/publish/{{webm_content_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v4", + "publish", + "{{webm_content_id}}" + ] + } + }, + "response": [] + }, + { + "name": "HTML Content Publish", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/collection/v4/publish/{{html_content_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "collection", + "v4", + "publish", + "{{html_content_id}}" + ] + } + }, + "response": [] + }, + { + "name": "H5P Content Publish", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/collection/v4/publish/{{h5p_content_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "collection", + "v4", + "publish", + "{{h5p_content_id}}" + ] + } + }, + "response": [] + }, + { + "name": "EPUB Content Publish", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/collection/v4/publish/{{epub_content_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "collection", + "v4", + "publish", + "{{epub_content_id}}" + ] + } + }, + "response": [] + }, + { + "name": "ECML Content Publish", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/collection/v4/publish/{{ecml_content_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "collection", + "v4", + "publish", + "{{ecml_content_id}}" + ] + } + }, + "response": [] + }, + { + "name": "Youtube Content Publish", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/collection/v4/publish/{{yt_content_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "collection", + "v4", + "publish", + "{{yt_content_id}}" + ] + } + }, + "response": [] + }, + { + "name": "Plugin Publish", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/collection/v4/publish/{{plugin_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "collection", + "v4", + "publish", + "{{plugin_id}}" + ] + } + }, + "response": [] + } + ] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "let jsonResponse = pm.response.json();", + "", + "//Verification", + "pm.test(\"Status code is 200\", () => {", + " pm.response.to.have.status(200)", + "});", + "//code name contains a string", + "tests[\"Status code name has string OK\"] = responseCode.name.has(\"OK\");", + "", + "// pm.test(\"Content-Encoding header is present\", () => {", + "// pm.response.to.have.header(\"Content-Encoding\");", + "// });", + "// pm.test(\"Content-Encoding header is gzip\", () => {", + "// pm.expect(pm.response.headers.get('Content-Encoding')).to.eql('gzip');", + "// });", + "", + "pm.test(\"Verify keys and data type of the params in positive scenario\", () => {", + " pm.expect(jsonResponse.params.resmsgid).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.msgid).to.be.null;", + " pm.expect(jsonResponse.params.err).to.be.null;", + " pm.expect(jsonResponse.params.status).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.errmsg).to.be.null;", + "});", + "", + "pm.test(\"params.status should be \\\"successful\\\"\", () => {", + " pm.expect(jsonResponse.params.status).to.eql(\"successful\");", + "});", + "pm.test(\"responseCode should be OK\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"OK\");", + "});" + ] + } + } + ] + }, + { + "name": "Negative", + "item": [ + { + "name": "Content Publish API", + "item": [ + { + "name": "Invalid Content ID Publish", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 404\", function () {", + " pm.response.to.have.status(404);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v4/publish/do_dummy_id", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v4", + "publish", + "do_dummy_id" + ] + } + }, + "response": [] + }, + { + "name": "Invalid Collection ID Publish", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 404\", function () {", + " pm.response.to.have.status(404);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/collection/v4/publish/do_dummy_id", + "host": [ + "{{host}}" + ], + "path": [ + "collection", + "v4", + "publish", + "do_dummy_id" + ] + } + }, + "response": [] + }, + { + "name": "PDF Content Publish wo artifactUrl", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 400\", function () {", + " pm.response.to.have.status(400);", + "});", + "", + "pm.test(\"Response throws CLIENT_ERROR\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.responseCode).equals(\"CLIENT_ERROR\")", + " ", + " const params = responseJson.params;", + " pm.expect(params.status).equals(\"failed\")", + " pm.expect(params.err).equals(\"VALIDATOR_ERROR\") ", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v4/publish/{{pdf_content_id_wo_artifact}}", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v4", + "publish", + "{{pdf_content_id_wo_artifact}}" + ] + } + }, + "response": [] + }, + { + "name": "MP4 Content Publish wo artifactUrl", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 400\", function () {", + " pm.response.to.have.status(400);", + "});", + "", + "pm.test(\"Response throws CLIENT_ERROR\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.responseCode).equals(\"CLIENT_ERROR\")", + " ", + " const params = responseJson.params;", + " pm.expect(params.status).equals(\"failed\")", + " pm.expect(params.err).equals(\"VALIDATOR_ERROR\") ", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v4/publish/{{mp4_content_id_wo_artifact}}", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v4", + "publish", + "{{mp4_content_id_wo_artifact}}" + ] + } + }, + "response": [] + }, + { + "name": "WEBM Content Publish wo artifactUrl", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 400\", function () {", + " pm.response.to.have.status(400);", + "});", + "", + "pm.test(\"Response throws CLIENT_ERROR\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.responseCode).equals(\"CLIENT_ERROR\")", + " ", + " const params = responseJson.params;", + " pm.expect(params.status).equals(\"failed\")", + " pm.expect(params.err).equals(\"VALIDATOR_ERROR\") ", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v4/publish/{{webm_content_id_wo_artifact}}", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v4", + "publish", + "{{webm_content_id_wo_artifact}}" + ] + } + }, + "response": [] + }, + { + "name": "HTML Content Publish wo artifactUrl", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 400\", function () {", + " pm.response.to.have.status(400);", + "});", + "", + "pm.test(\"Response throws CLIENT_ERROR\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.responseCode).equals(\"CLIENT_ERROR\")", + " ", + " const params = responseJson.params;", + " pm.expect(params.status).equals(\"failed\")", + " pm.expect(params.err).equals(\"VALIDATOR_ERROR\") ", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v4/publish/{{html_content_id_wo_artifact}}", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v4", + "publish", + "{{html_content_id_wo_artifact}}" + ] + } + }, + "response": [] + }, + { + "name": "H5P Content Publish wo artifactUrl", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 400\", function () {", + " pm.response.to.have.status(400);", + "});", + "", + "pm.test(\"Response throws CLIENT_ERROR\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.responseCode).equals(\"CLIENT_ERROR\")", + " ", + " const params = responseJson.params;", + " pm.expect(params.status).equals(\"failed\")", + " pm.expect(params.err).equals(\"VALIDATOR_ERROR\") ", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v4/publish/{{h5p_content_id_wo_artifact}}", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v4", + "publish", + "{{h5p_content_id_wo_artifact}}" + ] + } + }, + "response": [] + }, + { + "name": "EPUB Content Publish wo artifactUrl", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 400\", function () {", + " pm.response.to.have.status(400);", + "});", + "", + "pm.test(\"Response throws CLIENT_ERROR\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.responseCode).equals(\"CLIENT_ERROR\")", + " ", + " const params = responseJson.params;", + " pm.expect(params.status).equals(\"failed\")", + " pm.expect(params.err).equals(\"VALIDATOR_ERROR\") ", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v4/publish/{{epub_content_id_wo_artifact}}", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v4", + "publish", + "{{epub_content_id_wo_artifact}}" + ] + } + }, + "response": [] + }, + { + "name": "Youtube Content Publish wo artifactUrl", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 400\", function () {", + " pm.response.to.have.status(400);", + "});", + "", + "pm.test(\"Response throws CLIENT_ERROR\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.responseCode).equals(\"CLIENT_ERROR\")", + " ", + " const params = responseJson.params;", + " pm.expect(params.status).equals(\"failed\")", + " pm.expect(params.err).equals(\"VALIDATOR_ERROR\") ", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v4/publish/{{yt_content_id_wo_artifact}}", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v4", + "publish", + "{{yt_content_id_wo_artifact}}" + ] + } + }, + "response": [] + }, + { + "name": "Plugin Publish wo artifactUrl", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 400\", function () {", + " pm.response.to.have.status(400);", + "});", + "", + "pm.test(\"Response throws CLIENT_ERROR\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.responseCode).equals(\"CLIENT_ERROR\")", + " ", + " const params = responseJson.params;", + " pm.expect(params.status).equals(\"failed\")", + " pm.expect(params.err).equals(\"VALIDATOR_ERROR\") ", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v4/publish/{{plugin_id_wo_artifact}}", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v4", + "publish", + "{{plugin_id_wo_artifact}}" + ] + } + }, + "response": [] + }, + { + "name": "Collection Publish without hierarchy", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 400\", function () {", + " pm.response.to.have.status(400);", + "});", + "", + "pm.test(\"Response throws ERR_COLLECTION_REVIEW\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.responseCode).equals(\"CLIENT_ERROR\")", + " ", + " const params = responseJson.params;", + " pm.expect(params.status).equals(\"failed\")", + " pm.expect(params.err).equals(\"ERR_COLLECTION_REVIEW\") ", + "});", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "setTimeout(function(){}, 40000);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/collection/v4/publish/{{collection_id_wo_hierarchy}}", + "host": [ + "{{host}}" + ], + "path": [ + "collection", + "v4", + "publish", + "{{collection_id_wo_hierarchy}}" + ] + } + }, + "response": [] + }, + { + "name": "ECML Content Publish wo artifactUrl", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 400\", function () {", + " pm.response.to.have.status(400);", + "});", + "", + "pm.test(\"Response throws CLIENT_ERROR\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.responseCode).equals(\"CLIENT_ERROR\")", + " ", + " const params = responseJson.params;", + " pm.expect(params.status).equals(\"failed\")", + " pm.expect(params.err).equals(\"VALIDATOR_ERROR\") ", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "setTimeout(function(){}, 2000);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v4/publish/{{ecml_content_id_wo_body}}", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v4", + "publish", + "{{ecml_content_id_wo_body}}" + ] + } + }, + "response": [] + }, + { + "name": "ECML Content Publish invalid Body", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 400\", function () {", + " pm.response.to.have.status(400);", + "});", + "", + "pm.test(\"Response throws CLIENT_ERROR\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.responseCode).equals(\"CLIENT_ERROR\")", + " ", + " const params = responseJson.params;", + " pm.expect(params.status).equals(\"failed\")", + " pm.expect(params.err).equals(\"INVALID_CONTENT_BODY\") ", + "});", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "setTimeout(function(){}, 1000);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v4/publish/{{ecml_content_id_invalid_body}}", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v4", + "publish", + "{{ecml_content_id_invalid_body}}" + ] + } + }, + "response": [] + } + ] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "let jsonResponse = pm.response.json();", + "", + "//Verification", + "// pm.test(\"Content-Encoding header should not present\", () => {", + "// pm.response.to.not.have.header(\"Content-Encoding\");", + "// });", + "", + "pm.test(\"Verify keys and data type of the params in negative scenario\", () => {", + " pm.expect(jsonResponse.params.resmsgid).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.msgid).to.be.null;", + " pm.expect(jsonResponse.params.err).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.status).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.errmsg).to.be.a(\"string\");", + "});", + "", + "pm.test(\"params.status should be \\\"failed\\\"\", () => {", + " pm.expect(jsonResponse.params.status).to.eql(\"failed\");", + "});", + "" + ] + } + } + ] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "pm.test(\"Content-Type header is present\", () => {", + " pm.response.to.have.header(\"Content-Type\");", + "});", + "pm.test(\"Content-Type header is application/json\", () => {", + " pm.expect(pm.response.headers.get('Content-Type')).to.eql('application/json');", + "});", + "", + "pm.test(\"Validate keys and data type of the response body\", () => {", + " let jsonResponse = pm.response.json();", + " pm.expect(jsonResponse).to.be.an(\"object\").that.has.all.keys('id', 'ver', 'ts', 'params', 'responseCode', 'result');", + " pm.expect(jsonResponse.id).to.be.a(\"string\");", + " pm.expect(jsonResponse.ver).to.be.a(\"string\");", + " pm.expect(jsonResponse.ts).to.be.a(\"string\");", + " pm.expect(jsonResponse.params).to.be.an(\"object\").that.has.all.keys('resmsgid', 'msgid', 'err', 'status', 'errmsg');", + " pm.expect(jsonResponse.responseCode).to.be.a(\"string\");", + " pm.expect(jsonResponse.result).to.be.an(\"object\");", + "});" + ] + } + } + ] +} \ No newline at end of file diff --git a/content-api/api-tests/Reports/Publish API.postman_test_run.json b/content-api/api-tests/Reports/Publish API.postman_test_run.json new file mode 100644 index 000000000..d6d977823 --- /dev/null +++ b/content-api/api-tests/Reports/Publish API.postman_test_run.json @@ -0,0 +1,2679 @@ +{ + "id": "25eb49d4-fb87-4fdd-abb6-e860574ac33a", + "name": "Publish API", + "timestamp": "2022-08-23T08:43:52.665Z", + "collection_id": "13387833-607f186e-f5e0-4d51-a4c6-02c534d1689c", + "folder_id": 0, + "environment_id": "0e0b136d-70cc-437e-a0df-ccba162408d9", + "totalPass": 266, + "totalFail": 0, + "results": [ + { + "id": "80fd9644-56d3-4eb5-910f-c2f9a6bbcc39", + "name": "Collection Create", + "url": "http://localhost:9000/content/v4/create", + "time": 361, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Status code is 200": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 361 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true + } + ] + }, + { + "id": "7c5475c1-7c6a-4ca5-b71f-37b05e719446", + "name": "TOC Upload", + "url": "http://localhost:9000/collection/v4/import/do_11360891275809587211", + "time": 961, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 961 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true + } + ] + }, + { + "id": "f00000f4-e316-4081-a994-ac9078ff9a1b", + "name": "Collection Create wo hierarchy", + "url": "http://localhost:9000/content/v4/create", + "time": 45, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Status code is 200": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 45 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true + } + ] + }, + { + "id": "edfc8942-03f2-4b49-81d6-c29a1a01d2cb", + "name": "PDF Content Create", + "url": "http://localhost:9000/content/v4/create", + "time": 67, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Status code is 200": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 67 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true + } + ] + }, + { + "id": "13c92a31-a1b0-4bd1-95a8-e1e46af2e61f", + "name": "PDF Content wo artifactUrl", + "url": "http://localhost:9000/content/v4/create", + "time": 38, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Status code is 200": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 38 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true + } + ] + }, + { + "id": "637502c0-3bd9-427b-997e-d01372a846c5", + "name": "MP4 Content Create", + "url": "http://localhost:9000/content/v4/create", + "time": 47, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Status code is 200": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 47 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true + } + ] + }, + { + "id": "e4adecc9-606a-4091-b4c1-e3d2e655c370", + "name": "MP4 Content Create wo artifactUrl", + "url": "http://localhost:9000/content/v4/create", + "time": 43, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Status code is 200": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 43 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true + } + ] + }, + { + "id": "c0c3b560-fb40-4e22-accb-74498265940f", + "name": "WEBM Content Create", + "url": "http://localhost:9000/content/v4/create", + "time": 63, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Status code is 200": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 63 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true + } + ] + }, + { + "id": "8bb660d5-4a55-42ec-9f63-285fddf01712", + "name": "WEBM Content Create wo artifactUrl", + "url": "http://localhost:9000/content/v4/create", + "time": 55, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Status code is 200": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 55 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true + } + ] + }, + { + "id": "8ea033c2-ced7-4189-b1a9-d4ffa3e20196", + "name": "HTML Content Create", + "url": "http://localhost:9000/content/v4/create", + "time": 30, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Status code is 200": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 30 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true + } + ] + }, + { + "id": "6df047f6-2ac2-4967-949c-f10d9f6decb4", + "name": "HTML Content Create wo artifactUrl", + "url": "http://localhost:9000/content/v4/create", + "time": 31, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Status code is 200": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 31 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true + } + ] + }, + { + "id": "e4cee867-b30a-4426-b314-c81d8f28cb82", + "name": "H5P Content Create", + "url": "http://localhost:9000/content/v4/create", + "time": 41, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Status code is 200": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 41 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true + } + ] + }, + { + "id": "ebe40740-4050-43ce-a316-892100ca0676", + "name": "H5P Content Create wo artifactUrl", + "url": "http://localhost:9000/content/v4/create", + "time": 25, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Status code is 200": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 25 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true + } + ] + }, + { + "id": "2418c40f-9b7f-4e6e-90fe-9f0b3c04537d", + "name": "ECML Content Create", + "url": "http://localhost:9000/content/v4/create", + "time": 62, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Status code is 200": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 62 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true + } + ] + }, + { + "id": "87ac52e6-5672-4198-b13c-01fd45cb3db5", + "name": "ECML Content Create invalid body", + "url": "http://localhost:9000/content/v4/create", + "time": 48, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Status code is 200": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 48 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true + } + ] + }, + { + "id": "8cd499ee-3bcd-4efe-ada5-92b5f5ece200", + "name": "ECML Content Create wo body", + "url": "http://localhost:9000/content/v4/create", + "time": 45, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Status code is 200": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 45 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true + } + ] + }, + { + "id": "b339d20a-19ed-4281-9831-c10c6383e386", + "name": "EPUB Content Create", + "url": "http://localhost:9000/content/v4/create", + "time": 39, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Status code is 200": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 39 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true + } + ] + }, + { + "id": "869a5263-7711-4c53-bc38-0da493f4ece2", + "name": "EPUB Content Create wo artifactUrl", + "url": "http://localhost:9000/content/v4/create", + "time": 30, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Status code is 200": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 30 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true + } + ] + }, + { + "id": "d71c64ed-ab39-477e-95de-9eeea47b7986", + "name": "Youtube Content Create", + "url": "http://localhost:9000/content/v4/create", + "time": 27, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Status code is 200": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 27 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true + } + ] + }, + { + "id": "45065a85-6bf9-4cb5-9acc-2c06345f8a26", + "name": "Youtube Content Create wo artifactUrl", + "url": "http://localhost:9000/content/v4/create", + "time": 37, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Status code is 200": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 37 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true + } + ] + }, + { + "id": "5a9bc533-ecb7-4fb8-ae48-75ea2180b040", + "name": "Plugin Create", + "url": "http://localhost:9000/content/v4/create", + "time": 41, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Status code is 200": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 41 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true + } + ] + }, + { + "id": "b5dd8a58-9ab1-4833-b8bb-564eda36cbc9", + "name": "Plugin Create wo artifact", + "url": "http://localhost:9000/content/v4/create", + "time": 27, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Status code is 200": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 27 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true + } + ] + }, + { + "id": "f2342494-ae32-4093-8048-f852ad38223b", + "name": "Collection Publish", + "url": "http://localhost:9000/collection/v4/publish/do_11360891275809587211", + "time": 46, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true, + "Verify keys and data type of the params in positive scenario": true, + "params.status should be \"successful\"": true, + "responseCode should be OK": true, + "Status code name has string OK": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Status code is 200": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in positive scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"successful\"": { + "pass": 1, + "fail": 0 + }, + "responseCode should be OK": { + "pass": 1, + "fail": 0 + }, + "Status code name has string OK": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 46 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true, + "Verify keys and data type of the params in positive scenario": true, + "params.status should be \"successful\"": true, + "responseCode should be OK": true, + "Status code name has string OK": true + } + ] + }, + { + "id": "d7dcfe64-e0d3-48aa-a4ce-121f19eac58e", + "name": "PDF Content Publish", + "url": "http://localhost:9000/content/v4/publish/do_113608912769916928117", + "time": 65, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true, + "Verify keys and data type of the params in positive scenario": true, + "params.status should be \"successful\"": true, + "responseCode should be OK": true, + "Status code name has string OK": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Status code is 200": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in positive scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"successful\"": { + "pass": 1, + "fail": 0 + }, + "responseCode should be OK": { + "pass": 1, + "fail": 0 + }, + "Status code name has string OK": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 65 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true, + "Verify keys and data type of the params in positive scenario": true, + "params.status should be \"successful\"": true, + "responseCode should be OK": true, + "Status code name has string OK": true + } + ] + }, + { + "id": "79327a95-1346-4635-b8ab-a3427ff9d278", + "name": "MP4 Content Publish", + "url": "http://localhost:9000/content/v4/publish/do_113608912771776512119", + "time": 49, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true, + "Verify keys and data type of the params in positive scenario": true, + "params.status should be \"successful\"": true, + "responseCode should be OK": true, + "Status code name has string OK": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Status code is 200": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in positive scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"successful\"": { + "pass": 1, + "fail": 0 + }, + "responseCode should be OK": { + "pass": 1, + "fail": 0 + }, + "Status code name has string OK": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 49 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true, + "Verify keys and data type of the params in positive scenario": true, + "params.status should be \"successful\"": true, + "responseCode should be OK": true, + "Status code name has string OK": true + } + ] + }, + { + "id": "e203fc85-ac81-43d1-b744-054688acf98a", + "name": "WEBM Content Publish", + "url": "http://localhost:9000/content/v4/publish/do_113608912773750784121", + "time": 50, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true, + "Verify keys and data type of the params in positive scenario": true, + "params.status should be \"successful\"": true, + "responseCode should be OK": true, + "Status code name has string OK": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Status code is 200": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in positive scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"successful\"": { + "pass": 1, + "fail": 0 + }, + "responseCode should be OK": { + "pass": 1, + "fail": 0 + }, + "Status code name has string OK": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 50 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true, + "Verify keys and data type of the params in positive scenario": true, + "params.status should be \"successful\"": true, + "responseCode should be OK": true, + "Status code name has string OK": true + } + ] + }, + { + "id": "1f9def38-d148-49cc-a056-14bf1d72442f", + "name": "HTML Content Publish", + "url": "http://localhost:9000/collection/v4/publish/do_113608912775979008123", + "time": 56, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true, + "Verify keys and data type of the params in positive scenario": true, + "params.status should be \"successful\"": true, + "responseCode should be OK": true, + "Status code name has string OK": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Status code is 200": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in positive scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"successful\"": { + "pass": 1, + "fail": 0 + }, + "responseCode should be OK": { + "pass": 1, + "fail": 0 + }, + "Status code name has string OK": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 56 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true, + "Verify keys and data type of the params in positive scenario": true, + "params.status should be \"successful\"": true, + "responseCode should be OK": true, + "Status code name has string OK": true + } + ] + }, + { + "id": "af6b95b5-5466-4b36-bbf3-5e9762d900d0", + "name": "H5P Content Publish", + "url": "http://localhost:9000/collection/v4/publish/do_113608912777338880125", + "time": 46, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true, + "Verify keys and data type of the params in positive scenario": true, + "params.status should be \"successful\"": true, + "responseCode should be OK": true, + "Status code name has string OK": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Status code is 200": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in positive scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"successful\"": { + "pass": 1, + "fail": 0 + }, + "responseCode should be OK": { + "pass": 1, + "fail": 0 + }, + "Status code name has string OK": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 46 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true, + "Verify keys and data type of the params in positive scenario": true, + "params.status should be \"successful\"": true, + "responseCode should be OK": true, + "Status code name has string OK": true + } + ] + }, + { + "id": "1ac93190-744e-491e-aacd-cf8204b6a073", + "name": "EPUB Content Publish", + "url": "http://localhost:9000/collection/v4/publish/do_113608912781467648130", + "time": 40, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true, + "Verify keys and data type of the params in positive scenario": true, + "params.status should be \"successful\"": true, + "responseCode should be OK": true, + "Status code name has string OK": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Status code is 200": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in positive scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"successful\"": { + "pass": 1, + "fail": 0 + }, + "responseCode should be OK": { + "pass": 1, + "fail": 0 + }, + "Status code name has string OK": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 40 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true, + "Verify keys and data type of the params in positive scenario": true, + "params.status should be \"successful\"": true, + "responseCode should be OK": true, + "Status code name has string OK": true + } + ] + }, + { + "id": "9a2bbcf9-89d1-4af6-94a2-42c87774cdbf", + "name": "ECML Content Publish", + "url": "http://localhost:9000/collection/v4/publish/do_113608912778960896127", + "time": 844, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true, + "Verify keys and data type of the params in positive scenario": true, + "params.status should be \"successful\"": true, + "responseCode should be OK": true, + "Status code name has string OK": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Status code is 200": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in positive scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"successful\"": { + "pass": 1, + "fail": 0 + }, + "responseCode should be OK": { + "pass": 1, + "fail": 0 + }, + "Status code name has string OK": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 844 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true, + "Verify keys and data type of the params in positive scenario": true, + "params.status should be \"successful\"": true, + "responseCode should be OK": true, + "Status code name has string OK": true + } + ] + }, + { + "id": "113133ef-244f-46fc-b030-cf127e224ceb", + "name": "Youtube Content Publish", + "url": "http://localhost:9000/collection/v4/publish/do_113608912782786560132", + "time": 56, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true, + "Verify keys and data type of the params in positive scenario": true, + "params.status should be \"successful\"": true, + "responseCode should be OK": true, + "Status code name has string OK": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Status code is 200": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in positive scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"successful\"": { + "pass": 1, + "fail": 0 + }, + "responseCode should be OK": { + "pass": 1, + "fail": 0 + }, + "Status code name has string OK": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 56 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true, + "Verify keys and data type of the params in positive scenario": true, + "params.status should be \"successful\"": true, + "responseCode should be OK": true, + "Status code name has string OK": true + } + ] + }, + { + "id": "ab646ab4-85c5-4faf-867b-efb9f88f3154", + "name": "Plugin Publish", + "url": "http://localhost:9000/collection/v4/publish/org.ekstep.summary-test", + "time": 35, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true, + "Verify keys and data type of the params in positive scenario": true, + "params.status should be \"successful\"": true, + "responseCode should be OK": true, + "Status code name has string OK": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Status code is 200": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in positive scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"successful\"": { + "pass": 1, + "fail": 0 + }, + "responseCode should be OK": { + "pass": 1, + "fail": 0 + }, + "Status code name has string OK": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 35 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true, + "Verify keys and data type of the params in positive scenario": true, + "params.status should be \"successful\"": true, + "responseCode should be OK": true, + "Status code name has string OK": true + } + ] + }, + { + "id": "34908a0c-d5cf-48f9-9ab2-b851657949cf", + "name": "Invalid Content ID Publish", + "url": "http://localhost:9000/content/v4/publish/do_dummy_id", + "time": 37, + "responseCode": { + "code": 404, + "name": "Not Found" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "Status code is 404": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in negative scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"failed\"": { + "pass": 1, + "fail": 0 + }, + "Status code is 404": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 37 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "Status code is 404": true + } + ] + }, + { + "id": "e048689b-3df0-4b72-a024-1b032090d353", + "name": "Invalid Collection ID Publish", + "url": "http://localhost:9000/collection/v4/publish/do_dummy_id", + "time": 34, + "responseCode": { + "code": 404, + "name": "Not Found" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "Status code is 404": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in negative scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"failed\"": { + "pass": 1, + "fail": 0 + }, + "Status code is 404": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 34 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "Status code is 404": true + } + ] + }, + { + "id": "328e19c8-84f4-4a47-95d0-a4631db50b02", + "name": "PDF Content Publish wo artifactUrl", + "url": "http://localhost:9000/content/v4/publish/do_113608912770859008118", + "time": 49, + "responseCode": { + "code": 400, + "name": "Bad Request" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "Status code is 400": true, + "Response throws CLIENT_ERROR": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in negative scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"failed\"": { + "pass": 1, + "fail": 0 + }, + "Status code is 400": { + "pass": 1, + "fail": 0 + }, + "Response throws CLIENT_ERROR": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 49 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "Status code is 400": true, + "Response throws CLIENT_ERROR": true + } + ] + }, + { + "id": "371f42d7-4e91-467f-8dbd-700cc34982f1", + "name": "MP4 Content Publish wo artifactUrl", + "url": "http://localhost:9000/content/v4/publish/do_113608912772677632120", + "time": 80, + "responseCode": { + "code": 400, + "name": "Bad Request" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "Status code is 400": true, + "Response throws CLIENT_ERROR": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in negative scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"failed\"": { + "pass": 1, + "fail": 0 + }, + "Status code is 400": { + "pass": 1, + "fail": 0 + }, + "Response throws CLIENT_ERROR": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 80 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "Status code is 400": true, + "Response throws CLIENT_ERROR": true + } + ] + }, + { + "id": "e7e6b6d1-6312-4411-8231-59fa776f59cb", + "name": "WEBM Content Publish wo artifactUrl", + "url": "http://localhost:9000/content/v4/publish/do_113608912774955008122", + "time": 62, + "responseCode": { + "code": 400, + "name": "Bad Request" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "Status code is 400": true, + "Response throws CLIENT_ERROR": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in negative scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"failed\"": { + "pass": 1, + "fail": 0 + }, + "Status code is 400": { + "pass": 1, + "fail": 0 + }, + "Response throws CLIENT_ERROR": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 62 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "Status code is 400": true, + "Response throws CLIENT_ERROR": true + } + ] + }, + { + "id": "912c062c-8fe3-4159-b6fd-8b90c8fb1f10", + "name": "HTML Content Publish wo artifactUrl", + "url": "http://localhost:9000/content/v4/publish/do_113608912776773632124", + "time": 94, + "responseCode": { + "code": 400, + "name": "Bad Request" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "Status code is 400": true, + "Response throws CLIENT_ERROR": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in negative scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"failed\"": { + "pass": 1, + "fail": 0 + }, + "Status code is 400": { + "pass": 1, + "fail": 0 + }, + "Response throws CLIENT_ERROR": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 94 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "Status code is 400": true, + "Response throws CLIENT_ERROR": true + } + ] + }, + { + "id": "bfe28b75-08c8-4082-8bd4-3e7aa685c066", + "name": "H5P Content Publish wo artifactUrl", + "url": "http://localhost:9000/content/v4/publish/do_113608912778272768126", + "time": 48, + "responseCode": { + "code": 400, + "name": "Bad Request" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "Status code is 400": true, + "Response throws CLIENT_ERROR": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in negative scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"failed\"": { + "pass": 1, + "fail": 0 + }, + "Status code is 400": { + "pass": 1, + "fail": 0 + }, + "Response throws CLIENT_ERROR": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 48 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "Status code is 400": true, + "Response throws CLIENT_ERROR": true + } + ] + }, + { + "id": "78fe24b6-8442-426d-9ba9-fd968bbc2595", + "name": "EPUB Content Publish wo artifactUrl", + "url": "http://localhost:9000/content/v4/publish/do_113608912782188544131", + "time": 51, + "responseCode": { + "code": 400, + "name": "Bad Request" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "Status code is 400": true, + "Response throws CLIENT_ERROR": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in negative scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"failed\"": { + "pass": 1, + "fail": 0 + }, + "Status code is 400": { + "pass": 1, + "fail": 0 + }, + "Response throws CLIENT_ERROR": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 51 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "Status code is 400": true, + "Response throws CLIENT_ERROR": true + } + ] + }, + { + "id": "3125afd5-c8c4-4818-915f-0d1ad0b7ed9f", + "name": "Youtube Content Publish wo artifactUrl", + "url": "http://localhost:9000/content/v4/publish/do_113608912783646720133", + "time": 48, + "responseCode": { + "code": 400, + "name": "Bad Request" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "Status code is 400": true, + "Response throws CLIENT_ERROR": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in negative scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"failed\"": { + "pass": 1, + "fail": 0 + }, + "Status code is 400": { + "pass": 1, + "fail": 0 + }, + "Response throws CLIENT_ERROR": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 48 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "Status code is 400": true, + "Response throws CLIENT_ERROR": true + } + ] + }, + { + "id": "53dbf026-9d1d-4ee4-b80f-ff42a0dfb501", + "name": "Plugin Publish wo artifactUrl", + "url": "http://localhost:9000/content/v4/publish/test-plugin", + "time": 43, + "responseCode": { + "code": 400, + "name": "Bad Request" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "Status code is 400": true, + "Response throws CLIENT_ERROR": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in negative scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"failed\"": { + "pass": 1, + "fail": 0 + }, + "Status code is 400": { + "pass": 1, + "fail": 0 + }, + "Response throws CLIENT_ERROR": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 43 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "Status code is 400": true, + "Response throws CLIENT_ERROR": true + } + ] + }, + { + "id": "2d04e34f-860a-43ae-b076-4c3b5a26363c", + "name": "Collection Publish without hierarchy", + "url": "http://localhost:9000/collection/v4/publish/do_113608912768622592116", + "time": 50, + "responseCode": { + "code": 400, + "name": "Bad Request" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "Status code is 400": true, + "Response throws ERR_COLLECTION_REVIEW": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in negative scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"failed\"": { + "pass": 1, + "fail": 0 + }, + "Status code is 400": { + "pass": 1, + "fail": 0 + }, + "Response throws ERR_COLLECTION_REVIEW": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 50 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "Status code is 400": true, + "Response throws ERR_COLLECTION_REVIEW": true + } + ] + }, + { + "id": "b4ac53d8-d142-4487-a779-e9686caf972e", + "name": "ECML Content Publish wo artifactUrl", + "url": "http://localhost:9000/content/v4/publish/do_113608912780697600129", + "time": 43, + "responseCode": { + "code": 400, + "name": "Bad Request" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "Status code is 400": true, + "Response throws CLIENT_ERROR": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in negative scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"failed\"": { + "pass": 1, + "fail": 0 + }, + "Status code is 400": { + "pass": 1, + "fail": 0 + }, + "Response throws CLIENT_ERROR": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 43 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "Status code is 400": true, + "Response throws CLIENT_ERROR": true + } + ] + }, + { + "id": "203d51fd-1ed1-48f3-8c53-dee83997cf6a", + "name": "ECML Content Publish invalid Body", + "url": "http://localhost:9000/content/v4/publish/do_113608912779886592128", + "time": 61, + "responseCode": { + "code": 400, + "name": "Bad Request" + }, + "tests": { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "Status code is 400": true, + "Response throws CLIENT_ERROR": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Content-Type header is application/json": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in negative scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"failed\"": { + "pass": 1, + "fail": 0 + }, + "Status code is 400": { + "pass": 1, + "fail": 0 + }, + "Response throws CLIENT_ERROR": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 61 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Content-Type header is application/json": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "Status code is 400": true, + "Response throws CLIENT_ERROR": true + } + ] + } + ], + "count": 1, + "totalTime": 4150, + "collection": { + "requests": [ + { + "id": "80fd9644-56d3-4eb5-910f-c2f9a6bbcc39", + "method": "POST" + }, + { + "id": "7c5475c1-7c6a-4ca5-b71f-37b05e719446", + "method": "POST" + }, + { + "id": "f00000f4-e316-4081-a994-ac9078ff9a1b", + "method": "POST" + }, + { + "id": "edfc8942-03f2-4b49-81d6-c29a1a01d2cb", + "method": "POST" + }, + { + "id": "13c92a31-a1b0-4bd1-95a8-e1e46af2e61f", + "method": "POST" + }, + { + "id": "637502c0-3bd9-427b-997e-d01372a846c5", + "method": "POST" + }, + { + "id": "e4adecc9-606a-4091-b4c1-e3d2e655c370", + "method": "POST" + }, + { + "id": "c0c3b560-fb40-4e22-accb-74498265940f", + "method": "POST" + }, + { + "id": "8bb660d5-4a55-42ec-9f63-285fddf01712", + "method": "POST" + }, + { + "id": "8ea033c2-ced7-4189-b1a9-d4ffa3e20196", + "method": "POST" + }, + { + "id": "6df047f6-2ac2-4967-949c-f10d9f6decb4", + "method": "POST" + }, + { + "id": "e4cee867-b30a-4426-b314-c81d8f28cb82", + "method": "POST" + }, + { + "id": "ebe40740-4050-43ce-a316-892100ca0676", + "method": "POST" + }, + { + "id": "2418c40f-9b7f-4e6e-90fe-9f0b3c04537d", + "method": "POST" + }, + { + "id": "87ac52e6-5672-4198-b13c-01fd45cb3db5", + "method": "POST" + }, + { + "id": "8cd499ee-3bcd-4efe-ada5-92b5f5ece200", + "method": "POST" + }, + { + "id": "b339d20a-19ed-4281-9831-c10c6383e386", + "method": "POST" + }, + { + "id": "869a5263-7711-4c53-bc38-0da493f4ece2", + "method": "POST" + }, + { + "id": "d71c64ed-ab39-477e-95de-9eeea47b7986", + "method": "POST" + }, + { + "id": "45065a85-6bf9-4cb5-9acc-2c06345f8a26", + "method": "POST" + }, + { + "id": "5a9bc533-ecb7-4fb8-ae48-75ea2180b040", + "method": "POST" + }, + { + "id": "b5dd8a58-9ab1-4833-b8bb-564eda36cbc9", + "method": "POST" + }, + { + "id": "f2342494-ae32-4093-8048-f852ad38223b", + "method": "POST" + }, + { + "id": "d7dcfe64-e0d3-48aa-a4ce-121f19eac58e", + "method": "POST" + }, + { + "id": "79327a95-1346-4635-b8ab-a3427ff9d278", + "method": "POST" + }, + { + "id": "e203fc85-ac81-43d1-b744-054688acf98a", + "method": "POST" + }, + { + "id": "1f9def38-d148-49cc-a056-14bf1d72442f", + "method": "POST" + }, + { + "id": "af6b95b5-5466-4b36-bbf3-5e9762d900d0", + "method": "POST" + }, + { + "id": "1ac93190-744e-491e-aacd-cf8204b6a073", + "method": "POST" + }, + { + "id": "9a2bbcf9-89d1-4af6-94a2-42c87774cdbf", + "method": "POST" + }, + { + "id": "113133ef-244f-46fc-b030-cf127e224ceb", + "method": "POST" + }, + { + "id": "ab646ab4-85c5-4faf-867b-efb9f88f3154", + "method": "POST" + }, + { + "id": "34908a0c-d5cf-48f9-9ab2-b851657949cf", + "method": "POST" + }, + { + "id": "e048689b-3df0-4b72-a024-1b032090d353", + "method": "POST" + }, + { + "id": "328e19c8-84f4-4a47-95d0-a4631db50b02", + "method": "POST" + }, + { + "id": "371f42d7-4e91-467f-8dbd-700cc34982f1", + "method": "POST" + }, + { + "id": "e7e6b6d1-6312-4411-8231-59fa776f59cb", + "method": "POST" + }, + { + "id": "912c062c-8fe3-4159-b6fd-8b90c8fb1f10", + "method": "POST" + }, + { + "id": "bfe28b75-08c8-4082-8bd4-3e7aa685c066", + "method": "POST" + }, + { + "id": "78fe24b6-8442-426d-9ba9-fd968bbc2595", + "method": "POST" + }, + { + "id": "3125afd5-c8c4-4818-915f-0d1ad0b7ed9f", + "method": "POST" + }, + { + "id": "53dbf026-9d1d-4ee4-b80f-ff42a0dfb501", + "method": "POST" + }, + { + "id": "2d04e34f-860a-43ae-b076-4c3b5a26363c", + "method": "POST" + }, + { + "id": "b4ac53d8-d142-4487-a779-e9686caf972e", + "method": "POST" + }, + { + "id": "203d51fd-1ed1-48f3-8c53-dee83997cf6a", + "method": "POST" + } + ] + } +} \ No newline at end of file From 0997ac6e17a17445b10b2d19541d7ac138833af0 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Tue, 23 Aug 2022 15:32:47 +0530 Subject: [PATCH 113/490] Issue #KN-9 feat: Content Publish API refactor. --- ...ontent V2 API Test.postman_collection.json | 13047 ++++++++++++++-- 1 file changed, 12001 insertions(+), 1046 deletions(-) diff --git a/content-api/api-tests/Collections/Content V2 API Test.postman_collection.json b/content-api/api-tests/Collections/Content V2 API Test.postman_collection.json index 583e76895..4a0373bd6 100644 --- a/content-api/api-tests/Collections/Content V2 API Test.postman_collection.json +++ b/content-api/api-tests/Collections/Content V2 API Test.postman_collection.json @@ -1,6 +1,6 @@ { "info": { - "_postman_id": "58a713b4-b8bb-4885-9909-b23d253f8f31", + "_postman_id": "ec007894-70ee-4951-8e96-98f79de5908a", "name": "Content V2 API Test", "description": "The Content Management APIs allow you to create, manage and process content on the Sunbird platform. Content is the basic building block of the Knowledge Service. Some examples would be Videos, Audio, Pdfs, Html, ECML etc. The basic operations for this API include Create, Update, Read, Review, Publish.\n\nThe URL for Content Management API(s) is /content/v1.", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", @@ -741,66 +741,12 @@ } } ] - } - ], - "event": [ - { - "listen": "prerequest", - "script": { - "type": "text/javascript", - "exec": [ - "" - ] - } }, { - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "let jsonResponse = pm.response.json();", - "", - "//Verification", - "pm.test(\"Status code is 200\", () => {", - " pm.response.to.have.status(200)", - "});", - "//code name contains a string", - "tests[\"Status code name has string OK\"] = responseCode.name.has(\"OK\");", - "", - "// pm.test(\"Content-Encoding header is present\", () => {", - "// pm.response.to.have.header(\"Content-Encoding\");", - "// });", - "// pm.test(\"Content-Encoding header is gzip\", () => {", - "// pm.expect(pm.response.headers.get('Content-Encoding')).to.eql('gzip');", - "// });", - "", - "pm.test(\"Verify keys and data type of the params in positive scenario\", () => {", - " pm.expect(jsonResponse.params.resmsgid).to.be.a(\"string\");", - " pm.expect(jsonResponse.params.msgid).to.be.null;", - " pm.expect(jsonResponse.params.err).to.be.null;", - " pm.expect(jsonResponse.params.status).to.be.a(\"string\");", - " pm.expect(jsonResponse.params.errmsg).to.be.null;", - "});", - "", - "pm.test(\"params.status should be \\\"successful\\\"\", () => {", - " pm.expect(jsonResponse.params.status).to.eql(\"successful\");", - "});", - "pm.test(\"responseCode should be OK\", () => {", - " pm.expect(jsonResponse.responseCode).to.eql(\"OK\");", - "});" - ] - } - } - ] - }, - { - "name": "Negative", - "item": [ - { - "name": "Content Create", + "name": "Content Reserve DIAL code", "item": [ { - "name": "400 - missing channel", + "name": "Collection Create", "event": [ { "listen": "test", @@ -809,37 +755,52 @@ "let jsonResponse = pm.response.json()", "", "//Verification", - "pm.test(\"Status code is 400\", () => {", - " pm.response.to.have.status(400)", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "pm.test(\"Content-Type header is present\", () => {", + " pm.response.to.have.header(\"Content-Type\");", + "});", + "pm.test(\"Content-Type header is application/json\", () => {", + " pm.expect(pm.response.headers.get('Content-Type')).to.eql('application/json');", "});", - "//code name contains a string", - "tests[\"Status code name has string \\\"Bad Request\\\"\"] = responseCode.name.has(\"Bad Request\");", "", - "pm.test(\"Verify keys and data type of the response result\", () => {", + "pm.test(\"Test data type of the response and keys\", () => {", + " pm.expect(jsonResponse).to.be.an(\"object\").that.has.all.keys('id', 'ver', 'ts', 'params', 'responseCode', 'result');", + " pm.expect(jsonResponse.id).to.be.a(\"string\");", + " pm.expect(jsonResponse.ver).to.be.a(\"string\");", + " pm.expect(jsonResponse.ts).to.be.a(\"string\");", + " pm.expect(jsonResponse.responseCode).to.be.a(\"string\");", + " pm.expect(jsonResponse.params).to.be.an(\"object\").that.has.all.keys('resmsgid', 'msgid', 'err', 'status', 'errmsg');", + " pm.expect(jsonResponse.params.resmsgid).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.msgid).to.be.null;", + " pm.expect(jsonResponse.params.err).to.be.null;", + " pm.expect(jsonResponse.params.status).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.errmsg).to.be.null;", " pm.expect(jsonResponse.result).to.be.an(\"object\");", - " pm.expect(jsonResponse.result).to.have.all.keys('messages');", - " pm.expect(jsonResponse.result.messages).to.not.be.empty;", + " pm.expect(jsonResponse.result).to.have.all.keys('identifier', 'node_id', 'versionKey');", + " pm.expect(jsonResponse.result.identifier).to.be.a(\"string\");", + " pm.expect(jsonResponse.result.node_id).to.be.a(\"string\");", + " pm.expect(jsonResponse.result.versionKey).to.be.a(\"string\");", "});", "", "//Validation", - "// Response body", - "pm.test(\"responseCode should be \\\"CLIENT_ERROR\\\"\", () => {", - " pm.expect(jsonResponse.responseCode).to.eql(\"CLIENT_ERROR\");", + "pm.test(\"id should be api.content.create\", function () {", + " pm.expect(jsonResponse.id).to.eql(\"api.content.create\");", "});", - "", - "// Params", - "pm.test(\"params.err should be \\\"CLIENT_ERROR\\\"\", () => {", - " pm.expect(jsonResponse.params.err).to.eql(\"CLIENT_ERROR\");", + "pm.test(\"ver should be 4.0\", function () {", + " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", "});", - "pm.test(\"params.errmsg should be \\\"Validation Errors\\\"\", () => {", - " pm.expect(jsonResponse.params.errmsg).to.eql(\"Validation Errors\");", + "pm.test(\"params.status should be successful\", function () {", + " pm.expect(jsonResponse.params.status).to.eql(\"successful\");", "});", - "", - "// Result", - "pm.test(\"result body should include messages \\\"Required Metadata channel not set\\\"\", () => {", - " pm.expect(jsonResponse.result.messages).to.have.length(1)", - " pm.expect(jsonResponse.result.messages).to.include('Required Metadata channel not set');", + "pm.test(\"responseCode should be OK\", function () {", + " pm.expect(jsonResponse.responseCode).to.eql(\"OK\");", "});", + "", + "// Set global variable", + "pm.environment.set(\"collection_id\", jsonResponse.result.identifier);", + "pm.globals.set(\"versionKey\", jsonResponse.result.versionKey);", "" ], "type": "text/javascript" @@ -847,35 +808,36 @@ } ], "protocolProfileBehavior": { - "disabledSystemHeaders": { - "connection": true - } + "disabledSystemHeaders": {} }, "request": { "method": "POST", "header": [ { "key": "Content-Type", - "value": "application/json" + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." }, { "key": "X-Authenticated-User-token", - "value": "{{keycloak_access_token}}" + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." }, { "key": "Authorization", - "value": "{{kong_api_key}}" + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." }, { "key": "X-Channel-Id", "value": "{{channel_id}}", "type": "text", - "disabled": true + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." } ], "body": { "mode": "raw", - "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"createdBy\": \"530b19ea-dc8d-4cc7-a4b5-0c0214c8113a\",\n \"resourceType\": \"Learn\",\n \"framework\": \"ekstep_ncert_k-12\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Sample Collection\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/vnd.ekstep.content-collection\",\n \"primaryCategory\": \"Digital Textbook\"\n }\n }\n}" }, "url": { "raw": "{{host}}/api/content/v2/create", @@ -889,488 +851,6510 @@ "create" ] }, - "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" }, - "response": [] + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] }, { - "name": "400 - empty channel", + "name": "Reserve DIAL code", "event": [ { "listen": "test", "script": { "exec": [ - "let jsonResponse = pm.response.json()", - "", - "//Verification", - "pm.test(\"Status code is 400\", () => {", - " pm.response.to.have.status(400)", - "});", - "//code name contains a string", - "tests[\"Status code name has string \\\"Bad Request\\\"\"] = responseCode.name.has(\"Bad Request\");", - "", - "pm.test(\"Verify keys and data type of the response result\", () => {", - " pm.expect(jsonResponse.result).to.be.an(\"object\");", - " pm.expect(jsonResponse.result).to.have.all.keys('messages');", - " pm.expect(jsonResponse.result.messages).to.not.be.empty;", - "});", - "", - "//Validation", - "// Response body", - "pm.test(\"responseCode should be \\\"CLIENT_ERROR\\\"\", () => {", - " pm.expect(jsonResponse.responseCode).to.eql(\"CLIENT_ERROR\");", - "});", - "", - "// Params", - "pm.test(\"params.err should be \\\"CLIENT_ERROR\\\"\", () => {", - " pm.expect(jsonResponse.params.err).to.eql(\"CLIENT_ERROR\");", - "});", - "pm.test(\"params.errmsg should be \\\"Validation Errors\\\"\", () => {", - " pm.expect(jsonResponse.params.errmsg).to.eql(\"Validation Errors\");", - "});", - "", - "// Result", - "pm.test(\"result body should include messages \\\"Required Metadata channel not set\\\"\", () => {", - " pm.expect(jsonResponse.result.messages).to.have.length(1)", - " pm.expect(jsonResponse.result.messages).to.include('Required Metadata channel not set');", - "});", - "" + "// Multiple scenarios handled at one place", + "pm.test(\"Response contains DIAL code\", function () {", + " const responseJson = pm.response.json();", + " var req = JSON.parse(request.data);", + " if(req.request.dialcodes.count > 1000){", + " pm.response.to.have.status(403);", + " } else {", + " const reservedDialcodes = responseJson.result.reservedDialcodes;", + " pm.expect(reservedDialcodes).not.null", + " _.forEach(JSON.parse(reservedDialcodes), function(value, key) {", + " const oldDialcode = pm.globals.get(\"new_dialcode\")", + " pm.globals.set(\"replaced_dialcode\", oldDialcode);", + " pm.globals.set(\"new_dialcode\", key);", + " });", + " console.log(pm.globals.get(\"replaced_dialcode\")); ", + " console.log(pm.globals.get(\"new_dialcode\"));", + " }", + " ", + "});" ], "type": "text/javascript" } } ], - "protocolProfileBehavior": { - "disabledSystemHeaders": { - "connection": true - } - }, "request": { "method": "POST", "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "X-Authenticated-User-token", - "value": "{{keycloak_access_token}}" - }, { "key": "Authorization", "value": "{{kong_api_key}}" }, { - "key": "X-Channel-Id", - "value": "", - "type": "text" + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}" } ], "body": { "mode": "raw", - "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"createdBy\": \"530b19ea-dc8d-4cc7-a4b5-0c0214c8113a\",\n \"resourceType\": \"Learn\",\n \"framework\": \"ekstep_ncert_k-12\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + "raw": "{\n \"request\": {\n \"dialcodes\": {\n \"count\": 3,\n \"qrCodeSpec\": {\n \"errorCorrectionLevel\": \"H\"\n }\n }\n }\n}" }, "url": { - "raw": "{{host}}/api/content/v2/create", + "raw": "{{host}}/api/collection/v1/dialcode/reserve/{{collection_id}}", "host": [ "{{host}}" ], "path": [ "api", - "content", - "v2", - "create" + "collection", + "v1", + "dialcode", + "reserve", + "{{collection_id}}" ] - }, - "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + } }, "response": [] }, { - "name": "400 - missing name", + "name": "Update Reserved DIAL code", "event": [ { "listen": "test", "script": { "exec": [ - "let jsonResponse = pm.response.json()", - "", - "//Verification", - "pm.test(\"Status code is 400\", () => {", - " pm.response.to.have.status(400)", - "});", - "//code name contains a string", - "tests[\"Status code name has string \\\"Bad Request\\\"\"] = responseCode.name.has(\"Bad Request\");", - "", - "pm.test(\"Verify keys and data type of the response result\", () => {", - " pm.expect(jsonResponse.result).to.be.an(\"object\");", - " pm.expect(jsonResponse.result).to.have.all.keys('messages');", - " pm.expect(jsonResponse.result.messages).to.not.be.empty;", - "});", - "", - "//Validation", - "// Response body", - "pm.test(\"responseCode should be \\\"CLIENT_ERROR\\\"\", () => {", - " pm.expect(jsonResponse.responseCode).to.eql(\"CLIENT_ERROR\");", - "});", - "", - "// Params", - "pm.test(\"params.err should be \\\"CLIENT_ERROR\\\"\", () => {", - " pm.expect(jsonResponse.params.err).to.eql(\"CLIENT_ERROR\");", - "});", - "pm.test(\"params.errmsg should be \\\"Validation Errors\\\"\", () => {", - " pm.expect(jsonResponse.params.errmsg).to.eql(\"Validation Errors\");", - "});", - "", - "// Result", - "pm.test(\"result body should include messages \\\"Required Metadata name not set\\\"\", () => {", - " pm.expect(jsonResponse.result.messages).to.have.length(1)", - " pm.expect(jsonResponse.result.messages).to.include('Required Metadata name not set');", - "});", - "" + "// Multiple scenarios handled at one place", + "pm.test(\"Response contains DIAL code\", function () {", + " const responseJson = pm.response.json();", + " var req = JSON.parse(request.data);", + " if(req.request.dialcodes.count > 1000){", + " pm.response.to.have.status(403);", + " } else {", + " const reservedDialcodes = responseJson.result.reservedDialcodes;", + " pm.expect(reservedDialcodes).not.null", + " }", + " ", + "});" ], "type": "text/javascript" } } ], - "protocolProfileBehavior": { - "disabledSystemHeaders": { - "connection": true - } - }, "request": { "method": "POST", "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "X-Authenticated-User-token", - "value": "{{keycloak_access_token}}" - }, { "key": "Authorization", "value": "{{kong_api_key}}" }, { - "key": "X-Channel-Id", - "value": "{{channel_id}}", - "type": "text" + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}" } ], "body": { "mode": "raw", - "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + "raw": "{\n \"request\": {\n \"dialcodes\": {\n \"count\": 5,\n \"qrCodeSpec\": {\n \"errorCorrectionLevel\": \"H\"\n }\n }\n }\n}" }, "url": { - "raw": "{{host}}/api/content/v2/create", + "raw": "{{host}}/api/collection/v1/dialcode/reserve/{{collection_id}}", "host": [ "{{host}}" ], "path": [ "api", - "content", - "v2", - "create" + "collection", + "v1", + "dialcode", + "reserve", + "{{collection_id}}" ] - }, - "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + } }, "response": [] }, { - "name": "400 - name: invalid datatype", + "name": "Reserve DIAL codes without qr spec", "event": [ { "listen": "test", "script": { "exec": [ - "let jsonResponse = pm.response.json()", - "", - "//Verification", - "pm.test(\"Status code is 400\", () => {", - " pm.response.to.have.status(400)", - "});", - "//code name contains a string", - "tests[\"Status code name has string \\\"Bad Request\\\"\"] = responseCode.name.has(\"Bad Request\");", - "", - "pm.test(\"Verify keys and data type of the response result\", () => {", - " pm.expect(jsonResponse.result).to.be.an(\"object\");", - " pm.expect(jsonResponse.result).to.have.all.keys('messages');", - " pm.expect(jsonResponse.result.messages).to.not.be.empty;", - "});", - "", - "//Validation", - "// Response body", - "pm.test(\"responseCode should be \\\"CLIENT_ERROR\\\"\", () => {", - " pm.expect(jsonResponse.responseCode).to.eql(\"CLIENT_ERROR\");", - "});", - "", - "// Params", - "pm.test(\"params.err should be \\\"CLIENT_ERROR\\\"\", () => {", - " pm.expect(jsonResponse.params.err).to.eql(\"CLIENT_ERROR\");", - "});", - "pm.test(\"params.errmsg should be \\\"Validation Errors\\\"\", () => {", - " pm.expect(jsonResponse.params.errmsg).to.eql(\"Validation Errors\");", - "});", - "", - "// Result", - "pm.test(\"result body should include messages \\\"Metadata name should be a/an String value\\\"\", () => {", - " pm.expect(jsonResponse.result.messages).to.have.length(1)", - " pm.expect(jsonResponse.result.messages).to.include('Metadata name should be a/an String value');", - "});", - "" + "// Multiple scenarios handled at one place", + "pm.test(\"Response contains DIAL code\", function () {", + " const responseJson = pm.response.json();", + " var req = JSON.parse(request.data);", + " if(req.request.dialcodes.count > 1000){", + " pm.response.to.have.status(403);", + " } else {", + " const reservedDialcodes = responseJson.result.reservedDialcodes;", + " pm.expect(reservedDialcodes).not.null", + " }", + " ", + "});" ], "type": "text/javascript" } } ], - "protocolProfileBehavior": { - "disabledSystemHeaders": { - "connection": true - } - }, "request": { "method": "POST", "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "X-Authenticated-User-token", - "value": "{{keycloak_access_token}}" - }, { "key": "Authorization", "value": "{{kong_api_key}}" }, { - "key": "X-Channel-Id", - "value": "{{channel_id}}", - "type": "text" + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}" } ], "body": { "mode": "raw", - "raw": "{\n \"request\": {\n \"content\": {\n \"name\": 423432,\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + "raw": "{\n \"request\": {\n \"dialcodes\": {\n \"count\": 7\n }\n }\n}" }, "url": { - "raw": "{{host}}/api/content/v2/create", + "raw": "{{host}}/api/collection/v1/dialcode/reserve/{{collection_id}}", "host": [ "{{host}}" ], "path": [ "api", - "content", - "v2", - "create" + "collection", + "v1", + "dialcode", + "reserve", + "{{collection_id}}" ] - }, - "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + } }, "response": [] - }, + } + ] + }, + { + "name": "Content Link DIAL code", + "item": [ { - "name": "400 - name empty", + "name": "Link DIAL code", "event": [ { "listen": "test", "script": { "exec": [ - "let jsonResponse = pm.response.json()", - "", - "//Verification", - "pm.test(\"Status code is 400\", function () {", - " pm.response.to.have.status(400)", - "});", - "//code name contains a string", - "tests[\"Status code name has string \\\"Bad Request\\\"\"] = responseCode.name.has(\"Bad Request\");", - "", - "pm.test(\"Verify keys and data type of the response result\", () => {", - " pm.expect(jsonResponse.result).to.be.an(\"object\");", - " pm.expect(jsonResponse.result).to.have.all.keys('messages');", - " pm.expect(jsonResponse.result.messages).to.not.be.empty;", - "});", - "", - "//Validation", - "// Response body", - "pm.test(\"responseCode should be \\\"CLIENT_ERROR\\\"\", () => {", - " pm.expect(jsonResponse.responseCode).to.eql(\"CLIENT_ERROR\");", - "});", - "", - "// Params", - "pm.test(\"params.err should be \\\"CLIENT_ERROR\\\"\", () => {", - " pm.expect(jsonResponse.params.err).to.eql(\"CLIENT_ERROR\");", - "});", - "pm.test(\"params.errmsg should be \\\"Validation Errors\\\"\", () => {", - " pm.expect(jsonResponse.params.errmsg).to.eql(\"Validation Errors\");", - "});", - "", - "// Result", - "pm.test(\"result body should include messages \\\"Required Metadata name not set\\\"\", () => {", - " pm.expect(jsonResponse.result.messages).to.have.length(1)", - " pm.expect(jsonResponse.result.messages).to.include('Required Metadata name not set');", - "});", - "" + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "setTimeout(function(){}, 1000);" ], "type": "text/javascript" } } ], - "protocolProfileBehavior": { - "disabledSystemHeaders": { - "connection": true - } - }, "request": { "method": "POST", "header": [ { - "key": "Content-Type", - "value": "application/json" + "key": "X-Channel-ID", + "value": "{{channel_id}}" }, { - "key": "X-Authenticated-User-token", - "value": "{{keycloak_access_token}}" + "key": "Content-Type", + "value": "application/json" }, { "key": "Authorization", "value": "{{kong_api_key}}" }, { - "key": "X-Channel-Id", - "value": "{{channel_id}}", - "type": "text" + "key": "x-authenticated-user-token", + "value": "{{user-token}}" } ], "body": { "mode": "raw", - "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + "raw": "{\n \"request\": {\n \"content\": [{\n \"identifier\": [\"{{collection_id}}\"],\n \"dialcode\": [\"{{new_dialcode}}\"]\n }]\n }\n}" }, "url": { - "raw": "{{host}}/api/content/v2/create", + "raw": "{{host}}/api/collection/v1/dialcode/link/{{collection_id}}", "host": [ "{{host}}" ], "path": [ "api", - "content", - "v2", - "create" + "collection", + "v1", + "dialcode", + "link", + "{{collection_id}}" ] - }, - "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + } }, "response": [] }, { - "name": "400 - missing code", + "name": "Update linked DIAL code", "event": [ { "listen": "test", "script": { "exec": [ - "let jsonResponse = pm.response.json()", - "", - "//Verification", - "pm.test(\"Status code is 400\", function () {", - " pm.response.to.have.status(400)", - "});", - "//code name contains a string", - "tests[\"Status code name has string \\\"Bad Request\\\"\"] = responseCode.name.has(\"Bad Request\");", - "", - "pm.test(\"Verify keys and data type of the response result\", () => {", - " pm.expect(jsonResponse.result).to.be.an(\"object\");", - " pm.expect(jsonResponse.result).to.have.all.keys('messages');", - " pm.expect(jsonResponse.result.messages).to.not.be.empty;", - "});", - "", - "//Validation", - "// Response body", - "pm.test(\"responseCode should be \\\"CLIENT_ERROR\\\"\", () => {", - " pm.expect(jsonResponse.responseCode).to.eql(\"CLIENT_ERROR\");", - "});", - "", - "// Params", - "pm.test(\"params.err should be \\\"CLIENT_ERROR\\\"\", () => {", - " pm.expect(jsonResponse.params.err).to.eql(\"CLIENT_ERROR\");", - "});", - "pm.test(\"params.errmsg should be \\\"Validation Errors\\\"\", () => {", - " pm.expect(jsonResponse.params.errmsg).to.eql(\"Validation Errors\");", - "});", - "", - "// Result", - "pm.test(\"result body should include messages \\\"Required Metadata code not set\\\"\", () => {", - " pm.expect(jsonResponse.result.messages).to.have.length(1)", - " pm.expect(jsonResponse.result.messages).to.include('Required Metadata code not set');", - "});", - "" + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "setTimeout(function(){}, 1000);" ], "type": "text/javascript" } } ], - "protocolProfileBehavior": { - "disabledSystemHeaders": { - "connection": true - } - }, "request": { "method": "POST", "header": [ { - "key": "Content-Type", - "value": "application/json" + "key": "X-Channel-ID", + "value": "{{channel_id}}" }, { - "key": "X-Authenticated-User-token", - "value": "{{keycloak_access_token}}" + "key": "Content-Type", + "value": "application/json" }, { "key": "Authorization", "value": "{{kong_api_key}}" }, { - "key": "X-Channel-Id", - "value": "{{channel_id}}", - "type": "text" + "key": "x-authenticated-user-token", + "value": "{{user-token}}" } ], "body": { "mode": "raw", - "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + "raw": "{\n \"request\": {\n \"content\": [{\n \"identifier\": [\"{{collection_id}}\"],\n \"dialcode\": [\"{{replaced_dialcode}}\"]\n }]\n }\n}" }, "url": { - "raw": "{{host}}/api/content/v2/create", + "raw": "{{host}}/api/collection/v1/dialcode/link/{{collection_id}}", "host": [ "{{host}}" ], "path": [ "api", - "content", - "v2", - "create" + "collection", + "v1", + "dialcode", + "link", + "{{collection_id}}" ] - }, - "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + } }, "response": [] - }, + } + ] + }, + { + "name": "Content Publish API", + "item": [ { - "name": "400 - missing mimeType", - "event": [ + "name": "Pre-requisite", + "item": [ { - "listen": "test", - "script": { - "exec": [ - "let jsonResponse = pm.response.json()", - "", - "//Verification", - "pm.test(\"Status code is 400\", function () {", - " pm.response.to.have.status(400)", - "});", + "name": "Collection Create", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.environment.set(\"collection_id\", jsonResponse.result.identifier);", + "pm.globals.set(\"versionKey\", jsonResponse.result.versionKey);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Sample Collection\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/vnd.ekstep.content-collection\",\n \"primaryCategory\": \"Digital Textbook\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] + }, + { + "name": "TOC Upload", + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "fileUrl", + "value": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/hierarchy/do_113316820811522048194/createtoc.csv", + "type": "text" + } + ] + }, + "url": { + "raw": "{{host}}/api/collection/v1/import/{{collection_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "collection", + "v1", + "import", + "{{collection_id}}" + ] + } + }, + "response": [] + }, + { + "name": "PDF Content Create", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.globals.set(\"pdf_content_id\", jsonResponse.result.identifier);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\",\n \"artifactUrl\": \"https://sunbirdstagingpublic.blob.core.windows.net/sunbird-content-staging/content/assets/do_2136088747812372481510/samplepdf.pdf\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] + }, + { + "name": "MP4 Content Create", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.globals.set(\"mp4_content_id\", jsonResponse.result.identifier);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"video/mp4\",\n \"primaryCategory\": \"Explanation Content\",\n \"artifactUrl\": \"https://sunbirdstagingpublic.blob.core.windows.net/sunbird-content-staging/content/assets/do_21360643694649344011752/draftversion.mp4\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] + }, + { + "name": "WEBM Content Create", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.globals.set(\"webm_content_id\", jsonResponse.result.identifier);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"video/webm\",\n \"primaryCategory\": \"Explanation Content\",\n \"artifactUrl\": \"https://sunbirdstagingpublic.blob.core.windows.net/sunbird-content-staging/content/assets/do_21360604894303027211507/big-buck-bunny_trailer.webm\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] + }, + { + "name": "HTML Content Create", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.globals.set(\"html_content_id\", jsonResponse.result.identifier);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/vnd.ekstep.html-archive\",\n \"primaryCategory\": \"Explanation Content\",\n \"artifactUrl\": \"https://sunbirdstagingpublic.blob.core.windows.net/sunbird-content-staging/content/do_21357841995316428811795/artifact/2.-ekiikrnn-kii-prkrti-output-3_1657521986480_1657522093104.zip\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] + }, + { + "name": "H5P Content Create", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.globals.set(\"h5p_content_id\", jsonResponse.result.identifier);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/vnd.ekstep.h5p-archive\",\n \"primaryCategory\": \"Explanation Content\",\n \"artifactUrl\": \"https://sunbirdstagingpublic.blob.core.windows.net/sunbird-content-staging/content/do_2136088263163084801476/artifact/1661233685237_do_2136088263163084801476.zip\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] + }, + { + "name": "ECML Content Create", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.globals.set(\"ecml_content_id\", jsonResponse.result.identifier);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/vnd.ekstep.ecml-archive\",\n \"primaryCategory\": \"Explanation Content\",\n \"body\": \"{\\\"theme\\\":{\\\"id\\\":\\\"theme\\\",\\\"version\\\":\\\"1.0\\\",\\\"startStage\\\":\\\"463caa09-b78e-412c-ae20-a2776c76b4a0\\\",\\\"stage\\\":[{\\\"x\\\":0,\\\"y\\\":0,\\\"w\\\":100,\\\"h\\\":100,\\\"id\\\":\\\"463caa09-b78e-412c-ae20-a2776c76b4a0\\\",\\\"rotate\\\":null,\\\"config\\\":{\\\"__cdata\\\":\\\"{\\\\\\\"opacity\\\\\\\":100,\\\\\\\"strokeWidth\\\\\\\":1,\\\\\\\"stroke\\\\\\\":\\\\\\\"rgba(255, 255, 255, 0)\\\\\\\",\\\\\\\"autoplay\\\\\\\":false,\\\\\\\"visible\\\\\\\":true,\\\\\\\"color\\\\\\\":\\\\\\\"#FFFFFF\\\\\\\",\\\\\\\"genieControls\\\\\\\":false,\\\\\\\"instructions\\\\\\\":\\\\\\\"\\\\\\\"}\\\"},\\\"manifest\\\":{\\\"media\\\":[]},\\\"shape\\\":[{\\\"type\\\":\\\"ellipse\\\",\\\"x\\\":15,\\\"y\\\":15,\\\"fill\\\":\\\"#00FF00\\\",\\\"w\\\":18,\\\"h\\\":32,\\\"stroke\\\":\\\"rgba(255, 255, 255, 0)\\\",\\\"strokeWidth\\\":1,\\\"opacity\\\":1,\\\"rotate\\\":0,\\\"r\\\":64.8,\\\"z-index\\\":0,\\\"id\\\":\\\"7e35536a-1e50-4ada-872e-416b04da61a0\\\",\\\"config\\\":{\\\"__cdata\\\":\\\"{\\\\\\\"opacity\\\\\\\":100,\\\\\\\"strokeWidth\\\\\\\":1,\\\\\\\"stroke\\\\\\\":\\\\\\\"rgba(255, 255, 255, 0)\\\\\\\",\\\\\\\"autoplay\\\\\\\":false,\\\\\\\"visible\\\\\\\":true,\\\\\\\"color\\\\\\\":\\\\\\\"#00FF00\\\\\\\",\\\\\\\"radius\\\\\\\":64.8}\\\"}}]}],\\\"manifest\\\":{\\\"media\\\":[{\\\"id\\\":\\\"d91cc898-8885-4642-9e55-d16bd502d946\\\",\\\"plugin\\\":\\\"org.ekstep.navigation\\\",\\\"ver\\\":\\\"1.0\\\",\\\"src\\\":\\\"/content-plugins/org.ekstep.navigation-1.0/renderer/controller/navigation_ctrl.js\\\",\\\"type\\\":\\\"js\\\"},{\\\"id\\\":\\\"e02a792e-b75e-4eea-bae0-58666a4ee5c2\\\",\\\"plugin\\\":\\\"org.ekstep.navigation\\\",\\\"ver\\\":\\\"1.0\\\",\\\"src\\\":\\\"/content-plugins/org.ekstep.navigation-1.0/renderer/templates/navigation.html\\\",\\\"type\\\":\\\"js\\\"},{\\\"id\\\":\\\"org.ekstep.navigation\\\",\\\"plugin\\\":\\\"org.ekstep.navigation\\\",\\\"ver\\\":\\\"1.0\\\",\\\"src\\\":\\\"/content-plugins/org.ekstep.navigation-1.0/renderer/plugin.js\\\",\\\"type\\\":\\\"plugin\\\"},{\\\"id\\\":\\\"org.ekstep.navigation_manifest\\\",\\\"plugin\\\":\\\"org.ekstep.navigation\\\",\\\"ver\\\":\\\"1.0\\\",\\\"src\\\":\\\"/content-plugins/org.ekstep.navigation-1.0/manifest.json\\\",\\\"type\\\":\\\"json\\\"}]},\\\"plugin-manifest\\\":{\\\"plugin\\\":[{\\\"id\\\":\\\"org.ekstep.navigation\\\",\\\"ver\\\":\\\"1.0\\\",\\\"type\\\":\\\"plugin\\\",\\\"depends\\\":\\\"\\\"}]},\\\"compatibilityVersion\\\":2}}\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] + }, + { + "name": "EPUB Content Create", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.globals.set(\"epub_content_id\", jsonResponse.result.identifier);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/epub\",\n \"primaryCategory\": \"Explanation Content\",\n \"artifactUrl\": \"https://sunbirdstagingpublic.blob.core.windows.net/sunbird-content-staging/content/assets/do_2136085231556526081377/faulkner-sound-and-the-fury.epub\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] + }, + { + "name": "Youtube Content Create", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.globals.set(\"yt_content_id\", jsonResponse.result.identifier);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"video/x-youtube\",\n \"primaryCategory\": \"Explanation Content\",\n \"artifactUrl\": \"https://www.youtube.com/watch?v=fkLcI4R7unE\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] + }, + { + "name": "Plugin Create", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.globals.set(\"plugin_id\", jsonResponse.result.identifier);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"org.ekstep.summary-test\",\n \"mimeType\": \"application/vnd.ekstep.plugin-archive\",\n \"primaryCategory\": \"Explanation Content\",\n \"artifactUrl\": \"https://ntpproductionall.blob.core.windows.net/ntp-content-production/content/org.ekstep.summary/artifact/org.ekstep.summary-1.0-1_1574060894628.zip\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] + } + ] + }, + { + "name": "Test Cases", + "item": [ + { + "name": "Collection Publish", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/collection/v1/publish/{{collection_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "collection", + "v1", + "publish", + "{{collection_id}}" + ] + } + }, + "response": [] + }, + { + "name": "PDF Content Publish", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/publish/{{pdf_content_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "publish", + "{{pdf_content_id}}" + ] + } + }, + "response": [] + }, + { + "name": "MP4 Content Publish", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/publish/{{mp4_content_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "publish", + "{{mp4_content_id}}" + ] + } + }, + "response": [] + }, + { + "name": "WEBM Content Publish", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/publish/{{webm_content_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "publish", + "{{webm_content_id}}" + ] + } + }, + "response": [] + }, + { + "name": "HTML Content Publish", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/publish/{{html_content_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "publish", + "{{html_content_id}}" + ] + } + }, + "response": [] + }, + { + "name": "H5P Content Publish", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/publish/{{h5p_content_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "publish", + "{{h5p_content_id}}" + ] + } + }, + "response": [] + }, + { + "name": "EPUB Content Publish", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/publish/{{epub_content_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "publish", + "{{epub_content_id}}" + ] + } + }, + "response": [] + }, + { + "name": "ECML Content Publish", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/publish/{{ecml_content_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "publish", + "{{ecml_content_id}}" + ] + } + }, + "response": [] + }, + { + "name": "Youtube Content Publish", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/publish/{{yt_content_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "publish", + "{{yt_content_id}}" + ] + } + }, + "response": [] + }, + { + "name": "Plugin Publish", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/publish/{{plugin_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "publish", + "{{plugin_id}}" + ] + } + }, + "response": [] + } + ] + } + ] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "let jsonResponse = pm.response.json();", + "", + "//Verification", + "pm.test(\"Status code is 200\", () => {", + " pm.response.to.have.status(200)", + "});", + "//code name contains a string", + "tests[\"Status code name has string OK\"] = responseCode.name.has(\"OK\");", + "", + "// pm.test(\"Content-Encoding header is present\", () => {", + "// pm.response.to.have.header(\"Content-Encoding\");", + "// });", + "// pm.test(\"Content-Encoding header is gzip\", () => {", + "// pm.expect(pm.response.headers.get('Content-Encoding')).to.eql('gzip');", + "// });", + "", + "pm.test(\"Verify keys and data type of the params in positive scenario\", () => {", + " pm.expect(jsonResponse.params.resmsgid).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.msgid).to.be.null;", + " pm.expect(jsonResponse.params.err).to.be.null;", + " pm.expect(jsonResponse.params.status).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.errmsg).to.be.null;", + "});", + "", + "pm.test(\"params.status should be \\\"successful\\\"\", () => {", + " pm.expect(jsonResponse.params.status).to.eql(\"successful\");", + "});", + "pm.test(\"responseCode should be OK\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"OK\");", + "});" + ] + } + } + ] + }, + { + "name": "Negative", + "item": [ + { + "name": "Content Create", + "item": [ + { + "name": "400 - missing channel", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 400\", () => {", + " pm.response.to.have.status(400)", + "});", + "//code name contains a string", + "tests[\"Status code name has string \\\"Bad Request\\\"\"] = responseCode.name.has(\"Bad Request\");", + "", + "pm.test(\"Verify keys and data type of the response result\", () => {", + " pm.expect(jsonResponse.result).to.be.an(\"object\");", + " pm.expect(jsonResponse.result).to.have.all.keys('messages');", + " pm.expect(jsonResponse.result.messages).to.not.be.empty;", + "});", + "", + "//Validation", + "// Response body", + "pm.test(\"responseCode should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"CLIENT_ERROR\");", + "});", + "", + "// Params", + "pm.test(\"params.err should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.params.err).to.eql(\"CLIENT_ERROR\");", + "});", + "pm.test(\"params.errmsg should be \\\"Validation Errors\\\"\", () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(\"Validation Errors\");", + "});", + "", + "// Result", + "pm.test(\"result body should include messages \\\"Required Metadata channel not set\\\"\", () => {", + " pm.expect(jsonResponse.result.messages).to.have.length(1)", + " pm.expect(jsonResponse.result.messages).to.include('Required Metadata channel not set');", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": { + "connection": true + } + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"createdBy\": \"530b19ea-dc8d-4cc7-a4b5-0c0214c8113a\",\n \"resourceType\": \"Learn\",\n \"framework\": \"ekstep_ncert_k-12\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + }, + { + "name": "400 - empty channel", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 400\", () => {", + " pm.response.to.have.status(400)", + "});", + "//code name contains a string", + "tests[\"Status code name has string \\\"Bad Request\\\"\"] = responseCode.name.has(\"Bad Request\");", + "", + "pm.test(\"Verify keys and data type of the response result\", () => {", + " pm.expect(jsonResponse.result).to.be.an(\"object\");", + " pm.expect(jsonResponse.result).to.have.all.keys('messages');", + " pm.expect(jsonResponse.result.messages).to.not.be.empty;", + "});", + "", + "//Validation", + "// Response body", + "pm.test(\"responseCode should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"CLIENT_ERROR\");", + "});", + "", + "// Params", + "pm.test(\"params.err should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.params.err).to.eql(\"CLIENT_ERROR\");", + "});", + "pm.test(\"params.errmsg should be \\\"Validation Errors\\\"\", () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(\"Validation Errors\");", + "});", + "", + "// Result", + "pm.test(\"result body should include messages \\\"Required Metadata channel not set\\\"\", () => {", + " pm.expect(jsonResponse.result.messages).to.have.length(1)", + " pm.expect(jsonResponse.result.messages).to.include('Required Metadata channel not set');", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": { + "connection": true + } + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "X-Channel-Id", + "value": "", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"createdBy\": \"530b19ea-dc8d-4cc7-a4b5-0c0214c8113a\",\n \"resourceType\": \"Learn\",\n \"framework\": \"ekstep_ncert_k-12\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + }, + { + "name": "400 - missing name", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 400\", () => {", + " pm.response.to.have.status(400)", + "});", + "//code name contains a string", + "tests[\"Status code name has string \\\"Bad Request\\\"\"] = responseCode.name.has(\"Bad Request\");", + "", + "pm.test(\"Verify keys and data type of the response result\", () => {", + " pm.expect(jsonResponse.result).to.be.an(\"object\");", + " pm.expect(jsonResponse.result).to.have.all.keys('messages');", + " pm.expect(jsonResponse.result.messages).to.not.be.empty;", + "});", + "", + "//Validation", + "// Response body", + "pm.test(\"responseCode should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"CLIENT_ERROR\");", + "});", + "", + "// Params", + "pm.test(\"params.err should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.params.err).to.eql(\"CLIENT_ERROR\");", + "});", + "pm.test(\"params.errmsg should be \\\"Validation Errors\\\"\", () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(\"Validation Errors\");", + "});", + "", + "// Result", + "pm.test(\"result body should include messages \\\"Required Metadata name not set\\\"\", () => {", + " pm.expect(jsonResponse.result.messages).to.have.length(1)", + " pm.expect(jsonResponse.result.messages).to.include('Required Metadata name not set');", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": { + "connection": true + } + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + }, + { + "name": "400 - name: invalid datatype", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 400\", () => {", + " pm.response.to.have.status(400)", + "});", + "//code name contains a string", + "tests[\"Status code name has string \\\"Bad Request\\\"\"] = responseCode.name.has(\"Bad Request\");", + "", + "pm.test(\"Verify keys and data type of the response result\", () => {", + " pm.expect(jsonResponse.result).to.be.an(\"object\");", + " pm.expect(jsonResponse.result).to.have.all.keys('messages');", + " pm.expect(jsonResponse.result.messages).to.not.be.empty;", + "});", + "", + "//Validation", + "// Response body", + "pm.test(\"responseCode should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"CLIENT_ERROR\");", + "});", + "", + "// Params", + "pm.test(\"params.err should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.params.err).to.eql(\"CLIENT_ERROR\");", + "});", + "pm.test(\"params.errmsg should be \\\"Validation Errors\\\"\", () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(\"Validation Errors\");", + "});", + "", + "// Result", + "pm.test(\"result body should include messages \\\"Metadata name should be a/an String value\\\"\", () => {", + " pm.expect(jsonResponse.result.messages).to.have.length(1)", + " pm.expect(jsonResponse.result.messages).to.include('Metadata name should be a/an String value');", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": { + "connection": true + } + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": 423432,\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + }, + { + "name": "400 - name empty", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 400\", function () {", + " pm.response.to.have.status(400)", + "});", + "//code name contains a string", + "tests[\"Status code name has string \\\"Bad Request\\\"\"] = responseCode.name.has(\"Bad Request\");", + "", + "pm.test(\"Verify keys and data type of the response result\", () => {", + " pm.expect(jsonResponse.result).to.be.an(\"object\");", + " pm.expect(jsonResponse.result).to.have.all.keys('messages');", + " pm.expect(jsonResponse.result.messages).to.not.be.empty;", + "});", + "", + "//Validation", + "// Response body", + "pm.test(\"responseCode should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"CLIENT_ERROR\");", + "});", + "", + "// Params", + "pm.test(\"params.err should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.params.err).to.eql(\"CLIENT_ERROR\");", + "});", + "pm.test(\"params.errmsg should be \\\"Validation Errors\\\"\", () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(\"Validation Errors\");", + "});", + "", + "// Result", + "pm.test(\"result body should include messages \\\"Required Metadata name not set\\\"\", () => {", + " pm.expect(jsonResponse.result.messages).to.have.length(1)", + " pm.expect(jsonResponse.result.messages).to.include('Required Metadata name not set');", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": { + "connection": true + } + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + }, + { + "name": "400 - missing code", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 400\", function () {", + " pm.response.to.have.status(400)", + "});", + "//code name contains a string", + "tests[\"Status code name has string \\\"Bad Request\\\"\"] = responseCode.name.has(\"Bad Request\");", + "", + "pm.test(\"Verify keys and data type of the response result\", () => {", + " pm.expect(jsonResponse.result).to.be.an(\"object\");", + " pm.expect(jsonResponse.result).to.have.all.keys('messages');", + " pm.expect(jsonResponse.result.messages).to.not.be.empty;", + "});", + "", + "//Validation", + "// Response body", + "pm.test(\"responseCode should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"CLIENT_ERROR\");", + "});", + "", + "// Params", + "pm.test(\"params.err should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.params.err).to.eql(\"CLIENT_ERROR\");", + "});", + "pm.test(\"params.errmsg should be \\\"Validation Errors\\\"\", () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(\"Validation Errors\");", + "});", + "", + "// Result", + "pm.test(\"result body should include messages \\\"Required Metadata code not set\\\"\", () => {", + " pm.expect(jsonResponse.result.messages).to.have.length(1)", + " pm.expect(jsonResponse.result.messages).to.include('Required Metadata code not set');", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": { + "connection": true + } + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + }, + { + "name": "400 - missing mimeType", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 400\", function () {", + " pm.response.to.have.status(400)", + "});", + "//code name contains a string", + "tests[\"Status code name has string \\\"Bad Request\\\"\"] = responseCode.name.has(\"Bad Request\");", + "", + "pm.test(\"Verify keys and data type of the response result\", () => {", + " pm.expect(jsonResponse.result).to.be.an(\"object\");", + " pm.expect(jsonResponse.result).to.have.all.keys('messages');", + " pm.expect(jsonResponse.result.messages).to.not.be.empty;", + "});", + "", + "//Validation", + "// Response body", + "pm.test(\"responseCode should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"CLIENT_ERROR\");", + "});", + "", + "// Params", + "pm.test(\"params.err should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.params.err).to.eql(\"CLIENT_ERROR\");", + "});", + "pm.test(\"params.errmsg should be \\\"Validation Errors\\\"\", () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(\"Validation Errors\");", + "});", + "", + "// Result", + "pm.test(\"result body should include messages \\\"Required Metadata mimeType not set\\\"\", () => {", + " pm.expect(jsonResponse.result.messages).to.have.length(1)", + " pm.expect(jsonResponse.result.messages).to.include('Required Metadata mimeType not set');", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": { + "connection": true + } + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + }, + { + "name": "400 - invalid mimeType", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 400\", function () {", + " pm.response.to.have.status(400)", + "});", + "//code name contains a string", + "tests[\"Status code name has string \\\"Bad Request\\\"\"] = responseCode.name.has(\"Bad Request\");", + "", + "pm.test(\"Verify keys and data type of the response result\", () => {", + " pm.expect(jsonResponse.result).to.be.an(\"object\");", + " pm.expect(jsonResponse.result).to.have.all.keys('messages');", + " pm.expect(jsonResponse.result.messages).to.not.be.empty;", + "});", + "", + "//Validation", + "// Response body", + "pm.test(\"responseCode should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"CLIENT_ERROR\");", + "});", + "", + "// Params", + "pm.test(\"params.err should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.params.err).to.eql(\"CLIENT_ERROR\");", + "});", + "pm.test(\"params.errmsg should be \\\"Validation Errors\\\"\", () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(\"Validation Errors\");", + "});", + "", + "// Result", + "pm.test(\"result body should include messages \\\"Metadata mimeType should be one of: [application/vnd.ekstep.ecml-archive, application/vnd.ekstep.html-archive, application/vnd.android.package-archive, application/vnd.ekstep.content-archive, application/vnd.ekstep.content-collection, application/vnd.ekstep.plugin-archive, application/vnd.ekstep.h5p-archive, application/epub, text/x-url, video/x-youtube, application/octet-stream, application/msword, application/pdf, image/jpeg, image/jpg, image/png, image/tiff, image/bmp, image/gif, image/svg+xml, video/avi, video/mpeg, video/quicktime, video/3gpp, video/mp4, video/ogg, video/webm, audio/mp3, audio/mp4, audio/mpeg, audio/ogg, audio/webm, audio/x-wav, audio/wav, application/json, application/quiz]\\\"\", () => {", + " pm.expect(jsonResponse.result.messages).to.have.length(1)", + " pm.expect(jsonResponse.result.messages).to.include('Metadata mimeType should be one of: [application/vnd.ekstep.ecml-archive, application/vnd.ekstep.html-archive, application/vnd.android.package-archive, application/vnd.ekstep.content-archive, application/vnd.ekstep.content-collection, application/vnd.ekstep.plugin-archive, application/vnd.ekstep.h5p-archive, application/epub, text/x-url, video/x-youtube, application/octet-stream, application/msword, application/pdf, image/jpeg, image/jpg, image/png, image/tiff, image/bmp, image/gif, image/svg+xml, video/avi, video/mpeg, video/quicktime, video/3gpp, video/mp4, video/ogg, video/webm, audio/mp3, audio/mp4, audio/mpeg, audio/ogg, audio/webm, audio/x-wav, audio/wav, application/json, application/quiz]');", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": { + "connection": true + } + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"some-mimeType\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + }, + { + "name": "400 - missing primaryCategory", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 400\", () => {", + " pm.response.to.have.status(400)", + "});", + "//code name contains a string", + "tests[\"Status code name has string \\\"Bad Request\\\"\"] = responseCode.name.has(\"Bad Request\");", + "", + "pm.test(\"Verify keys and data type of the response result\", () => {", + " pm.expect(jsonResponse.result).to.be.an(\"object\");", + "});", + "", + "//Validation", + "// Response body", + "pm.test(\"responseCode should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"CLIENT_ERROR\");", + "});", + "", + "// Params", + "pm.test(\"params.err should be \\\"VALIDATION_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.params.err).to.eql(\"VALIDATION_ERROR\");", + "});", + "pm.test(\"params.errmsg should be \\\"primaryCategory is a mandatory parameter\\\"\", () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(\"primaryCategory is a mandatory parameter\");", + "});", + "", + "// Result", + "pm.test(\"result body should be empty\", () => {", + " pm.expect(jsonResponse.result).to.eql({});", + " pm.expect(jsonResponse.result).to.be.empty;", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": { + "connection": true + } + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + }, + { + "name": "400 - Invalid subject", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 400\", () => {", + " pm.response.to.have.status(400)", + "});", + "//code name contains a string", + "tests[\"Status code name has string \\\"Bad Request\\\"\"] = responseCode.name.has(\"Bad Request\");", + "", + "pm.test(\"Verify keys and data type of the response result\", () => {", + " pm.expect(jsonResponse.result).to.be.an(\"object\");", + " pm.expect(jsonResponse.result).to.have.all.keys('messages');", + " pm.expect(jsonResponse.result.messages).to.not.be.empty;", + "});", + "", + "//Validation", + "// Response body", + "pm.test(\"responseCode should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"CLIENT_ERROR\");", + "});", + "", + "// Params", + "pm.test(\"params.err should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.params.err).to.eql(\"CLIENT_ERROR\");", + "});", + "pm.test(\"params.errmsg should be \\\"Validation Errors.\\\"\", () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(\"Validation Errors.\");", + "});", + "", + "// Result", + "pm.test(\"result body should include messages \\\"subject range data is empty from the given framework.\\\"\", () => {", + " pm.expect(jsonResponse.result.messages).to.have.length(1)", + " pm.expect(jsonResponse.result.messages).to.include('subject range data is empty from the given framework.');", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": { + "connection": true + } + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"createdBy\": \"530b19ea-dc8d-4cc7-a4b5-0c0214c8113a\",\n \"resourceType\": \"Learn\",\n \"framework\": \"ekstep_ncert_k-12-erwe\",\n \"subject\": [\"test\"],\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + }, + { + "name": "404 - Invalid primaryCategory", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 404\", () => {", + " pm.response.to.have.status(404)", + "});", + "//code name contains a string", + "tests[\"Status code name has string \\\"Not Found\\\"\"] = responseCode.name.has(\"Not Found\");", + "", + "pm.test(\"Verify keys and data type of the response result\", () => {", + " pm.expect(jsonResponse.result).to.have.all.keys('messages');", + " pm.expect(jsonResponse.result.messages).to.be.null;", + "});", + "", + "//Validation", + "// Response body", + "pm.test(\"responseCode should be RESOURCE_NOT_FOUND\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"RESOURCE_NOT_FOUND\");", + "});", + "", + "// Params", + "pm.test(\"params.err should be \\\"404\\\"\", () => {", + " pm.expect(jsonResponse.params.err).to.eql(\"404\");", + "});", + "pm.test(`params.errmsg should be \\\"Entry is not found in cassandra for content with identifier: obj-cat:${JSON.parse(request.data).request.content.primaryCategory}_content_all {}\\\"`, () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(`Entry is not found in cassandra for content with identifier: obj-cat:${JSON.parse(request.data).request.content.primaryCategory}_content_all {}`);", + "});", + "", + "// Result", + "pm.test(\"messages should be null\", () => {", + " pm.expect(jsonResponse.result.messages).to.eql(null);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": { + "connection": true + } + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"createdBy\": \"530b19ea-dc8d-4cc7-a4b5-0c0214c8113a\",\n \"resourceType\": \"Learn\",\n \"framework\": \"ekstep_ncert_k-12\",\n \"primaryCategory\": \"aabaa\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "let jsonResponse = pm.response.json();", + "", + "pm.test(\"id should be api.content.create\", () => {", + " pm.expect(jsonResponse.id).to.eql(\"api.content.create\");", + "});", + "pm.test(\"ver should be 4.0\", () => {", + " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", + "});" + ] + } + } + ] + }, + { + "name": "Content Read", + "item": [ + { + "name": "404 - Not Found", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 404\", function () {", + " pm.response.to.have.status(404)", + "});", + "//code name contains a string", + "tests[\"Status code name has string \\\"Not Found\\\"\"] = responseCode.name.has(\"Not Found\");", + "", + "pm.test(\"Test data type of the response and keys\", () => {", + " pm.expect(jsonResponse.result).to.be.an(\"object\");", + " pm.expect(jsonResponse.result).to.have.all.keys('messages');", + " pm.expect(jsonResponse.result.messages).to.be.null;", + "});", + "", + "//Validation", + "// Response body", + "pm.test(\"id should be api.content.read\", () => {", + " pm.expect(jsonResponse.id).to.eql(\"api.content.read\");", + "});", + "pm.test(\"ver should be 4.0\", () => {", + " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", + "});", + "pm.test(\"responseCode should be RESOURCE_NOT_FOUND\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"RESOURCE_NOT_FOUND\");", + "});", + "", + "// Params", + "pm.test(\"params.err should be \\\"NOT_FOUND\\\"\", () => {", + " pm.expect(jsonResponse.params.err).to.eql(\"NOT_FOUND\");", + "});", + "pm.test(\"params.status should be \\\"failed\\\"\", () => {", + " pm.expect(jsonResponse.params.status).to.eql(\"failed\");", + "});", + "pm.test(`params.errmsg should be \\\"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: ${pm.globals.get('invalid_content_id')}\\\"`, () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(`Error! Node(s) doesn't Exists. | [Invalid Node Id.]: ${pm.globals.get('invalid_content_id')}`);", + "});", + "", + "// Result", + "pm.test(\"messages should be null\", () => {", + " pm.expect(jsonResponse.result.messages).to.eql(null);", + "});", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "pm.globals.set('invalid_content_id', 'do_42353245436534');" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "type": "text" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + } + ], + "url": { + "raw": "{{host}}/api/content/v2/read/{{invalid_content_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "read", + "{{invalid_content_id}}" + ] + }, + "description": "Fetch a particular Batch\nThis API is associated with fetching a particular batch on the Sunbird Platform.\nThe endpoint for Fetch a particular Batch is /batch/read/{Batch_ID}\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ] + }, + { + "name": "Content Update", + "item": [ + { + "name": "400 - Invalid versionKey", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 400\", () => {", + " pm.response.to.have.status(400)", + "});", + "//code name contains a string", + "tests[\"Status code name has string Bad Request\"] = responseCode.name.has(\"Bad Request\");", + "", + "pm.test(\"Content-Encoding header should not present\", () => {", + " pm.response.to.not.have.header(\"Content-Encoding\");", + "});", + "", + "pm.test(\"Verify keys and data type of the response result\", () => {", + " pm.expect(jsonResponse.result).to.be.an(\"object\");", + " pm.expect(jsonResponse.result).to.have.all.keys('messages');", + " pm.expect(jsonResponse.result.messages).to.be.null;", + "});", + "", + "//Validation", + "// Response body", + "pm.test(\"responseCode should be CLIENT_ERROR\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"CLIENT_ERROR\");", + "});", + "", + "// Params", + "pm.test(\"params.err should be CLIENT_ERROR\", () => {", + " pm.expect(jsonResponse.params.err).to.eql(\"CLIENT_ERROR\");", + "});", + "pm.test(\"params.status should be failed\", () => {", + " pm.expect(jsonResponse.params.status).to.eql(\"failed\");", + "});", + "pm.test(\"params.errmsg should be \\\"Invalid version Key\\\"\", () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(\"Invalid version Key\");", + "});", + "", + "// Result", + "pm.test(\"messages should be null\", () => {", + " pm.expect(jsonResponse.result.messages).to.eql(null);", + "});", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "pm.globals.set('invalid-versionKey', '3534534543');", + "// pm.globals.set(\"content_id\", '');" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "type": "text" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"{{invalid-versionKey}}\",\n \"lastUpdatedBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\",\n \"description\": \"dsd\",\n \"name\": \"API DOCUMENTATION CONTENT\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/update/{{content_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "update", + "{{content_id}}" + ] + }, + "description": "This API is associated with updating a batch on the Sunbird Platform.\nThe endpoint for Course Batch Update is /batch/update\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + }, + { + "name": "400 - Empty name", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 400\", () => {", + " pm.response.to.have.status(400)", + "});", "//code name contains a string", "tests[\"Status code name has string \\\"Bad Request\\\"\"] = responseCode.name.has(\"Bad Request\");", "", @@ -1395,9 +7379,9 @@ "});", "", "// Result", - "pm.test(\"result body should include messages \\\"Required Metadata mimeType not set\\\"\", () => {", + "pm.test(\"result body should include messages \\\"Required Metadata name not set\\\"\", () => {", " pm.expect(jsonResponse.result.messages).to.have.length(1)", - " pm.expect(jsonResponse.result.messages).to.include('Required Metadata mimeType not set');", + " pm.expect(jsonResponse.result.messages).to.include('Required Metadata name not set');", "});", "" ], @@ -1405,25 +7389,216 @@ } } ], - "protocolProfileBehavior": { - "disabledSystemHeaders": { - "connection": true + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "type": "text" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"{{versionKey}}\",\n \"description\": \"dsd\",\n \"name\": \"\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/update/{{content_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "update", + "{{content_id}}" + ] + }, + "description": "This API is associated with updating a batch on the Sunbird Platform.\nThe endpoint for Course Batch Update is /batch/update\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + }, + { + "name": "404 - Invalid content id", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 404\", () => {", + " pm.response.to.have.status(404)", + "});", + "//code name contains a string", + "tests[\"Status code name has string \\\"Not Found\\\"\"] = responseCode.name.has(\"Not Found\");", + "", + "pm.test(\"Verify keys and data type of the response result\", () => {", + " pm.expect(jsonResponse.result).to.be.an(\"object\");", + " pm.expect(jsonResponse.result).to.have.all.keys('messages');", + " pm.expect(jsonResponse.result.messages).to.be.null;", + "});", + "", + "//Validation", + "// Response body", + "pm.test(\"responseCode should be RESOURCE_NOT_FOUND\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"RESOURCE_NOT_FOUND\");", + "});", + "", + "// Params", + "pm.test(\"params.err should be NOT_FOUND\", () => {", + " pm.expect(jsonResponse.params.err).to.eql(\"NOT_FOUND\");", + "});", + "pm.test(`params.errmsg should be \\\"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: ${pm.globals.get(\"invalid_content_id\")}\\\"`, () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(`Error! Node(s) doesn't Exists. | [Invalid Node Id.]: ${pm.globals.get(\"invalid_content_id\")}`);", + "});", + "", + "// Result", + "pm.test(\"messages should be null\", () => {", + " pm.expect(jsonResponse.result.messages).to.eql(null);", + "});", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "pm.globals.set(\"invalid_content_id\", 'do_21357909972698726412211');" + ], + "type": "text/javascript" + } } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "type": "text" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"{{versionKey}}\",\n \"lastUpdatedBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\",\n \"description\": \"dsd\",\n \"name\": \"API DOCUMENTATION CONTENT\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/update/{{invalid_content_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "update", + "{{invalid_content_id}}" + ] + }, + "description": "This API is associated with updating a batch on the Sunbird Platform.\nThe endpoint for Course Batch Update is /batch/update\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." }, + "response": [] + }, + { + "name": "404 - Invalid primaryCategory", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 404\", () => {", + " pm.response.to.have.status(404)", + "});", + "//code name contains a string", + "tests[\"Status code name has string \\\"Not Found\\\"\"] = responseCode.name.has(\"Not Found\");", + "", + "pm.test(\"Verify keys and data type of the response result\", () => {", + " pm.expect(jsonResponse.result).to.be.an(\"object\");", + " pm.expect(jsonResponse.result).to.have.all.keys('messages');", + " pm.expect(jsonResponse.result.messages).to.be.null;", + "});", + "", + "//Validation", + "// Response body", + "pm.test(\"responseCode should be RESOURCE_NOT_FOUND\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"RESOURCE_NOT_FOUND\");", + "});", + "", + "// Params", + "pm.test(\"params.err should be \\\"404\\\"\", () => {", + " pm.expect(jsonResponse.params.err).to.eql(\"404\");", + "});", + "pm.test(\"params.status should be \\\"failed\\\"\", () => {", + " pm.expect(jsonResponse.params.status).to.eql(\"failed\");", + "});", + "pm.test(`params.errmsg should be \\\"Entry is not found in cassandra for content with identifier: obj-cat:${JSON.parse(request.data).request.content.primaryCategory}_content_all {}\\\"`, () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(`Entry is not found in cassandra for content with identifier: obj-cat:${JSON.parse(request.data).request.content.primaryCategory}_content_all {}`);", + "});", + "", + "pm.test(\"messages should be null\", () => {", + " pm.expect(jsonResponse.result.messages).to.eql(null);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], "request": { - "method": "POST", + "method": "PATCH", "header": [ { "key": "Content-Type", - "value": "application/json" + "value": "application/json", + "type": "text" }, { "key": "X-Authenticated-User-token", - "value": "{{keycloak_access_token}}" + "value": "{{keycloak_access_token}}", + "type": "text" }, { "key": "Authorization", - "value": "{{kong_api_key}}" + "value": "{{kong_api_key}}", + "type": "text" }, { "key": "X-Channel-Id", @@ -1433,10 +7608,10 @@ ], "body": { "mode": "raw", - "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"{{versionKey}}\",\n \"description\": \"dsd\",\n \"name\": \"API DOCUMENTATION CONTENT\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" }, "url": { - "raw": "{{host}}/api/content/v2/create", + "raw": "{{host}}/api/content/v2/update/{{content_id}}", "host": [ "{{host}}" ], @@ -1444,463 +7619,375 @@ "api", "content", "v2", - "create" + "update", + "{{content_id}}" ] }, - "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + "description": "This API is associated with updating a batch on the Sunbird Platform.\nThe endpoint for Course Batch Update is /batch/update\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." }, "response": [] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "// pm.globals.set(\"content_id\", '');" + ] + } }, { - "name": "400 - invalid mimeType", + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "let jsonResponse = pm.response.json();", + "", + "pm.test(\"id should be api.content.update\", () => {", + " pm.expect(jsonResponse.id).to.eql(\"api.content.update\");", + "});", + "pm.test(\"ver should be 4.0\", () => {", + " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", + "});" + ] + } + } + ] + }, + { + "name": "Content Reserve DIAL code", + "item": [ + { + "name": "Reserve DIAL code with Invalid channel Id in header", "event": [ { "listen": "test", "script": { "exec": [ - "let jsonResponse = pm.response.json()", - "", - "//Verification", "pm.test(\"Status code is 400\", function () {", - " pm.response.to.have.status(400)", - "});", - "//code name contains a string", - "tests[\"Status code name has string \\\"Bad Request\\\"\"] = responseCode.name.has(\"Bad Request\");", - "", - "pm.test(\"Verify keys and data type of the response result\", () => {", - " pm.expect(jsonResponse.result).to.be.an(\"object\");", - " pm.expect(jsonResponse.result).to.have.all.keys('messages');", - " pm.expect(jsonResponse.result.messages).to.not.be.empty;", - "});", - "", - "//Validation", - "// Response body", - "pm.test(\"responseCode should be \\\"CLIENT_ERROR\\\"\", () => {", - " pm.expect(jsonResponse.responseCode).to.eql(\"CLIENT_ERROR\");", - "});", - "", - "// Params", - "pm.test(\"params.err should be \\\"CLIENT_ERROR\\\"\", () => {", - " pm.expect(jsonResponse.params.err).to.eql(\"CLIENT_ERROR\");", - "});", - "pm.test(\"params.errmsg should be \\\"Validation Errors\\\"\", () => {", - " pm.expect(jsonResponse.params.errmsg).to.eql(\"Validation Errors\");", + " pm.response.to.have.status(400);", "});", "", - "// Result", - "pm.test(\"result body should include messages \\\"Metadata mimeType should be one of: [application/vnd.ekstep.ecml-archive, application/vnd.ekstep.html-archive, application/vnd.android.package-archive, application/vnd.ekstep.content-archive, application/vnd.ekstep.content-collection, application/vnd.ekstep.plugin-archive, application/vnd.ekstep.h5p-archive, application/epub, text/x-url, video/x-youtube, application/octet-stream, application/msword, application/pdf, image/jpeg, image/jpg, image/png, image/tiff, image/bmp, image/gif, image/svg+xml, video/avi, video/mpeg, video/quicktime, video/3gpp, video/mp4, video/ogg, video/webm, audio/mp3, audio/mp4, audio/mpeg, audio/ogg, audio/webm, audio/x-wav, audio/wav, application/json, application/quiz]\\\"\", () => {", - " pm.expect(jsonResponse.result.messages).to.have.length(1)", - " pm.expect(jsonResponse.result.messages).to.include('Metadata mimeType should be one of: [application/vnd.ekstep.ecml-archive, application/vnd.ekstep.html-archive, application/vnd.android.package-archive, application/vnd.ekstep.content-archive, application/vnd.ekstep.content-collection, application/vnd.ekstep.plugin-archive, application/vnd.ekstep.h5p-archive, application/epub, text/x-url, video/x-youtube, application/octet-stream, application/msword, application/pdf, image/jpeg, image/jpg, image/png, image/tiff, image/bmp, image/gif, image/svg+xml, video/avi, video/mpeg, video/quicktime, video/3gpp, video/mp4, video/ogg, video/webm, audio/mp3, audio/mp4, audio/mpeg, audio/ogg, audio/webm, audio/x-wav, audio/wav, application/json, application/quiz]');", - "});", - "" + "pm.test(\"Response throws ERR_INVALID_CHANNEL\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.responseCode).equals(\"CLIENT_ERROR\")", + " ", + " const params = responseJson.params;", + " pm.expect(params.status).equals(\"failed\")", + " pm.expect(params.err).equals(\"ERR_INVALID_CHANNEL\") ", + "});" ], "type": "text/javascript" } } ], - "protocolProfileBehavior": { - "disabledSystemHeaders": { - "connection": true - } - }, "request": { "method": "POST", "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "X-Authenticated-User-token", - "value": "{{keycloak_access_token}}" - }, { "key": "Authorization", "value": "{{kong_api_key}}" }, { - "key": "X-Channel-Id", - "value": "{{channel_id}}", - "type": "text" + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Channel-ID", + "value": "dummy" } ], "body": { "mode": "raw", - "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"some-mimeType\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + "raw": "{\n \"request\": {\n \"dialcodes\": {\n \"count\": 3,\n \"qrCodeSpec\": {\n \"errorCorrectionLevel\": \"H\"\n }\n }\n }\n}" }, "url": { - "raw": "{{host}}/api/content/v2/create", + "raw": "{{host}}/api/collection/v1/dialcode/reserve/{{collection_id}}", "host": [ "{{host}}" ], "path": [ "api", - "content", - "v2", - "create" + "collection", + "v1", + "dialcode", + "reserve", + "{{collection_id}}" ] - }, - "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + } }, "response": [] }, { - "name": "400 - missing primaryCategory", + "name": "Reserve DIAL code with Zero count", "event": [ { "listen": "test", "script": { "exec": [ - "let jsonResponse = pm.response.json()", - "", - "//Verification", - "pm.test(\"Status code is 400\", () => {", - " pm.response.to.have.status(400)", - "});", - "//code name contains a string", - "tests[\"Status code name has string \\\"Bad Request\\\"\"] = responseCode.name.has(\"Bad Request\");", - "", - "pm.test(\"Verify keys and data type of the response result\", () => {", - " pm.expect(jsonResponse.result).to.be.an(\"object\");", - "});", - "", - "//Validation", - "// Response body", - "pm.test(\"responseCode should be \\\"CLIENT_ERROR\\\"\", () => {", - " pm.expect(jsonResponse.responseCode).to.eql(\"CLIENT_ERROR\");", - "});", - "", - "// Params", - "pm.test(\"params.err should be \\\"VALIDATION_ERROR\\\"\", () => {", - " pm.expect(jsonResponse.params.err).to.eql(\"VALIDATION_ERROR\");", - "});", - "pm.test(\"params.errmsg should be \\\"primaryCategory is a mandatory parameter\\\"\", () => {", - " pm.expect(jsonResponse.params.errmsg).to.eql(\"primaryCategory is a mandatory parameter\");", + "pm.test(\"Status code is 400\", function () {", + " pm.response.to.have.status(400);", "});", "", - "// Result", - "pm.test(\"result body should be empty\", () => {", - " pm.expect(jsonResponse.result).to.eql({});", - " pm.expect(jsonResponse.result).to.be.empty;", - "});", - "" + "pm.test(\"Response throws ERR_INVALID_COUNT_RANGE\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.responseCode).equals(\"CLIENT_ERROR\")", + " ", + " const params = responseJson.params;", + " pm.expect(params.status).equals(\"failed\")", + " pm.expect(params.err).equals(\"ERR_INVALID_COUNT_RANGE\") ", + "});" ], "type": "text/javascript" } } ], - "protocolProfileBehavior": { - "disabledSystemHeaders": { - "connection": true - } - }, "request": { "method": "POST", "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "X-Authenticated-User-token", - "value": "{{keycloak_access_token}}" - }, { "key": "Authorization", "value": "{{kong_api_key}}" }, { - "key": "X-Channel-Id", - "value": "{{channel_id}}", - "type": "text" + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}" } ], "body": { "mode": "raw", - "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\"\n }\n }\n}" + "raw": "{\n \"request\": {\n \"dialcodes\": {\n \"count\": 0,\n \"qrCodeSpec\": {\n \"errorCorrectionLevel\": \"H\"\n }\n }\n }\n}" }, "url": { - "raw": "{{host}}/api/content/v2/create", + "raw": "{{host}}/api/collection/v1/dialcode/reserve/{{collection_id}}", "host": [ "{{host}}" ], "path": [ "api", - "content", - "v2", - "create" + "collection", + "v1", + "dialcode", + "reserve", + "{{collection_id}}" ] - }, - "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + } }, "response": [] }, { - "name": "400 - Invalid subject", + "name": "Reserve DIAL code with negative count", "event": [ { "listen": "test", "script": { "exec": [ - "let jsonResponse = pm.response.json()", - "", - "//Verification", - "pm.test(\"Status code is 400\", () => {", - " pm.response.to.have.status(400)", - "});", - "//code name contains a string", - "tests[\"Status code name has string \\\"Bad Request\\\"\"] = responseCode.name.has(\"Bad Request\");", - "", - "pm.test(\"Verify keys and data type of the response result\", () => {", - " pm.expect(jsonResponse.result).to.be.an(\"object\");", - " pm.expect(jsonResponse.result).to.have.all.keys('messages');", - " pm.expect(jsonResponse.result.messages).to.not.be.empty;", - "});", - "", - "//Validation", - "// Response body", - "pm.test(\"responseCode should be \\\"CLIENT_ERROR\\\"\", () => {", - " pm.expect(jsonResponse.responseCode).to.eql(\"CLIENT_ERROR\");", - "});", - "", - "// Params", - "pm.test(\"params.err should be \\\"CLIENT_ERROR\\\"\", () => {", - " pm.expect(jsonResponse.params.err).to.eql(\"CLIENT_ERROR\");", - "});", - "pm.test(\"params.errmsg should be \\\"Validation Errors.\\\"\", () => {", - " pm.expect(jsonResponse.params.errmsg).to.eql(\"Validation Errors.\");", + "pm.test(\"Status code is 400\", function () {", + " pm.response.to.have.status(400);", "});", "", - "// Result", - "pm.test(\"result body should include messages \\\"subject range data is empty from the given framework.\\\"\", () => {", - " pm.expect(jsonResponse.result.messages).to.have.length(1)", - " pm.expect(jsonResponse.result.messages).to.include('subject range data is empty from the given framework.');", - "});", - "" + "pm.test(\"Response throws ERR_INVALID_COUNT_RANGE\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.responseCode).equals(\"CLIENT_ERROR\")", + " ", + " const params = responseJson.params;", + " pm.expect(params.status).equals(\"failed\")", + " pm.expect(params.err).equals(\"ERR_INVALID_COUNT_RANGE\") ", + "});" ], "type": "text/javascript" } } ], - "protocolProfileBehavior": { - "disabledSystemHeaders": { - "connection": true - } - }, "request": { "method": "POST", "header": [ + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, { "key": "Content-Type", "value": "application/json" }, { - "key": "X-Authenticated-User-token", - "value": "{{keycloak_access_token}}" - }, + "key": "X-Channel-ID", + "value": "{{channel_id}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"dialcodes\": {\n \"count\": -2,\n \"qrCodeSpec\": {\n \"errorCorrectionLevel\": \"H\"\n }\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/collection/v1/dialcode/reserve/{{collection_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "collection", + "v1", + "dialcode", + "reserve", + "{{collection_id}}" + ] + } + }, + "response": [] + }, + { + "name": "Reserve DIAL code with count greater than max limit(250)", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 400\", function () {", + " pm.response.to.have.status(400);", + "});", + "", + "pm.test(\"Response throws ERR_INVALID_COUNT_RANGE\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.responseCode).equals(\"CLIENT_ERROR\")", + " ", + " const params = responseJson.params;", + " pm.expect(params.status).equals(\"failed\")", + " pm.expect(params.err).equals(\"ERR_INVALID_COUNT_RANGE\") ", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ { "key": "Authorization", "value": "{{kong_api_key}}" }, { - "key": "X-Channel-Id", - "value": "{{channel_id}}", - "type": "text" + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}" } ], "body": { "mode": "raw", - "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"createdBy\": \"530b19ea-dc8d-4cc7-a4b5-0c0214c8113a\",\n \"resourceType\": \"Learn\",\n \"framework\": \"ekstep_ncert_k-12-erwe\",\n \"subject\": [\"test\"],\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + "raw": "{\n \"request\": {\n \"dialcodes\": {\n \"count\": 251,\n \"qrCodeSpec\": {\n \"errorCorrectionLevel\": \"H\"\n }\n }\n }\n}" }, "url": { - "raw": "{{host}}/api/content/v2/create", + "raw": "{{host}}/api/collection/v1/dialcode/reserve/{{collection_id}}", "host": [ "{{host}}" ], "path": [ "api", - "content", - "v2", - "create" + "collection", + "v1", + "dialcode", + "reserve", + "{{collection_id}}" ] - }, - "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + } }, "response": [] }, { - "name": "404 - Invalid primaryCategory", + "name": "Reserve DIAL code with invalid collection Id", "event": [ { "listen": "test", "script": { "exec": [ - "let jsonResponse = pm.response.json()", - "", - "//Verification", - "pm.test(\"Status code is 404\", () => {", - " pm.response.to.have.status(404)", - "});", - "//code name contains a string", - "tests[\"Status code name has string \\\"Not Found\\\"\"] = responseCode.name.has(\"Not Found\");", - "", - "pm.test(\"Verify keys and data type of the response result\", () => {", - " pm.expect(jsonResponse.result).to.have.all.keys('messages');", - " pm.expect(jsonResponse.result.messages).to.be.null;", - "});", - "", - "//Validation", - "// Response body", - "pm.test(\"responseCode should be RESOURCE_NOT_FOUND\", () => {", - " pm.expect(jsonResponse.responseCode).to.eql(\"RESOURCE_NOT_FOUND\");", - "});", - "", - "// Params", - "pm.test(\"params.err should be \\\"404\\\"\", () => {", - " pm.expect(jsonResponse.params.err).to.eql(\"404\");", - "});", - "pm.test(`params.errmsg should be \\\"Entry is not found in cassandra for content with identifier: obj-cat:${JSON.parse(request.data).request.content.primaryCategory}_content_all {}\\\"`, () => {", - " pm.expect(jsonResponse.params.errmsg).to.eql(`Entry is not found in cassandra for content with identifier: obj-cat:${JSON.parse(request.data).request.content.primaryCategory}_content_all {}`);", + "pm.test(\"Status code is 404\", function () {", + " pm.response.to.have.status(404);", "});", "", - "// Result", - "pm.test(\"messages should be null\", () => {", - " pm.expect(jsonResponse.result.messages).to.eql(null);", - "});", - "" + "pm.test(\"Response throws RESOURCE_NOT_FOUND\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.responseCode).equals(\"RESOURCE_NOT_FOUND\")", + " ", + " const params = responseJson.params;", + " pm.expect(params.status).equals(\"failed\")", + " pm.expect(params.err).equals(\"NOT_FOUND\") ", + "});" ], "type": "text/javascript" } } ], - "protocolProfileBehavior": { - "disabledSystemHeaders": { - "connection": true - } - }, "request": { "method": "POST", "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "X-Authenticated-User-token", - "value": "{{keycloak_access_token}}" - }, { "key": "Authorization", "value": "{{kong_api_key}}" }, { - "key": "X-Channel-Id", - "value": "{{channel_id}}", - "type": "text" + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}" } ], "body": { "mode": "raw", - "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"createdBy\": \"530b19ea-dc8d-4cc7-a4b5-0c0214c8113a\",\n \"resourceType\": \"Learn\",\n \"framework\": \"ekstep_ncert_k-12\",\n \"primaryCategory\": \"aabaa\"\n }\n }\n}" + "raw": "{\n \"request\": {\n \"dialcodes\": {\n \"count\": 25,\n \"qrCodeSpec\": {\n \"errorCorrectionLevel\": \"H\"\n }\n }\n }\n}" }, "url": { - "raw": "{{host}}/api/content/v2/create", + "raw": "{{host}}/api/collection/v1/dialcode/reserve/do_dummy_id", "host": [ "{{host}}" ], "path": [ "api", - "content", - "v2", - "create" + "collection", + "v1", + "dialcode", + "reserve", + "do_dummy_id" ] - }, - "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + } }, "response": [] } - ], - "event": [ - { - "listen": "prerequest", - "script": { - "type": "text/javascript", - "exec": [ - "" - ] - } - }, - { - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "let jsonResponse = pm.response.json();", - "", - "pm.test(\"id should be api.content.create\", () => {", - " pm.expect(jsonResponse.id).to.eql(\"api.content.create\");", - "});", - "pm.test(\"ver should be 4.0\", () => {", - " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", - "});" - ] - } - } ] }, { - "name": "Content Read", + "name": "Content Link DIAL code", "item": [ { - "name": "404 - Not Found", + "name": "Link DIAL code with invalid collection id in the URL", "event": [ { "listen": "test", "script": { "exec": [ - "let jsonResponse = pm.response.json()", - "", - "//Verification", "pm.test(\"Status code is 404\", function () {", - " pm.response.to.have.status(404)", - "});", - "//code name contains a string", - "tests[\"Status code name has string \\\"Not Found\\\"\"] = responseCode.name.has(\"Not Found\");", - "", - "pm.test(\"Test data type of the response and keys\", () => {", - " pm.expect(jsonResponse.result).to.be.an(\"object\");", - " pm.expect(jsonResponse.result).to.have.all.keys('messages');", - " pm.expect(jsonResponse.result.messages).to.be.null;", - "});", - "", - "//Validation", - "// Response body", - "pm.test(\"id should be api.content.read\", () => {", - " pm.expect(jsonResponse.id).to.eql(\"api.content.read\");", - "});", - "pm.test(\"ver should be 4.0\", () => {", - " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", - "});", - "pm.test(\"responseCode should be RESOURCE_NOT_FOUND\", () => {", - " pm.expect(jsonResponse.responseCode).to.eql(\"RESOURCE_NOT_FOUND\");", - "});", - "", - "// Params", - "pm.test(\"params.err should be \\\"NOT_FOUND\\\"\", () => {", - " pm.expect(jsonResponse.params.err).to.eql(\"NOT_FOUND\");", - "});", - "pm.test(\"params.status should be \\\"failed\\\"\", () => {", - " pm.expect(jsonResponse.params.status).to.eql(\"failed\");", - "});", - "pm.test(`params.errmsg should be \\\"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: ${pm.globals.get('invalid_content_id')}\\\"`, () => {", - " pm.expect(jsonResponse.params.errmsg).to.eql(`Error! Node(s) doesn't Exists. | [Invalid Node Id.]: ${pm.globals.get('invalid_content_id')}`);", + " pm.response.to.have.status(404);", "});", "", - "// Result", - "pm.test(\"messages should be null\", () => {", - " pm.expect(jsonResponse.result.messages).to.eql(null);", - "});", - "" + "pm.test(\"Response throws RESOURCE_NOT_FOUND\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.responseCode).equals(\"RESOURCE_NOT_FOUND\")", + " ", + " const params = responseJson.params;", + " pm.expect(params.status).equals(\"failed\")", + " pm.expect(params.err).equals(\"NOT_FOUND\") ", + "});" ], "type": "text/javascript" } @@ -1909,116 +7996,72 @@ "listen": "prerequest", "script": { "exec": [ - "pm.globals.set('invalid_content_id', 'do_42353245436534');" + "setTimeout(function(){}, 1000);" ], "type": "text/javascript" } } ], "request": { - "method": "GET", + "method": "POST", "header": [ { - "key": "X-Authenticated-User-token", - "value": "{{keycloak_access_token}}", - "type": "text" + "key": "X-Channel-ID", + "value": "{{channel_id}}" + }, + { + "key": "Content-Type", + "value": "application/json" }, { "key": "Authorization", - "value": "{{kong_api_key}}", - "type": "text" + "value": "{{kong_api_key}}" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}" } ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": [{\n \"identifier\": [\"{{collection_id}}\"],\n \"dialcode\": [\"{{new_dialcode}}\"]\n }]\n }\n}" + }, "url": { - "raw": "{{host}}/api/content/v2/read/{{invalid_content_id}}", + "raw": "{{host}}/api/collection/v1/dialcode/link/do_dummy_id", "host": [ "{{host}}" ], "path": [ "api", - "content", - "v2", - "read", - "{{invalid_content_id}}" + "collection", + "v1", + "dialcode", + "link", + "do_dummy_id" ] - }, - "description": "Fetch a particular Batch\nThis API is associated with fetching a particular batch on the Sunbird Platform.\nThe endpoint for Fetch a particular Batch is /batch/read/{Batch_ID}\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + } }, "response": [] - } - ], - "event": [ - { - "listen": "prerequest", - "script": { - "type": "text/javascript", - "exec": [ - "" - ] - } }, { - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "" - ] - } - } - ] - }, - { - "name": "Content Update", - "item": [ - { - "name": "400 - Invalid versionKey", + "name": "Link DIAL code with invalid collection id in the request body", "event": [ { "listen": "test", "script": { "exec": [ - "let jsonResponse = pm.response.json()", - "", - "//Verification", - "pm.test(\"Status code is 400\", () => {", - " pm.response.to.have.status(400)", - "});", - "//code name contains a string", - "tests[\"Status code name has string Bad Request\"] = responseCode.name.has(\"Bad Request\");", - "", - "pm.test(\"Content-Encoding header should not present\", () => {", - " pm.response.to.not.have.header(\"Content-Encoding\");", - "});", - "", - "pm.test(\"Verify keys and data type of the response result\", () => {", - " pm.expect(jsonResponse.result).to.be.an(\"object\");", - " pm.expect(jsonResponse.result).to.have.all.keys('messages');", - " pm.expect(jsonResponse.result.messages).to.be.null;", - "});", - "", - "//Validation", - "// Response body", - "pm.test(\"responseCode should be CLIENT_ERROR\", () => {", - " pm.expect(jsonResponse.responseCode).to.eql(\"CLIENT_ERROR\");", - "});", - "", - "// Params", - "pm.test(\"params.err should be CLIENT_ERROR\", () => {", - " pm.expect(jsonResponse.params.err).to.eql(\"CLIENT_ERROR\");", - "});", - "pm.test(\"params.status should be failed\", () => {", - " pm.expect(jsonResponse.params.status).to.eql(\"failed\");", - "});", - "pm.test(\"params.errmsg should be \\\"Invalid version Key\\\"\", () => {", - " pm.expect(jsonResponse.params.errmsg).to.eql(\"Invalid version Key\");", + "pm.test(\"Status code is 404\", function () {", + " pm.response.to.have.status(404);", "});", "", - "// Result", - "pm.test(\"messages should be null\", () => {", - " pm.expect(jsonResponse.result.messages).to.eql(null);", - "});", - "" + "pm.test(\"Response throws RESOURCE_NOT_FOUND\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.responseCode).equals(\"RESOURCE_NOT_FOUND\")", + " ", + " const params = responseJson.params;", + " pm.expect(params.status).equals(\"failed\")", + " pm.expect(params.err).equals(\"ERR_DIALCODE_LINK\") ", + "});" ], "type": "text/javascript" } @@ -2027,369 +8070,5281 @@ "listen": "prerequest", "script": { "exec": [ - "pm.globals.set('invalid-versionKey', '3534534543');", - "// pm.globals.set(\"content_id\", '');" + "setTimeout(function(){}, 1000);" ], "type": "text/javascript" } } ], "request": { - "method": "PATCH", + "method": "POST", "header": [ { - "key": "Content-Type", - "value": "application/json", - "type": "text" + "key": "X-Channel-ID", + "value": "{{channel_id}}" }, { - "key": "X-Authenticated-User-token", - "value": "{{keycloak_access_token}}", - "type": "text" + "key": "Content-Type", + "value": "application/json" }, { "key": "Authorization", - "value": "{{kong_api_key}}", - "type": "text" + "value": "{{kong_api_key}}" }, { - "key": "X-Channel-Id", - "value": "{{channel_id}}", - "type": "text" + "key": "x-authenticated-user-token", + "value": "{{user-token}}" } ], "body": { "mode": "raw", - "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"{{invalid-versionKey}}\",\n \"lastUpdatedBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\",\n \"description\": \"dsd\",\n \"name\": \"API DOCUMENTATION CONTENT\"\n }\n }\n}" + "raw": "{\n \"request\": {\n \"content\": [{\n \"identifier\": [\"do_dummy_Id\"],\n \"dialcode\": [\"{{new_dialcode}}\"]\n }]\n }\n}" }, "url": { - "raw": "{{host}}/api/content/v2/update/{{content_id}}", + "raw": "{{host}}/api/collection/v1/dialcode/link/{{collection_id}}", "host": [ "{{host}}" - ], - "path": [ - "api", - "content", - "v2", - "update", - "{{content_id}}" + ], + "path": [ + "api", + "collection", + "v1", + "dialcode", + "link", + "{{collection_id}}" ] - }, - "description": "This API is associated with updating a batch on the Sunbird Platform.\nThe endpoint for Course Batch Update is /batch/update\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + } }, "response": [] }, { - "name": "400 - Empty name", + "name": "Link DIAL code with invalid DIAL code in the request body", "event": [ { "listen": "test", "script": { "exec": [ - "let jsonResponse = pm.response.json()", - "", - "//Verification", - "pm.test(\"Status code is 400\", () => {", - " pm.response.to.have.status(400)", - "});", - "//code name contains a string", - "tests[\"Status code name has string \\\"Bad Request\\\"\"] = responseCode.name.has(\"Bad Request\");", - "", - "pm.test(\"Verify keys and data type of the response result\", () => {", - " pm.expect(jsonResponse.result).to.be.an(\"object\");", - " pm.expect(jsonResponse.result).to.have.all.keys('messages');", - " pm.expect(jsonResponse.result.messages).to.not.be.empty;", - "});", - "", - "//Validation", - "// Response body", - "pm.test(\"responseCode should be \\\"CLIENT_ERROR\\\"\", () => {", - " pm.expect(jsonResponse.responseCode).to.eql(\"CLIENT_ERROR\");", - "});", - "", - "// Params", - "pm.test(\"params.err should be \\\"CLIENT_ERROR\\\"\", () => {", - " pm.expect(jsonResponse.params.err).to.eql(\"CLIENT_ERROR\");", - "});", - "pm.test(\"params.errmsg should be \\\"Validation Errors\\\"\", () => {", - " pm.expect(jsonResponse.params.errmsg).to.eql(\"Validation Errors\");", + "pm.test(\"Status code is 404\", function () {", + " pm.response.to.have.status(404);", "});", "", - "// Result", - "pm.test(\"result body should include messages \\\"Required Metadata name not set\\\"\", () => {", - " pm.expect(jsonResponse.result.messages).to.have.length(1)", - " pm.expect(jsonResponse.result.messages).to.include('Required Metadata name not set');", - "});", - "" + "pm.test(\"Response throws RESOURCE_NOT_FOUND\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.responseCode).equals(\"RESOURCE_NOT_FOUND\")", + " ", + " const params = responseJson.params;", + " pm.expect(params.status).equals(\"failed\")", + " pm.expect(params.err).equals(\"ERR_DIALCODE_LINK\") ", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "setTimeout(function(){}, 1000);" ], "type": "text/javascript" } } ], "request": { - "method": "PATCH", + "method": "POST", "header": [ { - "key": "Content-Type", - "value": "application/json", - "type": "text" + "key": "X-Channel-ID", + "value": "{{channel_id}}" }, { - "key": "X-Authenticated-User-token", - "value": "{{keycloak_access_token}}", - "type": "text" + "key": "Content-Type", + "value": "application/json" }, { "key": "Authorization", - "value": "{{kong_api_key}}", - "type": "text" + "value": "{{kong_api_key}}" }, { - "key": "X-Channel-Id", - "value": "{{channel_id}}", - "type": "text" + "key": "x-authenticated-user-token", + "value": "{{user-token}}" } ], "body": { "mode": "raw", - "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"{{versionKey}}\",\n \"description\": \"dsd\",\n \"name\": \"\"\n }\n }\n}" + "raw": "{\n \"request\": {\n \"content\": [{\n \"identifier\": [\"{{collection_id}}\"],\n \"dialcode\": [\"DUMMY\"]\n }]\n }\n}" }, "url": { - "raw": "{{host}}/api/content/v2/update/{{content_id}}", + "raw": "{{host}}/api/collection/v1/dialcode/link/{{collection_id}}", "host": [ "{{host}}" ], "path": [ "api", - "content", - "v2", - "update", - "{{content_id}}" + "collection", + "v1", + "dialcode", + "link", + "{{collection_id}}" ] - }, - "description": "This API is associated with updating a batch on the Sunbird Platform.\nThe endpoint for Course Batch Update is /batch/update\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + } }, "response": [] - }, + } + ] + }, + { + "name": "Content Publish API", + "item": [ { - "name": "404 - Invalid content id", - "event": [ + "name": "Pre-requisite", + "item": [ { - "listen": "test", - "script": { - "exec": [ - "let jsonResponse = pm.response.json()", - "", - "//Verification", - "pm.test(\"Status code is 404\", () => {", - " pm.response.to.have.status(404)", - "});", - "//code name contains a string", - "tests[\"Status code name has string \\\"Not Found\\\"\"] = responseCode.name.has(\"Not Found\");", - "", - "pm.test(\"Verify keys and data type of the response result\", () => {", - " pm.expect(jsonResponse.result).to.be.an(\"object\");", - " pm.expect(jsonResponse.result).to.have.all.keys('messages');", - " pm.expect(jsonResponse.result.messages).to.be.null;", - "});", - "", - "//Validation", - "// Response body", - "pm.test(\"responseCode should be RESOURCE_NOT_FOUND\", () => {", - " pm.expect(jsonResponse.responseCode).to.eql(\"RESOURCE_NOT_FOUND\");", - "});", - "", - "// Params", - "pm.test(\"params.err should be NOT_FOUND\", () => {", - " pm.expect(jsonResponse.params.err).to.eql(\"NOT_FOUND\");", - "});", - "pm.test(`params.errmsg should be \\\"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: ${pm.globals.get(\"invalid_content_id\")}\\\"`, () => {", - " pm.expect(jsonResponse.params.errmsg).to.eql(`Error! Node(s) doesn't Exists. | [Invalid Node Id.]: ${pm.globals.get(\"invalid_content_id\")}`);", - "});", - "", - "// Result", - "pm.test(\"messages should be null\", () => {", - " pm.expect(jsonResponse.result.messages).to.eql(null);", - "});", - "" + "name": "Collection Create wo hierarchy", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.environment.set(\"collection_id_wo_hierarchy\", jsonResponse.result.identifier);", + "pm.globals.set(\"collection_id_wo_hierarchy\", jsonResponse.result.identifier);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Sample Collection\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/vnd.ekstep.content-collection\",\n \"primaryCategory\": \"Digital Textbook\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] + }, + { + "name": "PDF Content wo artifactUrl", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.globals.set(\"pdf_content_id_wo_artifact\", jsonResponse.result.identifier);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] + }, + { + "name": "MP4 Content Create wo artifactUrl", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.globals.set(\"mp4_content_id_wo_artifact\", jsonResponse.result.identifier);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"video/mp4\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] + }, + { + "name": "WEBM Content Create wo artifactUrl", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.globals.set(\"webm_content_id_wo_artifact\", jsonResponse.result.identifier);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"video/webm\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] + }, + { + "name": "HTML Content Create wo artifactUrl", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.globals.set(\"html_content_id_wo_artifact\", jsonResponse.result.identifier);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/vnd.ekstep.html-archive\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] + }, + { + "name": "H5P Content Create wo artifactUrl", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.globals.set(\"h5p_content_id_wo_artifact\", jsonResponse.result.identifier);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/vnd.ekstep.h5p-archive\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] + }, + { + "name": "ECML Content Create invalid body", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.globals.set(\"ecml_content_id_invalid_body\", jsonResponse.result.identifier);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/vnd.ekstep.ecml-archive\",\n \"primaryCategory\": \"Explanation Content\",\n \"body\": \"dummy\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] + }, + { + "name": "ECML Content Create wo body", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.globals.set(\"ecml_content_id_wo_body\", jsonResponse.result.identifier);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/vnd.ekstep.ecml-archive\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] + }, + { + "name": "EPUB Content Create wo artifactUrl", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.globals.set(\"epub_content_id_wo_artifact\", jsonResponse.result.identifier);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/epub\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] + }, + { + "name": "Youtube Content Create wo artifactUrl", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.globals.set(\"yt_content_id_wo_artifact\", jsonResponse.result.identifier);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"video/x-youtube\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] + }, + { + "name": "Plugin Create wo artifact", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.globals.set(\"plugin_id_wo_artifact\", jsonResponse.result.identifier);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"test-plugin\",\n \"mimeType\": \"application/vnd.ekstep.plugin-archive\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [ + { + "name": "Content Create - 200 OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:42:53 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999995" + }, + { + "key": "request-time", + "value": "34" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "36" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "40" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "41" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:42:53ZZ\",\n \"params\": {\n \"resmsgid\": \"a06bcfc7-23b6-494a-8a63-bbdee8c6e373\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_21358408067678208014\",\n \"node_id\": \"do_21358408067678208014\",\n \"versionKey\": \"1658212973248\"\n }\n}" + }, + { + "name": "Content Create - 400 Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"Explanation Content\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:28:26 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Content-Length", + "value": "297" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999998" + }, + { + "key": "request-time", + "value": "11" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "13" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "18" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "125" + }, + { + "key": "Via", + "value": "kong/0.14.1" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:28:26ZZ\",\n \"params\": {\n \"resmsgid\": \"7efe066b-39e0-4f03-a569-0e9f19e8822b\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "Content Create - 404 Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Untitled Content\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/pdf\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/create", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "create" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Tue, 19 Jul 2022 06:33:43 GMT" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Transfer-Encoding", + "value": "chunked" + }, + { + "key": "Connection", + "value": "keep-alive" + }, + { + "key": "Vary", + "value": "Accept-Encoding" + }, + { + "key": "X-RateLimit-Limit-hour", + "value": "1000000" + }, + { + "key": "X-RateLimit-Remaining-hour", + "value": "999997" + }, + { + "key": "request-time", + "value": "16" + }, + { + "key": "x-envoy-upstream-service-time", + "value": "18" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "X-Kong-Upstream-Latency", + "value": "23" + }, + { + "key": "X-Kong-Proxy-Latency", + "value": "115" + }, + { + "key": "Via", + "value": "kong/0.14.1" + }, + { + "key": "Content-Encoding", + "value": "gzip" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-07-19T06:33:43ZZ\",\n \"params\": {\n \"resmsgid\": \"78745a2e-004b-4422-99e4-e2494387b3d4\",\n \"msgid\": null,\n \"err\": \"404\",\n \"status\": \"failed\",\n \"errmsg\": \"Entry is not found in cassandra for content with identifier: obj-cat:abcd_content_all {}\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + } + ] + } + ] + }, + { + "name": "Test Cases", + "item": [ + { + "name": "Invalid Content ID Publish", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 404\", function () {", + " pm.response.to.have.status(404);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } ], - "type": "text/javascript" - } + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/publish/do_dummy_id", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "publish", + "do_dummy_id" + ] + } + }, + "response": [] }, { - "listen": "prerequest", - "script": { - "exec": [ - "pm.globals.set(\"invalid_content_id\", 'do_21357909972698726412211');" + "name": "Invalid Collection ID Publish", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 404\", function () {", + " pm.response.to.have.status(404);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PATCH", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/collection/v1/publish/do_dummy_id", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "collection", + "v1", + "publish", + "do_dummy_id" + ] + } }, - { - "key": "X-Authenticated-User-token", - "value": "{{keycloak_access_token}}", - "type": "text" + "response": [] + }, + { + "name": "PDF Content Publish wo artifactUrl", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 400\", function () {", + " pm.response.to.have.status(400);", + "});", + "", + "pm.test(\"Response throws CLIENT_ERROR\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.responseCode).equals(\"CLIENT_ERROR\")", + " ", + " const params = responseJson.params;", + " pm.expect(params.status).equals(\"failed\")", + " pm.expect(params.err).equals(\"VALIDATOR_ERROR\") ", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/publish/{{pdf_content_id_wo_artifact}}", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "publish", + "{{pdf_content_id_wo_artifact}}" + ] + } }, - { - "key": "Authorization", - "value": "{{kong_api_key}}", - "type": "text" + "response": [] + }, + { + "name": "MP4 Content Publish wo artifactUrl", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 400\", function () {", + " pm.response.to.have.status(400);", + "});", + "", + "pm.test(\"Response throws CLIENT_ERROR\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.responseCode).equals(\"CLIENT_ERROR\")", + " ", + " const params = responseJson.params;", + " pm.expect(params.status).equals(\"failed\")", + " pm.expect(params.err).equals(\"VALIDATOR_ERROR\") ", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/publish/{{mp4_content_id_wo_artifact}}", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "publish", + "{{mp4_content_id_wo_artifact}}" + ] + } }, - { - "key": "X-Channel-Id", - "value": "{{channel_id}}", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"{{versionKey}}\",\n \"lastUpdatedBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\",\n \"description\": \"dsd\",\n \"name\": \"API DOCUMENTATION CONTENT\"\n }\n }\n}" + "response": [] }, - "url": { - "raw": "{{host}}/api/content/v2/update/{{invalid_content_id}}", - "host": [ - "{{host}}" + { + "name": "WEBM Content Publish wo artifactUrl", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 400\", function () {", + " pm.response.to.have.status(400);", + "});", + "", + "pm.test(\"Response throws CLIENT_ERROR\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.responseCode).equals(\"CLIENT_ERROR\")", + " ", + " const params = responseJson.params;", + " pm.expect(params.status).equals(\"failed\")", + " pm.expect(params.err).equals(\"VALIDATOR_ERROR\") ", + "});" + ], + "type": "text/javascript" + } + } ], - "path": [ - "api", - "content", - "v2", - "update", - "{{invalid_content_id}}" - ] + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/publish/{{webm_content_id_wo_artifact}}", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "publish", + "{{webm_content_id_wo_artifact}}" + ] + } + }, + "response": [] }, - "description": "This API is associated with updating a batch on the Sunbird Platform.\nThe endpoint for Course Batch Update is /batch/update\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." - }, - "response": [] - }, - { - "name": "404 - Invalid primaryCategory", - "event": [ { - "listen": "test", - "script": { - "exec": [ - "let jsonResponse = pm.response.json()", - "", - "//Verification", - "pm.test(\"Status code is 404\", () => {", - " pm.response.to.have.status(404)", - "});", - "//code name contains a string", - "tests[\"Status code name has string \\\"Not Found\\\"\"] = responseCode.name.has(\"Not Found\");", - "", - "pm.test(\"Verify keys and data type of the response result\", () => {", - " pm.expect(jsonResponse.result).to.be.an(\"object\");", - " pm.expect(jsonResponse.result).to.have.all.keys('messages');", - " pm.expect(jsonResponse.result.messages).to.be.null;", - "});", - "", - "//Validation", - "// Response body", - "pm.test(\"responseCode should be RESOURCE_NOT_FOUND\", () => {", - " pm.expect(jsonResponse.responseCode).to.eql(\"RESOURCE_NOT_FOUND\");", - "});", - "", - "// Params", - "pm.test(\"params.err should be \\\"404\\\"\", () => {", - " pm.expect(jsonResponse.params.err).to.eql(\"404\");", - "});", - "pm.test(\"params.status should be \\\"failed\\\"\", () => {", - " pm.expect(jsonResponse.params.status).to.eql(\"failed\");", - "});", - "pm.test(`params.errmsg should be \\\"Entry is not found in cassandra for content with identifier: obj-cat:${JSON.parse(request.data).request.content.primaryCategory}_content_all {}\\\"`, () => {", - " pm.expect(jsonResponse.params.errmsg).to.eql(`Entry is not found in cassandra for content with identifier: obj-cat:${JSON.parse(request.data).request.content.primaryCategory}_content_all {}`);", - "});", - "", - "pm.test(\"messages should be null\", () => {", - " pm.expect(jsonResponse.result.messages).to.eql(null);", - "});", - "" + "name": "HTML Content Publish wo artifactUrl", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 400\", function () {", + " pm.response.to.have.status(400);", + "});", + "", + "pm.test(\"Response throws CLIENT_ERROR\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.responseCode).equals(\"CLIENT_ERROR\")", + " ", + " const params = responseJson.params;", + " pm.expect(params.status).equals(\"failed\")", + " pm.expect(params.err).equals(\"VALIDATOR_ERROR\") ", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PATCH", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/publish/{{html_content_id_wo_artifact}}", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "publish", + "{{html_content_id_wo_artifact}}" + ] + } }, - { - "key": "X-Authenticated-User-token", - "value": "{{keycloak_access_token}}", - "type": "text" + "response": [] + }, + { + "name": "H5P Content Publish wo artifactUrl", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 400\", function () {", + " pm.response.to.have.status(400);", + "});", + "", + "pm.test(\"Response throws CLIENT_ERROR\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.responseCode).equals(\"CLIENT_ERROR\")", + " ", + " const params = responseJson.params;", + " pm.expect(params.status).equals(\"failed\")", + " pm.expect(params.err).equals(\"VALIDATOR_ERROR\") ", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/publish/{{h5p_content_id_wo_artifact}}", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "publish", + "{{h5p_content_id_wo_artifact}}" + ] + } }, - { - "key": "Authorization", - "value": "{{kong_api_key}}", - "type": "text" + "response": [] + }, + { + "name": "EPUB Content Publish wo artifactUrl", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 400\", function () {", + " pm.response.to.have.status(400);", + "});", + "", + "pm.test(\"Response throws CLIENT_ERROR\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.responseCode).equals(\"CLIENT_ERROR\")", + " ", + " const params = responseJson.params;", + " pm.expect(params.status).equals(\"failed\")", + " pm.expect(params.err).equals(\"VALIDATOR_ERROR\") ", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/publish/{{epub_content_id_wo_artifact}}", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "publish", + "{{epub_content_id_wo_artifact}}" + ] + } }, - { - "key": "X-Channel-Id", - "value": "{{channel_id}}", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"{{versionKey}}\",\n \"description\": \"dsd\",\n \"name\": \"API DOCUMENTATION CONTENT\",\n \"primaryCategory\": \"abcd\"\n }\n }\n}" + "response": [] }, - "url": { - "raw": "{{host}}/api/content/v2/update/{{content_id}}", - "host": [ - "{{host}}" + { + "name": "Youtube Content Publish wo artifactUrl", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 400\", function () {", + " pm.response.to.have.status(400);", + "});", + "", + "pm.test(\"Response throws CLIENT_ERROR\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.responseCode).equals(\"CLIENT_ERROR\")", + " ", + " const params = responseJson.params;", + " pm.expect(params.status).equals(\"failed\")", + " pm.expect(params.err).equals(\"VALIDATOR_ERROR\") ", + "});" + ], + "type": "text/javascript" + } + } ], - "path": [ - "api", - "content", - "v2", - "update", - "{{content_id}}" - ] + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/publish/{{yt_content_id_wo_artifact}}", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "publish", + "{{yt_content_id_wo_artifact}}" + ] + } + }, + "response": [] }, - "description": "This API is associated with updating a batch on the Sunbird Platform.\nThe endpoint for Course Batch Update is /batch/update\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." - }, - "response": [] - } - ], - "event": [ - { - "listen": "prerequest", - "script": { - "type": "text/javascript", - "exec": [ - "// pm.globals.set(\"content_id\", '');" - ] - } - }, - { - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "let jsonResponse = pm.response.json();", - "", - "pm.test(\"id should be api.content.update\", () => {", - " pm.expect(jsonResponse.id).to.eql(\"api.content.update\");", - "});", - "pm.test(\"ver should be 4.0\", () => {", - " pm.expect(jsonResponse.ver).to.eql(\"4.0\");", - "});" - ] - } + { + "name": "Plugin Publish wo artifactUrl", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 400\", function () {", + " pm.response.to.have.status(400);", + "});", + "", + "pm.test(\"Response throws CLIENT_ERROR\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.responseCode).equals(\"CLIENT_ERROR\")", + " ", + " const params = responseJson.params;", + " pm.expect(params.status).equals(\"failed\")", + " pm.expect(params.err).equals(\"VALIDATOR_ERROR\") ", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/publish/{{plugin_id_wo_artifact}}", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "publish", + "{{plugin_id_wo_artifact}}" + ] + } + }, + "response": [] + }, + { + "name": "Collection Publish without hierarchy", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 400\", function () {", + " pm.response.to.have.status(400);", + "});", + "", + "pm.test(\"Response throws ERR_COLLECTION_REVIEW\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.responseCode).equals(\"CLIENT_ERROR\")", + " ", + " const params = responseJson.params;", + " pm.expect(params.status).equals(\"failed\")", + " pm.expect(params.err).equals(\"ERR_COLLECTION_REVIEW\") ", + "});", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "setTimeout(function(){}, 40000);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/collection/v1/publish/{{collection_id_wo_hierarchy}}", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "collection", + "v1", + "publish", + "{{collection_id_wo_hierarchy}}" + ] + } + }, + "response": [] + }, + { + "name": "ECML Content Publish wo artifactUrl", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 400\", function () {", + " pm.response.to.have.status(400);", + "});", + "", + "pm.test(\"Response throws CLIENT_ERROR\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.responseCode).equals(\"CLIENT_ERROR\")", + " ", + " const params = responseJson.params;", + " pm.expect(params.status).equals(\"failed\")", + " pm.expect(params.err).equals(\"VALIDATOR_ERROR\") ", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "setTimeout(function(){}, 2000);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/publish/{{ecml_content_id_wo_body}}", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "publish", + "{{ecml_content_id_wo_body}}" + ] + } + }, + "response": [] + }, + { + "name": "ECML Content Publish invalid Body", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 400\", function () {", + " pm.response.to.have.status(400);", + "});", + "", + "pm.test(\"Response throws CLIENT_ERROR\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.responseCode).equals(\"CLIENT_ERROR\")", + " ", + " const params = responseJson.params;", + " pm.expect(params.status).equals(\"failed\")", + " pm.expect(params.err).equals(\"INVALID_CONTENT_BODY\") ", + "});", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "setTimeout(function(){}, 1000);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "type": "text" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}", + "type": "text" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Testing\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/api/content/v2/publish/{{ecml_content_id_invalid_body}}", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "content", + "v2", + "publish", + "{{ecml_content_id_invalid_body}}" + ] + } + }, + "response": [] + } + ] } ] } From 920b6024212832dbb03aa94fe3fcb87c917ed0ff Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Wed, 24 Aug 2022 12:08:52 +0530 Subject: [PATCH 114/490] Issue #KN-9 feat: Content Publish API refactor. --- ...ontent V2 API Test.postman_collection.json | 79 ++++++++++--------- .../Publish API.postman_collection.json | 48 +++++------ 2 files changed, 66 insertions(+), 61 deletions(-) diff --git a/content-api/api-tests/Collections/Content V2 API Test.postman_collection.json b/content-api/api-tests/Collections/Content V2 API Test.postman_collection.json index 4a0373bd6..35981e978 100644 --- a/content-api/api-tests/Collections/Content V2 API Test.postman_collection.json +++ b/content-api/api-tests/Collections/Content V2 API Test.postman_collection.json @@ -3,8 +3,7 @@ "_postman_id": "ec007894-70ee-4951-8e96-98f79de5908a", "name": "Content V2 API Test", "description": "The Content Management APIs allow you to create, manage and process content on the Sunbird platform. Content is the basic building block of the Knowledge Service. Some examples would be Videos, Audio, Pdfs, Html, ECML etc. The basic operations for this API include Create, Update, Read, Review, Publish.\n\nThe URL for Content Management API(s) is /content/v1.", - "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", - "_exporter_id": "1419050" + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" }, "item": [ { @@ -1362,7 +1361,8 @@ }, "response": [] } - ] + ], + "description": "The Content/Collection Reserve DIAL code APIs allow you to reserve DIAL codes to a content/collection on the Sunbird platform. Reserve DAIL code API was previously available as part of learning-service which has now been refactored into content-service.\n\nThe URL for Content Management API(s) is /content/v2/dialcode/reserve and /collection/v1/dialcode/reserve." }, { "name": "Content Link DIAL code", @@ -1408,7 +1408,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}" + "value": "{{keycloak_access_token}}" } ], "body": { @@ -1473,7 +1473,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}" + "value": "{{keycloak_access_token}}" } ], "body": { @@ -1497,7 +1497,8 @@ }, "response": [] } - ] + ], + "description": "The Content/Collection Link DIAL code APIs allow you to link DIAL codes to content/collection/collection units on the Sunbird platform. Reserve DAIL code API was previously available as part of learning-service which has now been refactored into content-service.\n\nThe URL for Content Management API(s) is /content/v2/dialcode/link and /collection/v1/dialcode/link." }, { "name": "Content Publish API", @@ -1904,7 +1905,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}" + "value": "{{keycloak_access_token}}" } ], "body": { @@ -5450,7 +5451,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -5508,7 +5509,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -5566,7 +5567,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -5624,7 +5625,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -5682,7 +5683,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -5740,7 +5741,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -5798,7 +5799,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -5856,7 +5857,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -5914,7 +5915,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -5972,7 +5973,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -6003,7 +6004,8 @@ } ] } - ] + ], + "description": "The Content/Collection Publish APIs allow you to publish content/collection on the Sunbird platform. Publish API was previously available as part of learning-service which has now been refactored into content-service.\n\nThe URL for Content Management API(s) is /content/v2/publish and /collection/v1/publish." } ], "event": [ @@ -7964,7 +7966,8 @@ }, "response": [] } - ] + ], + "description": "The Content/Collection Reserve DIAL code APIs allow you to reserve DIAL codes to a content/collection on the Sunbird platform. Reserve DAIL code API was previously available as part of learning-service which has now been refactored into content-service.\n\nThe URL for Content Management API(s) is /content/v2/dialcode/reserve and /collection/v1/dialcode/reserve." }, { "name": "Content Link DIAL code", @@ -8019,7 +8022,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}" + "value": "{{keycloak_access_token}}" } ], "body": { @@ -8093,7 +8096,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}" + "value": "{{keycloak_access_token}}" } ], "body": { @@ -8167,7 +8170,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}" + "value": "{{keycloak_access_token}}" } ], "body": { @@ -8191,7 +8194,8 @@ }, "response": [] } - ] + ], + "description": "The Content/Collection Link DIAL code APIs allow you to link DIAL codes to content/collection/collection units on the Sunbird platform. Reserve DAIL code API was previously available as part of learning-service which has now been refactored into content-service.\n\nThe URL for Content Management API(s) is /content/v2/dialcode/link and /collection/v1/dialcode/link" }, { "name": "Content Publish API", @@ -12491,7 +12495,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -12549,7 +12553,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -12616,7 +12620,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -12683,7 +12687,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -12750,7 +12754,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -12817,7 +12821,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -12884,7 +12888,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -12951,7 +12955,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -13018,7 +13022,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -13085,7 +13089,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -13162,7 +13166,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -13238,7 +13242,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -13315,7 +13319,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -13346,7 +13350,8 @@ } ] } - ] + ], + "description": "The Content/Collection Publish APIs allow you to publish content/collection on the Sunbird platform. Publish API was previously available as part of learning-service which has now been refactored into content-service.\n\nThe URL for Content Management API(s) is /content/v2/publish and /collection/v1/publish." } ], "event": [ diff --git a/content-api/api-tests/Collections/Publish API.postman_collection.json b/content-api/api-tests/Collections/Publish API.postman_collection.json index 75a526868..d060dd489 100644 --- a/content-api/api-tests/Collections/Publish API.postman_collection.json +++ b/content-api/api-tests/Collections/Publish API.postman_collection.json @@ -2,7 +2,7 @@ "info": { "_postman_id": "607f186e-f5e0-4d51-a4c6-02c534d1689c", "name": "Publish API", - "description": "The Content/Collection Publish APIs allow you to publish content/collection on the Sunbird platform. Publish API was previously available as part of learning-service which has now been refactored into content-service.\n\nThe URL for Content Management API(s) is /content/v2/publish and /collection/v2/publish.", + "description": "The Content/Collection Publish APIs allow you to publish content/collection on the Sunbird platform. Publish API was previously available as part of learning-service which has now been refactored into content-service.\n\nThe URL for Content Management API(s) is /content/v2/publish and /collection/v1/publish.", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" }, "item": [ @@ -8193,7 +8193,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -8250,7 +8250,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -8307,7 +8307,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -8364,7 +8364,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -8421,7 +8421,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -8478,7 +8478,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -8535,7 +8535,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -8592,7 +8592,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -8649,7 +8649,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -8706,7 +8706,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -8822,7 +8822,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -8879,7 +8879,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -8945,7 +8945,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -9011,7 +9011,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -9077,7 +9077,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -9143,7 +9143,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -9209,7 +9209,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -9275,7 +9275,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -9341,7 +9341,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -9407,7 +9407,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -9483,7 +9483,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -9558,7 +9558,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { @@ -9634,7 +9634,7 @@ }, { "key": "x-authenticated-user-token", - "value": "{{user-token}}", + "value": "{{keycloak_access_token}}", "type": "text" }, { From a286b1991f1d083cb61d9cc2c744b4c06c026646 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Wed, 24 Aug 2022 12:15:37 +0530 Subject: [PATCH 115/490] Issue #KN-9 feat: Content Publish API refactor. --- .../Content V2 API Test.postman_collection.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/content-api/api-tests/Collections/Content V2 API Test.postman_collection.json b/content-api/api-tests/Collections/Content V2 API Test.postman_collection.json index 35981e978..81e9d54fb 100644 --- a/content-api/api-tests/Collections/Content V2 API Test.postman_collection.json +++ b/content-api/api-tests/Collections/Content V2 API Test.postman_collection.json @@ -1362,7 +1362,7 @@ "response": [] } ], - "description": "The Content/Collection Reserve DIAL code APIs allow you to reserve DIAL codes to a content/collection on the Sunbird platform. Reserve DAIL code API was previously available as part of learning-service which has now been refactored into content-service.\n\nThe URL for Content Management API(s) is /content/v2/dialcode/reserve and /collection/v1/dialcode/reserve." + "description": "The Content/Collection Reserve DIAL code APIs allow you to reserve DIAL codes to a content/collection on the Sunbird platform. Reserve DAIL code API was previously available as part of learning-service which has now been refactored into content-service.\n\nThe URLs for Content Reserve DIAL code API(s) are /content/v2/dialcode/reserve and /collection/v1/dialcode/reserve." }, { "name": "Content Link DIAL code", @@ -1498,7 +1498,7 @@ "response": [] } ], - "description": "The Content/Collection Link DIAL code APIs allow you to link DIAL codes to content/collection/collection units on the Sunbird platform. Reserve DAIL code API was previously available as part of learning-service which has now been refactored into content-service.\n\nThe URL for Content Management API(s) is /content/v2/dialcode/link and /collection/v1/dialcode/link." + "description": "The Content/Collection Link DIAL code APIs allow you to link DIAL codes to content/collection/collection units on the Sunbird platform. Reserve DAIL code API was previously available as part of learning-service which has now been refactored into content-service.\n\nThe URLs for Content Link DIAL code API(s) are /content/v2/dialcode/link and /collection/v1/dialcode/link." }, { "name": "Content Publish API", @@ -6005,7 +6005,7 @@ ] } ], - "description": "The Content/Collection Publish APIs allow you to publish content/collection on the Sunbird platform. Publish API was previously available as part of learning-service which has now been refactored into content-service.\n\nThe URL for Content Management API(s) is /content/v2/publish and /collection/v1/publish." + "description": "The Content/Collection Publish APIs allow you to publish content/collection on the Sunbird platform. Publish API was previously available as part of learning-service which has now been refactored into content-service.\n\nThe URLs for Content Publish API(s) are /content/v2/publish and /collection/v1/publish." } ], "event": [ @@ -7967,7 +7967,7 @@ "response": [] } ], - "description": "The Content/Collection Reserve DIAL code APIs allow you to reserve DIAL codes to a content/collection on the Sunbird platform. Reserve DAIL code API was previously available as part of learning-service which has now been refactored into content-service.\n\nThe URL for Content Management API(s) is /content/v2/dialcode/reserve and /collection/v1/dialcode/reserve." + "description": "The Content/Collection Reserve DIAL code APIs allow you to reserve DIAL codes to a content/collection on the Sunbird platform. Reserve DAIL code API was previously available as part of learning-service which has now been refactored into content-service.\n\nThe URLs for Content Reserve DIAL code API(s) are /content/v2/dialcode/reserve and /collction/v1/dialcode/reserve." }, { "name": "Content Link DIAL code", @@ -8195,7 +8195,7 @@ "response": [] } ], - "description": "The Content/Collection Link DIAL code APIs allow you to link DIAL codes to content/collection/collection units on the Sunbird platform. Reserve DAIL code API was previously available as part of learning-service which has now been refactored into content-service.\n\nThe URL for Content Management API(s) is /content/v2/dialcode/link and /collection/v1/dialcode/link" + "description": "The Content/Collection Link DIAL code APIs allow you to link DIAL codes to content/collection/collection units on the Sunbird platform. Reserve DAIL code API was previously available as part of learning-service which has now been refactored into content-service.\n\nThe URLs for Content Link DIAL code API(s) are /content/v2/dialcode/link and /collection/v1/dialcode/link" }, { "name": "Content Publish API", @@ -13351,7 +13351,7 @@ ] } ], - "description": "The Content/Collection Publish APIs allow you to publish content/collection on the Sunbird platform. Publish API was previously available as part of learning-service which has now been refactored into content-service.\n\nThe URL for Content Management API(s) is /content/v2/publish and /collection/v1/publish." + "description": "The Content/Collection Publish APIs allow you to publish content/collection on the Sunbird platform. Publish API was previously available as part of learning-service which has now been refactored into content-service.\n\nThe URLs for Content Publish API(s) are /content/v2/publish and /collection/v1/publish." } ], "event": [ From 809ac27f2ee5280e4589f25ae42e914d95d5391b Mon Sep 17 00:00:00 2001 From: AmiableAnil Date: Tue, 30 Aug 2022 11:09:25 +0530 Subject: [PATCH 116/490] Issue #KN-231 feat: Added the support for google-cloud. --- .../sunbird/content/actors/ContentActor.scala | 3 +- .../sunbird/content/util/CopyManager.scala | 2 +- platform-modules/mimetype-manager/pom.xml | 2 +- .../sunbird/cloudstore/StorageService.scala | 51 +++++++++---------- .../cloudstore/StorageServiceTest.scala | 4 +- 5 files changed, 30 insertions(+), 32 deletions(-) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/actors/ContentActor.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/actors/ContentActor.scala index fabc95d43..df9047ff8 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/actors/ContentActor.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/actors/ContentActor.scala @@ -144,6 +144,7 @@ class ContentActor @Inject() (implicit oec: OntologyEngineContext, ss: StorageSe def uploadPreSignedUrl(request: Request): Future[Response] = { val `type`: String = request.get("type").asInstanceOf[String].toLowerCase() val fileName: String = request.get("fileName").asInstanceOf[String] + val mimeType: String = request.get("mimeType").asInstanceOf[String] val filePath: String = request.getRequest.getOrDefault("filePath","").asInstanceOf[String] .replaceAll("^/+|/+$", "") val identifier: String = request.get("identifier").asInstanceOf[String] @@ -152,7 +153,7 @@ class ContentActor @Inject() (implicit oec: OntologyEngineContext, ss: StorageSe val objectKey = if (StringUtils.isEmpty(filePath)) "content" + File.separator + `type` + File.separator + identifier + File.separator + Slug.makeSlug(fileName, true) else filePath + File.separator + "content" + File.separator + `type` + File.separator + identifier + File.separator + Slug.makeSlug(fileName, true) val expiry = Platform.config.getString("cloud_storage.upload.url.ttl") - val preSignedURL = ss.getSignedURL(objectKey, Option.apply(expiry.toInt), Option.apply("w")) + val preSignedURL = ss.getSignedURL(objectKey, Option.apply(expiry.toInt), Option.apply("w"), Option.apply(mimeType)) ResponseHandler.OK().put("identifier", identifier).put("pre_signed_url", preSignedURL) .put("url_expiry", expiry) }) recoverWith { case e: CompletionException => throw e.getCause } diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/util/CopyManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/util/CopyManager.scala index cbf52207d..f1c2f86cc 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/util/CopyManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/util/CopyManager.scala @@ -222,7 +222,7 @@ object CopyManager { protected def getFileNameFromURL(fileUrl: String): String = if (!FilenameUtils.getExtension(fileUrl).isEmpty) FilenameUtils.getBaseName(fileUrl) + "_" + System.currentTimeMillis + "." + FilenameUtils.getExtension(fileUrl) else FilenameUtils.getBaseName(fileUrl) + "_" + System.currentTimeMillis - protected def isInternalUrl(url: String)(implicit ss: StorageService): Boolean = url.contains(ss.getContainerName()) + protected def isInternalUrl(url: String)(implicit ss: StorageService): Boolean = url.contains(ss.getContainerName) def prepareHierarchyRequest(originHierarchy: util.Map[String, AnyRef], originNode: Node, node: Node, copyType: String, request: Request):util.HashMap[String, AnyRef] = { val children:util.List[util.Map[String, AnyRef]] = originHierarchy.get("children").asInstanceOf[util.List[util.Map[String, AnyRef]]] diff --git a/platform-modules/mimetype-manager/pom.xml b/platform-modules/mimetype-manager/pom.xml index c97b9bbd2..80a17876b 100644 --- a/platform-modules/mimetype-manager/pom.xml +++ b/platform-modules/mimetype-manager/pom.xml @@ -30,7 +30,7 @@ org.sunbird cloud-store-sdk - 1.4.0 + 1.4.1 org.scala-lang diff --git a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala index 0045caf82..f03a79627 100644 --- a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala +++ b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala @@ -1,7 +1,6 @@ package org.sunbird.cloudstore import java.io.File - import org.apache.commons.lang3.StringUtils import org.sunbird.cloud.storage.BaseStorageService import org.sunbird.common.Platform @@ -10,7 +9,7 @@ import org.sunbird.cloud.storage.factory.StorageServiceFactory import org.sunbird.common.exception.ServerException import org.sunbird.common.Slug -import scala.concurrent.ExecutionContext +import scala.concurrent.{ExecutionContext, Future} class StorageService { @@ -18,20 +17,20 @@ class StorageService { var storageService: BaseStorageService = null @throws[Exception] - def getService(): BaseStorageService = { + def getService: BaseStorageService = { if (null == storageService) { if (StringUtils.equalsIgnoreCase(storageType, "azure")) { val storageKey = Platform.config.getString("azure_storage_key") val storageSecret = Platform.config.getString("azure_storage_secret") - storageService = StorageServiceFactory.getStorageService(new StorageConfig(storageType, storageKey, storageSecret)) + storageService = StorageServiceFactory.getStorageService(StorageConfig(storageType, storageKey, storageSecret)) } else if (StringUtils.equalsIgnoreCase(storageType, "aws")) { val storageKey = Platform.config.getString("aws_storage_key") val storageSecret = Platform.config.getString("aws_storage_secret") - storageService = StorageServiceFactory.getStorageService(new StorageConfig(storageType, storageKey, storageSecret)) + storageService = StorageServiceFactory.getStorageService(StorageConfig(storageType, storageKey, storageSecret)) } else if (StringUtils.equalsIgnoreCase(storageType, "gcloud")) { val storageKey = Platform.config.getString("gcloud_client_key") val storageSecret = Platform.config.getString("gcloud_private_secret") - storageService = StorageServiceFactory.getStorageService(new StorageConfig(storageType, storageKey, storageSecret)) + storageService = StorageServiceFactory.getStorageService(StorageConfig(storageType, storageKey, storageSecret)) } // else if (StringUtils.equalsIgnoreCase(storageType, "cephs3")) { // val storageKey = Platform.config.getString("cephs3_storage_key") @@ -44,17 +43,13 @@ class StorageService { storageService } - def getContainerName(): String = { - if (StringUtils.equalsIgnoreCase(storageType, "azure")) - Platform.config.getString("azure_storage_container") - else if (StringUtils.equalsIgnoreCase(storageType, "aws")) - Platform.config.getString("aws_storage_container") - else if (StringUtils.equalsIgnoreCase(storageType, "gcloud")) - Platform.config.getString("gcloud_storage_bucket") - else if (StringUtils.equalsIgnoreCase(storageType, "cephs3")) - Platform.config.getString("cephs3_storage_container") - else - throw new ServerException("ERR_INVALID_CLOUD_STORAGE", "Container name not configured.") + def getContainerName: String = { + storageType match { + case "azure" => Platform.config.getString("azure_storage_container") + case "aws" => Platform.config.getString("aws_storage_container") + case "gcloud" => Platform.config.getString("gcloud_storage_bucket") + case _ => throw new ServerException("ERR_INVALID_CLOUD_STORAGE", "Container name not configured.") + } } def uploadFile(folderName: String, file: File, slug: Option[Boolean] = Option(true)): Array[String] = { @@ -67,11 +62,11 @@ class StorageService { def uploadDirectory(folderName: String, directory: File, slug: Option[Boolean] = Option(true)): Array[String] = { val slugFile = if (slug.getOrElse(true)) Slug.createSlugFile(directory) else directory val objectKey = folderName + File.separator - val url = getService.upload(getContainerName(), slugFile.getAbsolutePath, objectKey, Option.apply(true), Option.apply(1), Option.apply(5), Option.empty) + val url = getService.upload(getContainerName, slugFile.getAbsolutePath, objectKey, Option.apply(true), Option.apply(1), Option.apply(5), Option.empty) Array[String](objectKey, url) } - def uploadDirectoryAsync(folderName: String, directory: File, slug: Option[Boolean] = Option(true))(implicit ec: ExecutionContext) = { + def uploadDirectoryAsync(folderName: String, directory: File, slug: Option[Boolean] = Option(true))(implicit ec: ExecutionContext): Future[List[String]] = { val slugFile = if (slug.getOrElse(true)) Slug.createSlugFile(directory) else directory val objectKey = folderName + File.separator getService.uploadFolder(getContainerName, slugFile.getAbsolutePath, objectKey, Option.apply(false), None, None, 1) @@ -82,26 +77,28 @@ class StorageService { blob.contentLength } - def copyObjectsByPrefix(source: String, destination: String) = { + def copyObjectsByPrefix(source: String, destination: String): Unit = { getService.copyObjects(getContainerName, source, getContainerName, destination, Option.apply(true)) } - def deleteFile(key: String, isDirectory: Option[Boolean] = Option(false)) = { + def deleteFile(key: String, isDirectory: Option[Boolean] = Option(false)): Unit = { getService.deleteObject(getContainerName, key, isDirectory) } - def getSignedURL(key: String, ttl: Option[Int], permission: Option[String]): String = { - getService().getSignedURL(getContainerName, key, ttl, permission) + def getSignedURL(key: String, ttl: Option[Int], permission: Option[String], contentType: Option[String] = None): String = { + storageType match { + case "gcloud" => getService.getPutSignedURL(getContainerName, key, ttl, permission, contentType) + case _ => getService.getSignedURL (getContainerName, key, ttl, permission) + } } def getUri(key: String): String = { try { getService.getUri(getContainerName, key, Option.apply(false)) } catch { - case e:Exception => { - println("StorageService --> getUri --> Exception: " + e.getMessage) - "" - } + case e:Exception => + println("StorageService --> getUri --> Exception: " + e.getMessage) + "" } } } diff --git a/platform-modules/mimetype-manager/src/test/scala/org/sunbird/cloudstore/StorageServiceTest.scala b/platform-modules/mimetype-manager/src/test/scala/org/sunbird/cloudstore/StorageServiceTest.scala index 7cd55d82e..fe0d078bb 100644 --- a/platform-modules/mimetype-manager/src/test/scala/org/sunbird/cloudstore/StorageServiceTest.scala +++ b/platform-modules/mimetype-manager/src/test/scala/org/sunbird/cloudstore/StorageServiceTest.scala @@ -6,12 +6,12 @@ class StorageServiceTest extends AsyncFlatSpec with Matchers { val ss = new StorageService "getService" should "return a Storage Service" in { - val service = ss.getService() + val service = ss.getService assert(service != null) } "getContainerName" should "return the container name" in { - val container = ss.getContainerName() + val container = ss.getContainerName assert(container == "sunbird-content-dev") } } From c9449b8eeee541cfe1faff230eac6e232b0525aa Mon Sep 17 00:00:00 2001 From: AmiableAnil Date: Tue, 30 Aug 2022 13:05:08 +0530 Subject: [PATCH 117/490] Issue #KN-231 test: Fixed the unit test. --- .../scala/org/sunbird/content/actors/TestContentActor.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content-api/content-actors/src/test/scala/org/sunbird/content/actors/TestContentActor.scala b/content-api/content-actors/src/test/scala/org/sunbird/content/actors/TestContentActor.scala index 40778e99f..52b413a43 100644 --- a/content-api/content-actors/src/test/scala/org/sunbird/content/actors/TestContentActor.scala +++ b/content-api/content-actors/src/test/scala/org/sunbird/content/actors/TestContentActor.scala @@ -93,8 +93,8 @@ class TestContentActor extends BaseSpec with MockFactory { val graphDB = mock[GraphService] (oec.graphService _).expects().returns(graphDB) (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, *, *, *).returns(Future(getValidNode())) - implicit val ss = mock[StorageService] - (ss.getSignedURL(_: String, _: Option[Int], _: Option[String])).expects(*, *, *).returns("cloud store url") + implicit val ss: StorageService = mock[StorageService] + (ss.getSignedURL(_: String, _: Option[Int], _: Option[String], _: Option[String])).expects(*, *, *, *).returns("cloud store url") val request = getContentRequest() request.getRequest.putAll(mapAsJavaMap(Map("fileName" -> "presigned_url", "filePath" -> "/data/cloudstore/", "type" -> "assets", "identifier" -> "do_1234"))) request.setOperation("uploadPreSignedUrl") From 9c4884d57e58f597dc34f34e3b337bcb129b82af Mon Sep 17 00:00:00 2001 From: AmiableAnil Date: Thu, 1 Sep 2022 16:55:33 +0530 Subject: [PATCH 118/490] Issue #KN-231 test: Improved unit test coverage. --- .../org/sunbird/cloudstore/StorageServiceTest.scala | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/platform-modules/mimetype-manager/src/test/scala/org/sunbird/cloudstore/StorageServiceTest.scala b/platform-modules/mimetype-manager/src/test/scala/org/sunbird/cloudstore/StorageServiceTest.scala index fe0d078bb..47ba320e1 100644 --- a/platform-modules/mimetype-manager/src/test/scala/org/sunbird/cloudstore/StorageServiceTest.scala +++ b/platform-modules/mimetype-manager/src/test/scala/org/sunbird/cloudstore/StorageServiceTest.scala @@ -2,6 +2,8 @@ package org.sunbird.cloudstore import org.scalatest.{AsyncFlatSpec, Matchers} +import java.io.File + class StorageServiceTest extends AsyncFlatSpec with Matchers { val ss = new StorageService @@ -14,4 +16,15 @@ class StorageServiceTest extends AsyncFlatSpec with Matchers { val container = ss.getContainerName assert(container == "sunbird-content-dev") } + + "getSignedURL" should "return the signed url" in { + val objectKey = "content" + File.separator + "asset" + File.separator + "do_53245" + File.separator + "abc.png" + val preSignedURL = ss.getSignedURL(objectKey, Option.apply(600), Option.apply("w"), Option.apply("")) + assert(preSignedURL.contains(objectKey)) + } + + "getUri" should "return the signed url" in { + val uri = ss.getUri("content/abc.json") + assert(uri != null) + } } From 29e64c511706385ecf999bd8d78b109c7279cf09 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Thu, 1 Sep 2022 17:28:21 +0530 Subject: [PATCH 119/490] Issue #KN-257 feat: Content Release DIAL codes API refactor. --- .../sunbird/content/actors/ContentActor.scala | 3 + .../org/sunbird/content/dial/DIALErrors.scala | 8 +- .../sunbird/content/dial/DIALManager.scala | 104 +++++++++++++++++- .../controllers/v3/ContentController.scala | 10 +- .../controllers/v4/CollectionController.scala | 12 +- .../controllers/v4/ContentController.scala | 12 +- .../content-service/app/utils/ApiId.scala | 2 + content-api/content-service/conf/routes | 4 +- 8 files changed, 144 insertions(+), 11 deletions(-) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/actors/ContentActor.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/actors/ContentActor.scala index e3925113f..f3f3d2636 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/actors/ContentActor.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/actors/ContentActor.scala @@ -50,6 +50,7 @@ class ContentActor @Inject() (implicit oec: OntologyEngineContext, ss: StorageSe case "acceptFlag" => acceptFlag(request) case "linkDIALCode" => linkDIALCode(request) case "reserveDialCode" => reserveDialCode(request) + case "releaseDialCode" => releaseDialCode(request) case "importContent" => importContent(request) case "systemUpdate" => systemUpdate(request) case "reviewContent" => reviewContent(request) @@ -179,6 +180,8 @@ class ContentActor @Inject() (implicit oec: OntologyEngineContext, ss: StorageSe def reserveDialCode(request: Request): Future[Response] = DIALManager.reserve(request) + def releaseDialCode(request: Request): Future[Response] = DIALManager.release(request) + def importContent(request: Request): Future[Response] = importMgr.importObject(request) def reviewContent(request: Request): Future[Response] = { diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALErrors.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALErrors.scala index 743cbe86a..3ecd0acdb 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALErrors.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALErrors.scala @@ -13,6 +13,9 @@ object DIALErrors { val ERR_CONTENT_INVALID_OBJECT: String = "ERR_CONTENT_INVALID_OBJECT" val ERR_DUPLICATE_DIAL_CODES: String = "ERR_DUPLICATE_DIAL_CODES" val ERR_DIALCODE_CONTENT_LINK_FIELDS_MISSING: String = "ERR_DIALCODE_CONTENT_LINK_FIELDS_MISSING" + val ERR_CONTENT_RETIRED_OBJECT_ID: String = "ERR_CONTENT_RETIRED_OBJECT_ID" + val ERR_CONTENT_MISSING_RESERVED_DIAL_CODES: String = "ERR_CONTENT_MISSING_RESERVED_DIAL_CODES" + val ERR_ALL_DIALCODES_UTILIZED: String = "ERR_ALL_DIALCODES_UTILIZED" //Error Messages val ERR_INVALID_REQ_MSG: String = "Invalid Request! Please Provide Valid Request." @@ -29,7 +32,10 @@ object DIALErrors { val ERR_DIAL_GEN_LIST_EMPTY_MSG: String = "Dialcode generated list is empty. Please Try Again After Sometime!" val ERR_DIAL_GENERATION_MSG: String = "Error During generate Dialcode. Please Try Again After Sometime!" val ERR_DIAL_INVALID_COUNT_RESPONSE = "No new DIAL Codes have been generated, as requested count is less or equal to existing reserved dialcode count." - val ERR_CONTENT_INVALID_OBJECT_MSG = "Invalid Request. Cannot update status of Live Node." + val ERR_CONTENT_INVALID_OBJECT_MSG = "Invalid Request. Cannot update 'Live' content." val ERR_DUPLICATE_DIAL_CODES_MSG: String = "QR Code should not be linked to multiple contents. Please validate: " val ERR_DIALCODE_CONTENT_LINK_FIELDS_MISSING_MSG: String = "Required fields for content link dialcode are missing: " + val ERR_CONTENT_RETIRED_OBJECT_ID_MSG: String = "Invalid Request. Cannot update 'Retired' content." + val ERR_CONTENT_MISSING_RESERVED_DIAL_CODES_MSG: String = "Invalid Request. Content does not have reserved DIAL codes." + val ERR_ALL_DIALCODES_UTILIZED_MSG: String = "Error! All Reserved DIAL Codes are Utilized." } diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala index fa7385d17..5f47e9670 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala @@ -279,8 +279,8 @@ object DIALManager { val contentMetadata = rootNode.getMetadata validateChannel(contentMetadata.get(DIALConstants.CHANNEL).asInstanceOf[String], channelId) - validateContentForReservedDialcodes(contentMetadata) - validateCountForReservingDialCode(request.getRequest.get(DIALConstants.DIALCODES).asInstanceOf[util.Map[String, AnyRef]]) + validateContentForReserveAndReleaseDialcodes(contentMetadata) + validateCountForReservingAndReleasingDialCode(request.getRequest.get(DIALConstants.DIALCODES).asInstanceOf[util.Map[String, AnyRef]]) 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] @@ -302,17 +302,113 @@ object DIALManager { }) } + def release(request: Request)(implicit oec: OntologyEngineContext, ec: ExecutionContext): Future[Response] = { + val channelId: String = request.getContext.getOrDefault(DIALConstants.CHANNEL, "").asInstanceOf[String] + val contentId: String = request.get(ContentConstants.IDENTIFIER).asInstanceOf[String] + + if (contentId == null || contentId.isEmpty) throw new ClientException(DIALErrors.ERR_CONTENT_BLANK_OBJECT_ID, DIALErrors.ERR_CONTENT_BLANK_OBJECT_ID_MSG) + + val req = new Request() + req.setContext(request.getContext) + req.put(DIALConstants.IDENTIFIER, contentId) + req.put(ContentConstants.ROOT_ID, contentId) + req.put(ContentConstants.MODE, ContentConstants.EDIT_MODE) + DataNode.read(req).flatMap(rootNode => { + val contentMetadata = rootNode.getMetadata + + validateChannel(contentMetadata.get(DIALConstants.CHANNEL).asInstanceOf[String], channelId) + validateContentForReserveAndReleaseDialcodes(contentMetadata) + validateCountForReservingAndReleasingDialCode(request.getRequest.get(DIALConstants.DIALCODES).asInstanceOf[util.Map[String, AnyRef]]) + + 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) + + populateAssignedDialCodes(contentId, contentMetadata, req).map(assignedDialCodes => { + val toReleaseDIALCodes = reservedDialCodes.keySet -- assignedDialCodes.toSet + + if(toReleaseDIALCodes.isEmpty) throw new ClientException(DIALErrors.ERR_ALL_DIALCODES_UTILIZED, DIALErrors.ERR_ALL_DIALCODES_UTILIZED_MSG) + + val reqDialcodesCount = request.getRequest.get(DIALConstants.DIALCODES).asInstanceOf[util.Map[String, AnyRef]].get(DIALConstants.COUNT).asInstanceOf[Integer] + + val updatedReleaseDialcodes = if (toReleaseDIALCodes.size > reqDialcodesCount) { + toReleaseDIALCodes.take(reqDialcodesCount) + } else toReleaseDIALCodes + + val updatedReserveDialCodes = reservedDialCodes.filter(rec => !updatedReleaseDialcodes.contains(rec._1)).keySet.zipWithIndex.map { case (dialCode, idx) => + (dialCode -> idx.asInstanceOf[Integer]) + }.toMap + + val updateReq = new Request() + updateReq.setContext(request.getContext) + updateReq.getContext.put(ContentConstants.IDENTIFIER, rootNode.getIdentifier) + updateReq.put(ContentConstants.IDENTIFIER, rootNode.getIdentifier) + updateReq.put(DIALConstants.VERSION_KEY,rootNode.getMetadata.get(ContentConstants.VERSION_KEY)) + updateReq.put(DIALConstants.RESERVED_DIALCODES, if(updatedReserveDialCodes.nonEmpty) updatedReserveDialCodes.asJava else null) + DataNode.update(updateReq).map(node => { + ResponseHandler.OK.putAll(Map(ContentConstants.IDENTIFIER -> node.getIdentifier.replace(ContentConstants.IMAGE_SUFFIX, ""), + ContentConstants.VERSION_KEY -> node.getMetadata.get(ContentConstants.VERSION_KEY), + DIALConstants.RESERVED_DIALCODES -> node.getMetadata.get(DIALConstants.RESERVED_DIALCODES)).asJava) + }) + }).flatMap(f=>f) + }) + } + + def populateAssignedDialCodes(contentId: String, contentMetadata: util.Map[String, AnyRef], request: Request)(implicit oec: OntologyEngineContext, ec: ExecutionContext): Future[List[String]] = { + + request.getContext.put(ContentConstants.SCHEMA_NAME, ContentConstants.COLLECTION_SCHEMA_NAME) + request.getContext.put(ContentConstants.VERSION, ContentConstants.SCHEMA_VERSION) + request.put(ContentConstants.ROOT_ID, contentId) + + HierarchyManager.getHierarchy(request).flatMap(getHierarchyResponse => { + val collectionHierarchy = getHierarchyResponse.getResult.getOrDefault(ContentConstants.CONTENT, new java.util.HashMap[String, AnyRef]()).asInstanceOf[java.util.Map[String, AnyRef]] + val childrenHierarchy = collectionHierarchy.get(ContentConstants.CHILDREN).asInstanceOf[util.List[util.Map[String, AnyRef]]].asScala.toList + val childrenAssignedDIALList = getAssignedDIALcodes(childrenHierarchy) + val contentAssignedDIALList = childrenAssignedDIALList ++ collectionHierarchy.getOrDefault(DIALConstants.DIALCODES, List.empty[String]).asInstanceOf[List[String]] + + if(contentMetadata.getOrDefault(ContentConstants.IDENTIFIER,"").asInstanceOf[String].endsWith(ContentConstants.IMAGE_SUFFIX)) { + request.put(ContentConstants.ROOT_ID, contentMetadata.getOrDefault(ContentConstants.IDENTIFIER,"").asInstanceOf[String]) + request.put(ContentConstants.MODE, ContentConstants.EDIT_MODE) + HierarchyManager.getHierarchy(request).flatMap(getHierarchyResponse => { + val collectionHierarchy = getHierarchyResponse.getResult.getOrDefault(ContentConstants.CONTENT, new java.util.HashMap[String, AnyRef]()).asInstanceOf[java.util.Map[String, AnyRef]] + val childrenHierarchy = collectionHierarchy.get(ContentConstants.CHILDREN).asInstanceOf[util.List[util.Map[String, AnyRef]]].asScala.toList + val childrenAssignedDIALList = getAssignedDIALcodes(childrenHierarchy) + val contentImageAssignedDIALList = childrenAssignedDIALList ++ collectionHierarchy.getOrDefault(DIALConstants.DIALCODES, List.empty[String]).asInstanceOf[List[String]] + + Future(contentImageAssignedDIALList ++ contentAssignedDIALList) + }) + } else Future(contentAssignedDIALList) + }) + } + + def getAssignedDIALcodes(childrenHierarchy: List[util.Map[String, AnyRef]]): List[String] = { + childrenHierarchy.map(child => { + val subChildrenDIALMap = if(child.get(ContentConstants.CHILDREN)!=null) + getAssignedDIALcodes(child.get(ContentConstants.CHILDREN).asInstanceOf[util.List[util.Map[String, AnyRef]]].asScala.toList) + else List.empty[String] + + val childDIALMap = if(child.get(DIALConstants.DIALCODES)!=null) + child.get(DIALConstants.DIALCODES).asInstanceOf[List[String]] + else List.empty[String] + + subChildrenDIALMap ++ childDIALMap + }).filter(msg => msg.nonEmpty).flatten + } + + def validateChannel(contentChannel: String, channelId: String): Unit = { if(contentChannel == null || channelId == null || !contentChannel.equalsIgnoreCase(channelId)) throw new ClientException(DIALErrors.ERR_INVALID_CHANNEL, DIALErrors.ERR_INVALID_CHANNEL_MSG) } - def validateContentForReservedDialcodes(metaData: util.Map[String, AnyRef]): Unit = { + def validateContentForReserveAndReleaseDialcodes(metaData: util.Map[String, AnyRef]): Unit = { val validMimeType = if (Platform.config.hasPath("reserve_dialcode.mimeType")) Platform.config.getStringList("reserve_dialcode.mimeType") else util.Arrays.asList(ContentConstants.COLLECTION_MIME_TYPE) if (!validMimeType.contains(metaData.get(ContentConstants.MIME_TYPE))) throw new ClientException(DIALErrors.ERR_CONTENT_MIMETYPE, DIALErrors.ERR_CONTENT_MIMETYPE_MSG) + + if(metaData.get(ContentConstants.STATUS).asInstanceOf[String].equalsIgnoreCase("Retired")) + throw new ClientException(DIALErrors.ERR_CONTENT_RETIRED_OBJECT_ID, DIALErrors.ERR_CONTENT_RETIRED_OBJECT_ID_MSG) } - def validateCountForReservingDialCode(request: util.Map[String, AnyRef]): Unit = { + def validateCountForReservingAndReleasingDialCode(request: util.Map[String, AnyRef]): Unit = { if (null == request.get(DIALConstants.COUNT) || !request.get(DIALConstants.COUNT).isInstanceOf[Integer]) throw new ClientException(DIALErrors.ERR_INVALID_COUNT, DIALErrors.ERR_INVALID_COUNT_MSG) val count = request.get(DIALConstants.COUNT).asInstanceOf[Integer] val maxCount = if (Platform.config.hasPath("reserve_dialcode.max_count")) Platform.config.getInt("reserve_dialcode.max_count") else 250 diff --git a/content-api/content-service/app/controllers/v3/ContentController.scala b/content-api/content-service/app/controllers/v3/ContentController.scala index ab3538098..0729f625a 100644 --- a/content-api/content-service/app/controllers/v3/ContentController.scala +++ b/content-api/content-service/app/controllers/v3/ContentController.scala @@ -216,9 +216,13 @@ class ContentController @Inject()(@Named(ActorNames.CONTENT_ACTOR) contentActor: } def releaseDialcodes(identifier: String) = Action.async { implicit request => - val result = ResponseHandler.OK() - val response = JavaJsonUtils.serialize(result) - Future(Ok(response).as("application/json")) + val headers = commonHeaders() + val body = requestBody() + body.putAll(headers) + body.putAll(Map("identifier" -> identifier).asJava) + val releaseDialCode = getRequest(body, headers, "releaseDialCode") + setRequestContext(releaseDialCode, version, objectType, schemaName) + getResult(ApiId.RELEASE_DIAL_CONTENT, contentActor, releaseDialCode) } def publishUnlisted(identifier: String) = Action.async { implicit request => diff --git a/content-api/content-service/app/controllers/v4/CollectionController.scala b/content-api/content-service/app/controllers/v4/CollectionController.scala index 4dfd17829..2c07a292b 100644 --- a/content-api/content-service/app/controllers/v4/CollectionController.scala +++ b/content-api/content-service/app/controllers/v4/CollectionController.scala @@ -193,7 +193,17 @@ class CollectionController @Inject()(@Named(ActorNames.CONTENT_ACTOR) contentAc body.putAll(Map("identifier" -> identifier).asJava) val reserveDialCode = getRequest(body, headers, "reserveDialCode") setRequestContext(reserveDialCode, version, objectType, schemaName) - getResult(ApiId.RESERVE_DIAL_COLLECTION, contentActor, reserveDialCode) + getResult(ApiId.RESERVE_DIAL_COLLECTION, contentActor, reserveDialCode, version = apiVersion) + } + + def releaseDialCode(identifier: String) = Action.async { implicit request => + val headers = commonHeaders() + val body = requestBody() + body.putAll(headers) + body.putAll(Map("identifier" -> identifier).asJava) + val releaseDialCode = getRequest(body, headers, "releaseDialCode") + setRequestContext(releaseDialCode, version, objectType, schemaName) + getResult(ApiId.RELEASE_DIAL_COLLECTION, contentActor, releaseDialCode, version = apiVersion) } def copy(identifier: String, mode: Option[String], copyType: String) = Action.async { implicit request => diff --git a/content-api/content-service/app/controllers/v4/ContentController.scala b/content-api/content-service/app/controllers/v4/ContentController.scala index f5a25d0fe..8db895c56 100644 --- a/content-api/content-service/app/controllers/v4/ContentController.scala +++ b/content-api/content-service/app/controllers/v4/ContentController.scala @@ -138,7 +138,17 @@ class ContentController @Inject()(@Named(ActorNames.CONTENT_ACTOR) contentActor: body.putAll(Map("identifier" -> identifier).asJava) val reserveDialCode = getRequest(body, headers, "reserveDialCode") setRequestContext(reserveDialCode, version, objectType, schemaName) - getResult(ApiId.RESERVE_DIAL_CONTENT, contentActor, reserveDialCode) + getResult(ApiId.RESERVE_DIAL_CONTENT, contentActor, reserveDialCode, version = apiVersion) + } + + def releaseDialCode(identifier: String) = Action.async { implicit request => + val headers = commonHeaders() + val body = requestBody() + body.putAll(headers) + body.putAll(Map("identifier" -> identifier).asJava) + val releaseDialCode = getRequest(body, headers, "releaseDialCode") + setRequestContext(releaseDialCode, version, objectType, schemaName) + getResult(ApiId.RELEASE_DIAL_CONTENT, contentActor, releaseDialCode, version = apiVersion) } def upload(identifier: String, fileFormat: Option[String], validation: Option[String]) = Action.async { implicit request => diff --git a/content-api/content-service/app/utils/ApiId.scala b/content-api/content-service/app/utils/ApiId.scala index 8b5afe44f..9306d13f7 100644 --- a/content-api/content-service/app/utils/ApiId.scala +++ b/content-api/content-service/app/utils/ApiId.scala @@ -30,6 +30,7 @@ object ApiId { val GET_HIERARCHY = "api.content.hierarchy.get" val LINK_DIAL_COLLECTION = "api.collection.dialcode.link" val RESERVE_DIAL_CONTENT = "api.content.dialcode.reserve" + val RELEASE_DIAL_CONTENT = "api.content.dialcode.release" //License APIs val CREATE_LICENSE = "api.license.create" @@ -101,4 +102,5 @@ object ApiId { val IMPORT_CSV = "api.collection.import" val EXPORT_CSV = "api.collection.export" val RESERVE_DIAL_COLLECTION = "api.collection.dialcode.reserve" + val RELEASE_DIAL_COLLECTION = "api.collection.dialcode.release" } diff --git a/content-api/content-service/conf/routes b/content-api/content-service/conf/routes index 17b4e3059..d903bbe64 100644 --- a/content-api/content-service/conf/routes +++ b/content-api/content-service/conf/routes @@ -100,6 +100,7 @@ PATCH /content/v4/system/update/:identifier controllers.v4.ContentControl POST /content/v4/review/:identifier controllers.v4.ContentController.review(identifier:String) POST /content/v4/reject/:identifier controllers.v4.ContentController.reviewReject(identifier:String) POST /content/v4/dialcode/reserve/:identifier controllers.v4.ContentController.reserveDialCode(identifier:String) +POST /content/v4/dialcode/release/:identifier controllers.v4.ContentController.releaseDialCode(identifier:String) # App v4 APIs POST /app/v4/register controllers.v4.AppController.register @@ -131,4 +132,5 @@ GET /object/v4/read/:identifier controllers.v4.ObjectController POST /collection/v4/import/:collectionId controllers.v4.CollectionController.importCollection(collectionId:String) GET /collection/v4/export/:collectionId controllers.v4.CollectionController.exportCollection(collectionId:String, fileType:Option[String]) POST /collection/v4/review/:identifier controllers.v4.CollectionController.review(identifier:String) -POST /collection/v4/dialcode/reserve/:identifier controllers.v4.CollectionController.reserveDialCode(identifier:String) \ No newline at end of file +POST /collection/v4/dialcode/reserve/:identifier controllers.v4.CollectionController.reserveDialCode(identifier:String) +POST /collection/v4/dialcode/release/:identifier controllers.v4.CollectionController.releaseDialCode(identifier:String) \ No newline at end of file From ca85c978c18a32d6fcd9dc31cd2cca9654ab0299 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Tue, 6 Sep 2022 15:08:33 +0530 Subject: [PATCH 120/490] Issue #KN-257 feat: Content Release DIAL codes API refactor. --- .../sunbird/content/dial/DIALManager.scala | 55 ++++++++++--------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala index 5f47e9670..332fb07e2 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala @@ -127,23 +127,20 @@ object DIALManager { req.put(ContentConstants.IDENTIFIER, objectId) req.put(ContentConstants.MODE, ContentConstants.EDIT_MODE) DataNode.read(req).flatMap(rootNode => { - req.getContext.put(ContentConstants.SCHEMA_NAME, ContentConstants.COLLECTION_SCHEMA_NAME) - req.getContext.put(ContentConstants.VERSION, ContentConstants.SCHEMA_VERSION) - req.put(ContentConstants.ROOT_ID, objectId) - HierarchyManager.getHierarchy(req).flatMap(getHierarchyResponse => { - val updatedChildrenHierarchy = getUpdatedChildrenHierarchy(getHierarchyResponse, requestMap) - val consolidatedUnitDIALMap = getConsolidatedUnitDIALMap(updatedChildrenHierarchy, requestMap, objectId) - validateDuplicateDIALCodes(consolidatedUnitDIALMap.filter(rec => rec._2.asInstanceOf[List[String]].nonEmpty)) - - val hierarchyReq = getHierarchyRequest(req, objectId, updatedChildrenHierarchy, rootNode) - oec.graphService.saveExternalProps(hierarchyReq).flatMap(rec => if(requestMap.contains(objectId)) { - val updateReq = getLinkUpdateRequest(req, rootNode, requestMap, objectId) - - DataNode.update(updateReq).flatMap(response => { + val updateReq = getLinkUpdateRequest(req, rootNode, requestMap, objectId) + DataNode.update(updateReq).flatMap(response => { + req.getContext.put(ContentConstants.SCHEMA_NAME, ContentConstants.COLLECTION_SCHEMA_NAME) + req.getContext.put(ContentConstants.VERSION, ContentConstants.SCHEMA_VERSION) + req.put(ContentConstants.ROOT_ID, objectId) + HierarchyManager.getHierarchy(req).flatMap(getHierarchyResponse => { + val updatedChildrenHierarchy = getUpdatedChildrenHierarchy(getHierarchyResponse, requestMap) + val consolidatedUnitDIALMap = getConsolidatedUnitDIALMap(updatedChildrenHierarchy, requestMap, objectId) + validateDuplicateDIALCodes(consolidatedUnitDIALMap.filter(rec => rec._2.asInstanceOf[List[String]].nonEmpty)) + + val hierarchyReq = getHierarchyRequest(req, objectId, updatedChildrenHierarchy, rootNode) + oec.graphService.saveExternalProps(hierarchyReq).flatMap(rec => getResponseCollectionLink(requestMap, consolidatedUnitDIALMap.keySet.toList, requestMap.keySet.diff(consolidatedUnitDIALMap.keySet).toList) - }) - } else { - getResponseCollectionLink(requestMap, consolidatedUnitDIALMap.keySet.toList, requestMap.keySet.diff(consolidatedUnitDIALMap.keySet).toList) + ) }) }) }) @@ -175,9 +172,9 @@ object DIALManager { def getLinkUpdateRequest(req: Request, rootNode: Node, requestMap: Map[String, List[String]], objectId: String): Request = { val updateReq = new Request(req) updateReq.put(ContentConstants.IDENTIFIER, rootNode.getIdentifier) - updateReq.put(DIALConstants.VERSION_KEY,rootNode.getMetadata.get("versionKey")) + updateReq.put(DIALConstants.VERSION_KEY, rootNode.getMetadata.get("versionKey")) - if(requestMap(objectId).isEmpty) + if(!requestMap.contains(objectId)) updateReq.put(DIALConstants.DIALCODES, null) else updateReq.put(DIALConstants.DIALCODES, requestMap(objectId).toArray[String]) @@ -354,25 +351,29 @@ object DIALManager { } def populateAssignedDialCodes(contentId: String, contentMetadata: util.Map[String, AnyRef], request: Request)(implicit oec: OntologyEngineContext, ec: ExecutionContext): Future[List[String]] = { - + request.put(ContentConstants.MODE,"") request.getContext.put(ContentConstants.SCHEMA_NAME, ContentConstants.COLLECTION_SCHEMA_NAME) request.getContext.put(ContentConstants.VERSION, ContentConstants.SCHEMA_VERSION) - request.put(ContentConstants.ROOT_ID, contentId) + request.put(ContentConstants.ROOT_ID, contentId.replaceAll(ContentConstants.IMAGE_SUFFIX, "")) HierarchyManager.getHierarchy(request).flatMap(getHierarchyResponse => { val collectionHierarchy = getHierarchyResponse.getResult.getOrDefault(ContentConstants.CONTENT, new java.util.HashMap[String, AnyRef]()).asInstanceOf[java.util.Map[String, AnyRef]] val childrenHierarchy = collectionHierarchy.get(ContentConstants.CHILDREN).asInstanceOf[util.List[util.Map[String, AnyRef]]].asScala.toList val childrenAssignedDIALList = getAssignedDIALcodes(childrenHierarchy) - val contentAssignedDIALList = childrenAssignedDIALList ++ collectionHierarchy.getOrDefault(DIALConstants.DIALCODES, List.empty[String]).asInstanceOf[List[String]] + val contentAssignedDIALList = if(collectionHierarchy.containsKey(DIALConstants.DIALCODES) && collectionHierarchy.get(DIALConstants.DIALCODES) != null) + childrenAssignedDIALList ++ collectionHierarchy.getOrDefault(DIALConstants.DIALCODES, List.empty[String]).asInstanceOf[List[String]] + else childrenAssignedDIALList if(contentMetadata.getOrDefault(ContentConstants.IDENTIFIER,"").asInstanceOf[String].endsWith(ContentConstants.IMAGE_SUFFIX)) { request.put(ContentConstants.ROOT_ID, contentMetadata.getOrDefault(ContentConstants.IDENTIFIER,"").asInstanceOf[String]) request.put(ContentConstants.MODE, ContentConstants.EDIT_MODE) - HierarchyManager.getHierarchy(request).flatMap(getHierarchyResponse => { - val collectionHierarchy = getHierarchyResponse.getResult.getOrDefault(ContentConstants.CONTENT, new java.util.HashMap[String, AnyRef]()).asInstanceOf[java.util.Map[String, AnyRef]] - val childrenHierarchy = collectionHierarchy.get(ContentConstants.CHILDREN).asInstanceOf[util.List[util.Map[String, AnyRef]]].asScala.toList - val childrenAssignedDIALList = getAssignedDIALcodes(childrenHierarchy) - val contentImageAssignedDIALList = childrenAssignedDIALList ++ collectionHierarchy.getOrDefault(DIALConstants.DIALCODES, List.empty[String]).asInstanceOf[List[String]] + HierarchyManager.getHierarchy(request).flatMap(getImageHierarchyResponse => { + val imageCollectionHierarchy = getImageHierarchyResponse.getResult.getOrDefault(ContentConstants.CONTENT, new java.util.HashMap[String, AnyRef]()).asInstanceOf[java.util.Map[String, AnyRef]] + val imageChildrenHierarchy = imageCollectionHierarchy.get(ContentConstants.CHILDREN).asInstanceOf[util.List[util.Map[String, AnyRef]]].asScala.toList + val imageChildrenAssignedDIALList = getAssignedDIALcodes(imageChildrenHierarchy) + val contentImageAssignedDIALList = if(imageCollectionHierarchy.containsKey(DIALConstants.DIALCODES) && imageCollectionHierarchy.get(DIALConstants.DIALCODES) != null) + imageChildrenAssignedDIALList ++ imageCollectionHierarchy.getOrDefault(DIALConstants.DIALCODES, List.empty[String]).asInstanceOf[List[String]] + else imageChildrenAssignedDIALList Future(contentImageAssignedDIALList ++ contentAssignedDIALList) }) @@ -387,7 +388,7 @@ object DIALManager { else List.empty[String] val childDIALMap = if(child.get(DIALConstants.DIALCODES)!=null) - child.get(DIALConstants.DIALCODES).asInstanceOf[List[String]] + child.get(DIALConstants.DIALCODES).asInstanceOf[util.List[String]].asScala.toList else List.empty[String] subChildrenDIALMap ++ childDIALMap From 921801d8703504f2c6e7ce6c5daeaf828f52faf1 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Tue, 6 Sep 2022 15:13:19 +0530 Subject: [PATCH 121/490] Issue #KN-257 feat: Content Release DIAL codes API refactor. --- .../content/dial/DIALManagerTest.scala | 40 ++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala b/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala index 9457a4173..1ea02a355 100644 --- a/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala +++ b/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala @@ -283,6 +283,25 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory }) } + "release DIAL" should "update content with reservedDialcodes excluding the number of dialcodes mentioned in count" in { + (oec.httpUtil _).expects().returns(httpUtil) + (oec.graphService _).expects().returns(graphDB).anyNumberOfTimes() + + val nodes: util.List[Node] = getCategoryNode() + (graphDB.getNodeByUniqueIds(_: String, _: SearchCriteria)).expects(*, *).returns(Future(nodes)).anyNumberOfTimes() + + val contentId: String = "do_123456" + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, *, *, *).returns(Future(getNode(contentId))).anyNumberOfTimes() + (graphDB.getNodeProperty(_: String, _: String, _: String)).expects(*, *, *).returns(Future(new Property("versionKey", new org.neo4j.driver.internal.value.StringValue("1234")))) + (graphDB.upsertNode(_: String, _: Node, _: Request)).expects(*, *, *).returns(Future(getNode(contentId))) + + val request = getReleaseDIALRequest(contentId) + + val response = DIALManager.release(request) + response.map(result => { + assert(result.getResponseCode.toString=="OK") + }) + } def getDIALSearchResponse:Response = { val resString = "{\n \"id\": \"sunbird.dialcode.search\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-04-21T19:39:14ZZ\",\n \"params\": {\n \"resmsgid\": \"1dfcc25b-6c37-49f8-a6c3-7185063e8752\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"dialcodes\": [\n {\n \"dialcode_index\": 7609876,\n \"identifier\": \"N4Z7D5\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.603+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n },\n {\n \"dialcode_index\": 7610113,\n \"identifier\": \"E8B7Z6\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.635+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n },\n {\n \"dialcode_index\": 7610117,\n \"identifier\": \"R4X2P2\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.637+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n },\n {\n \"dialcode_index\": 7610961,\n \"identifier\": \"L4A6W8\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.734+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n },\n {\n \"dialcode_index\": 7611164,\n \"identifier\": \"D2E1J9\",\n \"channel\": \"testr01\",\n \"batchcode\": \"testPub0001.20200421T193801\",\n \"publisher\": \"testPub0001\",\n \"generated_on\": \"2020-04-21T19:38:01.759+0000\",\n \"status\": \"Draft\",\n \"objectType\": \"DialCode\"\n }\n ],\n \"count\": 5\n }\n}"; @@ -561,7 +580,7 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory def getReserveRequest():util.Map[String, AnyRef] = { val reqMap : java.util.Map[String, AnyRef] = new util.HashMap[String, AnyRef](){ put("dialcodes", new util.HashMap[String, AnyRef](){ - put("count", 2.asInstanceOf[Integer]) + put("count", 12.asInstanceOf[Integer]) put("qrCodeSpec", new util.HashMap[String, AnyRef](){ put("errorCorrectionLevel", "H") }) @@ -570,6 +589,25 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory reqMap } + def getReleaseDIALRequest(identifier: String): Request = { + val request = new Request() + request.setObjectType("Content") + request.setContext(getContext()) + request.getContext.put("identifier",identifier) + request.put("identifier",identifier) + request.putAll(getReserveRequest()) + request + } + + def getReleaseRequest():util.Map[String, AnyRef] = { + val reqMap : java.util.Map[String, AnyRef] = new util.HashMap[String, AnyRef](){ + put("dialcodes", new util.HashMap[String, AnyRef](){ + put("count", 2.asInstanceOf[Integer]) + }) + } + reqMap + } + def getGenerateDIALResponse:Response = { val resString = "{\"id\": \"api.dialcode.generate\",\"ver\": \"1.0\",\"ts\": \"2022-07-05T09:47:26.000Z\",\"params\": {\"resmsgid\": \"79eb8b00-fc47-11ec-af25-0f53946b16ec\",\"msgid\": \"79be1260-fc47-11ec-8c03-63ca5ce41074\",\"status\": \"successful\",\"err\": null,\"errmsg\": null},\"responseCode\": \"OK\",\"result\": {\"dialcodes\": [\"K2C3R6\",\"H2E8F9\"],\"count\": 2,\"batchcode\": \"do_11357423520695910411\",\"publisher\": null}}" JsonUtils.deserialize(resString, classOf[Response]) From 9c6b84d97b7d41b9d4973830c23465056cf84e31 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Tue, 6 Sep 2022 16:18:09 +0530 Subject: [PATCH 122/490] Issue #KN-257 feat: Content Release DIAL codes API refactor. --- .../test/scala/org/sunbird/content/dial/DIALManagerTest.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala b/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala index 1ea02a355..cd8fc38f7 100644 --- a/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala +++ b/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala @@ -250,6 +250,8 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, *).returns(Future(getCassandraHierarchy())).anyNumberOfTimes() (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, *, *, *).returns(Future(getNode("do_1111"))).anyNumberOfTimes() + (graphDB.getNodeProperty(_: String, _: String, _: String)).expects(*, *, *).returns(Future(new Property("versionKey", new org.neo4j.driver.internal.value.StringValue("1234")))) + (graphDB.upsertNode(_: String, _: Node, _: Request)).expects(*, *, *).returns(Future(getNode("do_1111"))) (graphDB.saveExternalProps(_: Request)).expects(*).returns(Future(new Response())) val request = getCollectionRNFRequest() From fa6402edaf2e3d0397a2dff25df28a7dd1ff9dc3 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Tue, 6 Sep 2022 16:51:51 +0530 Subject: [PATCH 123/490] Issue #KN-257 feat: Content Release DIAL codes API refactor. --- .../sunbird/content/dial/DIALManager.scala | 42 ++++++------ .../content/dial/DIALManagerTest.scala | 64 ++++++++++++++----- 2 files changed, 68 insertions(+), 38 deletions(-) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala index 332fb07e2..10fc83866 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala @@ -351,33 +351,33 @@ object DIALManager { } def populateAssignedDialCodes(contentId: String, contentMetadata: util.Map[String, AnyRef], request: Request)(implicit oec: OntologyEngineContext, ec: ExecutionContext): Future[List[String]] = { - request.put(ContentConstants.MODE,"") request.getContext.put(ContentConstants.SCHEMA_NAME, ContentConstants.COLLECTION_SCHEMA_NAME) request.getContext.put(ContentConstants.VERSION, ContentConstants.SCHEMA_VERSION) - request.put(ContentConstants.ROOT_ID, contentId.replaceAll(ContentConstants.IMAGE_SUFFIX, "")) - - HierarchyManager.getHierarchy(request).flatMap(getHierarchyResponse => { - val collectionHierarchy = getHierarchyResponse.getResult.getOrDefault(ContentConstants.CONTENT, new java.util.HashMap[String, AnyRef]()).asInstanceOf[java.util.Map[String, AnyRef]] - val childrenHierarchy = collectionHierarchy.get(ContentConstants.CHILDREN).asInstanceOf[util.List[util.Map[String, AnyRef]]].asScala.toList - val childrenAssignedDIALList = getAssignedDIALcodes(childrenHierarchy) - val contentAssignedDIALList = if(collectionHierarchy.containsKey(DIALConstants.DIALCODES) && collectionHierarchy.get(DIALConstants.DIALCODES) != null) - childrenAssignedDIALList ++ collectionHierarchy.getOrDefault(DIALConstants.DIALCODES, List.empty[String]).asInstanceOf[List[String]] - else childrenAssignedDIALList - + request.put(ContentConstants.ROOT_ID, contentMetadata.getOrDefault(ContentConstants.IDENTIFIER,"").asInstanceOf[String]) + request.put(ContentConstants.MODE, ContentConstants.EDIT_MODE) + + HierarchyManager.getHierarchy(request).flatMap(getImageHierarchyResponse => { + val imageCollectionHierarchy = getImageHierarchyResponse.getResult.getOrDefault(ContentConstants.CONTENT, new java.util.HashMap[String, AnyRef]()).asInstanceOf[java.util.Map[String, AnyRef]] + val imageChildrenHierarchy = imageCollectionHierarchy.get(ContentConstants.CHILDREN).asInstanceOf[util.List[util.Map[String, AnyRef]]].asScala.toList + val imageChildrenAssignedDIALList = getAssignedDIALcodes(imageChildrenHierarchy) + val contentImageAssignedDIALList = if(imageCollectionHierarchy.containsKey(DIALConstants.DIALCODES) && imageCollectionHierarchy.get(DIALConstants.DIALCODES) != null) + imageChildrenAssignedDIALList ++ imageCollectionHierarchy.getOrDefault(DIALConstants.DIALCODES, List.empty[String]).asInstanceOf[List[String]] + else imageChildrenAssignedDIALList + + request.put(ContentConstants.ROOT_ID, contentId) + request.put(ContentConstants.MODE, "") if(contentMetadata.getOrDefault(ContentConstants.IDENTIFIER,"").asInstanceOf[String].endsWith(ContentConstants.IMAGE_SUFFIX)) { - request.put(ContentConstants.ROOT_ID, contentMetadata.getOrDefault(ContentConstants.IDENTIFIER,"").asInstanceOf[String]) - request.put(ContentConstants.MODE, ContentConstants.EDIT_MODE) - HierarchyManager.getHierarchy(request).flatMap(getImageHierarchyResponse => { - val imageCollectionHierarchy = getImageHierarchyResponse.getResult.getOrDefault(ContentConstants.CONTENT, new java.util.HashMap[String, AnyRef]()).asInstanceOf[java.util.Map[String, AnyRef]] - val imageChildrenHierarchy = imageCollectionHierarchy.get(ContentConstants.CHILDREN).asInstanceOf[util.List[util.Map[String, AnyRef]]].asScala.toList - val imageChildrenAssignedDIALList = getAssignedDIALcodes(imageChildrenHierarchy) - val contentImageAssignedDIALList = if(imageCollectionHierarchy.containsKey(DIALConstants.DIALCODES) && imageCollectionHierarchy.get(DIALConstants.DIALCODES) != null) - imageChildrenAssignedDIALList ++ imageCollectionHierarchy.getOrDefault(DIALConstants.DIALCODES, List.empty[String]).asInstanceOf[List[String]] - else imageChildrenAssignedDIALList + HierarchyManager.getHierarchy(request).flatMap(getHierarchyResponse => { + val collectionHierarchy = getHierarchyResponse.getResult.getOrDefault(ContentConstants.CONTENT, new java.util.HashMap[String, AnyRef]()).asInstanceOf[java.util.Map[String, AnyRef]] + val childrenHierarchy = collectionHierarchy.get(ContentConstants.CHILDREN).asInstanceOf[util.List[util.Map[String, AnyRef]]].asScala.toList + val childrenAssignedDIALList = getAssignedDIALcodes(childrenHierarchy) + val contentAssignedDIALList = if(collectionHierarchy.containsKey(DIALConstants.DIALCODES) && collectionHierarchy.get(DIALConstants.DIALCODES) != null) + childrenAssignedDIALList ++ collectionHierarchy.getOrDefault(DIALConstants.DIALCODES, List.empty[String]).asInstanceOf[List[String]] + else childrenAssignedDIALList Future(contentImageAssignedDIALList ++ contentAssignedDIALList) }) - } else Future(contentAssignedDIALList) + } else Future(contentImageAssignedDIALList) }) } diff --git a/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala b/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala index cd8fc38f7..c17f31cf0 100644 --- a/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala +++ b/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala @@ -197,9 +197,10 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory val nodes: util.List[Node] = getCategoryNode() (graphDB.getNodeByUniqueIds(_: String, _: SearchCriteria)).expects(*, *).returns(Future(nodes)).anyNumberOfTimes() - (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, *).returns(Future(getCassandraHierarchy())).anyNumberOfTimes() - (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, *, *, *).returns(Future(getNode("do_1111"))).anyNumberOfTimes() - (graphDB.upsertNode(_: String, _: Node, _: Request)).expects(*, *, *).returns(Future(getNode("do_1111"))) + val contentId = "do_1111" + (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, *).returns(Future(getCassandraHierarchy(contentId))).anyNumberOfTimes() + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, *, *, *).returns(Future(getNode(contentId))).anyNumberOfTimes() + (graphDB.upsertNode(_: String, _: Node, _: Request)).expects(*, *, *).returns(Future(getNode(contentId))) (graphDB.saveExternalProps(_: Request)).expects(*).returns(Future(new Response())) (graphDB.getNodeProperty(_: String, _: String, _: String)).expects(*, *, *).returns(Future(new Property("versionKey", new org.neo4j.driver.internal.value.StringValue("1234")))) @@ -225,10 +226,10 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory val nodes: util.List[Node] = getCategoryNode() (graphDB.getNodeByUniqueIds(_: String, _: SearchCriteria)).expects(*, *).returns(Future(nodes)).anyNumberOfTimes() - - (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, *).returns(Future(getCassandraHierarchy())).anyNumberOfTimes() - (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, *, *, *).returns(Future(getNode("do_1111"))).anyNumberOfTimes() - (graphDB.upsertNode(_: String, _: Node, _: Request)).expects(*, *, *).returns(Future(getNode("do_1111"))) + val contentId = "do_1111" + (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, *).returns(Future(getCassandraHierarchy(contentId))).anyNumberOfTimes() + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, *, *, *).returns(Future(getNode(contentId))).anyNumberOfTimes() + (graphDB.upsertNode(_: String, _: Node, _: Request)).expects(*, *, *).returns(Future(getNode(contentId))) (graphDB.saveExternalProps(_: Request)).expects(*).returns(Future(new Response())) (graphDB.getNodeProperty(_: String, _: String, _: String)).expects(*, *, *).returns(Future(new Property("versionKey", new org.neo4j.driver.internal.value.StringValue("1234")))) @@ -247,11 +248,11 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory val nodes: util.List[Node] = getCategoryNode() (graphDB.getNodeByUniqueIds(_: String, _: SearchCriteria)).expects(*, *).returns(Future(nodes)).anyNumberOfTimes() - - (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, *).returns(Future(getCassandraHierarchy())).anyNumberOfTimes() - (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, *, *, *).returns(Future(getNode("do_1111"))).anyNumberOfTimes() + val contentId = "do_1111" + (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, *).returns(Future(getCassandraHierarchy(contentId))).anyNumberOfTimes() + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, *, *, *).returns(Future(getNode(contentId))).anyNumberOfTimes() (graphDB.getNodeProperty(_: String, _: String, _: String)).expects(*, *, *).returns(Future(new Property("versionKey", new org.neo4j.driver.internal.value.StringValue("1234")))) - (graphDB.upsertNode(_: String, _: Node, _: Request)).expects(*, *, *).returns(Future(getNode("do_1111"))) + (graphDB.upsertNode(_: String, _: Node, _: Request)).expects(*, *, *).returns(Future(getNode(contentId))) (graphDB.saveExternalProps(_: Request)).expects(*).returns(Future(new Response())) val request = getCollectionRNFRequest() @@ -286,14 +287,14 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory } "release DIAL" should "update content with reservedDialcodes excluding the number of dialcodes mentioned in count" in { - (oec.httpUtil _).expects().returns(httpUtil) (oec.graphService _).expects().returns(graphDB).anyNumberOfTimes() val nodes: util.List[Node] = getCategoryNode() (graphDB.getNodeByUniqueIds(_: String, _: SearchCriteria)).expects(*, *).returns(Future(nodes)).anyNumberOfTimes() val contentId: String = "do_123456" - (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, *, *, *).returns(Future(getNode(contentId))).anyNumberOfTimes() + (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, *, *, *).returns(Future(getReleaseNode(contentId))).anyNumberOfTimes() + (graphDB.readExternalProps(_: Request, _: List[String])).expects(*, *).returns(Future(getCassandraHierarchy(contentId))).anyNumberOfTimes() (graphDB.getNodeProperty(_: String, _: String, _: String)).expects(*, *, *).returns(Future(new Property("versionKey", new org.neo4j.driver.internal.value.StringValue("1234")))) (graphDB.upsertNode(_: String, _: Node, _: Request)).expects(*, *, *).returns(Future(getNode(contentId))) @@ -444,6 +445,35 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory node } + def getReleaseNode(identifier: String): Node = { + val node = new Node() + node.setIdentifier(identifier) + node.setNodeType("DATA_NODE") + node.setObjectType("Content") + val reservedDialcodesMap = new util.HashMap[String, Int] (){ + put("V4M9H3", 0); + put("W1I5W9", 1); + put("P7W5F1", 2); + put("A8H7V2", 3); + } + + node.setMetadata(new util.HashMap[String, AnyRef]() { + { + put("identifier", identifier) + put("name", "Test Content") + put("code", "test.resource") + put("contentType", "Resource") + put("mimeType", "application/pdf") + put("status", "Draft") + put("channel", "test") + put("versionKey", "1234") + put("primaryCategory", "Learning Resource") + put("reservedDialcodes", JsonUtils.serialize(reservedDialcodesMap)) + } + }) + node + } + def getCollectionPartialSuccessRequest(): Request = { val request = new Request() request.setObjectType("Content") @@ -540,11 +570,11 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory util.Arrays.asList(node) } - def getCassandraHierarchy(): Response = { + def getCassandraHierarchy(contentId: String): Response = { val hierarchyString: String = - """{"ownershipType": ["createdBy"],"subject": ["Mathematics"],"channel": "0126825293972439041","organisation": ["Sunbird"], + s"""{"ownershipType": ["createdBy"],"subject": ["Mathematics"],"channel": "0126825293972439041","organisation": ["Sunbird"], |"language": ["English"],"mimeType": "application/vnd.ekstep.content-collection","objectType": "Content","gradeLevel": ["Class 4"], - |"primaryCategory": "Digital Textbook","children": [{"ownershipType": ["createdBy"],"parent": "do_1111","code": "do_1132828084877148161531", + |"primaryCategory": "Digital Textbook","children": [{"ownershipType": ["createdBy"],"parent": "$contentId","code": "do_1132828084877148161531", |"keywords": [],"credentials": {"enabled": "No"},"channel": "0126825293972439041","description": "This chapter describes about human body", |"language": ["English"],"mimeType": "application/vnd.ekstep.content-collection","idealScreenSize": "normal","createdOn": "2021-05-20T08:58:33.470+0000", |"objectType": "Content","primaryCategory": "Textbook Unit","children": [{"ownershipType": ["createdBy"],"parent": "do_2222", @@ -564,7 +594,7 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory |"trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_1132339274094346241120","audience": ["Student"],"visibility": "Default", |"consumerId": "273f3b18-5dda-4a27-984a-060c7cd398d3","index": 1,"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"], |"lastPublishedBy": "8454cb21-3ce9-4e30-85b5-fade097880d8","version": 2,"license": "CC BY 4.0","prevState": "Live","size": 849897.0, - |"lastPublishedOn": "2021-03-21T13:06:11.272+0000","name": "Untitled Content","status": "Live","code": "a88b0257-670b-455b-98b8-6e359ebac009","credentials": {"enabled": "No"},"prevStatus": "Processing","description": "updated","streamingUrl": "https://sunbirddevmedia-inct.streaming.media.azure.net/9c0ebb33-af08-403f-afb4-eb24749f40a1/test-874-kb.ism/manifest(format=m3u8-aapl-v3)","idealScreenSize": "normal","createdOn": "2021-03-11T13:34:14.475+0000","contentDisposition": "inline","lastUpdatedOn": "2021-03-21T13:06:09.526+0000","dialcodeRequired": "No","lastStatusChangedOn": "2021-03-21T18:36:15.799+0530","createdFor": ["ORG_001"],"creator": "Reviewer User","os": ["All"],"se_FWIds": ["NCFCOPY"],"pkgVersion": 14.0,"versionKey": "1616331969523","idealScreenDensity": "hdpi","framework": "NCFCOPY","depth": 4,"s3Key": "ecar_files/do_1132339274094346241120/untitled-content_1616331971279_do_1132339274094346241120_14.0.ecar","createdBy": "95e4942d-cbe8-477d-aebd-ad8e6de4bfc8","compatibilityLevel": 1,"resourceType": "Learn"},{"ownershipType": ["createdBy"],"parent": "do_1132833371215872001720","unitIdentifiers": ["do_1132239562839900161634"],"copyright": "2021 MIT","previewUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/ecml/do_113223967141863424174-latest","plugins": [{"identifier": "org.sunbird.questionunit.quml","semanticVersion": "1.1"}],"subject": ["Hindi"],"channel": "01309282781705830427","downloadUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_113223967141863424174/esa_1614253812772_do_113223967141863424174_1.0.ecar","language": ["English"],"source": "https://dock.sunbirded.org/api/content/v1/read/do_1132239617341767681638","mimeType": "application/vnd.ekstep.ecml-archive","variants": {"spine": {"ecarUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_113223967141863424174/esa_1614253813394_do_113223967141863424174_1.0_spine.ecar","size": 24293.0}},"objectType": "Content","se_mediums": ["English"],"gradeLevel": ["Class 10"],"primaryCategory": "Exam Question","appId": "dev.dock.portal","contentEncoding": "gzip","artifactUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/do_113223967141863424174/artifact/1614253223147_do_1132239617341767681638.zip","sYS_INTERNAL_LAST_UPDATED_ON": "2021-02-25T11:50:16.032+0000","contentType": "Resource","se_gradeLevels": ["Class 10"],"trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_113223967141863424174","audience": ["Student"],"visibility": "Default","author": "anusha","maxQuestions": 1,"consumerId": "b7054510-3ca4-49fd-b373-b100b3f65e18","index": 2,"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"],"lastPublishedBy": "5a587cc1-e018-4859-a0a8-e842650b9d64","version": 2,"se_subjects": ["Hindi"],"license": "CC BY 4.0","prevState": "Review","size": 384798.0,"lastPublishedOn": "2021-02-25T11:50:12.771+0000","name": "esa","topic": ["तोप"],"status": "Live","code": "d19f43ce-753d-2c70-a9fd-70302af424a0","credentials": {"enabled": "No"},"prevStatus": "Processing","origin": "do_1132239617341767681638","streamingUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/ecml/do_113223967141863424174-latest","medium": ["English"],"idealScreenSize": "normal","createdOn": "2021-02-25T11:50:01.500+0000","se_boards": ["CBSE"],"processId": "2b00cca7-42d5-4e35-aa03-1c22dfe03de8","contentDisposition": "inline","lastUpdatedOn": "2021-02-25T11:50:12.678+0000","originData": {"identifier": "do_1132239617341767681638","repository": "https://dock.sunbirded.org/api/content/v1/read/do_1132239617341767681638"},"collectionId": "do_1132239562836049921627","dialcodeRequired": "No","editorVersion": 3,"lastStatusChangedOn": "2021-02-25T11:50:16.017+0000","creator": "anusha","os": ["All"],"questionCategories": ["SA"],"cloudStorageKey": "content/do_113223967141863424174/artifact/1614253223147_do_1132239617341767681638.zip","se_FWIds": ["ekstep_ncert_k-12"],"marks": "12","bloomsLevel": ["Apply"],"pkgVersion": 1.0,"versionKey": "1614253812678","idealScreenDensity": "hdpi","framework": "ekstep_ncert_k-12","depth": 4,"s3Key": "ecar_files/do_113223967141863424174/esa_1614253812772_do_113223967141863424174_1.0.ecar","lastSubmittedOn": "2021-02-25T11:50:11.539+0000","createdBy": "19ba0e4e-9285-4335-8dd0-f674bf03fa4d","se_topics": ["तोप"],"compatibilityLevel": 1,"itemSetPreviewUrl": "https://dockstorage.blob.core.windows.net/sunbird-content-dock/content/do_1132239617341767681638/artifact/do_1132239617341767681638_1614253222002.pdf","board": "CBSE","programId": "463cfa30-775c-11eb-8c56-93946e419809"}],"contentDisposition": "inline","lastUpdatedOn": "2021-05-20T08:58:33.475+0000","contentEncoding": "gzip","contentType": "TextBookUnit","dialcodeRequired": "Yes","identifier": "do_1132833371215872001720","lastStatusChangedOn": "2021-05-20T08:58:33.475+0000","audience": ["Student"],"os": ["All"],"visibility": "Parent","discussionForum": {"enabled": "Yes"},"index": 1,"mediaType": "content","osId": "org.ekstep.launcher","languageCode": ["en"],"version": 2,"versionKey": "1621501113475","license": "CC BY 4.0","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 3,"compatibilityLevel": 1,"name": "5.1.1 Key parts in the head","topic": [],"status": "Draft"},{"ownershipType": ["createdBy"],"parent": "do_1132833371215134721712","code": "do_1132828084876165121519","keywords": [],"credentials": {"enabled": "No"},"channel": "0126825293972439041","description": "","language": ["English"],"mimeType": "application/vnd.ekstep.content-collection","idealScreenSize": "normal","createdOn": "2021-05-20T08:58:33.473+0000","objectType": "Content","primaryCategory": "Textbook Unit","contentDisposition": "inline","lastUpdatedOn": "2021-05-20T08:58:33.473+0000","contentEncoding": "gzip","contentType": "TextBookUnit","dialcodeRequired": "No","identifier": "do_1132833371215708161718","lastStatusChangedOn": "2021-05-20T08:58:33.473+0000","audience": ["Student"],"os": ["All"],"visibility": "Parent","discussionForum": {"enabled": "Yes"},"index": 2,"mediaType": "content","osId": "org.ekstep.launcher","languageCode": ["en"],"version": 2,"versionKey": "1621501113473","license": "CC BY 4.0","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 3,"compatibilityLevel": 1,"name": "5.1.2 Other parts","topic": [],"status": "Draft"},{"ownershipType": ["createdBy"],"parent": "do_1132833371215134721712","unitIdentifiers": ["do_11323721176414617611924"],"copyright": "2021 MIT","organisationId": "e7328d77-42a7-44c8-84f4-8cfea235f07d","previewUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/ecml/do_1132372524622561281279-latest","plugins": [{"identifier": "org.sunbird.questionunit.quml","semanticVersion": "1.1"}],"subject": ["Mathematics"],"channel": "01309282781705830427","downloadUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_1132372524622561281279/untitled_1615875562931_do_1132372524622561281279_1.0.ecar","language": ["English"],"source": "https://dock.sunbirded.org/api/content/v1/read/do_11323724954450329611930","mimeType": "application/vnd.ekstep.ecml-archive","variants": {"spine": {"ecarUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_1132372524622561281279/untitled_1615875563539_do_1132372524622561281279_1.0_spine.ecar","size": 19563.0}},"objectType": "Content","se_mediums": ["English"],"gradeLevel": ["Class 5"],"primaryCategory": "Exam Question","appId": "dev.dock.portal","contentEncoding": "gzip","artifactUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/do_1132372524622561281279/artifact/1615875430184_do_11323724954450329611930.zip","sYS_INTERNAL_LAST_UPDATED_ON": "2021-03-16T06:19:26.162+0000","contentType": "Resource","se_gradeLevels": ["Class 5"],"trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_1132372524622561281279","audience": ["Student"],"visibility": "Default","author": "N18","maxQuestions": 1,"consumerId": "f73cfcc5-4d43-4fa0-8b81-46166c81bc2b","learningOutcome": ["identify the need to find area and perimeter of rectangle and square."],"index": 3,"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"],"lastPublishedBy": "5a587cc1-e018-4859-a0a8-e842650b9d64","version": 2,"se_subjects": ["Mathematics"],"license": "CC BY 4.0","prevState": "Review","size": 374996.0,"lastPublishedOn": "2021-03-16T06:19:22.931+0000","name": "Untitled","topic": ["Speed, Distance and Time"],"status": "Live","code": "2544c8b8-7946-b6c0-e1c7-ced4aee4ea8c","credentials": {"enabled": "No"},"prevStatus": "Processing","origin": "do_11323724954450329611930","streamingUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/ecml/do_1132372524622561281279-latest","medium": ["English"],"idealScreenSize": "normal","createdOn": "2021-03-16T06:19:04.712+0000","se_boards": ["CBSE"],"processId": "9995e013-a7c9-4da1-b2c9-2f59da33414a","contentDisposition": "inline","lastUpdatedOn": "2021-03-16T06:19:20.817+0000","originData": {"identifier": "do_11323724954450329611930","repository": "https://dock.sunbirded.org/api/content/v1/read/do_11323724954450329611930"},"collectionId": "do_11323721176353996811921","dialcodeRequired": "No","editorVersion": 3,"lastStatusChangedOn": "2021-03-16T06:19:26.147+0000","creator": "N18","os": ["All"],"questionCategories": ["MTF"],"cloudStorageKey": "content/do_1132372524622561281279/artifact/1615875430184_do_11323724954450329611930.zip","se_FWIds": ["ekstep_ncert_k-12"],"marks": "2","bloomsLevel": ["Apply"],"pkgVersion": 1.0,"versionKey": "1615875560817","idealScreenDensity": "hdpi","framework": "ekstep_ncert_k-12","depth": 3,"s3Key": "ecar_files/do_1132372524622561281279/untitled_1615875562931_do_1132372524622561281279_1.0.ecar","lastSubmittedOn": "2021-03-16T06:19:17.005+0000","createdBy": "60f91e9e-34ee-4f9f-a907-d312d0e8063e","se_topics": ["Speed, Distance and Time"],"compatibilityLevel": 1,"itemSetPreviewUrl": "https://dockstorage.blob.core.windows.net/sunbird-content-dock/content/do_11323724954450329611930/artifact/do_11323724954450329611930_1615875429226.pdf","board": "CBSE","programId": "800eb440-8613-11eb-a663-4f63bbe94184"}],"contentDisposition": "inline","lastUpdatedOn": "2021-05-20T08:58:33.466+0000","contentEncoding": "gzip","contentType": "TextBookUnit","dialcodeRequired": "Yes","identifier": "do_1132833371215134721712","lastStatusChangedOn": "2021-05-20T08:58:33.466+0000","audience": ["Student"],"os": ["All"],"visibility": "Parent","discussionForum": {"enabled": "Yes"},"index": 1,"mediaType": "content","osId": "org.ekstep.launcher","languageCode": ["en"],"version": 2,"versionKey": "1621501113466","license": "CC BY 4.0","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 2,"compatibilityLevel": 1,"name": "5.1 Parts of Body","topic": ["Role Of The Sense Organs"],"status": "Draft"},{"ownershipType": ["createdBy"],"parent": "do_2222","code": "do_1132828084877066241529","keywords": [],"credentials": {"enabled": "No"},"channel": "0126825293972439041","description": "","language": ["English"],"mimeType": "application/vnd.ekstep.content-collection","idealScreenSize": "normal","createdOn": "2021-05-20T08:58:33.476+0000","objectType": "Content","primaryCategory": "Textbook Unit","children": [{"ownershipType": ["createdBy"],"parent": "do_1132833371215953921722","code": "do_1132828084876492801521","keywords": ["test key","check"],"credentials": {"enabled": "No"},"channel": "0126825293972439041","description": "","language": ["English"],"mimeType": "application/vnd.ekstep.content-collection","idealScreenSize": "normal","createdOn": "2021-05-20T08:58:33.468+0000","objectType": "Content","primaryCategory": "Textbook Unit","children": [{"ownershipType": ["createdBy"],"parent": "do_1132833371215298561714","code": "do_1132828084876820481527","keywords": ["abcd","cgf"],"credentials": {"enabled": "No"},"channel": "0126825293972439041","description": "labeled new","language": ["English"],"mimeType": "application/vnd.ekstep.content-collection","idealScreenSize": "normal","createdOn": "2021-05-20T08:58:33.464+0000","objectType": "Content","primaryCategory": "Textbook Unit","children": [{"ownershipType": ["createdBy"],"parent": "do_1132833371214970881710","previewUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/assets/do_1132338069147811841118/test-874-kb.mp4","channel": "b00bc992ef25f1a9a8d63291e20efc8d","downloadUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_1132338069147811841118/untitled-content_1615468830522_do_1132338069147811841118_2.0.ecar","organisation": ["Sunbird"],"language": ["English"],"mimeType": "video/mp4","variants": {"spine": {"ecarUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_1132338069147811841118/untitled-content_1615468834470_do_1132338069147811841118_2.0_spine.ecar","size": 1361.0}},"objectType": "Content","primaryCategory": "Learning Resource","appId": "dev.sunbird.portal","contentEncoding": "identity","artifactUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/assets/do_1132338069147811841118/test-874-kb.mp4","lockKey": "d73707c8-9999-4fc9-9b34-0207f74faf43","sYS_INTERNAL_LAST_UPDATED_ON": "2021-03-12T08:10:31.335+0000","contentType": "Resource","trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_1132338069147811841118","audience": ["Student"],"visibility": "Default","consumerId": "273f3b18-5dda-4a27-984a-060c7cd398d3","index": 1,"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"],"lastPublishedBy": "8454cb21-3ce9-4e30-85b5-fade097880d8","version": 2,"license": "CC BY 4.0","prevState": "Live","size": 849876.0,"lastPublishedOn": "2021-03-11T13:20:30.514+0000","name": "Untitled Content","status": "Live","code": "9deb2c69-7240-472a-98e7-ed438e76262b","credentials": {"enabled": "No"},"prevStatus": "Processing","streamingUrl": "https://sunbirddevmedia-inct.streaming.media.azure.net/f17bccc5-cab3-4da8-a5eb-11d7211f1507/test-874-kb.ism/manifest(format=m3u8-aapl-v3)","idealScreenSize": "normal","createdOn": "2021-03-11T09:29:05.654+0000","contentDisposition": "inline","lastUpdatedOn": "2021-03-11T13:20:28.256+0000","dialcodeRequired": "No","lastStatusChangedOn": "2021-03-11T18:50:28.256+0530","createdFor": ["ORG_001"],"creator": "Reviewer User","os": ["All"],"se_FWIds": ["NCFCOPY"],"pkgVersion": 2.0,"versionKey": "1615455090358","idealScreenDensity": "hdpi","framework": "NCFCOPY","depth": 5,"s3Key": "ecar_files/do_1132338069147811841118/untitled-content_1615468830522_do_1132338069147811841118_2.0.ecar","createdBy": "95e4942d-cbe8-477d-aebd-ad8e6de4bfc8","compatibilityLevel": 1,"resourceType": "Learn"},{"ownershipType": ["createdBy"],"parent": "do_1132833371214970881710","unitIdentifiers": ["do_1132238266042040321422"],"copyright": "2021 MIT","previewUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/ecml/do_11322383952751820816-latest","plugins": [{"identifier": "org.sunbird.questionunit.quml","semanticVersion": "1.1"}],"subject": ["Hindi"],"channel": "01309282781705830427","downloadUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_11322383952751820816/sapractice_1614238238045_do_11322383952751820816_1.0.ecar","language": ["English"],"source": "https://dock.sunbirded.org/api/content/v1/read/do_1132238287156183041424","mimeType": "application/vnd.ekstep.ecml-archive","variants": {"spine": {"ecarUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_11322383952751820816/sapractice_1614238238800_do_11322383952751820816_1.0_spine.ecar","size": 13171.0}},"objectType": "Content","se_mediums": ["English"],"gradeLevel": ["Class 10"],"primaryCategory": "Exam Question","appId": "dev.dock.portal","contentEncoding": "gzip","artifactUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/do_11322383952751820816/artifact/1614237122171_do_1132238287156183041424.zip","sYS_INTERNAL_LAST_UPDATED_ON": "2021-02-25T07:30:44.916+0000","contentType": "Resource","se_gradeLevels": ["Class 10"],"trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_11322383952751820816","audience": ["Student"],"visibility": "Default","author": "anusha","maxQuestions": 1,"consumerId": "273f3b18-5dda-4a27-984a-060c7cd398d3","index": 2,"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"],"lastPublishedBy": "5a587cc1-e018-4859-a0a8-e842650b9d64","version": 2,"se_subjects": ["Hindi"],"license": "CC BY 4.0","prevState": "Review","size": 362236.0,"lastPublishedOn": "2021-02-25T07:30:38.043+0000","name": "sa:practice","status": "Live","code": "f239c77e-ed71-9133-0145-7468a92bce79","credentials": {"enabled": "No"},"prevStatus": "Processing","origin": "do_1132238287156183041424","streamingUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/ecml/do_11322383952751820816-latest","medium": ["English"],"idealScreenSize": "normal","createdOn": "2021-02-25T07:30:23.577+0000","se_boards": ["CBSE"],"processId": "04d5aec9-ed09-4a57-963d-9fa654fecf8d","contentDisposition": "inline","lastUpdatedOn": "2021-02-25T07:30:37.956+0000","originData": {"identifier": "do_1132238287156183041424","repository": "https://dock.sunbirded.org/api/content/v1/read/do_1132238287156183041424"},"collectionId": "do_1132238266036551681415","dialcodeRequired": "No","editorVersion": 3,"lastStatusChangedOn": "2021-02-25T07:30:44.908+0000","creator": "anusha","os": ["All"],"questionCategories": ["SA"],"cloudStorageKey": "content/do_11322383952751820816/artifact/1614237122171_do_1132238287156183041424.zip","se_FWIds": ["ekstep_ncert_k-12"],"marks": "12","pkgVersion": 1.0,"versionKey": "1614238237956","idealScreenDensity": "hdpi","framework": "ekstep_ncert_k-12","depth": 5,"s3Key": "ecar_files/do_11322383952751820816/sapractice_1614238238045_do_11322383952751820816_1.0.ecar","lastSubmittedOn": "2021-02-25T07:30:36.709+0000","createdBy": "19ba0e4e-9285-4335-8dd0-f674bf03fa4d","compatibilityLevel": 1,"itemSetPreviewUrl": "https://dockstorage.blob.core.windows.net/sunbird-content-dock/content/do_1132238287156183041424/artifact/do_1132238287156183041424_1614237121022.pdf","board": "CBSE","programId": "94564340-7737-11eb-96e0-29a9f8ed81cf"},{"ownershipType": ["createdBy"],"parent": "do_1132833371214970881710","unitIdentifiers": ["do_11322165488232038412588"],"copyright": "2021 MIT","previewUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/ecml/do_1132216902566133761410-latest","plugins": [{"identifier": "org.sunbird.questionunit.quml","semanticVersion": "1.1"}],"subject": ["Environmental Studies"],"channel": "01309282781705830427","downloadUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_1132216902566133761410/mcqmcq_1613975872529_do_1132216902566133761410_1.0.ecar","language": ["English"],"source": "https://dock.sunbirded.org/api/content/v1/read/do_11322168163282944012605","mimeType": "application/vnd.ekstep.ecml-archive","variants": {"spine": {"ecarUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_1132216902566133761410/mcqmcq_1613975873161_do_1132216902566133761410_1.0_spine.ecar","size": 17182.0}},"objectType": "Content","se_mediums": ["English"],"gradeLevel": ["Class 10"],"primaryCategory": "Exam Question","appId": "dev.dock.portal","contentEncoding": "gzip","artifactUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/do_1132216902566133761410/artifact/1613975740738_do_11322168163282944012605.zip","sYS_INTERNAL_LAST_UPDATED_ON": "2021-02-22T06:37:55.328+0000","contentType": "Resource","se_gradeLevels": ["Class 10"],"trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_1132216902566133761410","audience": ["Student"],"visibility": "Default","author": "color4","maxQuestions": 1,"consumerId": "7411b6bd-89f3-40ec-98d1-229dc64ce77d","learningOutcome": ["Understand the importance of values in life"],"index": 3,"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"],"lastPublishedBy": "ae94b68c-a535-4dce-8e7a-fb9662b0ad68","version": 2,"se_subjects": ["Environmental Studies"],"license": "CC BY 4.0","prevState": "Review","size": 370363.0,"lastPublishedOn": "2021-02-22T06:37:52.529+0000","name": "MCQMCQ","topic": ["Animals"],"status": "Live","code": "0cbae0f8-e3eb-1d31-e2e5-0337dc7d697d","credentials": {"enabled": "No"},"prevStatus": "Processing","origin": "do_11322168163282944012605","streamingUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/ecml/do_1132216902566133761410-latest","medium": ["English"],"idealScreenSize": "normal","createdOn": "2021-02-22T06:37:41.405+0000","se_boards": ["CBSE"],"processId": "fbcec2af-cb7a-4ed1-8683-ff04b475947e","contentDisposition": "inline","lastUpdatedOn": "2021-02-22T06:37:52.447+0000","originData": {"identifier": "do_11322168163282944012605","repository": "https://dock.sunbirded.org/api/content/v1/read/do_11322168163282944012605"},"collectionId": "do_11322165488181248012584","dialcodeRequired": "No","editorVersion": 3,"lastStatusChangedOn": "2021-02-22T06:37:55.314+0000","creator": "color4","os": ["All"],"questionCategories": ["MCQ"],"cloudStorageKey": "content/do_1132216902566133761410/artifact/1613975740738_do_11322168163282944012605.zip","se_FWIds": ["ekstep_ncert_k-12"],"marks": "1","bloomsLevel": ["Understand"],"pkgVersion": 1.0,"versionKey": "1613975872447","idealScreenDensity": "hdpi","framework": "ekstep_ncert_k-12","depth": 5,"s3Key": "ecar_files/do_1132216902566133761410/mcqmcq_1613975872529_do_1132216902566133761410_1.0.ecar","lastSubmittedOn": "2021-02-22T06:37:51.179+0000","createdBy": "0ce5b67e-b48e-489b-a818-e938e8bfc14b","se_topics": ["Animals"],"compatibilityLevel": 1,"itemSetPreviewUrl": "https://dockstorage.blob.core.windows.net/sunbird-content-dock/content/do_11322168163282944012605/artifact/do_11322168163282944012605_1613975739805.pdf","board": "CBSE","programId": "b2433a00-74cd-11eb-9f3c-f39a9ab9f5ce"}],"contentDisposition": "inline","lastUpdatedOn": "2021-05-20T08:58:33.464+0000","contentEncoding": "gzip","contentType": "TextBookUnit","dialcodeRequired": "Yes","identifier": "do_1132833371214970881710","lastStatusChangedOn": "2021-05-20T08:58:33.464+0000","audience": ["Student"],"os": ["All"],"visibility": "Parent","discussionForum": {"enabled": "Yes"},"index": 1,"mediaType": "content","osId": "org.ekstep.launcher","languageCode": ["en"],"version": 2,"versionKey": "1621501113464","license": "CC BY 4.0","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 4,"compatibilityLevel": 1,"name": "dsffgdg","topic": [],"status": "Draft"}],"contentDisposition": "inline","lastUpdatedOn": "2021-05-20T08:58:33.468+0000","contentEncoding": "gzip","contentType": "TextBookUnit","dialcodeRequired": "No","identifier": "do_1132833371215298561714","lastStatusChangedOn": "2021-05-20T08:58:33.468+0000","audience": ["Student"],"os": ["All"],"visibility": "Parent","discussionForum": {"enabled": "Yes"},"index": 1,"mediaType": "content","osId": "org.ekstep.launcher","languageCode": ["en"],"version": 2,"versionKey": "1621501113468","license": "CC BY 4.0","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 3,"compatibilityLevel": 1,"name": "5.2.1 Respiratory System","topic": ["Look and say","Role Of The Sense Organs"],"status": "Draft"}],"contentDisposition": "inline","lastUpdatedOn": "2021-05-20T08:58:33.476+0000","contentEncoding": "gzip","contentType": "TextBookUnit","dialcodeRequired": "No","identifier": "do_1132833371215953921722","lastStatusChangedOn": "2021-05-20T08:58:33.476+0000","audience": ["Student"],"os": ["All"],"visibility": "Parent","discussionForum": {"enabled": "Yes"},"index": 2,"mediaType": "content","osId": "org.ekstep.launcher","languageCode": ["en"],"version": 2,"versionKey": "1621501113476","license": "CC BY 4.0","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 2,"compatibilityLevel": 1,"name": "5.2 Organ Systems","topic": [],"status": "Draft"},{"ownershipType": ["createdBy"],"parent": "do_2222","copyright": "Sunbird","previewUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/assets/do_1132344630588948481134/test-874-kb.mp4","subject": ["Math"],"channel": "b00bc992ef25f1a9a8d63291e20efc8d","downloadUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_1132344630588948481134/untitled-content_1615535618825_do_1132344630588948481134_2.0.ecar","organisation": ["Sunbird"],"showNotification": true,"language": ["English"],"mimeType": "video/mp4","variants": {"spine": {"ecarUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_1132344630588948481134/untitled-content_1615535619590_do_1132344630588948481134_2.0_spine.ecar","size": 35301.0}},"objectType": "Content","se_mediums": ["English"],"gradeLevel": ["Grade 1"],"appIcon": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/do_1132344630588948481134/artifact/2a4b8abd789184932399d222d03d9b5c.thumb.jpg","primaryCategory": "Learning Resource","appId": "dev.sunbird.portal","contentEncoding": "identity","artifactUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/assets/do_1132344630588948481134/test-874-kb.mp4","lockKey": "1d28d983-2704-44bd-803e-5feb4e62da62","sYS_INTERNAL_LAST_UPDATED_ON": "2021-03-12T08:10:34.367+0000","contentType": "Resource","se_gradeLevels": ["Grade 1"],"trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_1132344630588948481134","lastUpdatedBy": "95e4942d-cbe8-477d-aebd-ad8e6de4bfc8","audience": ["Student"],"visibility": "Default","consumerId": "273f3b18-5dda-4a27-984a-060c7cd398d3","index": 3,"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"],"lastPublishedBy": "8454cb21-3ce9-4e30-85b5-fade097880d8","version": 2,"se_subjects": ["Math"],"license": "CC BY 4.0","prevState": "Review","size": 883817.0,"lastPublishedOn": "2021-03-12T07:53:38.825+0000","name": "Untitled Content","status": "Live","code": "8851e754-6e20-44d4-9070-e1a9664163ad","credentials": {"enabled": "No"},"prevStatus": "Review","description": "updated desrciption","streamingUrl": "https://sunbirddevmedia-inct.streaming.media.azure.net/40ae07aa-069e-4056-8f2b-014bc9a2d21b/test-874-kb.ism/manifest(format=m3u8-aapl-v3)","medium": ["English"],"posterImage": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/do_11299104587967692816/artifact/2a4b8abd789184932399d222d03d9b5c.jpg","idealScreenSize": "normal","createdOn": "2021-03-12T07:44:01.371+0000","se_boards": ["NCERT"],"copyrightYear": 2020,"contentDisposition": "inline","licenseterms": "By creating any type of content (resources, books, courses etc.) on DIKSHA, you consent to publish it under the Creative Commons License Framework. Please choose the applicable creative commons license you wish to apply to your content.","lastUpdatedOn": "2021-03-12T07:53:38.505+0000","dialcodeRequired": "No","lastStatusChangedOn": "2021-03-12T07:53:38.494+0000","createdFor": ["ORG_001"],"creator": "Reviewer User","os": ["All"],"se_FWIds": ["NCFCOPY"],"pkgVersion": 2.0,"versionKey": "1615535618583","idealScreenDensity": "hdpi","framework": "NCFCOPY","depth": 2,"s3Key": "ecar_files/do_1132344630588948481134/untitled-content_1615535618825_do_1132344630588948481134_2.0.ecar","lastSubmittedOn": "2021-03-12T07:53:10.005+0000","createdBy": "95e4942d-cbe8-477d-aebd-ad8e6de4bfc8","compatibilityLevel": 1,"board": "NCERT","resourceType": "Learn"}],"contentDisposition": "inline","lastUpdatedOn": "2021-05-20T08:58:33.470+0000","contentEncoding": "gzip","contentType": "TextBookUnit","dialcodeRequired": "No","identifier": "do_2222","lastStatusChangedOn": "2021-05-20T08:58:33.470+0000","audience": ["Student"],"os": ["All"],"visibility": "Parent","discussionForum": {"enabled": "Yes"},"index": 1,"mediaType": "content","osId": "org.ekstep.launcher","languageCode": ["en"],"version": 2,"versionKey": "1621501113470","license": "CC BY 4.0","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 1,"compatibilityLevel": 1,"name": "5. Human Body","topic": [],"status": "Draft"}],"appId": "dev.sunbird.portal","contentEncoding": "gzip","sYS_INTERNAL_LAST_UPDATED_ON": "2021-05-20T09:12:06.988+0000","contentType": "TextBook","trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_1111","audience": ["Student"],"visibility": "Default","consumerId": "01814e02-fc27-4165-ae53-3d1816e55817","childNodes": ["do_1132339274094346241120","do_1132833371215872001720","do_1132833371215134721712","do_2222","do_113223967141863424174","do_1132833371214970881710","do_1132833371215708161718","do_1132372524622561281279","do_1132338069147811841118","do_1132833371215298561714","do_1132833371215953921722","do_11322383952751820816","do_1132216902566133761410","do_1132344630588948481134"],"discussionForum": {"enabled": "Yes"},"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"],"version": 2,"license": "CC BY 4.0","name": "TestCSVUpload","status": "Draft","code": "org.sunbird.yhqB6L","credentials": {"enabled": "No"},"description": "Enter description for TextBook","medium": ["English"],"idealScreenSize": "normal","createdOn": "2021-05-19T15:00:44.279+0000","contentDisposition": "inline","additionalCategories": ["Textbook"],"lastUpdatedOn": "2021-05-20T07:10:32.805+0000","dialcodeRequired": "No","lastStatusChangedOn": "2021-05-19T15:00:44.279+0000","createdFor": ["0126825293972439041"],"creator": "Book Creator","os": ["All"],"versionKey": "1621501113536","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 0,"createdBy": "8454cb21-3ce9-4e30-85b5-fade097880d8","compatibilityLevel": 1,"userConsent": "Yes","board": "State (Tamil Nadu)","resourceType": "Book"}""".stripMargin + |"lastPublishedOn": "2021-03-21T13:06:11.272+0000","name": "Untitled Content","status": "Live","code": "a88b0257-670b-455b-98b8-6e359ebac009","credentials": {"enabled": "No"},"prevStatus": "Processing","description": "updated","streamingUrl": "https://sunbirddevmedia-inct.streaming.media.azure.net/9c0ebb33-af08-403f-afb4-eb24749f40a1/test-874-kb.ism/manifest(format=m3u8-aapl-v3)","idealScreenSize": "normal","createdOn": "2021-03-11T13:34:14.475+0000","contentDisposition": "inline","lastUpdatedOn": "2021-03-21T13:06:09.526+0000","dialcodeRequired": "No","lastStatusChangedOn": "2021-03-21T18:36:15.799+0530","createdFor": ["ORG_001"],"creator": "Reviewer User","os": ["All"],"se_FWIds": ["NCFCOPY"],"pkgVersion": 14.0,"versionKey": "1616331969523","idealScreenDensity": "hdpi","framework": "NCFCOPY","depth": 4,"s3Key": "ecar_files/do_1132339274094346241120/untitled-content_1616331971279_do_1132339274094346241120_14.0.ecar","createdBy": "95e4942d-cbe8-477d-aebd-ad8e6de4bfc8","compatibilityLevel": 1,"resourceType": "Learn"},{"ownershipType": ["createdBy"],"parent": "do_1132833371215872001720","unitIdentifiers": ["do_1132239562839900161634"],"copyright": "2021 MIT","previewUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/ecml/do_113223967141863424174-latest","plugins": [{"identifier": "org.sunbird.questionunit.quml","semanticVersion": "1.1"}],"subject": ["Hindi"],"channel": "01309282781705830427","downloadUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_113223967141863424174/esa_1614253812772_do_113223967141863424174_1.0.ecar","language": ["English"],"source": "https://dock.sunbirded.org/api/content/v1/read/do_1132239617341767681638","mimeType": "application/vnd.ekstep.ecml-archive","variants": {"spine": {"ecarUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_113223967141863424174/esa_1614253813394_do_113223967141863424174_1.0_spine.ecar","size": 24293.0}},"objectType": "Content","se_mediums": ["English"],"gradeLevel": ["Class 10"],"primaryCategory": "Exam Question","appId": "dev.dock.portal","contentEncoding": "gzip","artifactUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/do_113223967141863424174/artifact/1614253223147_do_1132239617341767681638.zip","sYS_INTERNAL_LAST_UPDATED_ON": "2021-02-25T11:50:16.032+0000","contentType": "Resource","se_gradeLevels": ["Class 10"],"trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_113223967141863424174","audience": ["Student"],"visibility": "Default","author": "anusha","maxQuestions": 1,"consumerId": "b7054510-3ca4-49fd-b373-b100b3f65e18","index": 2,"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"],"lastPublishedBy": "5a587cc1-e018-4859-a0a8-e842650b9d64","version": 2,"se_subjects": ["Hindi"],"license": "CC BY 4.0","prevState": "Review","size": 384798.0,"lastPublishedOn": "2021-02-25T11:50:12.771+0000","name": "esa","topic": ["तोप"],"status": "Live","code": "d19f43ce-753d-2c70-a9fd-70302af424a0","credentials": {"enabled": "No"},"prevStatus": "Processing","origin": "do_1132239617341767681638","streamingUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/ecml/do_113223967141863424174-latest","medium": ["English"],"idealScreenSize": "normal","createdOn": "2021-02-25T11:50:01.500+0000","se_boards": ["CBSE"],"processId": "2b00cca7-42d5-4e35-aa03-1c22dfe03de8","contentDisposition": "inline","lastUpdatedOn": "2021-02-25T11:50:12.678+0000","originData": {"identifier": "do_1132239617341767681638","repository": "https://dock.sunbirded.org/api/content/v1/read/do_1132239617341767681638"},"collectionId": "do_1132239562836049921627","dialcodeRequired": "No","editorVersion": 3,"lastStatusChangedOn": "2021-02-25T11:50:16.017+0000","creator": "anusha","os": ["All"],"questionCategories": ["SA"],"cloudStorageKey": "content/do_113223967141863424174/artifact/1614253223147_do_1132239617341767681638.zip","se_FWIds": ["ekstep_ncert_k-12"],"marks": "12","bloomsLevel": ["Apply"],"pkgVersion": 1.0,"versionKey": "1614253812678","idealScreenDensity": "hdpi","framework": "ekstep_ncert_k-12","depth": 4,"s3Key": "ecar_files/do_113223967141863424174/esa_1614253812772_do_113223967141863424174_1.0.ecar","lastSubmittedOn": "2021-02-25T11:50:11.539+0000","createdBy": "19ba0e4e-9285-4335-8dd0-f674bf03fa4d","se_topics": ["तोप"],"compatibilityLevel": 1,"itemSetPreviewUrl": "https://dockstorage.blob.core.windows.net/sunbird-content-dock/content/do_1132239617341767681638/artifact/do_1132239617341767681638_1614253222002.pdf","board": "CBSE","programId": "463cfa30-775c-11eb-8c56-93946e419809"}],"contentDisposition": "inline","lastUpdatedOn": "2021-05-20T08:58:33.475+0000","contentEncoding": "gzip","contentType": "TextBookUnit","dialcodeRequired": "Yes","identifier": "do_1132833371215872001720","lastStatusChangedOn": "2021-05-20T08:58:33.475+0000","audience": ["Student"],"os": ["All"],"visibility": "Parent","discussionForum": {"enabled": "Yes"},"index": 1,"mediaType": "content","osId": "org.ekstep.launcher","languageCode": ["en"],"version": 2,"versionKey": "1621501113475","license": "CC BY 4.0","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 3,"compatibilityLevel": 1,"name": "5.1.1 Key parts in the head","topic": [],"status": "Draft"},{"ownershipType": ["createdBy"],"parent": "do_1132833371215134721712","code": "do_1132828084876165121519","keywords": [],"credentials": {"enabled": "No"},"channel": "0126825293972439041","description": "","language": ["English"],"mimeType": "application/vnd.ekstep.content-collection","idealScreenSize": "normal","createdOn": "2021-05-20T08:58:33.473+0000","objectType": "Content","primaryCategory": "Textbook Unit","contentDisposition": "inline","lastUpdatedOn": "2021-05-20T08:58:33.473+0000","contentEncoding": "gzip","contentType": "TextBookUnit","dialcodeRequired": "No","identifier": "do_1132833371215708161718","lastStatusChangedOn": "2021-05-20T08:58:33.473+0000","audience": ["Student"],"os": ["All"],"visibility": "Parent","discussionForum": {"enabled": "Yes"},"index": 2,"mediaType": "content","osId": "org.ekstep.launcher","languageCode": ["en"],"version": 2,"versionKey": "1621501113473","license": "CC BY 4.0","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 3,"compatibilityLevel": 1,"name": "5.1.2 Other parts","topic": [],"status": "Draft"},{"ownershipType": ["createdBy"],"parent": "do_1132833371215134721712","unitIdentifiers": ["do_11323721176414617611924"],"copyright": "2021 MIT","organisationId": "e7328d77-42a7-44c8-84f4-8cfea235f07d","previewUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/ecml/do_1132372524622561281279-latest","plugins": [{"identifier": "org.sunbird.questionunit.quml","semanticVersion": "1.1"}],"subject": ["Mathematics"],"channel": "01309282781705830427","downloadUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_1132372524622561281279/untitled_1615875562931_do_1132372524622561281279_1.0.ecar","language": ["English"],"source": "https://dock.sunbirded.org/api/content/v1/read/do_11323724954450329611930","mimeType": "application/vnd.ekstep.ecml-archive","variants": {"spine": {"ecarUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_1132372524622561281279/untitled_1615875563539_do_1132372524622561281279_1.0_spine.ecar","size": 19563.0}},"objectType": "Content","se_mediums": ["English"],"gradeLevel": ["Class 5"],"primaryCategory": "Exam Question","appId": "dev.dock.portal","contentEncoding": "gzip","artifactUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/do_1132372524622561281279/artifact/1615875430184_do_11323724954450329611930.zip","sYS_INTERNAL_LAST_UPDATED_ON": "2021-03-16T06:19:26.162+0000","contentType": "Resource","se_gradeLevels": ["Class 5"],"trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_1132372524622561281279","audience": ["Student"],"visibility": "Default","author": "N18","maxQuestions": 1,"consumerId": "f73cfcc5-4d43-4fa0-8b81-46166c81bc2b","learningOutcome": ["identify the need to find area and perimeter of rectangle and square."],"index": 3,"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"],"lastPublishedBy": "5a587cc1-e018-4859-a0a8-e842650b9d64","version": 2,"se_subjects": ["Mathematics"],"license": "CC BY 4.0","prevState": "Review","size": 374996.0,"lastPublishedOn": "2021-03-16T06:19:22.931+0000","name": "Untitled","topic": ["Speed, Distance and Time"],"status": "Live","code": "2544c8b8-7946-b6c0-e1c7-ced4aee4ea8c","credentials": {"enabled": "No"},"prevStatus": "Processing","origin": "do_11323724954450329611930","streamingUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/ecml/do_1132372524622561281279-latest","medium": ["English"],"idealScreenSize": "normal","createdOn": "2021-03-16T06:19:04.712+0000","se_boards": ["CBSE"],"processId": "9995e013-a7c9-4da1-b2c9-2f59da33414a","contentDisposition": "inline","lastUpdatedOn": "2021-03-16T06:19:20.817+0000","originData": {"identifier": "do_11323724954450329611930","repository": "https://dock.sunbirded.org/api/content/v1/read/do_11323724954450329611930"},"collectionId": "do_11323721176353996811921","dialcodeRequired": "No","editorVersion": 3,"lastStatusChangedOn": "2021-03-16T06:19:26.147+0000","creator": "N18","os": ["All"],"questionCategories": ["MTF"],"cloudStorageKey": "content/do_1132372524622561281279/artifact/1615875430184_do_11323724954450329611930.zip","se_FWIds": ["ekstep_ncert_k-12"],"marks": "2","bloomsLevel": ["Apply"],"pkgVersion": 1.0,"versionKey": "1615875560817","idealScreenDensity": "hdpi","framework": "ekstep_ncert_k-12","depth": 3,"s3Key": "ecar_files/do_1132372524622561281279/untitled_1615875562931_do_1132372524622561281279_1.0.ecar","lastSubmittedOn": "2021-03-16T06:19:17.005+0000","createdBy": "60f91e9e-34ee-4f9f-a907-d312d0e8063e","se_topics": ["Speed, Distance and Time"],"compatibilityLevel": 1,"itemSetPreviewUrl": "https://dockstorage.blob.core.windows.net/sunbird-content-dock/content/do_11323724954450329611930/artifact/do_11323724954450329611930_1615875429226.pdf","board": "CBSE","programId": "800eb440-8613-11eb-a663-4f63bbe94184"}],"contentDisposition": "inline","lastUpdatedOn": "2021-05-20T08:58:33.466+0000","contentEncoding": "gzip","contentType": "TextBookUnit","dialcodeRequired": "Yes","identifier": "do_1132833371215134721712","lastStatusChangedOn": "2021-05-20T08:58:33.466+0000","audience": ["Student"],"os": ["All"],"visibility": "Parent","discussionForum": {"enabled": "Yes"},"index": 1,"mediaType": "content","osId": "org.ekstep.launcher","languageCode": ["en"],"version": 2,"versionKey": "1621501113466","license": "CC BY 4.0","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 2,"compatibilityLevel": 1,"name": "5.1 Parts of Body","topic": ["Role Of The Sense Organs"],"status": "Draft"},{"ownershipType": ["createdBy"],"parent": "do_2222","code": "do_1132828084877066241529","keywords": [],"credentials": {"enabled": "No"},"channel": "0126825293972439041","description": "","language": ["English"],"mimeType": "application/vnd.ekstep.content-collection","idealScreenSize": "normal","createdOn": "2021-05-20T08:58:33.476+0000","objectType": "Content","primaryCategory": "Textbook Unit","children": [{"ownershipType": ["createdBy"],"parent": "do_1132833371215953921722","code": "do_1132828084876492801521","keywords": ["test key","check"],"credentials": {"enabled": "No"},"channel": "0126825293972439041","description": "","language": ["English"],"mimeType": "application/vnd.ekstep.content-collection","idealScreenSize": "normal","createdOn": "2021-05-20T08:58:33.468+0000","objectType": "Content","primaryCategory": "Textbook Unit","children": [{"ownershipType": ["createdBy"],"parent": "do_1132833371215298561714","code": "do_1132828084876820481527","keywords": ["abcd","cgf"],"credentials": {"enabled": "No"},"channel": "0126825293972439041","description": "labeled new","language": ["English"],"mimeType": "application/vnd.ekstep.content-collection","idealScreenSize": "normal","createdOn": "2021-05-20T08:58:33.464+0000","objectType": "Content","primaryCategory": "Textbook Unit","children": [{"ownershipType": ["createdBy"],"parent": "do_1132833371214970881710","previewUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/assets/do_1132338069147811841118/test-874-kb.mp4","channel": "b00bc992ef25f1a9a8d63291e20efc8d","downloadUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_1132338069147811841118/untitled-content_1615468830522_do_1132338069147811841118_2.0.ecar","organisation": ["Sunbird"],"language": ["English"],"mimeType": "video/mp4","variants": {"spine": {"ecarUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_1132338069147811841118/untitled-content_1615468834470_do_1132338069147811841118_2.0_spine.ecar","size": 1361.0}},"objectType": "Content","primaryCategory": "Learning Resource","appId": "dev.sunbird.portal","contentEncoding": "identity","artifactUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/assets/do_1132338069147811841118/test-874-kb.mp4","lockKey": "d73707c8-9999-4fc9-9b34-0207f74faf43","sYS_INTERNAL_LAST_UPDATED_ON": "2021-03-12T08:10:31.335+0000","contentType": "Resource","trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_1132338069147811841118","audience": ["Student"],"visibility": "Default","consumerId": "273f3b18-5dda-4a27-984a-060c7cd398d3","index": 1,"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"],"lastPublishedBy": "8454cb21-3ce9-4e30-85b5-fade097880d8","version": 2,"license": "CC BY 4.0","prevState": "Live","size": 849876.0,"lastPublishedOn": "2021-03-11T13:20:30.514+0000","name": "Untitled Content","status": "Live","code": "9deb2c69-7240-472a-98e7-ed438e76262b","credentials": {"enabled": "No"},"prevStatus": "Processing","streamingUrl": "https://sunbirddevmedia-inct.streaming.media.azure.net/f17bccc5-cab3-4da8-a5eb-11d7211f1507/test-874-kb.ism/manifest(format=m3u8-aapl-v3)","idealScreenSize": "normal","createdOn": "2021-03-11T09:29:05.654+0000","contentDisposition": "inline","lastUpdatedOn": "2021-03-11T13:20:28.256+0000","dialcodeRequired": "No","lastStatusChangedOn": "2021-03-11T18:50:28.256+0530","createdFor": ["ORG_001"],"creator": "Reviewer User","os": ["All"],"se_FWIds": ["NCFCOPY"],"pkgVersion": 2.0,"versionKey": "1615455090358","idealScreenDensity": "hdpi","framework": "NCFCOPY","depth": 5,"s3Key": "ecar_files/do_1132338069147811841118/untitled-content_1615468830522_do_1132338069147811841118_2.0.ecar","createdBy": "95e4942d-cbe8-477d-aebd-ad8e6de4bfc8","compatibilityLevel": 1,"resourceType": "Learn"},{"ownershipType": ["createdBy"],"parent": "do_1132833371214970881710","unitIdentifiers": ["do_1132238266042040321422"],"copyright": "2021 MIT","previewUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/ecml/do_11322383952751820816-latest","plugins": [{"identifier": "org.sunbird.questionunit.quml","semanticVersion": "1.1"}],"subject": ["Hindi"],"channel": "01309282781705830427","downloadUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_11322383952751820816/sapractice_1614238238045_do_11322383952751820816_1.0.ecar","language": ["English"],"source": "https://dock.sunbirded.org/api/content/v1/read/do_1132238287156183041424","mimeType": "application/vnd.ekstep.ecml-archive","variants": {"spine": {"ecarUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_11322383952751820816/sapractice_1614238238800_do_11322383952751820816_1.0_spine.ecar","size": 13171.0}},"objectType": "Content","se_mediums": ["English"],"gradeLevel": ["Class 10"],"primaryCategory": "Exam Question","appId": "dev.dock.portal","contentEncoding": "gzip","artifactUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/do_11322383952751820816/artifact/1614237122171_do_1132238287156183041424.zip","sYS_INTERNAL_LAST_UPDATED_ON": "2021-02-25T07:30:44.916+0000","contentType": "Resource","se_gradeLevels": ["Class 10"],"trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_11322383952751820816","audience": ["Student"],"visibility": "Default","author": "anusha","maxQuestions": 1,"consumerId": "273f3b18-5dda-4a27-984a-060c7cd398d3","index": 2,"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"],"lastPublishedBy": "5a587cc1-e018-4859-a0a8-e842650b9d64","version": 2,"se_subjects": ["Hindi"],"license": "CC BY 4.0","prevState": "Review","size": 362236.0,"lastPublishedOn": "2021-02-25T07:30:38.043+0000","name": "sa:practice","status": "Live","code": "f239c77e-ed71-9133-0145-7468a92bce79","credentials": {"enabled": "No"},"prevStatus": "Processing","origin": "do_1132238287156183041424","streamingUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/ecml/do_11322383952751820816-latest","medium": ["English"],"idealScreenSize": "normal","createdOn": "2021-02-25T07:30:23.577+0000","se_boards": ["CBSE"],"processId": "04d5aec9-ed09-4a57-963d-9fa654fecf8d","contentDisposition": "inline","lastUpdatedOn": "2021-02-25T07:30:37.956+0000","originData": {"identifier": "do_1132238287156183041424","repository": "https://dock.sunbirded.org/api/content/v1/read/do_1132238287156183041424"},"collectionId": "do_1132238266036551681415","dialcodeRequired": "No","editorVersion": 3,"lastStatusChangedOn": "2021-02-25T07:30:44.908+0000","creator": "anusha","os": ["All"],"questionCategories": ["SA"],"cloudStorageKey": "content/do_11322383952751820816/artifact/1614237122171_do_1132238287156183041424.zip","se_FWIds": ["ekstep_ncert_k-12"],"marks": "12","pkgVersion": 1.0,"versionKey": "1614238237956","idealScreenDensity": "hdpi","framework": "ekstep_ncert_k-12","depth": 5,"s3Key": "ecar_files/do_11322383952751820816/sapractice_1614238238045_do_11322383952751820816_1.0.ecar","lastSubmittedOn": "2021-02-25T07:30:36.709+0000","createdBy": "19ba0e4e-9285-4335-8dd0-f674bf03fa4d","compatibilityLevel": 1,"itemSetPreviewUrl": "https://dockstorage.blob.core.windows.net/sunbird-content-dock/content/do_1132238287156183041424/artifact/do_1132238287156183041424_1614237121022.pdf","board": "CBSE","programId": "94564340-7737-11eb-96e0-29a9f8ed81cf"},{"ownershipType": ["createdBy"],"parent": "do_1132833371214970881710","unitIdentifiers": ["do_11322165488232038412588"],"copyright": "2021 MIT","previewUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/ecml/do_1132216902566133761410-latest","plugins": [{"identifier": "org.sunbird.questionunit.quml","semanticVersion": "1.1"}],"subject": ["Environmental Studies"],"channel": "01309282781705830427","downloadUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_1132216902566133761410/mcqmcq_1613975872529_do_1132216902566133761410_1.0.ecar","language": ["English"],"source": "https://dock.sunbirded.org/api/content/v1/read/do_11322168163282944012605","mimeType": "application/vnd.ekstep.ecml-archive","variants": {"spine": {"ecarUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_1132216902566133761410/mcqmcq_1613975873161_do_1132216902566133761410_1.0_spine.ecar","size": 17182.0}},"objectType": "Content","se_mediums": ["English"],"gradeLevel": ["Class 10"],"primaryCategory": "Exam Question","appId": "dev.dock.portal","contentEncoding": "gzip","artifactUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/do_1132216902566133761410/artifact/1613975740738_do_11322168163282944012605.zip","sYS_INTERNAL_LAST_UPDATED_ON": "2021-02-22T06:37:55.328+0000","contentType": "Resource","se_gradeLevels": ["Class 10"],"trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_1132216902566133761410","audience": ["Student"],"visibility": "Default","author": "color4","maxQuestions": 1,"consumerId": "7411b6bd-89f3-40ec-98d1-229dc64ce77d","learningOutcome": ["Understand the importance of values in life"],"index": 3,"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"],"lastPublishedBy": "ae94b68c-a535-4dce-8e7a-fb9662b0ad68","version": 2,"se_subjects": ["Environmental Studies"],"license": "CC BY 4.0","prevState": "Review","size": 370363.0,"lastPublishedOn": "2021-02-22T06:37:52.529+0000","name": "MCQMCQ","topic": ["Animals"],"status": "Live","code": "0cbae0f8-e3eb-1d31-e2e5-0337dc7d697d","credentials": {"enabled": "No"},"prevStatus": "Processing","origin": "do_11322168163282944012605","streamingUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/ecml/do_1132216902566133761410-latest","medium": ["English"],"idealScreenSize": "normal","createdOn": "2021-02-22T06:37:41.405+0000","se_boards": ["CBSE"],"processId": "fbcec2af-cb7a-4ed1-8683-ff04b475947e","contentDisposition": "inline","lastUpdatedOn": "2021-02-22T06:37:52.447+0000","originData": {"identifier": "do_11322168163282944012605","repository": "https://dock.sunbirded.org/api/content/v1/read/do_11322168163282944012605"},"collectionId": "do_11322165488181248012584","dialcodeRequired": "No","editorVersion": 3,"lastStatusChangedOn": "2021-02-22T06:37:55.314+0000","creator": "color4","os": ["All"],"questionCategories": ["MCQ"],"cloudStorageKey": "content/do_1132216902566133761410/artifact/1613975740738_do_11322168163282944012605.zip","se_FWIds": ["ekstep_ncert_k-12"],"marks": "1","bloomsLevel": ["Understand"],"pkgVersion": 1.0,"versionKey": "1613975872447","idealScreenDensity": "hdpi","framework": "ekstep_ncert_k-12","depth": 5,"s3Key": "ecar_files/do_1132216902566133761410/mcqmcq_1613975872529_do_1132216902566133761410_1.0.ecar","lastSubmittedOn": "2021-02-22T06:37:51.179+0000","createdBy": "0ce5b67e-b48e-489b-a818-e938e8bfc14b","se_topics": ["Animals"],"compatibilityLevel": 1,"itemSetPreviewUrl": "https://dockstorage.blob.core.windows.net/sunbird-content-dock/content/do_11322168163282944012605/artifact/do_11322168163282944012605_1613975739805.pdf","board": "CBSE","programId": "b2433a00-74cd-11eb-9f3c-f39a9ab9f5ce"}],"contentDisposition": "inline","lastUpdatedOn": "2021-05-20T08:58:33.464+0000","contentEncoding": "gzip","contentType": "TextBookUnit","dialcodeRequired": "Yes","identifier": "do_1132833371214970881710","lastStatusChangedOn": "2021-05-20T08:58:33.464+0000","audience": ["Student"],"os": ["All"],"visibility": "Parent","discussionForum": {"enabled": "Yes"},"index": 1,"mediaType": "content","osId": "org.ekstep.launcher","languageCode": ["en"],"version": 2,"versionKey": "1621501113464","license": "CC BY 4.0","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 4,"compatibilityLevel": 1,"name": "dsffgdg","topic": [],"status": "Draft"}],"contentDisposition": "inline","lastUpdatedOn": "2021-05-20T08:58:33.468+0000","contentEncoding": "gzip","contentType": "TextBookUnit","dialcodeRequired": "No","identifier": "do_1132833371215298561714","lastStatusChangedOn": "2021-05-20T08:58:33.468+0000","audience": ["Student"],"os": ["All"],"visibility": "Parent","discussionForum": {"enabled": "Yes"},"index": 1,"mediaType": "content","osId": "org.ekstep.launcher","languageCode": ["en"],"version": 2,"versionKey": "1621501113468","license": "CC BY 4.0","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 3,"compatibilityLevel": 1,"name": "5.2.1 Respiratory System","topic": ["Look and say","Role Of The Sense Organs"],"status": "Draft"}],"contentDisposition": "inline","lastUpdatedOn": "2021-05-20T08:58:33.476+0000","contentEncoding": "gzip","contentType": "TextBookUnit","dialcodeRequired": "No","identifier": "do_1132833371215953921722","lastStatusChangedOn": "2021-05-20T08:58:33.476+0000","audience": ["Student"],"os": ["All"],"visibility": "Parent","discussionForum": {"enabled": "Yes"},"index": 2,"mediaType": "content","osId": "org.ekstep.launcher","languageCode": ["en"],"version": 2,"versionKey": "1621501113476","license": "CC BY 4.0","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 2,"compatibilityLevel": 1,"name": "5.2 Organ Systems","topic": [],"status": "Draft"},{"ownershipType": ["createdBy"],"parent": "do_2222","copyright": "Sunbird","previewUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/assets/do_1132344630588948481134/test-874-kb.mp4","subject": ["Math"],"channel": "b00bc992ef25f1a9a8d63291e20efc8d","downloadUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_1132344630588948481134/untitled-content_1615535618825_do_1132344630588948481134_2.0.ecar","organisation": ["Sunbird"],"showNotification": true,"language": ["English"],"mimeType": "video/mp4","variants": {"spine": {"ecarUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_1132344630588948481134/untitled-content_1615535619590_do_1132344630588948481134_2.0_spine.ecar","size": 35301.0}},"objectType": "Content","se_mediums": ["English"],"gradeLevel": ["Grade 1"],"appIcon": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/do_1132344630588948481134/artifact/2a4b8abd789184932399d222d03d9b5c.thumb.jpg","primaryCategory": "Learning Resource","appId": "dev.sunbird.portal","contentEncoding": "identity","artifactUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/assets/do_1132344630588948481134/test-874-kb.mp4","lockKey": "1d28d983-2704-44bd-803e-5feb4e62da62","sYS_INTERNAL_LAST_UPDATED_ON": "2021-03-12T08:10:34.367+0000","contentType": "Resource","se_gradeLevels": ["Grade 1"],"trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_1132344630588948481134","lastUpdatedBy": "95e4942d-cbe8-477d-aebd-ad8e6de4bfc8","audience": ["Student"],"visibility": "Default","consumerId": "273f3b18-5dda-4a27-984a-060c7cd398d3","index": 3,"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"],"lastPublishedBy": "8454cb21-3ce9-4e30-85b5-fade097880d8","version": 2,"se_subjects": ["Math"],"license": "CC BY 4.0","prevState": "Review","size": 883817.0,"lastPublishedOn": "2021-03-12T07:53:38.825+0000","name": "Untitled Content","status": "Live","code": "8851e754-6e20-44d4-9070-e1a9664163ad","credentials": {"enabled": "No"},"prevStatus": "Review","description": "updated desrciption","streamingUrl": "https://sunbirddevmedia-inct.streaming.media.azure.net/40ae07aa-069e-4056-8f2b-014bc9a2d21b/test-874-kb.ism/manifest(format=m3u8-aapl-v3)","medium": ["English"],"posterImage": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/do_11299104587967692816/artifact/2a4b8abd789184932399d222d03d9b5c.jpg","idealScreenSize": "normal","createdOn": "2021-03-12T07:44:01.371+0000","se_boards": ["NCERT"],"copyrightYear": 2020,"contentDisposition": "inline","licenseterms": "By creating any type of content (resources, books, courses etc.) on DIKSHA, you consent to publish it under the Creative Commons License Framework. Please choose the applicable creative commons license you wish to apply to your content.","lastUpdatedOn": "2021-03-12T07:53:38.505+0000","dialcodeRequired": "No","lastStatusChangedOn": "2021-03-12T07:53:38.494+0000","createdFor": ["ORG_001"],"creator": "Reviewer User","os": ["All"],"se_FWIds": ["NCFCOPY"],"pkgVersion": 2.0,"versionKey": "1615535618583","idealScreenDensity": "hdpi","framework": "NCFCOPY","depth": 2,"s3Key": "ecar_files/do_1132344630588948481134/untitled-content_1615535618825_do_1132344630588948481134_2.0.ecar","lastSubmittedOn": "2021-03-12T07:53:10.005+0000","createdBy": "95e4942d-cbe8-477d-aebd-ad8e6de4bfc8","compatibilityLevel": 1,"board": "NCERT","resourceType": "Learn"}],"contentDisposition": "inline","lastUpdatedOn": "2021-05-20T08:58:33.470+0000","contentEncoding": "gzip","contentType": "TextBookUnit","dialcodeRequired": "No","identifier": "do_2222","lastStatusChangedOn": "2021-05-20T08:58:33.470+0000","audience": ["Student"],"os": ["All"],"visibility": "Parent","discussionForum": {"enabled": "Yes"},"index": 1,"mediaType": "content","osId": "org.ekstep.launcher","languageCode": ["en"],"version": 2,"versionKey": "1621501113470","license": "CC BY 4.0","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 1,"compatibilityLevel": 1,"name": "5. Human Body","topic": [],"status": "Draft"}],"appId": "dev.sunbird.portal","contentEncoding": "gzip","sYS_INTERNAL_LAST_UPDATED_ON": "2021-05-20T09:12:06.988+0000","contentType": "TextBook","trackable": {"enabled": "No","autoBatch": "No"},"identifier": "$contentId","audience": ["Student"],"visibility": "Default","consumerId": "01814e02-fc27-4165-ae53-3d1816e55817","childNodes": ["do_1132339274094346241120","do_1132833371215872001720","do_1132833371215134721712","do_2222","do_113223967141863424174","do_1132833371214970881710","do_1132833371215708161718","do_1132372524622561281279","do_1132338069147811841118","do_1132833371215298561714","do_1132833371215953921722","do_11322383952751820816","do_1132216902566133761410","do_1132344630588948481134"],"discussionForum": {"enabled": "Yes"},"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"],"version": 2,"license": "CC BY 4.0","name": "TestCSVUpload","status": "Draft","code": "org.sunbird.yhqB6L","credentials": {"enabled": "No"},"description": "Enter description for TextBook","medium": ["English"],"idealScreenSize": "normal","createdOn": "2021-05-19T15:00:44.279+0000","contentDisposition": "inline","additionalCategories": ["Textbook"],"lastUpdatedOn": "2021-05-20T07:10:32.805+0000","dialcodeRequired": "No","lastStatusChangedOn": "2021-05-19T15:00:44.279+0000","createdFor": ["0126825293972439041"],"creator": "Book Creator","os": ["All"],"versionKey": "1621501113536","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 0,"createdBy": "8454cb21-3ce9-4e30-85b5-fade097880d8","compatibilityLevel": 1,"userConsent": "Yes","board": "State (Tamil Nadu)","resourceType": "Book"}""".stripMargin val response = new Response response.put("hierarchy", hierarchyString) } From 96ba80b11e0b8e15e3a947bf2757264b271dc716 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Wed, 7 Sep 2022 11:30:07 +0530 Subject: [PATCH 124/490] Issue #KN-257 feat: Content Release DIAL codes API refactor. --- .../sunbird/content/actors/ContentActor.scala | 4 +- .../org/sunbird/content/dial/DIALErrors.scala | 2 + .../sunbird/content/dial/DIALManager.scala | 120 +++++++++--------- .../content/util/ContentConstants.scala | 2 + .../content/dial/DIALManagerTest.scala | 4 +- 5 files changed, 65 insertions(+), 67 deletions(-) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/actors/ContentActor.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/actors/ContentActor.scala index 0ac9ef335..89d19882f 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/actors/ContentActor.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/actors/ContentActor.scala @@ -181,9 +181,9 @@ class ContentActor @Inject() (implicit oec: OntologyEngineContext, ss: StorageSe def linkDIALCode(request: Request): Future[Response] = DIALManager.link(request) - def reserveDialCode(request: Request): Future[Response] = DIALManager.reserve(request) + def reserveDialCode(request: Request): Future[Response] = DIALManager.reserveOrRelease(request, ContentConstants.RESERVE) - def releaseDialCode(request: Request): Future[Response] = DIALManager.release(request) + def releaseDialCode(request: Request): Future[Response] = DIALManager.reserveOrRelease(request, ContentConstants.RELEASE) def importContent(request: Request): Future[Response] = importMgr.importObject(request) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALErrors.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALErrors.scala index 3ecd0acdb..bb7622e91 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALErrors.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALErrors.scala @@ -16,6 +16,7 @@ object DIALErrors { val ERR_CONTENT_RETIRED_OBJECT_ID: String = "ERR_CONTENT_RETIRED_OBJECT_ID" val ERR_CONTENT_MISSING_RESERVED_DIAL_CODES: String = "ERR_CONTENT_MISSING_RESERVED_DIAL_CODES" val ERR_ALL_DIALCODES_UTILIZED: String = "ERR_ALL_DIALCODES_UTILIZED" + val ERR_INVALID_OPERATION: String = "ERR_INVALID_OPERATION" //Error Messages val ERR_INVALID_REQ_MSG: String = "Invalid Request! Please Provide Valid Request." @@ -38,4 +39,5 @@ object DIALErrors { val ERR_CONTENT_RETIRED_OBJECT_ID_MSG: String = "Invalid Request. Cannot update 'Retired' content." val ERR_CONTENT_MISSING_RESERVED_DIAL_CODES_MSG: String = "Invalid Request. Content does not have reserved DIAL codes." val ERR_ALL_DIALCODES_UTILIZED_MSG: String = "Error! All Reserved DIAL Codes are Utilized." + val ERR_INVALID_OPERATION_MSG: String = "Invalid operation request!" } diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala index 10fc83866..755ebfb56 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala @@ -261,7 +261,7 @@ object DIALManager { throw new ClientException(DIALErrors.ERR_DUPLICATE_DIAL_CODES, DIALErrors.ERR_DUPLICATE_DIAL_CODES_MSG + duplicateDIALCodes) } - def reserve(request: Request)(implicit oec: OntologyEngineContext, ec: ExecutionContext): Future[Response] = { + def reserveOrRelease(request: Request, operation: String)(implicit oec: OntologyEngineContext, ec: ExecutionContext): Future[Response] = { val channelId: String = request.getContext.getOrDefault(DIALConstants.CHANNEL, "").asInstanceOf[String] val contentId: String = request.get(ContentConstants.IDENTIFIER).asInstanceOf[String] @@ -278,79 +278,73 @@ object DIALManager { validateChannel(contentMetadata.get(DIALConstants.CHANNEL).asInstanceOf[String], channelId) validateContentForReserveAndReleaseDialcodes(contentMetadata) validateCountForReservingAndReleasingDialCode(request.getRequest.get(DIALConstants.DIALCODES).asInstanceOf[util.Map[String, AnyRef]]) - 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) - - if(updateDialCodes.size > reservedDialCodes.size) { - val updateReq = getDIALReserveUpdateRequest(req, 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 - }) - } else { - val errorResponse = ResponseHandler.ERROR(ResponseCode.CLIENT_ERROR, DIALErrors.ERR_INVALID_COUNT, DIALErrors.ERR_DIAL_INVALID_COUNT_RESPONSE) - val updatedErrorResponse = getDIALReserveUpdateResponse(errorResponse, reservedDialCodes.size.asInstanceOf[Integer], contentId, rootNode) - Future(updatedErrorResponse) - } + + if(operation.nonEmpty && operation.equalsIgnoreCase(ContentConstants.RESERVE)) { + reserve(request, channelId, contentId, rootNode, contentMetadata) + } else if(operation.nonEmpty && operation.equalsIgnoreCase(ContentConstants.RELEASE)) { + release(request, contentId, rootNode, contentMetadata) + } else throw new ClientException(DIALErrors.ERR_INVALID_OPERATION, DIALErrors.ERR_INVALID_OPERATION_MSG) + }) } - def release(request: Request)(implicit oec: OntologyEngineContext, ec: ExecutionContext): Future[Response] = { - val channelId: String = request.getContext.getOrDefault(DIALConstants.CHANNEL, "").asInstanceOf[String] - val contentId: String = request.get(ContentConstants.IDENTIFIER).asInstanceOf[String] + def reserve(request: Request, channelId: String, contentId: String, rootNode: Node, contentMetadata: util.Map[String, AnyRef])(implicit oec: OntologyEngineContext, ec: ExecutionContext): Future[Response] = { + validateContentStatus(contentMetadata) - if (contentId == null || contentId.isEmpty) throw new ClientException(DIALErrors.ERR_CONTENT_BLANK_OBJECT_ID, DIALErrors.ERR_CONTENT_BLANK_OBJECT_ID_MSG) + 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 req = new Request() - req.setContext(request.getContext) - req.put(DIALConstants.IDENTIFIER, contentId) - req.put(ContentConstants.ROOT_ID, contentId) - req.put(ContentConstants.MODE, ContentConstants.EDIT_MODE) - DataNode.read(req).flatMap(rootNode => { - val contentMetadata = rootNode.getMetadata - - validateChannel(contentMetadata.get(DIALConstants.CHANNEL).asInstanceOf[String], channelId) - validateContentForReserveAndReleaseDialcodes(contentMetadata) - validateCountForReservingAndReleasingDialCode(request.getRequest.get(DIALConstants.DIALCODES).asInstanceOf[util.Map[String, AnyRef]]) + 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 + }) + } else { + val errorResponse = ResponseHandler.ERROR(ResponseCode.CLIENT_ERROR, DIALErrors.ERR_INVALID_COUNT, DIALErrors.ERR_DIAL_INVALID_COUNT_RESPONSE) + val updatedErrorResponse = getDIALReserveUpdateResponse(errorResponse, reservedDialCodes.size.asInstanceOf[Integer], contentId, rootNode) + Future(updatedErrorResponse) + } + } - 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) + 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) - populateAssignedDialCodes(contentId, contentMetadata, req).map(assignedDialCodes => { - val toReleaseDIALCodes = reservedDialCodes.keySet -- assignedDialCodes.toSet + populateAssignedDialCodes(contentId, contentMetadata, request).map(assignedDialCodes => { + val toReleaseDIALCodes = reservedDialCodes.keySet -- assignedDialCodes.toSet - if(toReleaseDIALCodes.isEmpty) throw new ClientException(DIALErrors.ERR_ALL_DIALCODES_UTILIZED, DIALErrors.ERR_ALL_DIALCODES_UTILIZED_MSG) + if(toReleaseDIALCodes.isEmpty) throw new ClientException(DIALErrors.ERR_ALL_DIALCODES_UTILIZED, DIALErrors.ERR_ALL_DIALCODES_UTILIZED_MSG) - val reqDialcodesCount = request.getRequest.get(DIALConstants.DIALCODES).asInstanceOf[util.Map[String, AnyRef]].get(DIALConstants.COUNT).asInstanceOf[Integer] + val reqDialcodesCount = request.getRequest.get(DIALConstants.DIALCODES).asInstanceOf[util.Map[String, AnyRef]].get(DIALConstants.COUNT).asInstanceOf[Integer] - val updatedReleaseDialcodes = if (toReleaseDIALCodes.size > reqDialcodesCount) { - toReleaseDIALCodes.take(reqDialcodesCount) - } else toReleaseDIALCodes + val updatedReleaseDialcodes = if (toReleaseDIALCodes.size > reqDialcodesCount) { + toReleaseDIALCodes.take(reqDialcodesCount) + } else toReleaseDIALCodes - val updatedReserveDialCodes = reservedDialCodes.filter(rec => !updatedReleaseDialcodes.contains(rec._1)).keySet.zipWithIndex.map { case (dialCode, idx) => - (dialCode -> idx.asInstanceOf[Integer]) - }.toMap + val updatedReserveDialCodes = reservedDialCodes.filter(rec => !updatedReleaseDialcodes.contains(rec._1)).keySet.zipWithIndex.map { case (dialCode, idx) => + (dialCode -> idx.asInstanceOf[Integer]) + }.toMap - val updateReq = new Request() - updateReq.setContext(request.getContext) - updateReq.getContext.put(ContentConstants.IDENTIFIER, rootNode.getIdentifier) - updateReq.put(ContentConstants.IDENTIFIER, rootNode.getIdentifier) - updateReq.put(DIALConstants.VERSION_KEY,rootNode.getMetadata.get(ContentConstants.VERSION_KEY)) - updateReq.put(DIALConstants.RESERVED_DIALCODES, if(updatedReserveDialCodes.nonEmpty) updatedReserveDialCodes.asJava else null) - DataNode.update(updateReq).map(node => { - ResponseHandler.OK.putAll(Map(ContentConstants.IDENTIFIER -> node.getIdentifier.replace(ContentConstants.IMAGE_SUFFIX, ""), - ContentConstants.VERSION_KEY -> node.getMetadata.get(ContentConstants.VERSION_KEY), - DIALConstants.RESERVED_DIALCODES -> node.getMetadata.get(DIALConstants.RESERVED_DIALCODES)).asJava) - }) - }).flatMap(f=>f) - }) + val updateReq = new Request() + updateReq.setContext(request.getContext) + updateReq.getContext.put(ContentConstants.IDENTIFIER, rootNode.getIdentifier) + updateReq.put(ContentConstants.IDENTIFIER, rootNode.getIdentifier) + updateReq.put(DIALConstants.VERSION_KEY,rootNode.getMetadata.get(ContentConstants.VERSION_KEY)) + updateReq.put(DIALConstants.RESERVED_DIALCODES, if(updatedReserveDialCodes.nonEmpty) updatedReserveDialCodes.asJava else null) + DataNode.update(updateReq).map(node => { + ResponseHandler.OK.putAll(Map(ContentConstants.IDENTIFIER -> node.getIdentifier.replace(ContentConstants.IMAGE_SUFFIX, ""), + ContentConstants.VERSION_KEY -> node.getMetadata.get(ContentConstants.VERSION_KEY), + DIALConstants.RESERVED_DIALCODES -> node.getMetadata.get(DIALConstants.RESERVED_DIALCODES)).asJava) + }) + }).flatMap(f=>f) } - def populateAssignedDialCodes(contentId: String, contentMetadata: util.Map[String, AnyRef], request: Request)(implicit oec: OntologyEngineContext, ec: ExecutionContext): Future[List[String]] = { + def populateAssignedDialCodes(contentId: String, contentMetadata: util.Map[String, AnyRef], req: Request)(implicit oec: OntologyEngineContext, ec: ExecutionContext): Future[List[String]] = { + val request = new Request() + request.setContext(req.getContext) request.getContext.put(ContentConstants.SCHEMA_NAME, ContentConstants.COLLECTION_SCHEMA_NAME) request.getContext.put(ContentConstants.VERSION, ContentConstants.SCHEMA_VERSION) request.put(ContentConstants.ROOT_ID, contentMetadata.getOrDefault(ContentConstants.IDENTIFIER,"").asInstanceOf[String]) @@ -462,9 +456,9 @@ object DIALManager { } } - def getDIALReserveUpdateRequest(req: Request, request: Request, rootNode: Node, updateDialCodes: Map[String, Integer]): Request = { - val updateReq = new Request(req) - updateReq.setContext(request.getContext) + def getDIALReserveUpdateRequest(req: Request, rootNode: Node, updateDialCodes: Map[String, Integer]): Request = { + val updateReq = new Request() + updateReq.setContext(req.getContext) updateReq.getContext.put(ContentConstants.IDENTIFIER, rootNode.getIdentifier) updateReq.put(ContentConstants.IDENTIFIER, rootNode.getIdentifier) updateReq.put(DIALConstants.VERSION_KEY,rootNode.getMetadata.get("versionKey")) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/util/ContentConstants.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/util/ContentConstants.scala index ffc31a8d2..aa199a481 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/util/ContentConstants.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/util/ContentConstants.scala @@ -75,4 +75,6 @@ object ContentConstants { val ENV: String = "env" val KAFKA_PUBLISH_TOPIC: String = "kafka.publish.request.topic" val PROCESSING: String = "Processing" + val RESERVE: String = "reserve" + val RELEASE: String = "release" } diff --git a/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala b/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala index c17f31cf0..cbc801156 100644 --- a/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala +++ b/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala @@ -280,7 +280,7 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory val request = getReserveDIALRequest(contentId) - val response = DIALManager.reserve(request) + val response = DIALManager.reserveOrRelease(request, "reserve") response.map(result => { assert(result.getResponseCode.toString=="OK") }) @@ -300,7 +300,7 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory val request = getReleaseDIALRequest(contentId) - val response = DIALManager.release(request) + val response = DIALManager.reserveOrRelease(request, "release") response.map(result => { assert(result.getResponseCode.toString=="OK") }) From f0c762b2ef2c9876788459f3bcdcaae685f30e56 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Wed, 7 Sep 2022 12:17:17 +0530 Subject: [PATCH 125/490] Issue #KN-257 feat: Content Release DIAL codes API refactor. --- .../test/controllers/v4/CollectionSpec.scala | 23 +++++++++++++++++++ .../test/controllers/v4/ContentSpec.scala | 22 ++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/content-api/content-service/test/controllers/v4/CollectionSpec.scala b/content-api/content-service/test/controllers/v4/CollectionSpec.scala index ae90503db..ec0f38969 100644 --- a/content-api/content-service/test/controllers/v4/CollectionSpec.scala +++ b/content-api/content-service/test/controllers/v4/CollectionSpec.scala @@ -167,4 +167,27 @@ class CollectionSpec extends BaseSpec { status(result) must equalTo(OK) } } + + "Collection Controller with valid request " should { + "return success response for dialcode reserve API" in { + val controller = app.injector.instanceOf[controllers.v4.CollectionController] + val json: JsValue = Json.parse("""{"request": {"dialcodes": {"count": 5, "qrCodeSpec": { "errorCorrectionLevel": "H" }}}}""".stripMargin) + val fakeRequest = FakeRequest("POST", "/collection/v4/dialcode/reserve/do_123").withJsonBody(json) + val result = controller.reserveDialCode("do_123")(fakeRequest) + isOK(result) + status(result) must equalTo(OK) + } + } + + "Collection Controller with valid request " should { + "return success response for dialcode release API" in { + val controller = app.injector.instanceOf[controllers.v4.CollectionController] + val json: JsValue = Json.parse("""{"request": {"dialcodes": {"count": 1}}}""".stripMargin) + val fakeRequest = FakeRequest("POST", "/collection/v4/dialcode/release/do_123").withJsonBody(json) + val result = controller.releaseDialCode("do_123")(fakeRequest) + isOK(result) + status(result) must equalTo(OK) + } + } + } diff --git a/content-api/content-service/test/controllers/v4/ContentSpec.scala b/content-api/content-service/test/controllers/v4/ContentSpec.scala index aeb8e6238..b3bec6705 100644 --- a/content-api/content-service/test/controllers/v4/ContentSpec.scala +++ b/content-api/content-service/test/controllers/v4/ContentSpec.scala @@ -191,4 +191,26 @@ class ContentSpec extends BaseSpec { } } + "Content Controller with valid request " should { + "return success response for dialcode reserve API" in { + val controller = app.injector.instanceOf[controllers.v4.CollectionController] + val json: JsValue = Json.parse("""{"request": {"dialcodes": {"count": 5, "qrCodeSpec": { "errorCorrectionLevel": "H" }}}}""".stripMargin) + val fakeRequest = FakeRequest("POST", "/collection/v4/dialcode/reserve/do_123").withJsonBody(json) + val result = controller.reserveDialCode("do_123")(fakeRequest) + isOK(result) + status(result) must equalTo(OK) + } + } + + "Content Controller with valid request " should { + "return success response for dialcode release API" in { + val controller = app.injector.instanceOf[controllers.v4.CollectionController] + val json: JsValue = Json.parse("""{"request": {"dialcodes": {"count": 1}}}""".stripMargin) + val fakeRequest = FakeRequest("POST", "/collection/v4/dialcode/release/do_123").withJsonBody(json) + val result = controller.releaseDialCode("do_123")(fakeRequest) + isOK(result) + status(result) must equalTo(OK) + } + } + } From b7d1722a3e5428996de8ddd4ff93cfeac5d8672b Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Wed, 7 Sep 2022 17:09:58 +0530 Subject: [PATCH 126/490] Issue #KN-257 feat: Content Release DIAL codes API refactor. --- .../src/main/scala/org/sunbird/content/dial/DIALErrors.scala | 2 ++ .../src/main/scala/org/sunbird/content/dial/DIALManager.scala | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALErrors.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALErrors.scala index bb7622e91..c96e00f5e 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALErrors.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALErrors.scala @@ -17,6 +17,7 @@ object DIALErrors { val ERR_CONTENT_MISSING_RESERVED_DIAL_CODES: String = "ERR_CONTENT_MISSING_RESERVED_DIAL_CODES" 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" //Error Messages val ERR_INVALID_REQ_MSG: String = "Invalid Request! Please Provide Valid Request." @@ -40,4 +41,5 @@ object DIALErrors { val ERR_CONTENT_MISSING_RESERVED_DIAL_CODES_MSG: String = "Invalid Request. Content does not have reserved DIAL codes." val ERR_ALL_DIALCODES_UTILIZED_MSG: String = "Error! All Reserved DIAL Codes are Utilized." val ERR_INVALID_OPERATION_MSG: String = "Invalid operation request!" + val ERR_COUNT_GREATER_THAN_RESERVED_DIAL_CODES_MSG: String = "Invalid Request. Count greater than reserved DIAL codes." } diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala index 755ebfb56..7a53b456e 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala @@ -313,6 +313,10 @@ object DIALManager { 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) + val countInRequest = request.get(DIALConstants.COUNT).asInstanceOf[Integer] + if(reservedDialCodes.keySet.size < countInRequest) + throw new ClientException(DIALErrors.ERR_COUNT_GREATER_THAN_RESERVED_DIAL_CODES, DIALErrors.ERR_COUNT_GREATER_THAN_RESERVED_DIAL_CODES_MSG) + populateAssignedDialCodes(contentId, contentMetadata, request).map(assignedDialCodes => { val toReleaseDIALCodes = reservedDialCodes.keySet -- assignedDialCodes.toSet From fc492fbfd4a86ae4845a1d970cfd595a786860a4 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Wed, 7 Sep 2022 17:25:18 +0530 Subject: [PATCH 127/490] Issue #KN-257 feat: Content Release DIAL codes API refactor. --- .../scala/org/sunbird/content/dial/DIALManager.scala | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala index 7a53b456e..61bfee6b2 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala @@ -313,8 +313,8 @@ object DIALManager { 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) - val countInRequest = request.get(DIALConstants.COUNT).asInstanceOf[Integer] - if(reservedDialCodes.keySet.size < countInRequest) + val countInRequest = request.getRequest.get(DIALConstants.DIALCODES).asInstanceOf[util.Map[String, AnyRef]].get(DIALConstants.COUNT).asInstanceOf[Integer] + if(reservedDialCodes.keys.size < countInRequest) throw new ClientException(DIALErrors.ERR_COUNT_GREATER_THAN_RESERVED_DIAL_CODES, DIALErrors.ERR_COUNT_GREATER_THAN_RESERVED_DIAL_CODES_MSG) populateAssignedDialCodes(contentId, contentMetadata, request).map(assignedDialCodes => { @@ -407,9 +407,9 @@ object DIALManager { throw new ClientException(DIALErrors.ERR_CONTENT_RETIRED_OBJECT_ID, DIALErrors.ERR_CONTENT_RETIRED_OBJECT_ID_MSG) } - def validateCountForReservingAndReleasingDialCode(request: util.Map[String, AnyRef]): Unit = { - if (null == request.get(DIALConstants.COUNT) || !request.get(DIALConstants.COUNT).isInstanceOf[Integer]) throw new ClientException(DIALErrors.ERR_INVALID_COUNT, DIALErrors.ERR_INVALID_COUNT_MSG) - val count = request.get(DIALConstants.COUNT).asInstanceOf[Integer] + def validateCountForReservingAndReleasingDialCode(requestDIALCodesMap: util.Map[String, AnyRef]): Unit = { + if (null == requestDIALCodesMap.get(DIALConstants.COUNT) || !requestDIALCodesMap.get(DIALConstants.COUNT).isInstanceOf[Integer]) throw new ClientException(DIALErrors.ERR_INVALID_COUNT, DIALErrors.ERR_INVALID_COUNT_MSG) + val count = requestDIALCodesMap.get(DIALConstants.COUNT).asInstanceOf[Integer] val maxCount = if (Platform.config.hasPath("reserve_dialcode.max_count")) Platform.config.getInt("reserve_dialcode.max_count") else 250 if (count < 1 || count > maxCount) throw new ClientException(DIALErrors.ERR_INVALID_COUNT_RANGE, DIALErrors.ERR_INVALID_COUNT_RANGE_MSG + maxCount + ".") } From 3a278b8348ef63debd484e88e3b5f50ddcb4d284 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Wed, 7 Sep 2022 17:30:47 +0530 Subject: [PATCH 128/490] Issue #KN-257 feat: Content Release DIAL codes API refactor. --- ...ease DIAL Code API.postman_collection.json | 907 ++++++++++++++++++ ...elease DIAL Code API.postman_test_run.json | 488 ++++++++++ 2 files changed, 1395 insertions(+) create mode 100644 content-api/api-tests/Collections/Release DIAL Code API.postman_collection.json create mode 100644 content-api/api-tests/Reports/Release DIAL Code API.postman_test_run.json diff --git a/content-api/api-tests/Collections/Release DIAL Code API.postman_collection.json b/content-api/api-tests/Collections/Release DIAL Code API.postman_collection.json new file mode 100644 index 000000000..2e825716c --- /dev/null +++ b/content-api/api-tests/Collections/Release DIAL Code API.postman_collection.json @@ -0,0 +1,907 @@ +{ + "info": { + "_postman_id": "d76732b8-56b7-4a54-84f1-4c43e694dcd9", + "name": "Release DIAL Code API", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "Pre-requisite", + "item": [ + { + "name": "Collection Create", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.environment.set(\"collection_id\", jsonResponse.result.identifier);", + "pm.globals.set(\"versionKey\", jsonResponse.result.versionKey);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Sample Collection\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/vnd.ekstep.content-collection\",\n \"primaryCategory\": \"Digital Textbook\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v4/create", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v4", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [] + }, + { + "name": "Collection Create wo reserved DIAL codes", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "// Set global variable", + "pm.environment.set(\"collection_id_wo_reserved_dial_codes\", jsonResponse.result.identifier);", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "The Content Type entity is the media type of the resource.Possible media types can be application/json." + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}", + "description": "Represents the authorized User ID to execute the API." + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}", + "description": "To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here." + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "description": "Represents a physical location (e.g: state) uniquely to which the user belongs." + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"Sample Collection\",\n \"code\": \"1242e9ea-660a-4536-a142-cc242a7a4162\",\n \"mimeType\": \"application/vnd.ekstep.content-collection\",\n \"primaryCategory\": \"Digital Textbook\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/content/v4/create", + "host": [ + "{{host}}" + ], + "path": [ + "content", + "v4", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform. Mandatory fields (name, code, mimeType and primaryCategory) cannot be null or empty.\n\nThe body refers to the format of the request.\n\n* The body contains metadata about the content to be updated.\n* Please refer for supported [metadata](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)" + }, + "response": [] + }, + { + "name": "TOC Upload", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Verification", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200)", + "});", + "", + "const responseJson = pm.response.json();", + "const unitIdentifiers = _.values(responseJson.result.identifiers)", + "", + "pm.globals.set(\"unitA\", unitIdentifiers[_.size(unitIdentifiers) - 2]);", + "pm.globals.set(\"unitB\", unitIdentifiers[_.size(unitIdentifiers) - 3]);", + "pm.globals.set(\"unitC\", unitIdentifiers[_.size(unitIdentifiers) - 4]);", + "console.log(pm.globals.get(\"unitA\") + \", \" + pm.globals.get(\"unitB\") + \", \" + pm.globals.get(\"unitC\"))", + "", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "x-authenticated-user-token", + "value": "{{user-token}}" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "fileUrl", + "value": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/hierarchy/do_113316820811522048194/createtoc.csv", + "type": "text" + } + ] + }, + "url": { + "raw": "{{host}}/collection/v4/import/{{collection_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "collection", + "v4", + "import", + "{{collection_id}}" + ] + } + }, + "response": [] + }, + { + "name": "Reserve DIAL code", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const responseJson = pm.response.json();", + "", + "var reservedDialcodesObj = JSON.parse(responseJson.result.reservedDialcodes);", + "const reservedDialCodesList = _.keys(reservedDialcodesObj)", + "", + "pm.globals.set(\"originalReservedDIALCodes\", reservedDialCodesList)", + "pm.globals.set(\"dialcodeA\", reservedDialCodesList[_.size(reservedDialCodesList) - 2]);", + "pm.globals.set(\"dialcodeB\", reservedDialCodesList[_.size(reservedDialCodesList) - 3]);", + "pm.globals.set(\"dialcodeC\", reservedDialCodesList[_.size(reservedDialCodesList) - 4]);", + "", + "console.log(pm.globals.get(\"dialcodeA\") + \", \" + pm.globals.get(\"dialcodeB\") + \", \" + pm.globals.get(\"dialcodeC\"))" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "pm.globals.set(\"ReservedDialCodesCount\", 8);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"dialcodes\": {\n \"count\": {{ReservedDialCodesCount}},\n \"qrCodeSpec\": {\n \"errorCorrectionLevel\": \"H\"\n }\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/collection/v4/dialcode/reserve/{{collection_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "collection", + "v4", + "dialcode", + "reserve", + "{{collection_id}}" + ] + } + }, + "response": [] + }, + { + "name": "Link DIAL code", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "setTimeout(function(){}, 1000);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "X-Channel-ID", + "value": "{{channel_id}}" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "x-authenticated-user-token", + "value": "{{keycloak_access_token}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": [{\n \"identifier\": [\"{{unitA}}\"],\n \"dialcode\": [\"{{dialcodeA}}\"]\n },\n {\n \"identifier\": [\"{{unitB}}\"],\n \"dialcode\": [\"{{dialcodeB}}\"]\n },\n {\n \"identifier\": [\"{{unitC}}\"],\n \"dialcode\": [\"{{dialcodeC}}\"]\n }]\n }\n}" + }, + "url": { + "raw": "{{host}}/collection/v4/dialcode/link/{{collection_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "collection", + "v4", + "dialcode", + "link", + "{{collection_id}}" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Positive", + "item": [ + { + "name": "Release DIAL code", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "pm.test(\"Response returns with reservedDialcodes with one less\", function () {", + " const responseJson = pm.response.json();", + " ", + " var reservedDialcodesObj = JSON.parse(responseJson.result.reservedDialcodes);", + " const reservedDialCodesList = _.keys(reservedDialcodesObj)", + " var expectedCount = pm.globals.get(\"ReservedDialCodesCount\")-1", + " console.log(\"expected Count:: \" + expectedCount)", + " console.log(\"returned Count:: \" + _.size(reservedDialCodesList))", + " pm.expect(_.size(reservedDialCodesList) == expectedCount)", + "", + " var orgReservedDIALList = pm.globals.get(\"originalReservedDIALCodes\")", + " var releasedDialcode = _.difference(orgReservedDIALList, reservedDialCodesList)", + " console.log(\"releasedDialcode:: \" + releasedDialcode)", + "", + " pm.expect((releasedDialcode != pm.globals.get(\"dialcodeA\")) && ", + " (releasedDialcode != pm.globals.get(\"dialcodeB\")) && ", + " (releasedDialcode != pm.globals.get(\"dialcodeC\")))", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"dialcodes\": {\n \"count\": 5\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/collection/v4/dialcode/release/{{collection_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "collection", + "v4", + "dialcode", + "release", + "{{collection_id}}" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Negative", + "item": [ + { + "name": "Release DIAL code Invalid Channel Id header", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 400\", function () {", + " pm.response.to.have.status(400);", + "});", + "", + "pm.test(\"Response throws ERR_INVALID_CHANNEL\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.responseCode).equals(\"CLIENT_ERROR\")", + " ", + " const params = responseJson.params;", + " pm.expect(params.status).equals(\"failed\")", + " pm.expect(params.err).equals(\"ERR_INVALID_CHANNEL\") ", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Channel-ID", + "value": "dummy" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"dialcodes\": {\n \"count\": 1\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/collection/v4/dialcode/release/{{collection_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "collection", + "v4", + "dialcode", + "release", + "{{collection_id}}" + ] + } + }, + "response": [] + }, + { + "name": "Release DIAL code with Zero count", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 400\", function () {", + " pm.response.to.have.status(400);", + "});", + "", + "pm.test(\"Response throws ERR_INVALID_COUNT_RANGE\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.responseCode).equals(\"CLIENT_ERROR\")", + " ", + " const params = responseJson.params;", + " pm.expect(params.status).equals(\"failed\")", + " pm.expect(params.err).equals(\"ERR_INVALID_COUNT_RANGE\") ", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"dialcodes\": {\n \"count\": 0\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/collection/v4/dialcode/release/{{collection_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "collection", + "v4", + "dialcode", + "release", + "{{collection_id}}" + ] + } + }, + "response": [] + }, + { + "name": "Release DIAL code with negative count", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 400\", function () {", + " pm.response.to.have.status(400);", + "});", + "", + "pm.test(\"Response throws ERR_INVALID_COUNT_RANGE\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.responseCode).equals(\"CLIENT_ERROR\")", + " ", + " const params = responseJson.params;", + " pm.expect(params.status).equals(\"failed\")", + " pm.expect(params.err).equals(\"ERR_INVALID_COUNT_RANGE\") ", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"dialcodes\": {\n \"count\": -2\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/collection/v4/dialcode/release/{{collection_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "collection", + "v4", + "dialcode", + "release", + "{{collection_id}}" + ] + } + }, + "response": [] + }, + { + "name": "Release DIAL code with count greater than max limit(250)", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 400\", function () {", + " pm.response.to.have.status(400);", + "});", + "", + "pm.test(\"Response throws ERR_INVALID_COUNT_RANGE\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.responseCode).equals(\"CLIENT_ERROR\")", + " ", + " const params = responseJson.params;", + " pm.expect(params.status).equals(\"failed\")", + " pm.expect(params.err).equals(\"ERR_INVALID_COUNT_RANGE\") ", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"dialcodes\": {\n \"count\": 251\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/collection/v4/dialcode/release/{{collection_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "collection", + "v4", + "dialcode", + "release", + "{{collection_id}}" + ] + } + }, + "response": [] + }, + { + "name": "Release DIAL code with invalid collection Id", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 404\", function () {", + " pm.response.to.have.status(404);", + "});", + "", + "pm.test(\"Response throws RESOURCE_NOT_FOUND\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.responseCode).equals(\"RESOURCE_NOT_FOUND\")", + " ", + " const params = responseJson.params;", + " pm.expect(params.status).equals(\"failed\")", + " pm.expect(params.err).equals(\"NOT_FOUND\") ", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"dialcodes\": {\n \"count\": 25\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/collection/v4/dialcode/release/do_dummy_id", + "host": [ + "{{host}}" + ], + "path": [ + "collection", + "v4", + "dialcode", + "release", + "do_dummy_id" + ] + } + }, + "response": [] + }, + { + "name": "Release DIAL code for collection wo reserved DIAL codes", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 400\", function () {", + " pm.response.to.have.status(400);", + "});", + "", + "pm.test(\"Response throws ERR_CONTENT_MISSING_RESERVED_DIAL_CODES\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.responseCode).equals(\"CLIENT_ERROR\")", + " ", + " const params = responseJson.params;", + " pm.expect(params.status).equals(\"failed\")", + " pm.expect(params.err).equals(\"ERR_CONTENT_MISSING_RESERVED_DIAL_CODES\") ", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"dialcodes\": {\n \"count\": 2\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/collection/v4/dialcode/release/{{collection_id_wo_reserved_dial_codes}}", + "host": [ + "{{host}}" + ], + "path": [ + "collection", + "v4", + "dialcode", + "release", + "{{collection_id_wo_reserved_dial_codes}}" + ] + } + }, + "response": [] + }, + { + "name": "Release DIAL code with count greater than reserved DIAL codes count", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 400\", function () {", + " pm.response.to.have.status(400);", + "});", + "", + "pm.test(\"Response throws ERR_COUNT_GREATER_THAN_RESERVED_DIAL_CODES\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.responseCode).equals(\"CLIENT_ERROR\")", + " ", + " const params = responseJson.params;", + " pm.expect(params.status).equals(\"failed\")", + " pm.expect(params.err).equals(\"ERR_COUNT_GREATER_THAN_RESERVED_DIAL_CODES\") ", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"dialcodes\": {\n \"count\": 25\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/collection/v4/dialcode/release/{{collection_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "collection", + "v4", + "dialcode", + "release", + "{{collection_id}}" + ] + } + }, + "response": [] + }, + { + "name": "Release DIAL code with count same as linked DIAL codes", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 400\", function () {", + " pm.response.to.have.status(400);", + "});", + "", + "pm.test(\"Response throws ERR_ALL_DIALCODES_UTILIZED\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.responseCode).equals(\"CLIENT_ERROR\")", + " ", + " const params = responseJson.params;", + " pm.expect(params.status).equals(\"failed\")", + " pm.expect(params.err).equals(\"ERR_ALL_DIALCODES_UTILIZED\") ", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Channel-ID", + "value": "{{channel_id}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"dialcodes\": {\n \"count\": 2\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/collection/v4/dialcode/release/{{collection_id}}", + "host": [ + "{{host}}" + ], + "path": [ + "collection", + "v4", + "dialcode", + "release", + "{{collection_id}}" + ] + } + }, + "response": [] + } + ] + } + ] +} \ No newline at end of file diff --git a/content-api/api-tests/Reports/Release DIAL Code API.postman_test_run.json b/content-api/api-tests/Reports/Release DIAL Code API.postman_test_run.json new file mode 100644 index 000000000..0595d0940 --- /dev/null +++ b/content-api/api-tests/Reports/Release DIAL Code API.postman_test_run.json @@ -0,0 +1,488 @@ +{ + "id": "0f65eca4-84c1-4ad3-9ca7-4c0c035fe6a1", + "name": "Release DIAL Code API", + "timestamp": "2022-09-07T11:58:49.914Z", + "collection_id": "13387833-d76732b8-56b7-4a54-84f1-4c43e694dcd9", + "folder_id": 0, + "environment_id": "0e0b136d-70cc-437e-a0df-ccba162408d9", + "totalPass": 21, + "totalFail": 0, + "results": [ + { + "id": "32d3736c-e0f7-4d97-9230-4a4c8d8a80eb", + "name": "Collection Create", + "url": "http://localhost:9000/content/v4/create", + "time": 49, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Status code is 200": true + }, + "testPassFailCounts": { + "Status code is 200": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 49 + ], + "allTests": [ + { + "Status code is 200": true + } + ] + }, + { + "id": "ce3e135b-72fc-4561-badc-5094ecab1ab2", + "name": "Collection Create wo reserved DIAL codes", + "url": "http://localhost:9000/content/v4/create", + "time": 28, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Status code is 200": true + }, + "testPassFailCounts": { + "Status code is 200": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 28 + ], + "allTests": [ + { + "Status code is 200": true + } + ] + }, + { + "id": "1f0520aa-3f59-4c3e-be73-2574ea09a123", + "name": "TOC Upload", + "url": "http://localhost:9000/collection/v4/import/do_113619625412968448149", + "time": 533, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Status code is 200": true + }, + "testPassFailCounts": { + "Status code is 200": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 533 + ], + "allTests": [ + { + "Status code is 200": true + } + ] + }, + { + "id": "c7ebfb77-7f9e-483b-afd6-4b6b44067e8b", + "name": "Reserve DIAL code", + "url": "http://localhost:9000/collection/v4/dialcode/reserve/do_113619625412968448149", + "time": 1702, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": {}, + "testPassFailCounts": {}, + "times": [ + 1702 + ], + "allTests": [ + {} + ] + }, + { + "id": "5a0c02b8-ab74-4d12-9ce3-06ba874311b9", + "name": "Link DIAL code", + "url": "http://localhost:9000/collection/v4/dialcode/link/do_113619625412968448149", + "time": 199, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": {}, + "testPassFailCounts": {}, + "times": [ + 199 + ], + "allTests": [ + {} + ] + }, + { + "id": "48eaac17-ecf6-45e2-ad72-b95ef26d9e55", + "name": "Release DIAL code", + "url": "http://localhost:9000/collection/v4/dialcode/release/do_113619625412968448149", + "time": 101, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Status code is 200": true, + "Response returns with reservedDialcodes with one less": true + }, + "testPassFailCounts": { + "Status code is 200": { + "pass": 1, + "fail": 0 + }, + "Response returns with reservedDialcodes with one less": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 101 + ], + "allTests": [ + { + "Status code is 200": true, + "Response returns with reservedDialcodes with one less": true + } + ] + }, + { + "id": "cec90850-4020-4b24-a4f3-76b2c52b9da6", + "name": "Release DIAL code Invalid Channel Id header", + "url": "http://localhost:9000/collection/v4/dialcode/release/do_113619625412968448149", + "time": 24, + "responseCode": { + "code": 400, + "name": "Bad Request" + }, + "tests": { + "Status code is 400": true, + "Response throws ERR_INVALID_CHANNEL": true + }, + "testPassFailCounts": { + "Status code is 400": { + "pass": 1, + "fail": 0 + }, + "Response throws ERR_INVALID_CHANNEL": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 24 + ], + "allTests": [ + { + "Status code is 400": true, + "Response throws ERR_INVALID_CHANNEL": true + } + ] + }, + { + "id": "60202782-e345-4c4c-8f1e-939cd7ee8f0b", + "name": "Release DIAL code with Zero count", + "url": "http://localhost:9000/collection/v4/dialcode/release/do_113619625412968448149", + "time": 30, + "responseCode": { + "code": 400, + "name": "Bad Request" + }, + "tests": { + "Status code is 400": true, + "Response throws ERR_INVALID_COUNT_RANGE": true + }, + "testPassFailCounts": { + "Status code is 400": { + "pass": 1, + "fail": 0 + }, + "Response throws ERR_INVALID_COUNT_RANGE": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 30 + ], + "allTests": [ + { + "Status code is 400": true, + "Response throws ERR_INVALID_COUNT_RANGE": true + } + ] + }, + { + "id": "28f49920-9a42-441b-90b6-34260a2cd834", + "name": "Release DIAL code with negative count", + "url": "http://localhost:9000/collection/v4/dialcode/release/do_113619625412968448149", + "time": 31, + "responseCode": { + "code": 400, + "name": "Bad Request" + }, + "tests": { + "Status code is 400": true, + "Response throws ERR_INVALID_COUNT_RANGE": true + }, + "testPassFailCounts": { + "Status code is 400": { + "pass": 1, + "fail": 0 + }, + "Response throws ERR_INVALID_COUNT_RANGE": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 31 + ], + "allTests": [ + { + "Status code is 400": true, + "Response throws ERR_INVALID_COUNT_RANGE": true + } + ] + }, + { + "id": "b0dd02cf-d5b8-44bb-acd7-367bdc5f9592", + "name": "Release DIAL code with count greater than max limit(250)", + "url": "http://localhost:9000/collection/v4/dialcode/release/do_113619625412968448149", + "time": 35, + "responseCode": { + "code": 400, + "name": "Bad Request" + }, + "tests": { + "Status code is 400": true, + "Response throws ERR_INVALID_COUNT_RANGE": true + }, + "testPassFailCounts": { + "Status code is 400": { + "pass": 1, + "fail": 0 + }, + "Response throws ERR_INVALID_COUNT_RANGE": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 35 + ], + "allTests": [ + { + "Status code is 400": true, + "Response throws ERR_INVALID_COUNT_RANGE": true + } + ] + }, + { + "id": "208caab7-3f00-47ab-ae97-838e9b4815b4", + "name": "Release DIAL code with invalid collection Id", + "url": "http://localhost:9000/collection/v4/dialcode/release/do_dummy_id", + "time": 35, + "responseCode": { + "code": 404, + "name": "Not Found" + }, + "tests": { + "Status code is 404": true, + "Response throws RESOURCE_NOT_FOUND": true + }, + "testPassFailCounts": { + "Status code is 404": { + "pass": 1, + "fail": 0 + }, + "Response throws RESOURCE_NOT_FOUND": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 35 + ], + "allTests": [ + { + "Status code is 404": true, + "Response throws RESOURCE_NOT_FOUND": true + } + ] + }, + { + "id": "9a169a58-785e-468e-b8fe-014b0d3898d9", + "name": "Release DIAL code for collection wo reserved DIAL codes", + "url": "http://localhost:9000/collection/v4/dialcode/release/do_113619625413853184150", + "time": 55, + "responseCode": { + "code": 400, + "name": "Bad Request" + }, + "tests": { + "Status code is 400": true, + "Response throws ERR_CONTENT_MISSING_RESERVED_DIAL_CODES": true + }, + "testPassFailCounts": { + "Status code is 400": { + "pass": 1, + "fail": 0 + }, + "Response throws ERR_CONTENT_MISSING_RESERVED_DIAL_CODES": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 55 + ], + "allTests": [ + { + "Status code is 400": true, + "Response throws ERR_CONTENT_MISSING_RESERVED_DIAL_CODES": true + } + ] + }, + { + "id": "5d662783-fe26-41db-a2b6-66ed08be9430", + "name": "Release DIAL code with count greater than reserved DIAL codes count", + "url": "http://localhost:9000/collection/v4/dialcode/release/do_113619625412968448149", + "time": 31, + "responseCode": { + "code": 400, + "name": "Bad Request" + }, + "tests": { + "Status code is 400": true, + "Response throws ERR_COUNT_GREATER_THAN_RESERVED_DIAL_CODES": true + }, + "testPassFailCounts": { + "Status code is 400": { + "pass": 1, + "fail": 0 + }, + "Response throws ERR_COUNT_GREATER_THAN_RESERVED_DIAL_CODES": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 31 + ], + "allTests": [ + { + "Status code is 400": true, + "Response throws ERR_COUNT_GREATER_THAN_RESERVED_DIAL_CODES": true + } + ] + }, + { + "id": "2610536f-2c69-41c1-be92-6df6b4a153dc", + "name": "Release DIAL code with count same as linked DIAL codes", + "url": "http://localhost:9000/collection/v4/dialcode/release/do_113619625412968448149", + "time": 89, + "responseCode": { + "code": 400, + "name": "Bad Request" + }, + "tests": { + "Status code is 400": true, + "Response throws ERR_ALL_DIALCODES_UTILIZED": true + }, + "testPassFailCounts": { + "Status code is 400": { + "pass": 1, + "fail": 0 + }, + "Response throws ERR_ALL_DIALCODES_UTILIZED": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 89 + ], + "allTests": [ + { + "Status code is 400": true, + "Response throws ERR_ALL_DIALCODES_UTILIZED": true + } + ] + } + ], + "count": 1, + "totalTime": 2942, + "collection": { + "requests": [ + { + "id": "32d3736c-e0f7-4d97-9230-4a4c8d8a80eb", + "method": "POST" + }, + { + "id": "ce3e135b-72fc-4561-badc-5094ecab1ab2", + "method": "POST" + }, + { + "id": "1f0520aa-3f59-4c3e-be73-2574ea09a123", + "method": "POST" + }, + { + "id": "c7ebfb77-7f9e-483b-afd6-4b6b44067e8b", + "method": "POST" + }, + { + "id": "5a0c02b8-ab74-4d12-9ce3-06ba874311b9", + "method": "POST" + }, + { + "id": "48eaac17-ecf6-45e2-ad72-b95ef26d9e55", + "method": "POST" + }, + { + "id": "cec90850-4020-4b24-a4f3-76b2c52b9da6", + "method": "POST" + }, + { + "id": "60202782-e345-4c4c-8f1e-939cd7ee8f0b", + "method": "POST" + }, + { + "id": "28f49920-9a42-441b-90b6-34260a2cd834", + "method": "POST" + }, + { + "id": "b0dd02cf-d5b8-44bb-acd7-367bdc5f9592", + "method": "POST" + }, + { + "id": "208caab7-3f00-47ab-ae97-838e9b4815b4", + "method": "POST" + }, + { + "id": "9a169a58-785e-468e-b8fe-014b0d3898d9", + "method": "POST" + }, + { + "id": "5d662783-fe26-41db-a2b6-66ed08be9430", + "method": "POST" + }, + { + "id": "2610536f-2c69-41c1-be92-6df6b4a153dc", + "method": "POST" + } + ] + } +} \ No newline at end of file From 2821a10f5d697ba4ec46283f7a0e62499a5bb965 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Wed, 7 Sep 2022 17:34:25 +0530 Subject: [PATCH 129/490] Issue #KN-257 feat: Content Release DIAL codes API refactor. --- .../content-service/test/controllers/v4/ContentSpec.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content-api/content-service/test/controllers/v4/ContentSpec.scala b/content-api/content-service/test/controllers/v4/ContentSpec.scala index b3bec6705..02244b35b 100644 --- a/content-api/content-service/test/controllers/v4/ContentSpec.scala +++ b/content-api/content-service/test/controllers/v4/ContentSpec.scala @@ -195,7 +195,7 @@ class ContentSpec extends BaseSpec { "return success response for dialcode reserve API" in { val controller = app.injector.instanceOf[controllers.v4.CollectionController] val json: JsValue = Json.parse("""{"request": {"dialcodes": {"count": 5, "qrCodeSpec": { "errorCorrectionLevel": "H" }}}}""".stripMargin) - val fakeRequest = FakeRequest("POST", "/collection/v4/dialcode/reserve/do_123").withJsonBody(json) + val fakeRequest = FakeRequest("POST", "/content/v4/dialcode/reserve/do_123").withJsonBody(json) val result = controller.reserveDialCode("do_123")(fakeRequest) isOK(result) status(result) must equalTo(OK) @@ -206,7 +206,7 @@ class ContentSpec extends BaseSpec { "return success response for dialcode release API" in { val controller = app.injector.instanceOf[controllers.v4.CollectionController] val json: JsValue = Json.parse("""{"request": {"dialcodes": {"count": 1}}}""".stripMargin) - val fakeRequest = FakeRequest("POST", "/collection/v4/dialcode/release/do_123").withJsonBody(json) + val fakeRequest = FakeRequest("POST", "/content/v4/dialcode/release/do_123").withJsonBody(json) val result = controller.releaseDialCode("do_123")(fakeRequest) isOK(result) status(result) must equalTo(OK) From f984da3d8f6d0845704a33863362087fc08a60af Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Wed, 7 Sep 2022 20:05:21 +0530 Subject: [PATCH 130/490] Issue #KN-257 feat: Content Release DIAL codes API refactor. --- .../test/scala/org/sunbird/content/dial/DIALManagerTest.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala b/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala index cbc801156..8b54cd6d6 100644 --- a/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala +++ b/content-api/content-actors/src/test/scala/org/sunbird/content/dial/DIALManagerTest.scala @@ -627,7 +627,7 @@ class DIALManagerTest extends AsyncFlatSpec with Matchers with AsyncMockFactory request.setContext(getContext()) request.getContext.put("identifier",identifier) request.put("identifier",identifier) - request.putAll(getReserveRequest()) + request.putAll(getReleaseRequest()) request } From 6906a14a2cd438df4d7569d99c0c76a48f969091 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Wed, 7 Sep 2022 20:44:41 +0530 Subject: [PATCH 131/490] Issue #KN-257 feat: Content Release DIAL codes API refactor. --- .../content-service/test/controllers/v4/ContentSpec.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content-api/content-service/test/controllers/v4/ContentSpec.scala b/content-api/content-service/test/controllers/v4/ContentSpec.scala index 02244b35b..e69c69181 100644 --- a/content-api/content-service/test/controllers/v4/ContentSpec.scala +++ b/content-api/content-service/test/controllers/v4/ContentSpec.scala @@ -193,7 +193,7 @@ class ContentSpec extends BaseSpec { "Content Controller with valid request " should { "return success response for dialcode reserve API" in { - val controller = app.injector.instanceOf[controllers.v4.CollectionController] + val controller = app.injector.instanceOf[controllers.v4.ContentController] val json: JsValue = Json.parse("""{"request": {"dialcodes": {"count": 5, "qrCodeSpec": { "errorCorrectionLevel": "H" }}}}""".stripMargin) val fakeRequest = FakeRequest("POST", "/content/v4/dialcode/reserve/do_123").withJsonBody(json) val result = controller.reserveDialCode("do_123")(fakeRequest) @@ -204,7 +204,7 @@ class ContentSpec extends BaseSpec { "Content Controller with valid request " should { "return success response for dialcode release API" in { - val controller = app.injector.instanceOf[controllers.v4.CollectionController] + val controller = app.injector.instanceOf[controllers.v4.ContentController] val json: JsValue = Json.parse("""{"request": {"dialcodes": {"count": 1}}}""".stripMargin) val fakeRequest = FakeRequest("POST", "/content/v4/dialcode/release/do_123").withJsonBody(json) val result = controller.releaseDialCode("do_123")(fakeRequest) From 40462ff73136cacd3f3381214886c84b4ae72d45 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Tue, 13 Sep 2022 11:28:37 +0530 Subject: [PATCH 132/490] Issue #KN-257 feat: Content Release DIAL codes API refactor. --- .../org/sunbird/content/dial/DIALManager.scala | 15 +++++++-------- .../sunbird/content/util/ContentConstants.scala | 1 + content-api/content-service/conf/application.conf | 1 + 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala index 61bfee6b2..b5d3eada4 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala @@ -279,12 +279,11 @@ object DIALManager { validateContentForReserveAndReleaseDialcodes(contentMetadata) validateCountForReservingAndReleasingDialCode(request.getRequest.get(DIALConstants.DIALCODES).asInstanceOf[util.Map[String, AnyRef]]) - if(operation.nonEmpty && operation.equalsIgnoreCase(ContentConstants.RESERVE)) { - reserve(request, channelId, contentId, rootNode, contentMetadata) - } else if(operation.nonEmpty && operation.equalsIgnoreCase(ContentConstants.RELEASE)) { - release(request, contentId, rootNode, contentMetadata) - } else throw new ClientException(DIALErrors.ERR_INVALID_OPERATION, DIALErrors.ERR_INVALID_OPERATION_MSG) - + operation match { + case ContentConstants.RESERVE => reserve(request, channelId, contentId, rootNode, contentMetadata) + case ContentConstants.RELEASE => release(request, contentId, rootNode, contentMetadata) + case _ => throw new ClientException(DIALErrors.ERR_INVALID_OPERATION, DIALErrors.ERR_INVALID_OPERATION_MSG) + } }) } @@ -403,8 +402,8 @@ object DIALManager { val validMimeType = if (Platform.config.hasPath("reserve_dialcode.mimeType")) Platform.config.getStringList("reserve_dialcode.mimeType") else util.Arrays.asList(ContentConstants.COLLECTION_MIME_TYPE) if (!validMimeType.contains(metaData.get(ContentConstants.MIME_TYPE))) throw new ClientException(DIALErrors.ERR_CONTENT_MIMETYPE, DIALErrors.ERR_CONTENT_MIMETYPE_MSG) - if(metaData.get(ContentConstants.STATUS).asInstanceOf[String].equalsIgnoreCase("Retired")) - throw new ClientException(DIALErrors.ERR_CONTENT_RETIRED_OBJECT_ID, DIALErrors.ERR_CONTENT_RETIRED_OBJECT_ID_MSG) + val validContentStatus = if (Platform.config.hasPath("reserve_dialcode.valid_content_status")) Platform.config.getStringList("reserve_dialcode.valid_content_status") else util.Arrays.asList(ContentConstants.DRAFT) + if(!validContentStatus.contains(metaData.get(ContentConstants.STATUS).asInstanceOf[String])) throw new ClientException(DIALErrors.ERR_CONTENT_RETIRED_OBJECT_ID, DIALErrors.ERR_CONTENT_RETIRED_OBJECT_ID_MSG) } def validateCountForReservingAndReleasingDialCode(requestDIALCodesMap: util.Map[String, AnyRef]): Unit = { diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/util/ContentConstants.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/util/ContentConstants.scala index aa199a481..8ae26a27a 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/util/ContentConstants.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/util/ContentConstants.scala @@ -77,4 +77,5 @@ object ContentConstants { val PROCESSING: String = "Processing" val RESERVE: String = "reserve" val RELEASE: String = "release" + val DRAFT: String = "Draft" } diff --git a/content-api/content-service/conf/application.conf b/content-api/content-service/conf/application.conf index 910dd9406..5a77be1c5 100644 --- a/content-api/content-service/conf/application.conf +++ b/content-api/content-service/conf/application.conf @@ -612,6 +612,7 @@ dial_service { reserve_dialcode { mimeType = ["application/vnd.ekstep.content-collection"] max_count = 250 + valid_content_status = ["Draft","Live"] } content.link_dialcode.validation=true From 7924fe67e1f3dff40ddf67da08445f3311f74446 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Tue, 13 Sep 2022 11:29:58 +0530 Subject: [PATCH 133/490] Issue #KN-257 feat: Content Release DIAL codes API refactor. --- .../src/main/scala/org/sunbird/content/dial/DIALManager.scala | 2 +- .../main/scala/org/sunbird/content/util/ContentConstants.scala | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala index b5d3eada4..5bb14c093 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala @@ -402,7 +402,7 @@ object DIALManager { val validMimeType = if (Platform.config.hasPath("reserve_dialcode.mimeType")) Platform.config.getStringList("reserve_dialcode.mimeType") else util.Arrays.asList(ContentConstants.COLLECTION_MIME_TYPE) if (!validMimeType.contains(metaData.get(ContentConstants.MIME_TYPE))) throw new ClientException(DIALErrors.ERR_CONTENT_MIMETYPE, DIALErrors.ERR_CONTENT_MIMETYPE_MSG) - val validContentStatus = if (Platform.config.hasPath("reserve_dialcode.valid_content_status")) Platform.config.getStringList("reserve_dialcode.valid_content_status") else util.Arrays.asList(ContentConstants.DRAFT) + val validContentStatus = if (Platform.config.hasPath("reserve_dialcode.valid_content_status")) Platform.config.getStringList("reserve_dialcode.valid_content_status") else util.Arrays.asList(ContentConstants.DRAFT, ContentConstants.LIVE) if(!validContentStatus.contains(metaData.get(ContentConstants.STATUS).asInstanceOf[String])) throw new ClientException(DIALErrors.ERR_CONTENT_RETIRED_OBJECT_ID, DIALErrors.ERR_CONTENT_RETIRED_OBJECT_ID_MSG) } diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/util/ContentConstants.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/util/ContentConstants.scala index 8ae26a27a..6ddc98881 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/util/ContentConstants.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/util/ContentConstants.scala @@ -78,4 +78,5 @@ object ContentConstants { val RESERVE: String = "reserve" val RELEASE: String = "release" val DRAFT: String = "Draft" + val LIVE: String = "Live" } From 8db5754e56693becd7559638ca19c1367a598e64 Mon Sep 17 00:00:00 2001 From: AmiableAnil Date: Mon, 19 Sep 2022 10:02:59 +0530 Subject: [PATCH 134/490] Issue #KN-231 test: Improved unit test coverage. --- .../sunbird/content/actors/ContentActor.scala | 3 +- .../content/actors/TestContentActor.scala | 2 +- .../sunbird/cloudstore/StorageService.scala | 67 ++++++++++++++++++- 3 files changed, 68 insertions(+), 4 deletions(-) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/actors/ContentActor.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/actors/ContentActor.scala index df9047ff8..fabc95d43 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/actors/ContentActor.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/actors/ContentActor.scala @@ -144,7 +144,6 @@ class ContentActor @Inject() (implicit oec: OntologyEngineContext, ss: StorageSe def uploadPreSignedUrl(request: Request): Future[Response] = { val `type`: String = request.get("type").asInstanceOf[String].toLowerCase() val fileName: String = request.get("fileName").asInstanceOf[String] - val mimeType: String = request.get("mimeType").asInstanceOf[String] val filePath: String = request.getRequest.getOrDefault("filePath","").asInstanceOf[String] .replaceAll("^/+|/+$", "") val identifier: String = request.get("identifier").asInstanceOf[String] @@ -153,7 +152,7 @@ class ContentActor @Inject() (implicit oec: OntologyEngineContext, ss: StorageSe val objectKey = if (StringUtils.isEmpty(filePath)) "content" + File.separator + `type` + File.separator + identifier + File.separator + Slug.makeSlug(fileName, true) else filePath + File.separator + "content" + File.separator + `type` + File.separator + identifier + File.separator + Slug.makeSlug(fileName, true) val expiry = Platform.config.getString("cloud_storage.upload.url.ttl") - val preSignedURL = ss.getSignedURL(objectKey, Option.apply(expiry.toInt), Option.apply("w"), Option.apply(mimeType)) + val preSignedURL = ss.getSignedURL(objectKey, Option.apply(expiry.toInt), Option.apply("w")) ResponseHandler.OK().put("identifier", identifier).put("pre_signed_url", preSignedURL) .put("url_expiry", expiry) }) recoverWith { case e: CompletionException => throw e.getCause } diff --git a/content-api/content-actors/src/test/scala/org/sunbird/content/actors/TestContentActor.scala b/content-api/content-actors/src/test/scala/org/sunbird/content/actors/TestContentActor.scala index 52b413a43..5c15e56d2 100644 --- a/content-api/content-actors/src/test/scala/org/sunbird/content/actors/TestContentActor.scala +++ b/content-api/content-actors/src/test/scala/org/sunbird/content/actors/TestContentActor.scala @@ -94,7 +94,7 @@ class TestContentActor extends BaseSpec with MockFactory { (oec.graphService _).expects().returns(graphDB) (graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, *, *, *).returns(Future(getValidNode())) implicit val ss: StorageService = mock[StorageService] - (ss.getSignedURL(_: String, _: Option[Int], _: Option[String], _: Option[String])).expects(*, *, *, *).returns("cloud store url") + (ss.getSignedURL(_: String, _: Option[Int], _: Option[String])).expects(*, *, *).returns("cloud store url") val request = getContentRequest() request.getRequest.putAll(mapAsJavaMap(Map("fileName" -> "presigned_url", "filePath" -> "/data/cloudstore/", "type" -> "assets", "identifier" -> "do_1234"))) request.setOperation("uploadPreSignedUrl") diff --git a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala index f03a79627..fbb2c2197 100644 --- a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala +++ b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala @@ -9,6 +9,8 @@ import org.sunbird.cloud.storage.factory.StorageServiceFactory import org.sunbird.common.exception.ServerException import org.sunbird.common.Slug +import java.net.URLConnection +import java.util import scala.concurrent.{ExecutionContext, Future} class StorageService { @@ -87,7 +89,7 @@ class StorageService { def getSignedURL(key: String, ttl: Option[Int], permission: Option[String], contentType: Option[String] = None): String = { storageType match { - case "gcloud" => getService.getPutSignedURL(getContainerName, key, ttl, permission, contentType) + case "gcloud" => getService.getPutSignedURL(getContainerName, key, ttl, permission, Option.apply(getMimeType(key))) case _ => getService.getSignedURL (getContainerName, key, ttl, permission) } } @@ -101,4 +103,67 @@ class StorageService { "" } } + + def getMimeType(fileName: String): String = { // 1. first use java's built-in utils + val mimeTypes = URLConnection.getFileNameMap + var contentType = mimeTypes.getContentTypeFor(fileName) + // 2. nothing found -> lookup our in extension map to find types like ".doc" or ".docx" + if (contentType == null) { + val extension = fileName.substring(fileName.lastIndexOf('.') + 1, fileName.length) + contentType = fileExtensionMap.getOrDefault(extension, "application/octet-stream") + } + contentType + } + + private val fileExtensionMap: util.Map[String, String] = new java.util.HashMap().asInstanceOf[java.util.Map[String, String]] + + // MS Office + fileExtensionMap.put("doc", "application/msword") + fileExtensionMap.put("dot", "application/msword") + fileExtensionMap.put("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document") + fileExtensionMap.put("dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template") + fileExtensionMap.put("docm", "application/vnd.ms-word.document.macroEnabled.12") + fileExtensionMap.put("dotm", "application/vnd.ms-word.template.macroEnabled.12") + fileExtensionMap.put("xls", "application/vnd.ms-excel") + fileExtensionMap.put("xlt", "application/vnd.ms-excel") + fileExtensionMap.put("xla", "application/vnd.ms-excel") + fileExtensionMap.put("xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") + fileExtensionMap.put("xltx", "application/vnd.openxmlformats-officedocument.spreadsheetml.template") + fileExtensionMap.put("xlsm", "application/vnd.ms-excel.sheet.macroEnabled.12") + fileExtensionMap.put("xltm", "application/vnd.ms-excel.template.macroEnabled.12") + fileExtensionMap.put("xlam", "application/vnd.ms-excel.addin.macroEnabled.12") + fileExtensionMap.put("xlsb", "application/vnd.ms-excel.sheet.binary.macroEnabled.12") + fileExtensionMap.put("ppt", "application/vnd.ms-powerpoint") + fileExtensionMap.put("pot", "application/vnd.ms-powerpoint") + fileExtensionMap.put("pps", "application/vnd.ms-powerpoint") + fileExtensionMap.put("ppa", "application/vnd.ms-powerpoint") + fileExtensionMap.put("pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation") + fileExtensionMap.put("potx", "application/vnd.openxmlformats-officedocument.presentationml.template") + fileExtensionMap.put("ppsx", "application/vnd.openxmlformats-officedocument.presentationml.slideshow") + fileExtensionMap.put("ppam", "application/vnd.ms-powerpoint.addin.macroEnabled.12") + fileExtensionMap.put("pptm", "application/vnd.ms-powerpoint.presentation.macroEnabled.12") + fileExtensionMap.put("potm", "application/vnd.ms-powerpoint.presentation.macroEnabled.12") + fileExtensionMap.put("ppsm", "application/vnd.ms-powerpoint.slideshow.macroEnabled.12") + // Open Office + fileExtensionMap.put("odt", "application/vnd.oasis.opendocument.text") + fileExtensionMap.put("ott", "application/vnd.oasis.opendocument.text-template") + fileExtensionMap.put("oth", "application/vnd.oasis.opendocument.text-web") + fileExtensionMap.put("odm", "application/vnd.oasis.opendocument.text-master") + fileExtensionMap.put("odg", "application/vnd.oasis.opendocument.graphics") + fileExtensionMap.put("otg", "application/vnd.oasis.opendocument.graphics-template") + fileExtensionMap.put("odp", "application/vnd.oasis.opendocument.presentation") + fileExtensionMap.put("otp", "application/vnd.oasis.opendocument.presentation-template") + fileExtensionMap.put("ods", "application/vnd.oasis.opendocument.spreadsheet") + fileExtensionMap.put("ots", "application/vnd.oasis.opendocument.spreadsheet-template") + fileExtensionMap.put("odc", "application/vnd.oasis.opendocument.chart") + fileExtensionMap.put("odf", "application/vnd.oasis.opendocument.formula") + fileExtensionMap.put("odb", "application/vnd.oasis.opendocument.database") + fileExtensionMap.put("odi", "application/vnd.oasis.opendocument.image") + fileExtensionMap.put("oxt", "application/vnd.openofficeorg.extension") + // Other + fileExtensionMap.put("rtf", "application/rtf") + fileExtensionMap.put("pdf", "application/pdf") + fileExtensionMap.put("h5p", "application/octet-stream") + fileExtensionMap.put("csv", "text/csv") + fileExtensionMap.put("epub", "application/epub+zip") } From 8affd4650ce954a9b6a843ec79c805bd5b591554 Mon Sep 17 00:00:00 2001 From: AmiableAnil Date: Mon, 19 Sep 2022 10:37:01 +0530 Subject: [PATCH 135/490] Issue #KN-231 test: Improved unit test coverage. --- .../src/main/scala/org/sunbird/cloudstore/StorageService.scala | 2 +- .../test/scala/org/sunbird/cloudstore/StorageServiceTest.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala index fbb2c2197..3744ae902 100644 --- a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala +++ b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala @@ -87,7 +87,7 @@ class StorageService { getService.deleteObject(getContainerName, key, isDirectory) } - def getSignedURL(key: String, ttl: Option[Int], permission: Option[String], contentType: Option[String] = None): String = { + def getSignedURL(key: String, ttl: Option[Int], permission: Option[String]): String = { storageType match { case "gcloud" => getService.getPutSignedURL(getContainerName, key, ttl, permission, Option.apply(getMimeType(key))) case _ => getService.getSignedURL (getContainerName, key, ttl, permission) diff --git a/platform-modules/mimetype-manager/src/test/scala/org/sunbird/cloudstore/StorageServiceTest.scala b/platform-modules/mimetype-manager/src/test/scala/org/sunbird/cloudstore/StorageServiceTest.scala index 47ba320e1..66481b690 100644 --- a/platform-modules/mimetype-manager/src/test/scala/org/sunbird/cloudstore/StorageServiceTest.scala +++ b/platform-modules/mimetype-manager/src/test/scala/org/sunbird/cloudstore/StorageServiceTest.scala @@ -19,7 +19,7 @@ class StorageServiceTest extends AsyncFlatSpec with Matchers { "getSignedURL" should "return the signed url" in { val objectKey = "content" + File.separator + "asset" + File.separator + "do_53245" + File.separator + "abc.png" - val preSignedURL = ss.getSignedURL(objectKey, Option.apply(600), Option.apply("w"), Option.apply("")) + val preSignedURL = ss.getSignedURL(objectKey, Option.apply(600), Option.apply("w")) assert(preSignedURL.contains(objectKey)) } From f17859f0378ef4819e52798038b2d30a687abc82 Mon Sep 17 00:00:00 2001 From: AmiableAnil Date: Fri, 23 Sep 2022 18:17:47 +0530 Subject: [PATCH 136/490] Issue #KN-231 test: Used the tika to detect the mimetype from filename. --- .../sunbird/cloudstore/StorageService.scala | 74 ++----------------- 1 file changed, 7 insertions(+), 67 deletions(-) diff --git a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala index 3744ae902..3904888a0 100644 --- a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala +++ b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala @@ -1,16 +1,13 @@ package org.sunbird.cloudstore -import java.io.File import org.apache.commons.lang3.StringUtils +import org.apache.tika.Tika import org.sunbird.cloud.storage.BaseStorageService -import org.sunbird.common.Platform -import org.sunbird.cloud.storage.factory.StorageConfig -import org.sunbird.cloud.storage.factory.StorageServiceFactory +import org.sunbird.cloud.storage.factory.{StorageConfig, StorageServiceFactory} +import org.sunbird.common.{Platform, Slug} import org.sunbird.common.exception.ServerException -import org.sunbird.common.Slug -import java.net.URLConnection -import java.util +import java.io.File import scala.concurrent.{ExecutionContext, Future} class StorageService { @@ -104,66 +101,9 @@ class StorageService { } } - def getMimeType(fileName: String): String = { // 1. first use java's built-in utils - val mimeTypes = URLConnection.getFileNameMap - var contentType = mimeTypes.getContentTypeFor(fileName) - // 2. nothing found -> lookup our in extension map to find types like ".doc" or ".docx" - if (contentType == null) { - val extension = fileName.substring(fileName.lastIndexOf('.') + 1, fileName.length) - contentType = fileExtensionMap.getOrDefault(extension, "application/octet-stream") - } - contentType + def getMimeType(fileName: String): String = { + val tika: Tika = new Tika() + tika.detect(fileName) } - private val fileExtensionMap: util.Map[String, String] = new java.util.HashMap().asInstanceOf[java.util.Map[String, String]] - - // MS Office - fileExtensionMap.put("doc", "application/msword") - fileExtensionMap.put("dot", "application/msword") - fileExtensionMap.put("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document") - fileExtensionMap.put("dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template") - fileExtensionMap.put("docm", "application/vnd.ms-word.document.macroEnabled.12") - fileExtensionMap.put("dotm", "application/vnd.ms-word.template.macroEnabled.12") - fileExtensionMap.put("xls", "application/vnd.ms-excel") - fileExtensionMap.put("xlt", "application/vnd.ms-excel") - fileExtensionMap.put("xla", "application/vnd.ms-excel") - fileExtensionMap.put("xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") - fileExtensionMap.put("xltx", "application/vnd.openxmlformats-officedocument.spreadsheetml.template") - fileExtensionMap.put("xlsm", "application/vnd.ms-excel.sheet.macroEnabled.12") - fileExtensionMap.put("xltm", "application/vnd.ms-excel.template.macroEnabled.12") - fileExtensionMap.put("xlam", "application/vnd.ms-excel.addin.macroEnabled.12") - fileExtensionMap.put("xlsb", "application/vnd.ms-excel.sheet.binary.macroEnabled.12") - fileExtensionMap.put("ppt", "application/vnd.ms-powerpoint") - fileExtensionMap.put("pot", "application/vnd.ms-powerpoint") - fileExtensionMap.put("pps", "application/vnd.ms-powerpoint") - fileExtensionMap.put("ppa", "application/vnd.ms-powerpoint") - fileExtensionMap.put("pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation") - fileExtensionMap.put("potx", "application/vnd.openxmlformats-officedocument.presentationml.template") - fileExtensionMap.put("ppsx", "application/vnd.openxmlformats-officedocument.presentationml.slideshow") - fileExtensionMap.put("ppam", "application/vnd.ms-powerpoint.addin.macroEnabled.12") - fileExtensionMap.put("pptm", "application/vnd.ms-powerpoint.presentation.macroEnabled.12") - fileExtensionMap.put("potm", "application/vnd.ms-powerpoint.presentation.macroEnabled.12") - fileExtensionMap.put("ppsm", "application/vnd.ms-powerpoint.slideshow.macroEnabled.12") - // Open Office - fileExtensionMap.put("odt", "application/vnd.oasis.opendocument.text") - fileExtensionMap.put("ott", "application/vnd.oasis.opendocument.text-template") - fileExtensionMap.put("oth", "application/vnd.oasis.opendocument.text-web") - fileExtensionMap.put("odm", "application/vnd.oasis.opendocument.text-master") - fileExtensionMap.put("odg", "application/vnd.oasis.opendocument.graphics") - fileExtensionMap.put("otg", "application/vnd.oasis.opendocument.graphics-template") - fileExtensionMap.put("odp", "application/vnd.oasis.opendocument.presentation") - fileExtensionMap.put("otp", "application/vnd.oasis.opendocument.presentation-template") - fileExtensionMap.put("ods", "application/vnd.oasis.opendocument.spreadsheet") - fileExtensionMap.put("ots", "application/vnd.oasis.opendocument.spreadsheet-template") - fileExtensionMap.put("odc", "application/vnd.oasis.opendocument.chart") - fileExtensionMap.put("odf", "application/vnd.oasis.opendocument.formula") - fileExtensionMap.put("odb", "application/vnd.oasis.opendocument.database") - fileExtensionMap.put("odi", "application/vnd.oasis.opendocument.image") - fileExtensionMap.put("oxt", "application/vnd.openofficeorg.extension") - // Other - fileExtensionMap.put("rtf", "application/rtf") - fileExtensionMap.put("pdf", "application/pdf") - fileExtensionMap.put("h5p", "application/octet-stream") - fileExtensionMap.put("csv", "text/csv") - fileExtensionMap.put("epub", "application/epub+zip") } From 2c95ba7e9ecc63f5058fff98fc633e9bd3a2a36e Mon Sep 17 00:00:00 2001 From: AmiableAnil Date: Fri, 23 Sep 2022 18:56:15 +0530 Subject: [PATCH 137/490] Issue #KN-231 test: Improved the unit test. --- .../cloudstore/StorageServiceTest.scala | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/platform-modules/mimetype-manager/src/test/scala/org/sunbird/cloudstore/StorageServiceTest.scala b/platform-modules/mimetype-manager/src/test/scala/org/sunbird/cloudstore/StorageServiceTest.scala index 66481b690..071a9e20c 100644 --- a/platform-modules/mimetype-manager/src/test/scala/org/sunbird/cloudstore/StorageServiceTest.scala +++ b/platform-modules/mimetype-manager/src/test/scala/org/sunbird/cloudstore/StorageServiceTest.scala @@ -27,4 +27,29 @@ class StorageServiceTest extends AsyncFlatSpec with Matchers { val uri = ss.getUri("content/abc.json") assert(uri != null) } + + "getMimeType" should "return the mimetype application/epub+zip for epub" in { + val result = ss.getMimeType("test.alert.epub") + assert(result == "application/epub+zip") + } + + "getMimeType" should "return the mimetype application/octet-stream for h5p" in { + val result = ss.getMimeType("test.alert.h5p") + assert(result == "application/octet-stream") + } + + "getMimeType" should "return the mimetype text/csv for csv" in { + val result = ss.getMimeType("test.alert.csv") + assert(result == "text/csv") + } + + "getMimeType" should "return the mimetype application/pdf for pdf" in { + val result = ss.getMimeType("test.alert.pdf") + assert(result == "application/pdf") + } + + "getMimeType" should "return the mimetype application/zip for zip" in { + val result = ss.getMimeType("test.alert.zip") + assert(result == "application/zip") + } } From c6e093ab8e1f841f013ca4a2ccfd55a234012825 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Thu, 6 Oct 2022 15:29:51 +0530 Subject: [PATCH 138/490] Issue #KN-257 feat: Content Release DIAL codes API refactor. --- .../scala/org/sunbird/content/dial/DIALManager.scala | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala index 5bb14c093..5177b1dc2 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala @@ -357,8 +357,13 @@ object DIALManager { val imageCollectionHierarchy = getImageHierarchyResponse.getResult.getOrDefault(ContentConstants.CONTENT, new java.util.HashMap[String, AnyRef]()).asInstanceOf[java.util.Map[String, AnyRef]] val imageChildrenHierarchy = imageCollectionHierarchy.get(ContentConstants.CHILDREN).asInstanceOf[util.List[util.Map[String, AnyRef]]].asScala.toList val imageChildrenAssignedDIALList = getAssignedDIALcodes(imageChildrenHierarchy) - val contentImageAssignedDIALList = if(imageCollectionHierarchy.containsKey(DIALConstants.DIALCODES) && imageCollectionHierarchy.get(DIALConstants.DIALCODES) != null) - imageChildrenAssignedDIALList ++ imageCollectionHierarchy.getOrDefault(DIALConstants.DIALCODES, List.empty[String]).asInstanceOf[List[String]] + val contentImageAssignedDIALList = if(imageCollectionHierarchy.containsKey(DIALConstants.DIALCODES) && imageCollectionHierarchy.get(DIALConstants.DIALCODES) != null) { + val hierarchyDialCode: List[String] = imageCollectionHierarchy.get(DIALConstants.DIALCODES) match { + case strVal: String => ScalaJsonUtils.deserialize(strVal).asInstanceOf[List[String]] + case anyVal => anyVal.asInstanceOf[List[String]] + } + imageChildrenAssignedDIALList ++ hierarchyDialCode + } else imageChildrenAssignedDIALList request.put(ContentConstants.ROOT_ID, contentId) From fd8f7c5a0926e521aab46bf86438ecad75939646 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Fri, 7 Oct 2022 11:09:26 +0530 Subject: [PATCH 139/490] Issue #KN-547 debug: Collection hierarchy dialcodes coming as string --- .../src/main/scala/org/sunbird/content/dial/DIALManager.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala index 5177b1dc2..7c05ab339 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala @@ -10,6 +10,7 @@ import org.sunbird.graph.dac.model.Node import org.sunbird.graph.nodes.DataNode import org.sunbird.graph.utils.ScalaJsonUtils import org.sunbird.managers.HierarchyManager +import org.sunbird.telemetry.logger.TelemetryManager import java.util import scala.collection.JavaConverters._ @@ -358,6 +359,7 @@ object DIALManager { val imageChildrenHierarchy = imageCollectionHierarchy.get(ContentConstants.CHILDREN).asInstanceOf[util.List[util.Map[String, AnyRef]]].asScala.toList val imageChildrenAssignedDIALList = getAssignedDIALcodes(imageChildrenHierarchy) val contentImageAssignedDIALList = if(imageCollectionHierarchy.containsKey(DIALConstants.DIALCODES) && imageCollectionHierarchy.get(DIALConstants.DIALCODES) != null) { + TelemetryManager.info("DIALManager:: populateAssignedDialCodes:: collection DIAL codes:: " + imageCollectionHierarchy.get(DIALConstants.DIALCODES).toString) val hierarchyDialCode: List[String] = imageCollectionHierarchy.get(DIALConstants.DIALCODES) match { case strVal: String => ScalaJsonUtils.deserialize(strVal).asInstanceOf[List[String]] case anyVal => anyVal.asInstanceOf[List[String]] From d77e55d181186d68235dcbee69086bdab6506ff8 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Fri, 7 Oct 2022 12:41:44 +0530 Subject: [PATCH 140/490] Issue #KN-547 debug: Collection hierarchy dialcodes coming as string --- .../scala/org/sunbird/content/dial/DIALManager.scala | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala index 7c05ab339..65f3d551f 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala @@ -356,14 +356,13 @@ object DIALManager { HierarchyManager.getHierarchy(request).flatMap(getImageHierarchyResponse => { val imageCollectionHierarchy = getImageHierarchyResponse.getResult.getOrDefault(ContentConstants.CONTENT, new java.util.HashMap[String, AnyRef]()).asInstanceOf[java.util.Map[String, AnyRef]] + TelemetryManager.info("DIALManager:: populateAssignedDialCodes:: imageCollectionHierarchy:: " + imageCollectionHierarchy) val imageChildrenHierarchy = imageCollectionHierarchy.get(ContentConstants.CHILDREN).asInstanceOf[util.List[util.Map[String, AnyRef]]].asScala.toList val imageChildrenAssignedDIALList = getAssignedDIALcodes(imageChildrenHierarchy) val contentImageAssignedDIALList = if(imageCollectionHierarchy.containsKey(DIALConstants.DIALCODES) && imageCollectionHierarchy.get(DIALConstants.DIALCODES) != null) { - TelemetryManager.info("DIALManager:: populateAssignedDialCodes:: collection DIAL codes:: " + imageCollectionHierarchy.get(DIALConstants.DIALCODES).toString) - val hierarchyDialCode: List[String] = imageCollectionHierarchy.get(DIALConstants.DIALCODES) match { - case strVal: String => ScalaJsonUtils.deserialize(strVal).asInstanceOf[List[String]] - case anyVal => anyVal.asInstanceOf[List[String]] - } + TelemetryManager.info("DIALManager:: populateAssignedDialCodes:: collection DIAL codes:: " + imageCollectionHierarchy.get(DIALConstants.DIALCODES)) + val hierarchyDialCodeStr = ScalaJsonUtils.serialize(imageCollectionHierarchy.get(DIALConstants.DIALCODES)) + val hierarchyDialCode = ScalaJsonUtils.deserialize(hierarchyDialCodeStr).asInstanceOf[List[String]] imageChildrenAssignedDIALList ++ hierarchyDialCode } else imageChildrenAssignedDIALList From 64d13ad12cef8f4df02a05a9482a1014230b120b Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Fri, 7 Oct 2022 13:51:08 +0530 Subject: [PATCH 141/490] Issue #KN-547 debug: Collection hierarchy dialcodes coming as string --- .../main/scala/org/sunbird/content/dial/DIALManager.scala | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala index 65f3d551f..1262be12a 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala @@ -361,8 +361,10 @@ object DIALManager { val imageChildrenAssignedDIALList = getAssignedDIALcodes(imageChildrenHierarchy) val contentImageAssignedDIALList = if(imageCollectionHierarchy.containsKey(DIALConstants.DIALCODES) && imageCollectionHierarchy.get(DIALConstants.DIALCODES) != null) { TelemetryManager.info("DIALManager:: populateAssignedDialCodes:: collection DIAL codes:: " + imageCollectionHierarchy.get(DIALConstants.DIALCODES)) - val hierarchyDialCodeStr = ScalaJsonUtils.serialize(imageCollectionHierarchy.get(DIALConstants.DIALCODES)) - val hierarchyDialCode = ScalaJsonUtils.deserialize(hierarchyDialCodeStr).asInstanceOf[List[String]] + val hierarchyDialCode = imageCollectionHierarchy.get(DIALConstants.DIALCODES) match { + case javaStr: java.lang.String => ScalaJsonUtils.deserialize[List[String]](javaStr) + case anyVal => anyVal.asInstanceOf[List[String]] + } imageChildrenAssignedDIALList ++ hierarchyDialCode } else imageChildrenAssignedDIALList From 76ad98db9bd96978246743f49483a7f6aa9f5b85 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Fri, 7 Oct 2022 14:42:45 +0530 Subject: [PATCH 142/490] Issue #KN-547 debug: Collection hierarchy dialcodes coming as string --- .../main/scala/org/sunbird/content/dial/DIALManager.scala | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala index 1262be12a..9a1deaf91 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala @@ -361,10 +361,9 @@ object DIALManager { val imageChildrenAssignedDIALList = getAssignedDIALcodes(imageChildrenHierarchy) val contentImageAssignedDIALList = if(imageCollectionHierarchy.containsKey(DIALConstants.DIALCODES) && imageCollectionHierarchy.get(DIALConstants.DIALCODES) != null) { TelemetryManager.info("DIALManager:: populateAssignedDialCodes:: collection DIAL codes:: " + imageCollectionHierarchy.get(DIALConstants.DIALCODES)) - val hierarchyDialCode = imageCollectionHierarchy.get(DIALConstants.DIALCODES) match { - case javaStr: java.lang.String => ScalaJsonUtils.deserialize[List[String]](javaStr) - case anyVal => anyVal.asInstanceOf[List[String]] - } + val hierarchyDialCodeStr = ScalaJsonUtils.serialize(imageCollectionHierarchy.get(DIALConstants.DIALCODES)) + TelemetryManager.info("DIALManager:: populateAssignedDialCodes:: hierarchyDialCodeStr:: " + hierarchyDialCodeStr) + val hierarchyDialCode = ScalaJsonUtils.deserialize[List[String]](hierarchyDialCodeStr) imageChildrenAssignedDIALList ++ hierarchyDialCode } else imageChildrenAssignedDIALList From 4529c4fc6eb570ddac719367264ec2fa3aa49e98 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Fri, 7 Oct 2022 15:26:00 +0530 Subject: [PATCH 143/490] Issue #KN-547 fix: Collection hierarchy dialcodes coming as string --- .../org/sunbird/content/dial/DIALManager.scala | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala index 9a1deaf91..c50ea16c5 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala @@ -356,15 +356,12 @@ object DIALManager { HierarchyManager.getHierarchy(request).flatMap(getImageHierarchyResponse => { val imageCollectionHierarchy = getImageHierarchyResponse.getResult.getOrDefault(ContentConstants.CONTENT, new java.util.HashMap[String, AnyRef]()).asInstanceOf[java.util.Map[String, AnyRef]] - TelemetryManager.info("DIALManager:: populateAssignedDialCodes:: imageCollectionHierarchy:: " + imageCollectionHierarchy) val imageChildrenHierarchy = imageCollectionHierarchy.get(ContentConstants.CHILDREN).asInstanceOf[util.List[util.Map[String, AnyRef]]].asScala.toList val imageChildrenAssignedDIALList = getAssignedDIALcodes(imageChildrenHierarchy) val contentImageAssignedDIALList = if(imageCollectionHierarchy.containsKey(DIALConstants.DIALCODES) && imageCollectionHierarchy.get(DIALConstants.DIALCODES) != null) { - TelemetryManager.info("DIALManager:: populateAssignedDialCodes:: collection DIAL codes:: " + imageCollectionHierarchy.get(DIALConstants.DIALCODES)) - val hierarchyDialCodeStr = ScalaJsonUtils.serialize(imageCollectionHierarchy.get(DIALConstants.DIALCODES)) - TelemetryManager.info("DIALManager:: populateAssignedDialCodes:: hierarchyDialCodeStr:: " + hierarchyDialCodeStr) - val hierarchyDialCode = ScalaJsonUtils.deserialize[List[String]](hierarchyDialCodeStr) - imageChildrenAssignedDIALList ++ hierarchyDialCode + val collectionDialCodeStr = ScalaJsonUtils.serialize(imageCollectionHierarchy.get(DIALConstants.DIALCODES)) + val collectionDialCode = ScalaJsonUtils.deserialize[List[String]](collectionDialCodeStr) + imageChildrenAssignedDIALList ++ collectionDialCode } else imageChildrenAssignedDIALList @@ -375,9 +372,11 @@ object DIALManager { val collectionHierarchy = getHierarchyResponse.getResult.getOrDefault(ContentConstants.CONTENT, new java.util.HashMap[String, AnyRef]()).asInstanceOf[java.util.Map[String, AnyRef]] val childrenHierarchy = collectionHierarchy.get(ContentConstants.CHILDREN).asInstanceOf[util.List[util.Map[String, AnyRef]]].asScala.toList val childrenAssignedDIALList = getAssignedDIALcodes(childrenHierarchy) - val contentAssignedDIALList = if(collectionHierarchy.containsKey(DIALConstants.DIALCODES) && collectionHierarchy.get(DIALConstants.DIALCODES) != null) - childrenAssignedDIALList ++ collectionHierarchy.getOrDefault(DIALConstants.DIALCODES, List.empty[String]).asInstanceOf[List[String]] - else childrenAssignedDIALList + val contentAssignedDIALList = if(collectionHierarchy.containsKey(DIALConstants.DIALCODES) && collectionHierarchy.get(DIALConstants.DIALCODES) != null) { + val collectionDialCodeStr = ScalaJsonUtils.serialize(collectionHierarchy.get(DIALConstants.DIALCODES)) + val collectionDialCode = ScalaJsonUtils.deserialize[List[String]](collectionDialCodeStr) + childrenAssignedDIALList ++ collectionDialCode + } else childrenAssignedDIALList Future(contentImageAssignedDIALList ++ contentAssignedDIALList) }) From 2b9501efa7eb830fc3f81856fa51ee6172501bb6 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Fri, 7 Oct 2022 15:26:39 +0530 Subject: [PATCH 144/490] Issue #KN-547 fix: Collection hierarchy dialcodes coming as string --- .../main/scala/org/sunbird/content/dial/DIALManager.scala | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala index c50ea16c5..2bffec273 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/dial/DIALManager.scala @@ -372,11 +372,9 @@ object DIALManager { val collectionHierarchy = getHierarchyResponse.getResult.getOrDefault(ContentConstants.CONTENT, new java.util.HashMap[String, AnyRef]()).asInstanceOf[java.util.Map[String, AnyRef]] val childrenHierarchy = collectionHierarchy.get(ContentConstants.CHILDREN).asInstanceOf[util.List[util.Map[String, AnyRef]]].asScala.toList val childrenAssignedDIALList = getAssignedDIALcodes(childrenHierarchy) - val contentAssignedDIALList = if(collectionHierarchy.containsKey(DIALConstants.DIALCODES) && collectionHierarchy.get(DIALConstants.DIALCODES) != null) { - val collectionDialCodeStr = ScalaJsonUtils.serialize(collectionHierarchy.get(DIALConstants.DIALCODES)) - val collectionDialCode = ScalaJsonUtils.deserialize[List[String]](collectionDialCodeStr) - childrenAssignedDIALList ++ collectionDialCode - } else childrenAssignedDIALList + val contentAssignedDIALList = if(collectionHierarchy.containsKey(DIALConstants.DIALCODES) && collectionHierarchy.get(DIALConstants.DIALCODES) != null) + childrenAssignedDIALList ++ collectionHierarchy.getOrDefault(DIALConstants.DIALCODES, List.empty[String]).asInstanceOf[List[String]] + else childrenAssignedDIALList Future(contentImageAssignedDIALList ++ contentAssignedDIALList) }) From 5ee124ff31976268c8f0af261bb075bf3d521fdd Mon Sep 17 00:00:00 2001 From: Anil Gupta Date: Fri, 7 Oct 2022 16:35:07 +0530 Subject: [PATCH 145/490] Issue #KN-231 chore: Bump up the cloud-store-sdk to 1.4.3 --- platform-modules/mimetype-manager/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform-modules/mimetype-manager/pom.xml b/platform-modules/mimetype-manager/pom.xml index 80a17876b..ea978cbd5 100644 --- a/platform-modules/mimetype-manager/pom.xml +++ b/platform-modules/mimetype-manager/pom.xml @@ -30,7 +30,7 @@ org.sunbird cloud-store-sdk - 1.4.1 + 1.4.3 org.scala-lang @@ -143,4 +143,4 @@ - \ No newline at end of file + From 1a256a7982c48e73085be5b60c9c9185b31e8469 Mon Sep 17 00:00:00 2001 From: Kartheek Palla Date: Thu, 3 Nov 2022 19:06:44 +0530 Subject: [PATCH 146/490] Issue #IQ-45 feat: CSP changes --- .../sunbird/cloudstore/StorageService.scala | 35 +++++-------------- .../src/test/resources/application.conf | 6 ++-- 2 files changed, 12 insertions(+), 29 deletions(-) diff --git a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala index 3904888a0..5929bf079 100644 --- a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala +++ b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala @@ -18,37 +18,20 @@ class StorageService { @throws[Exception] def getService: BaseStorageService = { if (null == storageService) { - if (StringUtils.equalsIgnoreCase(storageType, "azure")) { - val storageKey = Platform.config.getString("azure_storage_key") - val storageSecret = Platform.config.getString("azure_storage_secret") - storageService = StorageServiceFactory.getStorageService(StorageConfig(storageType, storageKey, storageSecret)) - } else if (StringUtils.equalsIgnoreCase(storageType, "aws")) { - val storageKey = Platform.config.getString("aws_storage_key") - val storageSecret = Platform.config.getString("aws_storage_secret") - storageService = StorageServiceFactory.getStorageService(StorageConfig(storageType, storageKey, storageSecret)) - } else if (StringUtils.equalsIgnoreCase(storageType, "gcloud")) { - val storageKey = Platform.config.getString("gcloud_client_key") - val storageSecret = Platform.config.getString("gcloud_private_secret") - storageService = StorageServiceFactory.getStorageService(StorageConfig(storageType, storageKey, storageSecret)) - } -// else if (StringUtils.equalsIgnoreCase(storageType, "cephs3")) { -// val storageKey = Platform.config.getString("cephs3_storage_key") -// val storageSecret = Platform.config.getString("cephs3_storage_secret") -// val endpoint = Platform.config.getString("cephs3_storage_endpoint") -// storageService = StorageServiceFactory.getStorageService(new StorageConfig(storageType, storageKey, storageSecret, Option(endpoint))) -// } - else throw new ServerException("ERR_INVALID_CLOUD_STORAGE", "Error while initialising cloud storage") + val storageKey = Platform.config.getString("cloud_storage_key") + val storageSecret = Platform.config.getString("cloud_storage_secret") + // TODO: endPoint defined to support "cephs3". Make code changes after cloud-store-sdk 2.11 support it. + val endPoint = if (Platform.config.hasPath("cloud_storage_endpoint")) Option(Platform.config.getString("cloud_storage_endpoint")) else None + storageService = StorageServiceFactory.getStorageService(new StorageConfig(storageType, storageKey, storageSecret)) } storageService } def getContainerName: String = { - storageType match { - case "azure" => Platform.config.getString("azure_storage_container") - case "aws" => Platform.config.getString("aws_storage_container") - case "gcloud" => Platform.config.getString("gcloud_storage_bucket") - case _ => throw new ServerException("ERR_INVALID_CLOUD_STORAGE", "Container name not configured.") - } + if(Platform.config.hasPath("cloud_storage_container")) + Platform.config.getString("cloud_storage_container") + else + throw new ServerException("ERR_INVALID_CLOUD_STORAGE", "Cloud Storage Container name not configured.") } def uploadFile(folderName: String, file: File, slug: Option[Boolean] = Option(true)): Array[String] = { diff --git a/platform-modules/mimetype-manager/src/test/resources/application.conf b/platform-modules/mimetype-manager/src/test/resources/application.conf index cad20dd73..9a2134e56 100644 --- a/platform-modules/mimetype-manager/src/test/resources/application.conf +++ b/platform-modules/mimetype-manager/src/test/resources/application.conf @@ -473,9 +473,9 @@ content.h5p.library.path="https://s3.ap-south-1.amazonaws.com/ekstep-public-dev/ # This is added to handle large artifacts sizes differently content.artifact.size.for_online=209715200 cloud_storage_type="azure" -azure_storage_key="asdfgh" -azure_storage_secret="jhgfdcvb" -azure_storage_container="sunbird-content-dev" +cloud_storage_key="asdfgh" +cloud_storage_secret="jhgfdcvb" +cloud_storage_container="sunbird-content-dev" validation.strictMimeType = ["image/svg+xml"] From 8b39d3d7dc056559b77e32b7402e3a9c5dd61e4e Mon Sep 17 00:00:00 2001 From: vinukumar-vs Date: Sun, 6 Nov 2022 22:11:13 +0530 Subject: [PATCH 147/490] Issue #KN-585 feat: Single click local installation of knowledge platform --- README.md | 21 +++++++++++++++- docker-compose.yml | 61 ++++++++++++++++++++++++++++++++++++++++++++++ local-setup.sh | 6 +++++ 3 files changed, 87 insertions(+), 1 deletion(-) create mode 100755 docker-compose.yml create mode 100755 local-setup.sh diff --git a/README.md b/README.md index 773722a36..9241fb1d1 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,32 @@ Repository for Knowledge Platform - 2.0 -## Knowledge-platform local setup +## Knowledge-platform local setup This readme file contains the instruction to set up and run the content-service in local machine. ### System Requirements: ### Prerequisites: * Java 11 +* Docker, Docker Compose + + +## One step installation + +1. Go to Root folder (knowledge-platform) +2. Run "local-setup.sh" file +``` shell +sh ./local-setup.md +``` + + This will install all the requied dcoker images & local folders for DB mounting. + 3. Follow the below manual setps of running content service + refer: [Running Content Service:](#running-content-service) + + + +## Manual steps to install all the dependents +Please follow the manual steps in [One step installation](#one-step-installation) is failed. ### Prepare folders for database data and logs diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100755 index 000000000..27479d98f --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,61 @@ +version: "3.0" +services: + sunbird-neo4j: + image: neo4j:3.3.0 + container_name: "sunbird_neo4j" + ports: + - 7473:7473 + - 7474:7474 + - 7687:7687 + volumes: + - $sunbird_dbs_path/neo4j/data:/var/lib/neo4j/data + - $sunbird_dbs_path/neo4j/logs:/var/lib/neo4j/logs + - $sunbird_dbs_path/neo4j/plugins:/var/lib/neo4j/plugins + environment: + NEO4J_AUTH: none + NEO4J_dbms_connector_https_advertised__address: "localhost:7473" + NEO4J_dbms_connector_http_advertised__address: "localhost:7474" + NEO4J_dbms_connector_bolt_advertised__address: "localhost:7687" + + sunbird-cassandra: + image: cassandra:3.11.11 + container_name: "sunbird_cassandra" + ports: + - 9042:9042 + depends_on: + - sunbird-neo4j + volumes: + - $sunbird_dbs_path/cassandra/data:/var/lib/cassandra + - $sunbird_dbs_path/cassandra/logs:/opt/cassandra/logs + - $sunbird_dbs_path/cassandra/backups:/mnt/backups + + sunbird-redis: + image: redis:6.0.8 + container_name: "sunbird_redis" + ports: + - 6379:6379 + command: redis-server /usr/local/etc/redis/redis.conf + volumes: + - $sunbird_dbs_path/redis/redis.conf:/usr/local/etc/redis/redis.conf + + zookeeper: + image: 'wurstmeister/zookeeper:latest' + container_name: zookeeper + ports: + - "2181:2181" + environment: + - KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://127.0.0.1:2181 + + kafka: + image: 'wurstmeister/kafka:2.11-1.0.1' + container_name: kafka + ports: + - "9092:9092" + environment: + - KAFKA_BROKER_ID=1 + - KAFKA_LISTENERS=PLAINTEXT://:9092 + - KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://127.0.0.1:9092 + - KAFKA_ZOOKEEPER_CONNECT=zookeeper:2181 + - ALLOW_PLAINTEXT_LISTENER=yes + depends_on: + - zookeeper \ No newline at end of file diff --git a/local-setup.sh b/local-setup.sh new file mode 100755 index 000000000..a65dfad9b --- /dev/null +++ b/local-setup.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +mkdir -p ~/sunbird-dbs/neo4j ~/sunbird-dbs/cassandra ~/sunbird-dbs/redis ~/sunbird-dbs/es ~/sunbird-dbs/kafka +export sunbird_dbs_path=~/sunbird-dbs + +docker compose up From a9d59bca360369c95b5205f3a1f4ff304f9c83a4 Mon Sep 17 00:00:00 2001 From: Kartheek Palla Date: Wed, 9 Nov 2022 15:01:48 +0530 Subject: [PATCH 148/490] Issue #KN-603 fix: CSP changes --- .../src/main/scala/org/sunbird/cloudstore/StorageService.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala index 5929bf079..5abac23d7 100644 --- a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala +++ b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala @@ -69,6 +69,7 @@ class StorageService { def getSignedURL(key: String, ttl: Option[Int], permission: Option[String]): String = { storageType match { + // TODO: We need to remove CSP name related code. Below code needs to be refactored. case "gcloud" => getService.getPutSignedURL(getContainerName, key, ttl, permission, Option.apply(getMimeType(key))) case _ => getService.getSignedURL (getContainerName, key, ttl, permission) } From 85b0d5e374f71f4cffe6affda6080d67e0d93555 Mon Sep 17 00:00:00 2001 From: Kartheek Palla Date: Fri, 11 Nov 2022 11:16:08 +0530 Subject: [PATCH 149/490] Issue #KN-603 fix: remove gcloud hard dependency --- .../main/scala/org/sunbird/cloudstore/StorageService.scala | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala index 5abac23d7..0eea29c95 100644 --- a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala +++ b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala @@ -68,11 +68,7 @@ class StorageService { } def getSignedURL(key: String, ttl: Option[Int], permission: Option[String]): String = { - storageType match { - // TODO: We need to remove CSP name related code. Below code needs to be refactored. - case "gcloud" => getService.getPutSignedURL(getContainerName, key, ttl, permission, Option.apply(getMimeType(key))) - case _ => getService.getSignedURL (getContainerName, key, ttl, permission) - } + getService.getPutSignedURL(getContainerName, key, ttl, permission, Option.apply(getMimeType(key))) } def getUri(key: String): String = { From 5bb5f81c6557152bde752a9fdfd760b95210e3d7 Mon Sep 17 00:00:00 2001 From: Gourav More Date: Wed, 16 Nov 2022 11:44:28 +0530 Subject: [PATCH 150/490] Issue #KN-15 fix:Postman search-service API positive test cases - --- ...mposite Search API.postman_collection.json | 341 ++++++++++++++++++ ...Composite Search API.postman_test_run.json | 291 +++++++++++++++ 2 files changed, 632 insertions(+) create mode 100644 search-api/api-tests/Collections/Composite Search API.postman_collection.json create mode 100644 search-api/api-tests/Reports/Composite Search API.postman_test_run.json diff --git a/search-api/api-tests/Collections/Composite Search API.postman_collection.json b/search-api/api-tests/Collections/Composite Search API.postman_collection.json new file mode 100644 index 000000000..ae135919b --- /dev/null +++ b/search-api/api-tests/Collections/Composite Search API.postman_collection.json @@ -0,0 +1,341 @@ +{ + "info": { + "_postman_id": "65bcacf8-0c90-4f3f-874b-a5d2c01cdc36", + "name": "Composite Search API", + "description": "The Composite Search APIs allow you to search the Content, assets and Assesment on the Sunbird platform.\n\nThe URL for Composite Search API(s) is /composite/v3/search.", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "Positive", + "item": [ + { + "name": "Content Search", + "item": [ + { + "name": "200 - Get live Content list", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json();", + "", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "pm.test(\"Verify keys and data type of the response result\", () => {", + " pm.expect(jsonResponse.result.content).to.be.a(\"array\");", + "});", + "", + "pm.test(\"Content type should be Content\", function () {", + " pm.expect(jsonResponse.result.content[0].contentType).to.eql('Resource');", + "});", + "", + "//Validation", + "pm.test(\"id should be api.v1.search\", function () {", + " pm.expect(jsonResponse.id).to.eql(\"api.v1.search\");", + "});", + "pm.test(\"ver should be 1.0\", function () {", + " pm.expect(jsonResponse.ver).to.eql(\"1.0\");", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": { + "connection": true + }, + "disableUrlEncoding": false + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "user-id", + "value": "ae94b68c-a535-4dce-8e7a-fb9662b0ad68", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"filters\": {\n \"status\": [\n \"Live\"\n ],\n \"mimeType\": \"application/vnd.ekstep.ecml-archive\",\n \"contentType\": \"Resource\",\n \"objectType\": \"Content\"\n },\n \"sort_by\": {\n \"lastUpdatedOn\": \"desc\"\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/action/composite/v3/search", + "host": [ + "{{host}}" + ], + "path": [ + "action", + "composite", + "v3", + "search" + ] + }, + "description": "This API is associated with search API\nThe endpoint for search is /composite/v3/search" + }, + "response": [] + } + ] + }, + { + "name": "Assessment Search", + "item": [ + { + "name": "200 - Get live Assesment list", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json();", + "", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "pm.test(\"Verify keys and data type of the response result\", () => {", + " pm.expect(jsonResponse.result.items).to.be.a(\"array\");", + "});", + "", + "pm.test(\"Content type should be Asset\", function () {", + " pm.expect(jsonResponse.result.items[0].objectType).to.eql('AssessmentItem');", + "});", + "", + "//Validation", + "pm.test(\"id should be api.v1.search\", function () {", + " pm.expect(jsonResponse.id).to.eql(\"api.v1.search\");", + "});", + "pm.test(\"ver should be 1.0\", function () {", + " pm.expect(jsonResponse.ver).to.eql(\"1.0\");", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": { + "connection": true + }, + "disableUrlEncoding": false + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "user-id", + "value": "ae94b68c-a535-4dce-8e7a-fb9662b0ad68", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"filters\": {\n \"objectType\": [\n \"AssessmentItem\"\n ],\n \"status\": [\n \"Live\"\n ]\n },\n \"sort_by\": {\n \"lastUpdatedOn\": \"desc\"\n },\n \"limit\": 50\n }\n}" + }, + "url": { + "raw": "{{host}}/action/composite/v3/search", + "host": [ + "{{host}}" + ], + "path": [ + "action", + "composite", + "v3", + "search" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Asset Search", + "item": [ + { + "name": "200 - Get live assets list", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json();", + "", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "pm.test(\"Verify keys and data type of the response result\", () => {", + " pm.expect(jsonResponse.result.content).to.be.a(\"array\");", + "});", + "", + "pm.test(\"Content type should be Asset\", function () {", + " pm.expect(jsonResponse.result.content[0].contentType).to.eql('Asset');", + "});", + "", + "//Validation", + "pm.test(\"id should be api.v1.search\", function () {", + " pm.expect(jsonResponse.id).to.eql(\"api.v1.search\");", + "});", + "pm.test(\"ver should be 1.0\", function () {", + " pm.expect(jsonResponse.ver).to.eql(\"1.0\");", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disableUrlEncoding": false, + "disabledSystemHeaders": { + "connection": true + } + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "user-id", + "type": "text", + "value": "ae94b68c-a535-4dce-8e7a-fb9662b0ad68" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"filters\": {\n \"mediaType\": [\n \"image\"\n ],\n \"contentType\": [\n \"Asset\"\n ],\n \"status\": [\n \"Live\"\n ]\n },\n \"limit\": 50,\n \"offset\": 0\n }\n}" + }, + "url": { + "raw": "{{host}}/action/composite/v3/search", + "host": [ + "{{host}}" + ], + "path": [ + "action", + "composite", + "v3", + "search" + ] + } + }, + "response": [] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "let jsonResponse = pm.response.json();", + "", + "//Verification", + "pm.test(\"Status code is 200\", () => {", + " pm.response.to.have.status(200)", + "});", + "//code name contains a string", + "tests[\"Status code name has string OK\"] = responseCode.name.has(\"OK\");", + "", + "pm.test(\"Verify keys and data type of the params in positive scenario\", () => {", + " pm.expect(jsonResponse.params.resmsgid).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.err).to.be.null;", + " pm.expect(jsonResponse.params.status).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.errmsg).to.be.null;", + "});", + "", + "pm.test(\"params.status should be \\\"successful\\\"\", () => {", + " pm.expect(jsonResponse.params.status).to.eql(\"successful\");", + "});", + "pm.test(\"responseCode should be OK\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"OK\");", + "});" + ] + } + } + ] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "pm.test(\"Content-Type header is present\", () => {", + " pm.response.to.have.header(\"Content-Type\");", + "});", + "", + "pm.test(\"Validate keys and data type of the response body\", () => {", + " let jsonResponse = pm.response.json();", + " pm.expect(jsonResponse).to.be.an(\"object\").that.has.all.keys('id', 'ver', 'ts', 'params', 'responseCode', 'result');", + " pm.expect(jsonResponse.id).to.be.a(\"string\");", + " pm.expect(jsonResponse.ver).to.be.a(\"string\");", + " pm.expect(jsonResponse.ts).to.be.a(\"string\");", + " pm.expect(jsonResponse.params).to.be.an(\"object\").that.has.all.keys('resmsgid', 'msgid', 'err', 'status', 'errmsg');", + " pm.expect(jsonResponse.responseCode).to.be.a(\"string\");", + " pm.expect(jsonResponse.result).to.be.an(\"object\");", + "});" + ] + } + } + ] +} \ No newline at end of file diff --git a/search-api/api-tests/Reports/Composite Search API.postman_test_run.json b/search-api/api-tests/Reports/Composite Search API.postman_test_run.json new file mode 100644 index 000000000..c90daa095 --- /dev/null +++ b/search-api/api-tests/Reports/Composite Search API.postman_test_run.json @@ -0,0 +1,291 @@ +{ + "id": "cf102fa4-c8e2-4199-a6e6-6a04dae9262d", + "name": "Composite Search API", + "timestamp": "2022-11-16T06:10:20.538Z", + "collection_id": "65bcacf8-0c90-4f3f-874b-a5d2c01cdc36", + "folder_id": 0, + "environment_id": "43ba92f2-e81b-446b-8b2a-589126842629", + "totalPass": 36, + "totalFail": 0, + "results": [ + { + "id": "6c4437c8-62ec-4917-bc5a-838fd333b71c", + "name": "200 - Get live Content list", + "url": "https://dev.knowlg.sunbird.org//action/composite/v3/search", + "time": 727, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true, + "Verify keys and data type of the params in positive scenario": true, + "params.status should be \"successful\"": true, + "responseCode should be OK": true, + "Status code name has string OK": true, + "Verify keys and data type of the response result": true, + "Content type should be Content": true, + "id should be api.v1.search": true, + "ver should be 1.0": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Status code is 200": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in positive scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"successful\"": { + "pass": 1, + "fail": 0 + }, + "responseCode should be OK": { + "pass": 1, + "fail": 0 + }, + "Status code name has string OK": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the response result": { + "pass": 1, + "fail": 0 + }, + "Content type should be Content": { + "pass": 1, + "fail": 0 + }, + "id should be api.v1.search": { + "pass": 1, + "fail": 0 + }, + "ver should be 1.0": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 727 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true, + "Verify keys and data type of the params in positive scenario": true, + "params.status should be \"successful\"": true, + "responseCode should be OK": true, + "Status code name has string OK": true, + "Verify keys and data type of the response result": true, + "Content type should be Content": true, + "id should be api.v1.search": true, + "ver should be 1.0": true + } + ] + }, + { + "id": "eaba0158-fbb9-480a-8971-e5f4aad1fc47", + "name": "200 - Get live Assesment list", + "url": "https://dev.knowlg.sunbird.org//action/composite/v3/search", + "time": 323, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true, + "Verify keys and data type of the params in positive scenario": true, + "params.status should be \"successful\"": true, + "responseCode should be OK": true, + "Status code name has string OK": true, + "Verify keys and data type of the response result": true, + "Content type should be Asset": true, + "id should be api.v1.search": true, + "ver should be 1.0": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Status code is 200": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in positive scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"successful\"": { + "pass": 1, + "fail": 0 + }, + "responseCode should be OK": { + "pass": 1, + "fail": 0 + }, + "Status code name has string OK": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the response result": { + "pass": 1, + "fail": 0 + }, + "Content type should be Asset": { + "pass": 1, + "fail": 0 + }, + "id should be api.v1.search": { + "pass": 1, + "fail": 0 + }, + "ver should be 1.0": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 323 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true, + "Verify keys and data type of the params in positive scenario": true, + "params.status should be \"successful\"": true, + "responseCode should be OK": true, + "Status code name has string OK": true, + "Verify keys and data type of the response result": true, + "Content type should be Asset": true, + "id should be api.v1.search": true, + "ver should be 1.0": true + } + ] + }, + { + "id": "97be2029-e5dc-4dcd-8736-9178818022cf", + "name": "200 - Get live assets list", + "url": "https://dev.knowlg.sunbird.org//action/composite/v3/search", + "time": 268, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": { + "Content-Type header is present": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true, + "Verify keys and data type of the params in positive scenario": true, + "params.status should be \"successful\"": true, + "responseCode should be OK": true, + "Status code name has string OK": true, + "Verify keys and data type of the response result": true, + "Content type should be Asset": true, + "id should be api.v1.search": true, + "ver should be 1.0": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Status code is 200": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in positive scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"successful\"": { + "pass": 1, + "fail": 0 + }, + "responseCode should be OK": { + "pass": 1, + "fail": 0 + }, + "Status code name has string OK": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the response result": { + "pass": 1, + "fail": 0 + }, + "Content type should be Asset": { + "pass": 1, + "fail": 0 + }, + "id should be api.v1.search": { + "pass": 1, + "fail": 0 + }, + "ver should be 1.0": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 268 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Validate keys and data type of the response body": true, + "Status code is 200": true, + "Verify keys and data type of the params in positive scenario": true, + "params.status should be \"successful\"": true, + "responseCode should be OK": true, + "Status code name has string OK": true, + "Verify keys and data type of the response result": true, + "Content type should be Asset": true, + "id should be api.v1.search": true, + "ver should be 1.0": true + } + ] + } + ], + "count": 1, + "totalTime": 1318, + "collection": { + "requests": [ + { + "id": "6c4437c8-62ec-4917-bc5a-838fd333b71c", + "method": "POST" + }, + { + "id": "eaba0158-fbb9-480a-8971-e5f4aad1fc47", + "method": "POST" + }, + { + "id": "97be2029-e5dc-4dcd-8736-9178818022cf", + "method": "POST" + } + ] + } +} \ No newline at end of file From e234c8ad6daf805449ee1168453ba693f3266744 Mon Sep 17 00:00:00 2001 From: Mahesh Kumar Gangula Date: Thu, 17 Nov 2022 19:57:14 +0530 Subject: [PATCH 151/490] feat (doc): local setup using kubernetes. Setup of DBs and Taxonomy-Service. --- KNOWLG-SETUP.md | 117 +++++ knowlg-docker-image.sh | 23 + kubernetes/sunbird-dbs/cassandra/Chart.yaml | 5 + .../cassandra/templates/deployment.yaml | 33 ++ kubernetes/sunbird-dbs/cassandra/values.yaml | 4 + kubernetes/sunbird-dbs/neo4j/Chart.yaml | 5 + .../neo4j/templates/deployment.yaml | 51 +++ kubernetes/sunbird-dbs/neo4j/values.yaml | 18 + kubernetes/sunbird-dbs/redis/Chart.yaml | 5 + .../redis/templates/deployment.yaml | 34 ++ kubernetes/sunbird-dbs/redis/values.yaml | 5 + kubernetes/taxonomy/Chart.yaml | 5 + .../taxonomy-service_application.conf | 399 ++++++++++++++++++ .../taxonomy/taxonomy-service_logback.xml | 31 ++ kubernetes/taxonomy/templates/deployment.yaml | 74 ++++ kubernetes/taxonomy/values.yaml | 28 ++ 16 files changed, 837 insertions(+) create mode 100644 KNOWLG-SETUP.md create mode 100755 knowlg-docker-image.sh create mode 100644 kubernetes/sunbird-dbs/cassandra/Chart.yaml create mode 100644 kubernetes/sunbird-dbs/cassandra/templates/deployment.yaml create mode 100644 kubernetes/sunbird-dbs/cassandra/values.yaml create mode 100644 kubernetes/sunbird-dbs/neo4j/Chart.yaml create mode 100644 kubernetes/sunbird-dbs/neo4j/templates/deployment.yaml create mode 100644 kubernetes/sunbird-dbs/neo4j/values.yaml create mode 100644 kubernetes/sunbird-dbs/redis/Chart.yaml create mode 100644 kubernetes/sunbird-dbs/redis/templates/deployment.yaml create mode 100644 kubernetes/sunbird-dbs/redis/values.yaml create mode 100644 kubernetes/taxonomy/Chart.yaml create mode 100644 kubernetes/taxonomy/taxonomy-service_application.conf create mode 100644 kubernetes/taxonomy/taxonomy-service_logback.xml create mode 100644 kubernetes/taxonomy/templates/deployment.yaml create mode 100644 kubernetes/taxonomy/values.yaml diff --git a/KNOWLG-SETUP.md b/KNOWLG-SETUP.md new file mode 100644 index 000000000..95f49b5f0 --- /dev/null +++ b/KNOWLG-SETUP.md @@ -0,0 +1,117 @@ + +Below are the steps to set up the Sunbird Knowlg Microservices, DBs with seed data and Jobs. It uses a local Kubernetes cluster deploy the required services. + +### Prerequisites: +* Java 11 +* Maven +* Docker +* Minikube - It implements a local Kubernetes cluster on macOS, Linux, and Windows. +* KubeCtl - The Kubernetes command-line tool + +### Prepare folders for database data and logs + +```shell +mkdir -p ~/sunbird-dbs/neo4j ~/sunbird-dbs/cassandra ~/sunbird-dbs/redis ~/sunbird-dbs/es ~/sunbird-dbs/kafka +export sunbird_dbs_path=~/sunbird-dbs +``` + + + +### Docker Images of Knowlg MicroServices +Start Docker in your machine and create the Docker Images of below microservices using the shell script. +1. taxonomy-service +2. content-service +3. search-service + +```shell +sh ./knowlg-docker-image.sh # provide the TAG for the docker image. +``` +**Note:** Please specify the TAG for the Docker Images and update the configuration in helm chart of respective deployment. + +Check the Docker Images +```shell +docker image ls -a +``` +**Output:** +```shell +❯❯ docker image ls -a +REPOSITORY TAG IMAGE ID CREATED SIZE +assessment-service R5.0.0 72a9cc1b2cc4 14 seconds ago 479MB +search-service R5.0.0 24b7d8947a4f 23 seconds ago 465MB +content-service R5.0.0 afcbc9c10fa3 33 seconds ago 556MB +taxonomy-service R5.0.0 a8a24a6241f2 47 seconds ago 480MB +``` + +### Kubernetes Cluster Setup +Please use the minikube to quickly set up the kubernetes cluster in local machine. + +```shell +minikube start +``` + +### Load Docker Images to Minikube Cluster +```shell +minikube image load neo4j:3.3.0 +minikube image load taxonomy-service:R5.0.0 +``` + +### Create Namespace +Create the namespaces to deploy the API microservices, DBs and Jobs. +1. knowlg-api +2. knowlg-db +3. knowlg-job + +```shell +kubectl create namespace knowlg-api +kubectl create namespace knowlg-db +kubectl create namespace knowlg-job +``` + +### Setup Databases +Please run the below `helm` commands to set up the required databases within the kubernets cluster. +It requires the below DBs for Knowlg. +1. Neo4J +2. Cassandra +3. Elasticsearch +4. Kafka +5. Redis + +```shell +cd kubernetes +helm install redis sunbird-dbs/redis -n knowlg-db +helm install neo4j sunbird-dbs/neo4j -n knowlg-db +helm install cassandra sunbird-dbs/cassandra -n knowlg-db +``` +**Note:** +1. The `helm` charts for Kafka, Elasticsearch will be added soon. +2. The documentation will be updated to enable seed data while starting the service. + +### Define ConfigMap +We use the configmap to load the configuration for the microservices. + +#### ConfigMap for Taxonomy-Service +Use the below commands to load the configmap of taxonomy-service. +1. `taxonomy-config` - this has the application configuration. Please update the variables with respect to your context and load. +2. `taxonomy-xml-config` - this has the logback configuration to handle the logs. + +We have to update the below configurations in `taxonomy/templates/taxonomy-service_application.conf` specific to your context. + +```shell +cd kubernetes +kubectl create configmap taxonomy-xml-config --from-file=taxonomy/taxonomy-service_logback.xml -n knowlg-api -o=yaml +kubectl create configmap taxonomy-config --from-file=taxonomy/taxonomy-service_application.conf -n knowlg-api -o=yaml +``` + +### Run Taxonomy-Service +Use the `taxonomy` helm chart to run the taxonomy-service in local kubernetes cluster. + +```shell +cd kubernetes +helm install taxonomy taxonomy -n knowlg-api +``` +Use Port Forwarding to access the application in the cluster from local. + +```shell +kubectl port-forward 9000:9000 -n knowlg-api +curl 'localhost:9000/health' +``` \ No newline at end of file diff --git a/knowlg-docker-image.sh b/knowlg-docker-image.sh new file mode 100755 index 000000000..2d51dc341 --- /dev/null +++ b/knowlg-docker-image.sh @@ -0,0 +1,23 @@ +#!/bin/bash +TAG=$1 +docker rmi $(docker images -a | grep taxonomy-service | awk '{print $1":"$2}') +docker rmi $(docker images -a | grep content-service | awk '{print $1":"$2}') +docker rmi $(docker images -a | grep search-service | awk '{print $1":"$2}') + +# Taxonomy Service +cd taxonomy-api/taxonomy-service +mvn play2:dist +cd ../.. +docker build -f build/taxonomy-service/Dockerfile -t taxonomy-service:${TAG} . + +# Content Service +cd content-api/content-service +mvn play2:dist +cd ../.. +docker build -f build/content-service/Dockerfile -t content-service:${TAG} . + +# Search Service +cd search-api/search-service +mvn play2:dist +cd ../.. +docker build -f build/search-service/Dockerfile -t search-service:${TAG} . diff --git a/kubernetes/sunbird-dbs/cassandra/Chart.yaml b/kubernetes/sunbird-dbs/cassandra/Chart.yaml new file mode 100644 index 000000000..4d9cd6c3a --- /dev/null +++ b/kubernetes/sunbird-dbs/cassandra/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: cassandra +version: 0.1.0 diff --git a/kubernetes/sunbird-dbs/cassandra/templates/deployment.yaml b/kubernetes/sunbird-dbs/cassandra/templates/deployment.yaml new file mode 100644 index 000000000..4d00c736b --- /dev/null +++ b/kubernetes/sunbird-dbs/cassandra/templates/deployment.yaml @@ -0,0 +1,33 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Chart.Name }} + namespace: {{ .Values.namespace }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ .Chart.Name }} + template: + metadata: + labels: + app: {{ .Chart.Name }} + spec: + containers: + - name: {{ .Chart.Name }} + image: cassandra:3.11.11 + ports: + - containerPort: {{ .Values.network.port }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ .Chart.Name }}-db + namespace: {{ .Values.namespace }} + labels: + app: {{ .Chart.Name }} +spec: + ports: + - port: {{ .Values.network.port }} + selector: + app: {{ .Chart.Name }} \ No newline at end of file diff --git a/kubernetes/sunbird-dbs/cassandra/values.yaml b/kubernetes/sunbird-dbs/cassandra/values.yaml new file mode 100644 index 000000000..ef6b4aad2 --- /dev/null +++ b/kubernetes/sunbird-dbs/cassandra/values.yaml @@ -0,0 +1,4 @@ +namespace: knowlg-db +replicaCount: 1 +network: + port: 9042 \ No newline at end of file diff --git a/kubernetes/sunbird-dbs/neo4j/Chart.yaml b/kubernetes/sunbird-dbs/neo4j/Chart.yaml new file mode 100644 index 000000000..e1d41363b --- /dev/null +++ b/kubernetes/sunbird-dbs/neo4j/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: neo4j +version: 0.1.0 diff --git a/kubernetes/sunbird-dbs/neo4j/templates/deployment.yaml b/kubernetes/sunbird-dbs/neo4j/templates/deployment.yaml new file mode 100644 index 000000000..c16035936 --- /dev/null +++ b/kubernetes/sunbird-dbs/neo4j/templates/deployment.yaml @@ -0,0 +1,51 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Chart.Name }} + namespace: {{ .Values.namespace }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ .Chart.Name }} + template: + metadata: + labels: + app: {{ .Chart.Name }} + spec: + containers: + - name: {{ .Chart.Name }} + image: {{ .Values.image }} + imagePullPolicy: Never + env: ## Env variables to pass + - name: NEO4J_dbms_security_auth__enabled + value: "false" + ports: + - containerPort: 7474 + name: cypher-port + - containerPort: 7687 + name: bolt-port-1 + - containerPort: 8687 + name: bolt-port-2 + +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ .Chart.Name }}-db + namespace: {{ .Values.namespace }} + labels: + app: {{ .Chart.Name }} +spec: + ports: + - name: cypher-shell-port + port: 7474 + targetPort: 7474 + - name: bolt-port-1 + port: 7687 + targetPort: 7687 + - name: bolt-port-2 + port: 8687 + targetPort: 8687 + selector: + app: {{ .Chart.Name }} \ No newline at end of file diff --git a/kubernetes/sunbird-dbs/neo4j/values.yaml b/kubernetes/sunbird-dbs/neo4j/values.yaml new file mode 100644 index 000000000..d96c03466 --- /dev/null +++ b/kubernetes/sunbird-dbs/neo4j/values.yaml @@ -0,0 +1,18 @@ +namespace: knowlg-db +replicaCount: 1 +image: neo4j:3.3.0 +authEnabled: false +network: + port: 7474 +neo4j: + name: neo4j + resources: + cpu: "0.5" + memory: "2Gi" + +volumes: + data: + mode: defaultStorageClass + defaultStorageClass: + requests: + storage: 2Gi \ No newline at end of file diff --git a/kubernetes/sunbird-dbs/redis/Chart.yaml b/kubernetes/sunbird-dbs/redis/Chart.yaml new file mode 100644 index 000000000..dce1bc61d --- /dev/null +++ b/kubernetes/sunbird-dbs/redis/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: redis +version: 0.1.0 diff --git a/kubernetes/sunbird-dbs/redis/templates/deployment.yaml b/kubernetes/sunbird-dbs/redis/templates/deployment.yaml new file mode 100644 index 000000000..d57727710 --- /dev/null +++ b/kubernetes/sunbird-dbs/redis/templates/deployment.yaml @@ -0,0 +1,34 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Chart.Name }} + namespace: {{ .Values.namespace }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ .Chart.Name }} + template: + metadata: + labels: + app: {{ .Chart.Name }} + spec: + containers: + - name: {{ .Chart.Name }} + image: redis + ports: + - containerPort: {{ .Values.network.port }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ .Chart.Name }}-db + namespace: {{ .Values.namespace }} + labels: + app: {{ .Chart.Name }} +spec: + ports: + - port: {{ .Values.network.port }} + targetPort: {{ .Values.network.targetport }} + selector: + app: {{ .Chart.Name }} \ No newline at end of file diff --git a/kubernetes/sunbird-dbs/redis/values.yaml b/kubernetes/sunbird-dbs/redis/values.yaml new file mode 100644 index 000000000..209d4b932 --- /dev/null +++ b/kubernetes/sunbird-dbs/redis/values.yaml @@ -0,0 +1,5 @@ +namespace: knowlg-db +replicaCount: 1 +network: + port: 6379 + targetport: 6379 \ No newline at end of file diff --git a/kubernetes/taxonomy/Chart.yaml b/kubernetes/taxonomy/Chart.yaml new file mode 100644 index 000000000..8462df87c --- /dev/null +++ b/kubernetes/taxonomy/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: taxonomy +version: 0.1.0 diff --git a/kubernetes/taxonomy/taxonomy-service_application.conf b/kubernetes/taxonomy/taxonomy-service_application.conf new file mode 100644 index 000000000..149f3bf71 --- /dev/null +++ b/kubernetes/taxonomy/taxonomy-service_application.conf @@ -0,0 +1,399 @@ +# This is the main configuration file for the application. +# https://www.playframework.com/documentation/latest/ConfigFile +# ~~~~~ +# Play uses HOCON as its configuration file format. HOCON has a number +# of advantages over other config formats, but there are two things that +# can be used when modifying settings. +# +# You can include other configuration files in this main application.conf file: +#include "extra-config.conf" +# +# You can declare variables and substitute for them: +#mykey = ${some.value} +# +# And if an environment variable exists when there is no other substitution, then +# HOCON will fall back to substituting environment variable: +#mykey = ${JAVA_HOME} + +## Akka +# https://www.playframework.com/documentation/latest/ScalaAkka#Configuration +# https://www.playframework.com/documentation/latest/JavaAkka#Configuration +# ~~~~~ +# Play uses Akka internally and exposes Akka Streams and actors in Websockets and +# other streaming HTTP responses. +akka { + # "akka.log-config-on-start" is extraordinarly useful because it log the complete + # configuration at INFO level, including defaults and overrides, so it s worth + # putting at the very top. + # + # Put the following in your conf/logback.xml file: + # + # + # + # And then uncomment this line to debug the configuration. + # + #log-config-on-start = true + default-dispatcher { + # This will be used if you have set "executor = "fork-join-executor"" + fork-join-executor { + # Min number of threads to cap factor-based parallelism number to + parallelism-min = 8 + + # The parallelism factor is used to determine thread pool size using the + # following formula: ceil(available processors * factor). Resulting size + # is then bounded by the parallelism-min and parallelism-max values. + parallelism-factor = 32.0 + + # Max number of threads to cap factor-based parallelism number to + parallelism-max = 64 + + # Setting to "FIFO" to use queue like peeking mode which "poll" or "LIFO" to use stack + # like peeking mode which "pop". + task-peeking-mode = "FIFO" + } + } + actors-dispatcher { + type = "Dispatcher" + executor = "fork-join-executor" + fork-join-executor { + parallelism-min = 8 + parallelism-factor = 32.0 + parallelism-max = 64 + } + # Throughput for default Dispatcher, set to 1 for as fair as possible + throughput = 1 + } + actor { + deployment { + /contentActor + { + router = smallest-mailbox-pool + nr-of-instances = 10 + dispatcher = actors-dispatcher + } + } + } +} + +## Secret key +# http://www.playframework.com/documentation/latest/ApplicationSecret +# ~~~~~ +# The secret key is used to sign Play's session cookie. +# This must be changed for production, but we don't recommend you change it in this file. +play.http.secret.key="jd5ECm/o0BXwQCe8PfZY1NoUkB9HN41QjA80p22MKyRIcP5RW4qHw8sZztCzv87M" + +## Modules +# https://www.playframework.com/documentation/latest/Modules +# ~~~~~ +# Control which modules are loaded when Play starts. Note that modules are +# the replacement for "GlobalSettings", which are deprecated in 2.5.x. +# Please see https://www.playframework.com/documentation/latest/GlobalSettings +# for more information. +# +# You can also extend Play functionality by using one of the publically available +# Play modules: https://playframework.com/documentation/latest/ModuleDirectory +play.modules { + # By default, Play will load any class called Module that is defined + # in the root package (the "app" directory), or you can define them + # explicitly below. + # If there are any built-in modules that you want to enable, you can list them here. + enabled += modules.TaxonomyModule + + # If there are any built-in modules that you want to disable, you can list them here. + #disabled += "" +} + +## IDE +# https://www.playframework.com/documentation/latest/IDE +# ~~~~~ +# Depending on your IDE, you can add a hyperlink for errors that will jump you +# directly to the code location in the IDE in dev mode. The following line makes +# use of the IntelliJ IDEA REST interface: +#play.editor="http://localhost:63342/api/file/?file=%s&line=%s" + +## Internationalisation +# https://www.playframework.com/documentation/latest/JavaI18N +# https://www.playframework.com/documentation/latest/ScalaI18N +# ~~~~~ +# Play comes with its own i18n settings, which allow the user's preferred language +# to map through to internal messages, or allow the language to be stored in a cookie. +play.i18n { + # The application languages + langs = [ "en" ] + + # Whether the language cookie should be secure or not + #langCookieSecure = true + + # Whether the HTTP only attribute of the cookie should be set to true + #langCookieHttpOnly = true +} + +## Play HTTP settings +# ~~~~~ +play.http { + ## Router + # https://www.playframework.com/documentation/latest/JavaRouting + # https://www.playframework.com/documentation/latest/ScalaRouting + # ~~~~~ + # Define the Router object to use for this application. + # This router will be looked up first when the application is starting up, + # so make sure this is the entry point. + # Furthermore, it's assumed your route file is named properly. + # So for an application router like `my.application.Router`, + # you may need to define a router file `conf/my.application.routes`. + # Default to Routes in the root package (aka "apps" folder) (and conf/routes) + #router = my.application.Router + + ## Action Creator + # https://www.playframework.com/documentation/latest/JavaActionCreator + # ~~~~~ + #actionCreator = null + + ## ErrorHandler + # https://www.playframework.com/documentation/latest/JavaRouting + # https://www.playframework.com/documentation/latest/ScalaRouting + # ~~~~~ + # If null, will attempt to load a class called ErrorHandler in the root package, + #errorHandler = null + + ## Session & Flash + # https://www.playframework.com/documentation/latest/JavaSessionFlash + # https://www.playframework.com/documentation/latest/ScalaSessionFlash + # ~~~~~ + session { + # Sets the cookie to be sent only over HTTPS. + #secure = true + + # Sets the cookie to be accessed only by the server. + #httpOnly = true + + # Sets the max-age field of the cookie to 5 minutes. + # NOTE: this only sets when the browser will discard the cookie. Play will consider any + # cookie value with a valid signature to be a valid session forever. To implement a server side session timeout, + # you need to put a timestamp in the session and check it at regular intervals to possibly expire it. + #maxAge = 300 + + # Sets the domain on the session cookie. + #domain = "example.com" + } + + flash { + # Sets the cookie to be sent only over HTTPS. + #secure = true + + # Sets the cookie to be accessed only by the server. + #httpOnly = true + } +} + +play.server.http.idleTimeout = 60s +play.http.parser.maxDiskBuffer = 10MB +parsers.anyContent.maxLength = 10MB + +## Netty Provider +# https://www.playframework.com/documentation/latest/SettingsNetty +# ~~~~~ +play.server.netty { + # Whether the Netty wire should be logged + log.wire = true + + # If you run Play on Linux, you can use Netty's native socket transport + # for higher performance with less garbage. + transport = "native" +} + +## WS (HTTP Client) +# https://www.playframework.com/documentation/latest/ScalaWS#Configuring-WS +# ~~~~~ +# The HTTP client primarily used for REST APIs. The default client can be +# configured directly, but you can also create different client instances +# with customized settings. You must enable this by adding to build.sbt: +# +# libraryDependencies += ws // or javaWs if using java +# +play.ws { + # Sets HTTP requests not to follow 302 requests + #followRedirects = false + + # Sets the maximum number of open HTTP connections for the client. + #ahc.maxConnectionsTotal = 50 + + ## WS SSL + # https://www.playframework.com/documentation/latest/WsSSL + # ~~~~~ + ssl { + # Configuring HTTPS with Play WS does not require programming. You can + # set up both trustManager and keyManager for mutual authentication, and + # turn on JSSE debugging in development with a reload. + #debug.handshake = true + #trustManager = { + # stores = [ + # { type = "JKS", path = "exampletrust.jks" } + # ] + #} + } +} + +## Cache +# https://www.playframework.com/documentation/latest/JavaCache +# https://www.playframework.com/documentation/latest/ScalaCache +# ~~~~~ +# Play comes with an integrated cache API that can reduce the operational +# overhead of repeated requests. You must enable this by adding to build.sbt: +# +# libraryDependencies += cache +# +play.cache { + # If you want to bind several caches, you can bind the individually + #bindCaches = ["db-cache", "user-cache", "session-cache"] +} + +## Filter Configuration +# https://www.playframework.com/documentation/latest/Filters +# ~~~~~ +# There are a number of built-in filters that can be enabled and configured +# to give Play greater security. +# +play.filters { + + # Enabled filters are run automatically against Play. + # CSRFFilter, AllowedHostFilters, and SecurityHeadersFilters are enabled by default. + enabled = [filters.AccessLogFilter] + + # Disabled filters remove elements from the enabled list. + # disabled += filters.CSRFFilter + + + ## CORS filter configuration + # https://www.playframework.com/documentation/latest/CorsFilter + # ~~~~~ + # CORS is a protocol that allows web applications to make requests from the browser + # across different domains. + # NOTE: You MUST apply the CORS configuration before the CSRF filter, as CSRF has + # dependencies on CORS settings. + cors { + # Filter paths by a whitelist of path prefixes + #pathPrefixes = ["/some/path", ...] + + # The allowed origins. If null, all origins are allowed. + #allowedOrigins = ["http://www.example.com"] + + # The allowed HTTP methods. If null, all methods are allowed + #allowedHttpMethods = ["GET", "POST"] + } + + ## Security headers filter configuration + # https://www.playframework.com/documentation/latest/SecurityHeaders + # ~~~~~ + # Defines security headers that prevent XSS attacks. + # If enabled, then all options are set to the below configuration by default: + headers { + # The X-Frame-Options header. If null, the header is not set. + #frameOptions = "DENY" + + # The X-XSS-Protection header. If null, the header is not set. + #xssProtection = "1; mode=block" + + # The X-Content-Type-Options header. If null, the header is not set. + #contentTypeOptions = "nosniff" + + # The X-Permitted-Cross-Domain-Policies header. If null, the header is not set. + #permittedCrossDomainPolicies = "master-only" + + # The Content-Security-Policy header. If null, the header is not set. + #contentSecurityPolicy = "default-src 'self'" + } + + ## Allowed hosts filter configuration + # https://www.playframework.com/documentation/latest/AllowedHostsFilter + # ~~~~~ + # Play provides a filter that lets you configure which hosts can access your application. + # This is useful to prevent cache poisoning attacks. + hosts { + # Allow requests to example.com, its subdomains, and localhost:9000. + #allowed = [".example.com", "localhost:9000"] + } +} + +play.http.parser.maxMemoryBuffer = 50MB +akka.http.parsing.max-content-length = 50MB +schema.base_path="https://sunbirddev.blob.core.windows.net/sunbird-content-dev/schemas/local" + +# Cassandra Configuration +cassandra { + lp { + connection: "cassandra-db.knowlg-db.svc.cluster.local:9042" + } + lpa { + connection: "cassandra-db.knowlg-db.svc.cluster.local:9042" + } +} + +# Redis Configuration +redis { + host: "redis-db.knowlg-db.svc.cluster.local" + port: 6379 + maxConnections: 128 +} + +# Configuration +akka.request_timeout: 30 +environment.id: 10000000 +graph { + dir: "/data/graphDB" + ids: ["domain"] + passport.key.base: "jd5ECm/o0BXwQCe8PfZY1NoUkB9HN41QjA80p22MKyRIcP5RW4qHw8sZztCzv87M" +} +route { + domain: "bolt://neo4j-db.knowlg-db.svc.cluster.local:7687" + all: "bolt://neo4j-db.knowlg-db.svc.cluster.local:8687" + bolt { + write { + domain: "bolt://neo4j-db.knowlg-db.svc.cluster.local:7687" + all: "bolt://neo4j-db.knowlg-db.svc.cluster.local:8687" + }, + read { + domain: "bolt://neo4j-db.knowlg-db.svc.cluster.local:7687" + all: "bolt://neo4j-db.knowlg-db.svc.cluster.local:8687" + } + } +} + +shard.id: 1 +platform { + auth.check.enabled: false + cache.ttl: 3600000 +} + +# Cloud Storage Config +cloud_storage_type: "azure" +azure_storage_key: "" +azure_storage_secret: "" +azure_storage_container: "" + +installation.id: ekstep + +kafka { + urls : "kafka.knowlg-db.svc.cluster.local:9092" +} + +channel { + default: "org.sunbird" +} + +languageCode { + assamese : "as" + bengali : "bn" + english : "en" + gujarati : "gu" + hindi : "hi" + kannada : "ka" + marathi : "mr" + odia : "or" + tamil : "ta" + telugu : "te" +} +objectcategorydefinition.keyspace="category_store" + +# Framework master category validation Supported values are Yes/No +master.category.validation.enabled="Yes" diff --git a/kubernetes/taxonomy/taxonomy-service_logback.xml b/kubernetes/taxonomy/taxonomy-service_logback.xml new file mode 100644 index 000000000..21cb53d9e --- /dev/null +++ b/kubernetes/taxonomy/taxonomy-service_logback.xml @@ -0,0 +1,31 @@ + + + + + + + + + + %d %msg%n + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/kubernetes/taxonomy/templates/deployment.yaml b/kubernetes/taxonomy/templates/deployment.yaml new file mode 100644 index 000000000..064f43fb2 --- /dev/null +++ b/kubernetes/taxonomy/templates/deployment.yaml @@ -0,0 +1,74 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Chart.Name }} + namespace: {{ .Values.namespace }} + annotations: + reloader.stakater.com/auto: "true" +spec: + replicas: {{ .Values.replicaCount }} + strategy: + rollingUpdate: + maxSurge: {{ .Values.strategy.maxsurge }} + maxUnavailable: {{ .Values.strategy.maxunavailable }} + selector: + matchLabels: + app: {{ .Chart.Name }} + template: + metadata: + labels: + app: {{ .Chart.Name }} + spec: + volumes: + - name: {{ .Chart.Name }}-config + configMap: + name: {{ .Chart.Name }}-config + - name: {{ .Chart.Name }}-xml-config + configMap: + name: {{ .Chart.Name }}-xml-config + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.repository }}:{{ .Values.image_tag }}" + imagePullPolicy: Never + env: + - name: JAVA_OPTIONS + value: {{ .Values.env.javaoptions | quote }} + - name: _JAVA_OPTIONS + value: -Dlog4j2.formatMsgNoLookups=true + envFrom: + - configMapRef: + name: {{ .Chart.Name }}-config + resources: +{{ toYaml .Values.resources | indent 10 }} + ports: + - containerPort: {{ .Values.network.port }} + {{- if .Values.healthcheck }} + livenessProbe: +{{ toYaml .Values.livenessProbe | indent 10 }} + readinessProbe: +{{ toYaml .Values.readinessProbe | indent 10 }} + {{- end }} + volumeMounts: + - name: {{ .Chart.Name }}-config + mountPath: /home/sunbird/taxonomy-service-1.0-SNAPSHOT/config/application.conf + subPath: taxonomy-service_application.conf + - name: {{ .Chart.Name }}-xml-config + mountPath: /home/sunbird/taxonomy-service-1.0-SNAPSHOT/config/logback.xml + subPath: taxonomy-service_logback.xml + +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ .Chart.Name }}-service + namespace: {{ .Values.namespace }} + labels: + app: {{ .Chart.Name }} +spec: + ports: + - name: http-{{ .Chart.Name }} + protocol: TCP + port: {{ .Values.network.targetport }} + selector: + app: {{ .Chart.Name }} diff --git a/kubernetes/taxonomy/values.yaml b/kubernetes/taxonomy/values.yaml new file mode 100644 index 000000000..1c1fe162c --- /dev/null +++ b/kubernetes/taxonomy/values.yaml @@ -0,0 +1,28 @@ +### Default variable file for taxonomy-service ### + +namespace: knowlg-api + +env: + javaoptions: -Xmx600m + +replicaCount: 1 +repository: taxonomy-service +image_tag: R5.0.0 +resources: + requests: + cpu: 100m + memory: 100Mi + limits: + cpu: 1 + memory: 1024Mi +network: + port: 9006 + targetport: 9000 +strategy: + type: RollingUpdate + maxsurge: 25% + maxunavailable: 25% + + + + From 9c399c4861d5275c5a2531cc19940cc3e6de327a Mon Sep 17 00:00:00 2001 From: Gourav More Date: Fri, 18 Nov 2022 10:02:43 +0530 Subject: [PATCH 152/490] Issue #KN-15 fix:Postman search-service API Negative test cases --- ...mposite Search API.postman_collection.json | 212 ++++++++++++++++++ ...Composite Search API.postman_test_run.json | 208 ++++++++++++++++- 2 files changed, 410 insertions(+), 10 deletions(-) diff --git a/search-api/api-tests/Collections/Composite Search API.postman_collection.json b/search-api/api-tests/Collections/Composite Search API.postman_collection.json index ae135919b..a557142c2 100644 --- a/search-api/api-tests/Collections/Composite Search API.postman_collection.json +++ b/search-api/api-tests/Collections/Composite Search API.postman_collection.json @@ -303,6 +303,218 @@ } } ] + }, + { + "name": "Negative", + "item": [ + { + "name": "400 - Search filters missing", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 400\", () => {", + " pm.response.to.have.status(400)", + "});", + "//code name contains a string", + "tests[\"Status code name has string \\\"Bad Request\\\"\"] = responseCode.name.has(\"Bad Request\");", + "", + "pm.test(\"Verify keys and data type of the response result\", () => {", + " pm.expect(jsonResponse.result).to.be.an(\"object\");", + "});", + "", + "//Validation", + "// Response body", + "pm.test(\"responseCode should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"CLIENT_ERROR\");", + "});", + "", + "// Params", + "pm.test(\"params.err should be \\\"ERR_CONTENT_SEARCH_FIELDS_MISSING\\\"\", () => {", + " pm.expect(jsonResponse.params.err).to.eql(\"ERR_CONTENT_SEARCH_FIELDS_MISSING\");", + "});", + "pm.test(\"params.errmsg should be \\\"Required fields for search content are missing\\\"\", () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(\"Required fields for search content are missing\");", + "});" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": { + "connection": true + } + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n }\n}" + }, + "url": { + "raw": "{{host}}/action/composite/v3/search", + "host": [ + "{{host}}" + ], + "path": [ + "action", + "composite", + "v3", + "search" + ] + }, + "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + }, + { + "name": "500 - parameter cannot be negative", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "//Verification", + "pm.test(\"Status code is 500\", () => {", + " pm.response.to.have.status(500)", + "});", + "", + "pm.test(\"Verify keys and data type of the response result\", () => {", + " pm.expect(jsonResponse.result).to.be.an(\"object\");", + "});", + "", + "//Validation", + "// Response body", + "pm.test(\"responseCode should be \\\"SERVER_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"SERVER_ERROR\");", + "});", + "", + "// Params", + "pm.test(\"params.err should be \\\"ERR_SYSTEM_EXCEPTION\\\"\", () => {", + " pm.expect(jsonResponse.params.err).to.eql(\"ERR_SYSTEM_EXCEPTION\");", + "});", + "pm.test(\"params.errmsg should be \\\"[size] parameter cannot be negative, found [-1]\\\"\", () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(\"[size] parameter cannot be negative, found [-1]\");", + "});" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": { + "connection": true + } + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "X-Channel-Id", + "value": "{{channel_id}}", + "type": "text", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"filters\": {\n \"objectType\": [\n \"AssessmentItem\"\n ],\n \"status\": [\n \"Live\"\n ]\n },\n \"sort_by\": {\n \"lastUpdatedOn\": \"desc\"\n },\n \"limit\": -1\n }\n}" + }, + "url": { + "raw": "{{host}}/action/composite/v3/search", + "host": [ + "{{host}}" + ], + "path": [ + "action", + "composite", + "v3", + "search" + ] + }, + "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "let jsonResponse = pm.response.json();", + "", + "pm.test(\"Verify keys and data type of the params in negative scenario\", () => {", + " pm.expect(jsonResponse.params.resmsgid).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.msgid).to.be.null;", + " pm.expect(jsonResponse.params.err).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.status).to.be.a(\"string\");", + " pm.expect(jsonResponse.params.errmsg).to.be.a(\"string\");", + "});", + "", + "pm.test(\"params.status should be \\\"failed\\\"\", () => {", + " pm.expect(jsonResponse.params.status).to.eql(\"failed\");", + "});", + "", + "pm.test(\"id should be api.v1.search\", () => {", + " pm.expect(jsonResponse.id).to.eql(\"api.v1.search\");", + "});", + "pm.test(\"ver should be 1.0\", () => {", + " pm.expect(jsonResponse.ver).to.eql(\"1.0\");", + "});" + ] + } + } + ] } ], "event": [ diff --git a/search-api/api-tests/Reports/Composite Search API.postman_test_run.json b/search-api/api-tests/Reports/Composite Search API.postman_test_run.json index c90daa095..913a356e0 100644 --- a/search-api/api-tests/Reports/Composite Search API.postman_test_run.json +++ b/search-api/api-tests/Reports/Composite Search API.postman_test_run.json @@ -1,18 +1,18 @@ { - "id": "cf102fa4-c8e2-4199-a6e6-6a04dae9262d", + "id": "320b48d5-cb8d-48c0-a7b9-3743821dbd86", "name": "Composite Search API", - "timestamp": "2022-11-16T06:10:20.538Z", + "timestamp": "2022-11-18T04:22:08.826Z", "collection_id": "65bcacf8-0c90-4f3f-874b-a5d2c01cdc36", "folder_id": 0, "environment_id": "43ba92f2-e81b-446b-8b2a-589126842629", - "totalPass": 36, + "totalPass": 59, "totalFail": 0, "results": [ { "id": "6c4437c8-62ec-4917-bc5a-838fd333b71c", "name": "200 - Get live Content list", "url": "https://dev.knowlg.sunbird.org//action/composite/v3/search", - "time": 727, + "time": 2389, "responseCode": { "code": 200, "name": "OK" @@ -77,7 +77,7 @@ } }, "times": [ - 727 + 2389 ], "allTests": [ { @@ -99,7 +99,7 @@ "id": "eaba0158-fbb9-480a-8971-e5f4aad1fc47", "name": "200 - Get live Assesment list", "url": "https://dev.knowlg.sunbird.org//action/composite/v3/search", - "time": 323, + "time": 303, "responseCode": { "code": 200, "name": "OK" @@ -164,7 +164,7 @@ } }, "times": [ - 323 + 303 ], "allTests": [ { @@ -186,7 +186,7 @@ "id": "97be2029-e5dc-4dcd-8736-9178818022cf", "name": "200 - Get live assets list", "url": "https://dev.knowlg.sunbird.org//action/composite/v3/search", - "time": 268, + "time": 285, "responseCode": { "code": 200, "name": "OK" @@ -251,7 +251,7 @@ } }, "times": [ - 268 + 285 ], "allTests": [ { @@ -268,10 +268,190 @@ "ver should be 1.0": true } ] + }, + { + "id": "74f75390-3349-4cff-b105-20788af46c95", + "name": "400 - Search filters missing", + "url": "https://dev.knowlg.sunbird.org//action/composite/v3/search", + "time": 290, + "responseCode": { + "code": 400, + "name": "Bad Request" + }, + "tests": { + "Content-Type header is present": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "id should be api.v1.search": true, + "ver should be 1.0": true, + "Status code is 400": true, + "Verify keys and data type of the response result": true, + "responseCode should be \"CLIENT_ERROR\"": true, + "params.err should be \"ERR_CONTENT_SEARCH_FIELDS_MISSING\"": true, + "params.errmsg should be \"Required fields for search content are missing\"": true, + "Status code name has string \"Bad Request\"": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in negative scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"failed\"": { + "pass": 1, + "fail": 0 + }, + "id should be api.v1.search": { + "pass": 1, + "fail": 0 + }, + "ver should be 1.0": { + "pass": 1, + "fail": 0 + }, + "Status code is 400": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the response result": { + "pass": 1, + "fail": 0 + }, + "responseCode should be \"CLIENT_ERROR\"": { + "pass": 1, + "fail": 0 + }, + "params.err should be \"ERR_CONTENT_SEARCH_FIELDS_MISSING\"": { + "pass": 1, + "fail": 0 + }, + "params.errmsg should be \"Required fields for search content are missing\"": { + "pass": 1, + "fail": 0 + }, + "Status code name has string \"Bad Request\"": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 290 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "id should be api.v1.search": true, + "ver should be 1.0": true, + "Status code is 400": true, + "Verify keys and data type of the response result": true, + "responseCode should be \"CLIENT_ERROR\"": true, + "params.err should be \"ERR_CONTENT_SEARCH_FIELDS_MISSING\"": true, + "params.errmsg should be \"Required fields for search content are missing\"": true, + "Status code name has string \"Bad Request\"": true + } + ] + }, + { + "id": "8fb24515-12e0-430f-af59-7837ed777afb", + "name": "500 - parameter cannot be negative", + "url": "https://dev.knowlg.sunbird.org//action/composite/v3/search", + "time": 255, + "responseCode": { + "code": 500, + "name": "Internal Server Error" + }, + "tests": { + "Content-Type header is present": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "id should be api.v1.search": true, + "ver should be 1.0": true, + "Status code is 500": true, + "Verify keys and data type of the response result": true, + "responseCode should be \"SERVER_ERROR\"": true, + "params.err should be \"ERR_SYSTEM_EXCEPTION\"": true, + "params.errmsg should be \"[size] parameter cannot be negative, found [-1]\"": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in negative scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"failed\"": { + "pass": 1, + "fail": 0 + }, + "id should be api.v1.search": { + "pass": 1, + "fail": 0 + }, + "ver should be 1.0": { + "pass": 1, + "fail": 0 + }, + "Status code is 500": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the response result": { + "pass": 1, + "fail": 0 + }, + "responseCode should be \"SERVER_ERROR\"": { + "pass": 1, + "fail": 0 + }, + "params.err should be \"ERR_SYSTEM_EXCEPTION\"": { + "pass": 1, + "fail": 0 + }, + "params.errmsg should be \"[size] parameter cannot be negative, found [-1]\"": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 255 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "id should be api.v1.search": true, + "ver should be 1.0": true, + "Status code is 500": true, + "Verify keys and data type of the response result": true, + "responseCode should be \"SERVER_ERROR\"": true, + "params.err should be \"ERR_SYSTEM_EXCEPTION\"": true, + "params.errmsg should be \"[size] parameter cannot be negative, found [-1]\"": true + } + ] } ], "count": 1, - "totalTime": 1318, + "totalTime": 3522, "collection": { "requests": [ { @@ -285,6 +465,14 @@ { "id": "97be2029-e5dc-4dcd-8736-9178818022cf", "method": "POST" + }, + { + "id": "74f75390-3349-4cff-b105-20788af46c95", + "method": "POST" + }, + { + "id": "8fb24515-12e0-430f-af59-7837ed777afb", + "method": "POST" } ] } From f645333fca454f4829bee26da78ea15e8fafb341 Mon Sep 17 00:00:00 2001 From: Mahesh Kumar Gangula Date: Sun, 20 Nov 2022 12:49:08 +0530 Subject: [PATCH 153/490] feat (doc): local setup using kubernetes. --- KNOWLG-SETUP.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/KNOWLG-SETUP.md b/KNOWLG-SETUP.md index 95f49b5f0..1e93c580d 100644 --- a/KNOWLG-SETUP.md +++ b/KNOWLG-SETUP.md @@ -79,12 +79,20 @@ It requires the below DBs for Knowlg. ```shell cd kubernetes helm install redis sunbird-dbs/redis -n knowlg-db + +minikube mount :/var/lib/neo4j/data // LOCAL_SOURCE_DIR is where neo4j dump is extracted Ex: /Users/abc/sunbird-dbs/neo4j/data helm install neo4j sunbird-dbs/neo4j -n knowlg-db + +minikube mount :/mnt/backups // LOCAL_SOURCE_DIR is where neo4j dump is extracted Ex: /Users/abc/sunbird-dbs/cassandra/backups helm install cassandra sunbird-dbs/cassandra -n knowlg-db + +ssh to cassandra pod +run => cqlsh +run => source '/mnt/backups/cassandra_backup/db_schema.cql'; ``` + **Note:** -1. The `helm` charts for Kafka, Elasticsearch will be added soon. -2. The documentation will be updated to enable seed data while starting the service. +- The `helm` charts for Kafka, Elasticsearch will be added soon. ### Define ConfigMap We use the configmap to load the configuration for the microservices. From 10fd889335ae6551ed536ea1a4956a0e7a174e7a Mon Sep 17 00:00:00 2001 From: anilgupta Date: Tue, 22 Nov 2022 21:51:20 +0530 Subject: [PATCH 154/490] Issue #KN-439 feat: Handles domain agnostic for Neo4J. --- .../org/sunbird/graph/GraphService.scala | 24 +++--- .../org/sunbird/graph/util/CSPMetaUtil.scala | 73 +++++++++++++++++++ .../scala/org/sunbird/graph/BaseSpec.scala | 3 +- 3 files changed, 88 insertions(+), 12 deletions(-) create mode 100644 ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala diff --git a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/GraphService.scala b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/GraphService.scala index f7986ffbc..cba56e437 100644 --- a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/GraphService.scala +++ b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/GraphService.scala @@ -1,12 +1,11 @@ package org.sunbird.graph -import java.util - import org.sunbird.common.dto.{Property, Request, Response} import org.sunbird.graph.dac.model.{Node, SearchCriteria} import org.sunbird.graph.external.ExternalPropsManager import org.sunbird.graph.external.store.ExternalStore import org.sunbird.graph.service.operation.{GraphAsyncOperations, Neo4JBoltSearchOperations, NodeAsyncOperations, SearchAsyncOperations} +import org.sunbird.graph.util.CSPMetaUtil import scala.concurrent.{ExecutionContext, Future} @@ -14,10 +13,14 @@ class GraphService { implicit val ec: ExecutionContext = ExecutionContext.global def addNode(graphId: String, node: Node): Future[Node] = { + val metadata = CSPMetaUtil.updateRelativePath(node.getMetadata) + node.setMetadata(metadata) NodeAsyncOperations.addNode(graphId, node) } def upsertNode(graphId: String, node: Node, request: Request): Future[Node] = { + val metadata = CSPMetaUtil.updateRelativePath(node.getMetadata) + node.setMetadata(metadata) NodeAsyncOperations.upsertNode(graphId, node, request) } @@ -26,7 +29,7 @@ class GraphService { } def getNodeByUniqueId(graphId: String, nodeId: String, getTags: Boolean, request: Request): Future[Node] = { - SearchAsyncOperations.getNodeByUniqueId(graphId, nodeId, getTags, request) + SearchAsyncOperations.getNodeByUniqueId(graphId, nodeId, getTags, request).map(node => CSPMetaUtil.updateAbsolutePath(node)) } def deleteNode(graphId: String, nodeId: String, request: Request): Future[java.lang.Boolean] = { @@ -34,14 +37,15 @@ class GraphService { } def getNodeProperty(graphId: String, identifier: String, property: String): Future[Property] = { - SearchAsyncOperations.getNodeProperty(graphId, identifier, property) + SearchAsyncOperations.getNodeProperty(graphId, identifier, property).map(property => CSPMetaUtil.updateAbsolutePath(property)) } - def updateNodes(graphId: String, identifiers:util.List[String], metadata:util.Map[String,AnyRef]):Future[util.Map[String, Node]] = { - NodeAsyncOperations.updateNodes(graphId, identifiers, metadata) + def updateNodes(graphId: String, identifiers:java.util.List[String], metadata:java.util.Map[String,AnyRef]):Future[java.util.Map[String, Node]] = { + val updatedMetadata = CSPMetaUtil.updateRelativePath(metadata) + NodeAsyncOperations.updateNodes(graphId, identifiers, updatedMetadata) } - def getNodeByUniqueIds(graphId:String, searchCriteria: SearchCriteria): Future[util.List[Node]] = { - SearchAsyncOperations.getNodeByUniqueIds(graphId, searchCriteria) + def getNodeByUniqueIds(graphId:String, searchCriteria: SearchCriteria): Future[java.util.List[Node]] = { + SearchAsyncOperations.getNodeByUniqueIds(graphId, searchCriteria).map(nodes => CSPMetaUtil.updateAbsolutePath(nodes)) } def readExternalProps(request: Request, fields: List[String]): Future[Response] = { @@ -63,11 +67,11 @@ class GraphService { Neo4JBoltSearchOperations.checkCyclicLoop(graphId, endNodeId, relationType, startNodeId) } - def removeRelation(graphId: String, relationMap: util.List[util.Map[String, AnyRef]]) = { + def removeRelation(graphId: String, relationMap: java.util.List[java.util.Map[String, AnyRef]]) = { GraphAsyncOperations.removeRelation(graphId, relationMap) } - def createRelation(graphId: String, relationMap: util.List[util.Map[String, AnyRef]]) = { + def createRelation(graphId: String, relationMap: java.util.List[java.util.Map[String, AnyRef]]) = { GraphAsyncOperations.createRelation(graphId, relationMap) } } diff --git a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala new file mode 100644 index 000000000..2f5688d2a --- /dev/null +++ b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala @@ -0,0 +1,73 @@ +package org.sunbird.graph.util + +import org.apache.commons.collections4.MapUtils +import org.slf4j.LoggerFactory +import org.sunbird.common.Platform +import org.sunbird.common.dto.Property +import org.sunbird.graph.dac.model.Node + +import scala.collection.JavaConverters._ + +object CSPMetaUtil { + private[this] val logger = LoggerFactory.getLogger(classOf[CSPMetaUtil]) + private val defaultMetadataList = java.util.Arrays.asList("appIcon","posterImage","artifactUrl","downloadUrl","variants","previewUrl","pdfUrl") + private val defaultWriteBasePath = java.util.Arrays.asList("https://sunbirddev.blob.core.windows.net","https://obj.dev.sunbird.org") + + def updateAbsolutePath(data: java.util.Map[String, AnyRef]): java.util.Map[String, AnyRef] = { + val cspMeta = Platform.getStringList("cloudstorage.metadata.list", defaultMetadataList).asScala.toList + val absolutePath = Platform.getString("cloudstorage.read_base_path", "") + java.io.File.separator + Platform.getString("cloud_storage_container", "") + if (MapUtils.isNotEmpty(data)) { + val updatedMeta: java.util.Map[String, AnyRef] = data.asScala.map(x => if (cspMeta.contains(x._1)) (x._1, x._2.asInstanceOf[String].replace("CLOUD_STORAGE_BASE_PATH", absolutePath)) else (x._1, x._2)).toMap.asJava + updatedMeta + } else data + } + + def updateAbsolutePath(node: Node): Node = { + val metadata = updateAbsolutePath(node.getMetadata) + node.setMetadata(metadata) + node + } + + def updateAbsolutePath(nodes: java.util.List[Node]): java.util.List[Node] = { + nodes.asScala.toList.map(node => { + updateAbsolutePath(node) + }).asJava + } + + def updateAbsolutePath(property: Property): Property = { + val cspMeta = Platform.getStringList("cloudstorage.metadata.list", defaultMetadataList) + val absolutePath = Platform.getString("cloudstorage.read_base_path", "") + java.io.File.separator + Platform.getString("cloud_storage_container", "") + if(cspMeta.contains(property.getPropertyName)) { + val value = property.getPropertyValue + value match { + case str: String => + property.setPropertyValue(str.replace("CLOUD_STORAGE_BASE_PATH", absolutePath)) + case _ => + } + } + property + } + +// def updateRelativePath(data: java.util.Map[String, AnyRef]): java.util.Map[String, AnyRef] = { +// val cspMeta = Platform.getStringList("cloudstorage.metadata.list", defaultMetadataList) +// val validCSPSource: java.util.List[String] = Platform.getStringList("cloudstorage.write_base_path", defaultWriteBasePath) +// val basePath: List[String] = validCSPSource.asScala.toList.map(source => source + java.io.File.separator + Platform.getString("cloud_storage_container", "")) +// if (MapUtils.isNotEmpty(data)) { +// val updatedMeta: java.util.Map[String, AnyRef] = data.asScala.map(x => if (cspMeta.contains(x._1)) (x._1, basePath.map(path => {if (x._2.asInstanceOf[String].contains(path)) x._2.asInstanceOf[String].replace(path, "CLOUD_STORAGE_BASE_PATH") else x._2 })) else (x._1, x._2)).toMap.asJava +// updatedMeta +// } else data +// } + + def updateRelativePath(data: java.util.Map[String, AnyRef]): java.util.Map[String, AnyRef] = { + val cspMeta: java.util.List[String] = Platform.getStringList("cloudstorage.metadata.list", defaultMetadataList) + val validCSPSource: java.util.List[String] = Platform.getStringList("cloudstorage.write_base_path", defaultWriteBasePath) + val basePath: List[String] = validCSPSource.asScala.toList.map(source => source + java.io.File.separator + Platform.getString("cloud_storage_container", "")) + if (MapUtils.isNotEmpty(data)) { + val updatedMeta: java.util.Map[String, AnyRef] = data.asScala.map(x => if (cspMeta.contains(x._1)) (x._1, basePath.map(path => if (x._2.asInstanceOf[String].contains(path)) x._2.asInstanceOf[String].replace(path, "CLOUD_STORAGE_BASE_PATH") else x._2)) else (x._1, x._2)).toMap.asJava + updatedMeta + } else data + } + +} + +class CSPMetaUtil {} diff --git a/ontology-engine/graph-engine_2.11/src/test/scala/org/sunbird/graph/BaseSpec.scala b/ontology-engine/graph-engine_2.11/src/test/scala/org/sunbird/graph/BaseSpec.scala index 830e612c5..83359dedb 100644 --- a/ontology-engine/graph-engine_2.11/src/test/scala/org/sunbird/graph/BaseSpec.scala +++ b/ontology-engine/graph-engine_2.11/src/test/scala/org/sunbird/graph/BaseSpec.scala @@ -1,7 +1,6 @@ package org.sunbird.graph import java.io.File -import java.util import com.datastax.driver.core.Session import org.apache.commons.io.FileUtils @@ -134,7 +133,7 @@ class BaseSpec extends AsyncFlatSpec with Matchers with BeforeAndAfterAll { node.setIdentifier("board") node.setNodeType("DATA_NODE") node.setObjectType("Category") - node.setMetadata(new util.HashMap[String, AnyRef]() { + node.setMetadata(new java.util.HashMap[String, AnyRef]() { { put("code", "board") put("orgIdFieldName", "boardIds") From 21b1ebc7b6e3ebbf1a2a1b22368c06ef3aea683d Mon Sep 17 00:00:00 2001 From: anilgupta Date: Wed, 23 Nov 2022 12:40:35 +0530 Subject: [PATCH 155/490] Issue #KN-439 feat: Handles domain agnostic for Neo4J. --- .../content-service/conf/application.conf | 11 ++++++++ .../org/sunbird/graph/GraphService.scala | 22 ++++++++++------ .../org/sunbird/graph/util/CSPMetaUtil.scala | 25 ++++++++++--------- 3 files changed, 38 insertions(+), 20 deletions(-) diff --git a/content-api/content-service/conf/application.conf b/content-api/content-service/conf/application.conf index 5a77be1c5..54dcde15a 100644 --- a/content-api/content-service/conf/application.conf +++ b/content-api/content-service/conf/application.conf @@ -752,3 +752,14 @@ actor.timeoutMillisec = 30000 #Index file validation isIndexHtmlValidationRequired=true + +cloud_storage_type="" +cloud_storage_key="" +cloud_storage_secret="" +cloud_storage_container="" +cloud_storage_endpoint="" + +cloudstorage.metadata.replace_absolute_path=true +cloudstorage.read_base_path="https://sunbirddev.blob.core.windows.net" +cloudstorage.write_base_path=["https://sunbirddev.blob.core.windows.net","https://obj.dev.sunbird.org"] +cloudstorage.metadata.list=["appIcon","posterImage","artifactUrl","downloadUrl","variants","previewUrl","pdfUrl"] diff --git a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/GraphService.scala b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/GraphService.scala index cba56e437..4b919889f 100644 --- a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/GraphService.scala +++ b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/GraphService.scala @@ -1,5 +1,6 @@ package org.sunbird.graph +import org.sunbird.common.Platform import org.sunbird.common.dto.{Property, Request, Response} import org.sunbird.graph.dac.model.{Node, SearchCriteria} import org.sunbird.graph.external.ExternalPropsManager @@ -11,16 +12,21 @@ import scala.concurrent.{ExecutionContext, Future} class GraphService { implicit val ec: ExecutionContext = ExecutionContext.global + val isrRelativePathEnabled = Platform.getBoolean("cloudstorage.metadata.replace_absolute_path", false) def addNode(graphId: String, node: Node): Future[Node] = { - val metadata = CSPMetaUtil.updateRelativePath(node.getMetadata) - node.setMetadata(metadata) + if(isrRelativePathEnabled) { + val metadata = CSPMetaUtil.updateRelativePath(node.getMetadata) + node.setMetadata(metadata) + } NodeAsyncOperations.addNode(graphId, node) } def upsertNode(graphId: String, node: Node, request: Request): Future[Node] = { - val metadata = CSPMetaUtil.updateRelativePath(node.getMetadata) - node.setMetadata(metadata) + if(isrRelativePathEnabled) { + val metadata = CSPMetaUtil.updateRelativePath(node.getMetadata) + node.setMetadata(metadata) + } NodeAsyncOperations.upsertNode(graphId, node, request) } @@ -29,7 +35,7 @@ class GraphService { } def getNodeByUniqueId(graphId: String, nodeId: String, getTags: Boolean, request: Request): Future[Node] = { - SearchAsyncOperations.getNodeByUniqueId(graphId, nodeId, getTags, request).map(node => CSPMetaUtil.updateAbsolutePath(node)) + SearchAsyncOperations.getNodeByUniqueId(graphId, nodeId, getTags, request).map(node => if(isrRelativePathEnabled) CSPMetaUtil.updateAbsolutePath(node) else node) } def deleteNode(graphId: String, nodeId: String, request: Request): Future[java.lang.Boolean] = { @@ -37,15 +43,15 @@ class GraphService { } def getNodeProperty(graphId: String, identifier: String, property: String): Future[Property] = { - SearchAsyncOperations.getNodeProperty(graphId, identifier, property).map(property => CSPMetaUtil.updateAbsolutePath(property)) + SearchAsyncOperations.getNodeProperty(graphId, identifier, property).map(property => if(isrRelativePathEnabled) CSPMetaUtil.updateAbsolutePath(property) else property) } def updateNodes(graphId: String, identifiers:java.util.List[String], metadata:java.util.Map[String,AnyRef]):Future[java.util.Map[String, Node]] = { - val updatedMetadata = CSPMetaUtil.updateRelativePath(metadata) + val updatedMetadata = if(isrRelativePathEnabled) CSPMetaUtil.updateRelativePath(metadata) else metadata NodeAsyncOperations.updateNodes(graphId, identifiers, updatedMetadata) } def getNodeByUniqueIds(graphId:String, searchCriteria: SearchCriteria): Future[java.util.List[Node]] = { - SearchAsyncOperations.getNodeByUniqueIds(graphId, searchCriteria).map(nodes => CSPMetaUtil.updateAbsolutePath(nodes)) + SearchAsyncOperations.getNodeByUniqueIds(graphId, searchCriteria).map(nodes => if(isrRelativePathEnabled) CSPMetaUtil.updateAbsolutePath(nodes) else nodes) } def readExternalProps(request: Request, fields: List[String]): Future[Response] = { diff --git a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala index 2f5688d2a..475424f54 100644 --- a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala +++ b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala @@ -17,7 +17,12 @@ object CSPMetaUtil { val cspMeta = Platform.getStringList("cloudstorage.metadata.list", defaultMetadataList).asScala.toList val absolutePath = Platform.getString("cloudstorage.read_base_path", "") + java.io.File.separator + Platform.getString("cloud_storage_container", "") if (MapUtils.isNotEmpty(data)) { - val updatedMeta: java.util.Map[String, AnyRef] = data.asScala.map(x => if (cspMeta.contains(x._1)) (x._1, x._2.asInstanceOf[String].replace("CLOUD_STORAGE_BASE_PATH", absolutePath)) else (x._1, x._2)).toMap.asJava + val updatedMeta: java.util.Map[String, AnyRef] = new java.util.HashMap[String, AnyRef] + data.asScala.map(x => + if (cspMeta.contains(x._1)) + updatedMeta.put(x._1, x._2.asInstanceOf[String].replace("CLOUD_STORAGE_BASE_PATH", absolutePath)) + else updatedMeta.put(x._1, x._2) + ).asJava updatedMeta } else data } @@ -48,22 +53,18 @@ object CSPMetaUtil { property } -// def updateRelativePath(data: java.util.Map[String, AnyRef]): java.util.Map[String, AnyRef] = { -// val cspMeta = Platform.getStringList("cloudstorage.metadata.list", defaultMetadataList) -// val validCSPSource: java.util.List[String] = Platform.getStringList("cloudstorage.write_base_path", defaultWriteBasePath) -// val basePath: List[String] = validCSPSource.asScala.toList.map(source => source + java.io.File.separator + Platform.getString("cloud_storage_container", "")) -// if (MapUtils.isNotEmpty(data)) { -// val updatedMeta: java.util.Map[String, AnyRef] = data.asScala.map(x => if (cspMeta.contains(x._1)) (x._1, basePath.map(path => {if (x._2.asInstanceOf[String].contains(path)) x._2.asInstanceOf[String].replace(path, "CLOUD_STORAGE_BASE_PATH") else x._2 })) else (x._1, x._2)).toMap.asJava -// updatedMeta -// } else data -// } - def updateRelativePath(data: java.util.Map[String, AnyRef]): java.util.Map[String, AnyRef] = { val cspMeta: java.util.List[String] = Platform.getStringList("cloudstorage.metadata.list", defaultMetadataList) val validCSPSource: java.util.List[String] = Platform.getStringList("cloudstorage.write_base_path", defaultWriteBasePath) val basePath: List[String] = validCSPSource.asScala.toList.map(source => source + java.io.File.separator + Platform.getString("cloud_storage_container", "")) if (MapUtils.isNotEmpty(data)) { - val updatedMeta: java.util.Map[String, AnyRef] = data.asScala.map(x => if (cspMeta.contains(x._1)) (x._1, basePath.map(path => if (x._2.asInstanceOf[String].contains(path)) x._2.asInstanceOf[String].replace(path, "CLOUD_STORAGE_BASE_PATH") else x._2)) else (x._1, x._2)).toMap.asJava + val updatedMeta: java.util.Map[String, AnyRef] = new java.util.HashMap[String, AnyRef] + data.asScala.map(x => + if (cspMeta.contains(x._1)) { + val filteredPath = basePath.filter(path => x._2.asInstanceOf[String].contains(path)) + updatedMeta.put(x._1, if(filteredPath.nonEmpty) x._2.asInstanceOf[String].replaceAll(filteredPath.head, "CLOUD_STORAGE_BASE_PATH") else x._2) + } else updatedMeta.put(x._1, x._2) + ).asJava updatedMeta } else data } From f0f1704ec1ac4997960b7553a58745767114c4b0 Mon Sep 17 00:00:00 2001 From: anilgupta Date: Wed, 23 Nov 2022 12:41:34 +0530 Subject: [PATCH 156/490] Issue #KN-439 feat: Handles domain agnostic for Neo4J. --- content-api/content-service/conf/application.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content-api/content-service/conf/application.conf b/content-api/content-service/conf/application.conf index 54dcde15a..dccfcd3ab 100644 --- a/content-api/content-service/conf/application.conf +++ b/content-api/content-service/conf/application.conf @@ -759,7 +759,7 @@ cloud_storage_secret="" cloud_storage_container="" cloud_storage_endpoint="" -cloudstorage.metadata.replace_absolute_path=true +cloudstorage.metadata.replace_absolute_path=false cloudstorage.read_base_path="https://sunbirddev.blob.core.windows.net" cloudstorage.write_base_path=["https://sunbirddev.blob.core.windows.net","https://obj.dev.sunbird.org"] cloudstorage.metadata.list=["appIcon","posterImage","artifactUrl","downloadUrl","variants","previewUrl","pdfUrl"] From b8fc64681d4b249189f5e9083fd5a705d27ff584 Mon Sep 17 00:00:00 2001 From: anilgupta Date: Wed, 23 Nov 2022 13:20:09 +0530 Subject: [PATCH 157/490] Issue #KN-439 test: Added the config for unit test. --- .../graph-core_2.11/src/test/resources/application.conf | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ontology-engine/graph-core_2.11/src/test/resources/application.conf b/ontology-engine/graph-core_2.11/src/test/resources/application.conf index b5fb5b0cb..8a4335e81 100644 --- a/ontology-engine/graph-core_2.11/src/test/resources/application.conf +++ b/ontology-engine/graph-core_2.11/src/test/resources/application.conf @@ -486,3 +486,8 @@ languageCode { platform.language.codes=["as","bn","en","gu","hi","hoc","jun","ka","mai","mr","unx","or","san","sat","ta","te","urd"] objectcategorydefinition.keyspace=category_store + +cloudstorage.metadata.replace_absolute_path=true +cloudstorage.read_base_path="https://sunbirddev.blob.core.windows.net" +cloudstorage.write_base_path=["https://sunbirddev.blob.core.windows.net","https://obj.dev.sunbird.org"] +cloudstorage.metadata.list=["appIcon","posterImage","artifactUrl","downloadUrl","variants","previewUrl","pdfUrl"] \ No newline at end of file From 7682ab812b2a28404093a47fa6f27057abda2fa8 Mon Sep 17 00:00:00 2001 From: anilgupta Date: Thu, 24 Nov 2022 10:04:48 +0530 Subject: [PATCH 158/490] Issue #KN-439 feat: Fixed the replacement issue. --- .../org/sunbird/graph/util/CSPMetaUtil.scala | 59 +++++++++++++++---- 1 file changed, 46 insertions(+), 13 deletions(-) diff --git a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala index 475424f54..1b81dc7c2 100644 --- a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala +++ b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala @@ -1,8 +1,9 @@ package org.sunbird.graph.util import org.apache.commons.collections4.MapUtils +import org.apache.commons.lang3.StringUtils import org.slf4j.LoggerFactory -import org.sunbird.common.Platform +import org.sunbird.common.{JsonUtils, Platform} import org.sunbird.common.dto.Property import org.sunbird.graph.dac.model.Node @@ -10,11 +11,9 @@ import scala.collection.JavaConverters._ object CSPMetaUtil { private[this] val logger = LoggerFactory.getLogger(classOf[CSPMetaUtil]) - private val defaultMetadataList = java.util.Arrays.asList("appIcon","posterImage","artifactUrl","downloadUrl","variants","previewUrl","pdfUrl") - private val defaultWriteBasePath = java.util.Arrays.asList("https://sunbirddev.blob.core.windows.net","https://obj.dev.sunbird.org") def updateAbsolutePath(data: java.util.Map[String, AnyRef]): java.util.Map[String, AnyRef] = { - val cspMeta = Platform.getStringList("cloudstorage.metadata.list", defaultMetadataList).asScala.toList + val cspMeta = Platform.getStringList("cloudstorage.metadata.list", new java.util.ArrayList[String]()).asScala.toList val absolutePath = Platform.getString("cloudstorage.read_base_path", "") + java.io.File.separator + Platform.getString("cloud_storage_container", "") if (MapUtils.isNotEmpty(data)) { val updatedMeta: java.util.Map[String, AnyRef] = new java.util.HashMap[String, AnyRef] @@ -40,7 +39,7 @@ object CSPMetaUtil { } def updateAbsolutePath(property: Property): Property = { - val cspMeta = Platform.getStringList("cloudstorage.metadata.list", defaultMetadataList) + val cspMeta = Platform.getStringList("cloudstorage.metadata.list", new java.util.ArrayList[String]()) val absolutePath = Platform.getString("cloudstorage.read_base_path", "") + java.io.File.separator + Platform.getString("cloud_storage_container", "") if(cspMeta.contains(property.getPropertyName)) { val value = property.getPropertyValue @@ -54,19 +53,53 @@ object CSPMetaUtil { } def updateRelativePath(data: java.util.Map[String, AnyRef]): java.util.Map[String, AnyRef] = { - val cspMeta: java.util.List[String] = Platform.getStringList("cloudstorage.metadata.list", defaultMetadataList) - val validCSPSource: java.util.List[String] = Platform.getStringList("cloudstorage.write_base_path", defaultWriteBasePath) - val basePath: List[String] = validCSPSource.asScala.toList.map(source => source + java.io.File.separator + Platform.getString("cloud_storage_container", "")) - if (MapUtils.isNotEmpty(data)) { + logger.info("CSPMetaUtil ::: updateRelativePath util.Map[String, AnyRef] ::: data before url replace :: " + data) + val cspMeta: java.util.List[String] = Platform.getStringList("cloudstorage.metadata.list", new java.util.ArrayList[String]()) + val validCSPSource: List[String] = Platform.getStringList("cloudstorage.write_base_path", new java.util.ArrayList[String]()).asScala.toList + val basePaths: Array[String] = validCSPSource.map(source => source + java.io.File.separator + Platform.getString("cloud_storage_container", "")).toArray + val repArray = getReplacementData(basePaths, "CLOUD_STORAGE_BASE_PATH") + val result = if (MapUtils.isNotEmpty(data)) { val updatedMeta: java.util.Map[String, AnyRef] = new java.util.HashMap[String, AnyRef] data.asScala.map(x => - if (cspMeta.contains(x._1)) { - val filteredPath = basePath.filter(path => x._2.asInstanceOf[String].contains(path)) - updatedMeta.put(x._1, if(filteredPath.nonEmpty) x._2.asInstanceOf[String].replaceAll(filteredPath.head, "CLOUD_STORAGE_BASE_PATH") else x._2) - } else updatedMeta.put(x._1, x._2) + if (cspMeta.contains(x._1)) + updatedMeta.put(x._1, getBasePath(x._1, x._2, basePaths, repArray)) + else updatedMeta.put(x._1, x._2) ).asJava updatedMeta } else data + logger.info("CSPMetaUtil ::: updateRelativePath util.Map[String, AnyRef] ::: data after url replace :: " + result) + result + } + + private def getBasePath(key: String, value: AnyRef, oldPath: Array[String], newPath: Array[String]): AnyRef = { + logger.info(s"CSPMetaUtil ::: getBasePath ::: Updating Path for Key : $key & Value : $value") + val res = if (null != value) { + value match { + case x: String => if (StringUtils.isNotBlank(x)) StringUtils.replaceEach(x, oldPath, newPath) else x + case y: Map[String, AnyRef] => { + val dStr = JsonUtils.serialize(y) + val result = StringUtils.replaceEach(dStr, oldPath, newPath) + val output: Map[String, AnyRef] = JsonUtils.deserialize(result, classOf[Map[String, AnyRef]]) + output + } + case z: java.util.Map[String, AnyRef] => { + val dStr = JsonUtils.serialize(z) + val result = StringUtils.replaceEach(dStr, oldPath, newPath) + val output: java.util.Map[String, AnyRef] = JsonUtils.deserialize(result, classOf[java.util.Map[String, AnyRef]]) + output + } + } + } else value + logger.info(s"CSPMetaUtil ::: getBasePath ::: Updated Path for Key : $key & Updated Value is : $res") + res + } + + private def getReplacementData(oldPath: Array[String], repStr: String): Array[String] = { + val repArray = new Array[String](oldPath.length) + for (i <- oldPath.indices) { + repArray(i) = repStr + } + repArray } } From 1e7706a59c80b7279599cbff8477bfea05f47dbb Mon Sep 17 00:00:00 2001 From: anilgupta Date: Mon, 28 Nov 2022 09:08:24 +0530 Subject: [PATCH 159/490] Issue #KN-439 feat: Handles domain agnostic for Cassandra. --- .../content-service/conf/application.conf | 4 ++-- .../org/sunbird/graph/GraphService.scala | 20 +++++++++++++---- .../org/sunbird/graph/util/CSPMetaUtil.scala | 22 ++++++++++++++++++- .../src/test/resources/application.conf | 2 +- .../taxonomy-service/conf/application.conf | 7 +++++- 5 files changed, 46 insertions(+), 9 deletions(-) diff --git a/content-api/content-service/conf/application.conf b/content-api/content-service/conf/application.conf index dccfcd3ab..1d4696944 100644 --- a/content-api/content-service/conf/application.conf +++ b/content-api/content-service/conf/application.conf @@ -761,5 +761,5 @@ cloud_storage_endpoint="" cloudstorage.metadata.replace_absolute_path=false cloudstorage.read_base_path="https://sunbirddev.blob.core.windows.net" -cloudstorage.write_base_path=["https://sunbirddev.blob.core.windows.net","https://obj.dev.sunbird.org"] -cloudstorage.metadata.list=["appIcon","posterImage","artifactUrl","downloadUrl","variants","previewUrl","pdfUrl"] +cloudstorage.write_base_path=["https://sunbirddev.blob.core.windows.net"] +cloudstorage.metadata.list=["appIcon","posterImage","artifactUrl","downloadUrl","variants","previewUrl","pdfUrl", "streamingUrl", "toc_url"] diff --git a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/GraphService.scala b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/GraphService.scala index 4b919889f..4e7df6d5f 100644 --- a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/GraphService.scala +++ b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/GraphService.scala @@ -1,18 +1,18 @@ package org.sunbird.graph import org.sunbird.common.Platform -import org.sunbird.common.dto.{Property, Request, Response} +import org.sunbird.common.dto.{Property, Request, Response, ResponseHandler} import org.sunbird.graph.dac.model.{Node, SearchCriteria} import org.sunbird.graph.external.ExternalPropsManager -import org.sunbird.graph.external.store.ExternalStore import org.sunbird.graph.service.operation.{GraphAsyncOperations, Neo4JBoltSearchOperations, NodeAsyncOperations, SearchAsyncOperations} import org.sunbird.graph.util.CSPMetaUtil +import java.lang import scala.concurrent.{ExecutionContext, Future} class GraphService { implicit val ec: ExecutionContext = ExecutionContext.global - val isrRelativePathEnabled = Platform.getBoolean("cloudstorage.metadata.replace_absolute_path", false) + val isrRelativePathEnabled: lang.Boolean = Platform.getBoolean("cloudstorage.metadata.replace_absolute_path", false) def addNode(graphId: String, node: Node): Future[Node] = { if(isrRelativePathEnabled) { @@ -55,14 +55,26 @@ class GraphService { } def readExternalProps(request: Request, fields: List[String]): Future[Response] = { - ExternalPropsManager.fetchProps(request, fields) + ExternalPropsManager.fetchProps(request, fields).map(res => + if(isrRelativePathEnabled) { + val updatedResult = CSPMetaUtil.updateAbsolutePath(res.getResult) + val response = ResponseHandler.OK() + response.putAll(updatedResult) + response + } else res) } def saveExternalProps(request: Request): Future[Response] = { + val externalProps: java.util.Map[String, AnyRef] = request.getRequest + val updatedExternalProps = if(isrRelativePathEnabled) CSPMetaUtil.updateExternalRelativePath(externalProps) else externalProps + request.setRequest(updatedExternalProps) ExternalPropsManager.saveProps(request) } def updateExternalProps(request: Request): Future[Response] = { + val externalProps: java.util.Map[String, AnyRef] = request.getRequest + val updatedExternalProps = if (isrRelativePathEnabled) CSPMetaUtil.updateExternalRelativePath(externalProps) else externalProps + request.setRequest(updatedExternalProps) ExternalPropsManager.update(request) } diff --git a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala index 1b81dc7c2..142e66a55 100644 --- a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala +++ b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala @@ -3,8 +3,8 @@ package org.sunbird.graph.util import org.apache.commons.collections4.MapUtils import org.apache.commons.lang3.StringUtils import org.slf4j.LoggerFactory -import org.sunbird.common.{JsonUtils, Platform} import org.sunbird.common.dto.Property +import org.sunbird.common.{JsonUtils, Platform} import org.sunbird.graph.dac.model.Node import scala.collection.JavaConverters._ @@ -71,6 +71,26 @@ object CSPMetaUtil { result } + def updateExternalRelativePath(data: java.util.Map[String, AnyRef]): java.util.Map[String, AnyRef] = { + logger.info("CSPMetaUtil ::: updateExternalRelativePath util.Map[String, AnyRef] ::: data before url replace :: " + data) + + val values = data.get("values") + val updatedValues = values match { + case x: List[AnyRef] => { + val validCSPSource: List[String] = Platform.getStringList("cloudstorage.write_base_path", new java.util.ArrayList[String]()).asScala.toList + val basePaths: Array[String] = validCSPSource.map(source => source + java.io.File.separator + Platform.getString("cloud_storage_container", "")).toArray + val repArray = getReplacementData(basePaths, "CLOUD_STORAGE_BASE_PATH") + + x.map(value => StringUtils.replaceEach(value.asInstanceOf[String], basePaths, repArray)) + } + case _ => values + } + + data.put("values", updatedValues) + logger.info("CSPMetaUtil ::: updateExternalRelativePath util.Map[String, AnyRef] ::: data after url replace :: " + data) + data + } + private def getBasePath(key: String, value: AnyRef, oldPath: Array[String], newPath: Array[String]): AnyRef = { logger.info(s"CSPMetaUtil ::: getBasePath ::: Updating Path for Key : $key & Value : $value") val res = if (null != value) { diff --git a/ontology-engine/graph-core_2.11/src/test/resources/application.conf b/ontology-engine/graph-core_2.11/src/test/resources/application.conf index 8a4335e81..6504a3c99 100644 --- a/ontology-engine/graph-core_2.11/src/test/resources/application.conf +++ b/ontology-engine/graph-core_2.11/src/test/resources/application.conf @@ -490,4 +490,4 @@ objectcategorydefinition.keyspace=category_store cloudstorage.metadata.replace_absolute_path=true cloudstorage.read_base_path="https://sunbirddev.blob.core.windows.net" cloudstorage.write_base_path=["https://sunbirddev.blob.core.windows.net","https://obj.dev.sunbird.org"] -cloudstorage.metadata.list=["appIcon","posterImage","artifactUrl","downloadUrl","variants","previewUrl","pdfUrl"] \ No newline at end of file +cloudstorage.metadata.list=["appIcon","posterImage","artifactUrl","downloadUrl","variants","previewUrl","pdfUrl", "streamingUrl", "toc_url"] \ No newline at end of file diff --git a/taxonomy-api/taxonomy-service/conf/application.conf b/taxonomy-api/taxonomy-service/conf/application.conf index 11b9db9b1..73b8f5ec5 100644 --- a/taxonomy-api/taxonomy-service/conf/application.conf +++ b/taxonomy-api/taxonomy-service/conf/application.conf @@ -316,5 +316,10 @@ languageCode { } platform.language.codes=["as","bn","en","gu","hi","hoc","jun","ka","mai","mr","unx","or","san","sat","ta","te","urd"] -objectcategorydefinition.keyspace=category_store +objectcategorydefinition.keyspace=dev_category_store +cloudstorage.metadata.replace_absolute_path=true +cloudstorage.read_base_path="https://sunbirddev.blob.core.windows.net" +cloudstorage.write_base_path=["https://sunbirddev.blob.core.windows.net"] +cloudstorage.metadata.list=["appIcon","posterImage","artifactUrl","downloadUrl","variants","previewUrl","pdfUrl", "streamingUrl", "toc_url"] +cloud_storage_container="sunbird-content-dev" \ No newline at end of file From 67834ad50cdd3ed99a5b85c86d1ada26db433e7c Mon Sep 17 00:00:00 2001 From: anilgupta Date: Tue, 29 Nov 2022 18:10:18 +0530 Subject: [PATCH 160/490] Issue #KN-439 feat: Handles domain agnostic for Cassandra. --- .../scala/org/sunbird/graph/GraphService.scala | 2 +- .../org/sunbird/graph/util/CSPMetaUtil.scala | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/GraphService.scala b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/GraphService.scala index 4e7df6d5f..c931095f8 100644 --- a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/GraphService.scala +++ b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/GraphService.scala @@ -66,7 +66,7 @@ class GraphService { def saveExternalProps(request: Request): Future[Response] = { val externalProps: java.util.Map[String, AnyRef] = request.getRequest - val updatedExternalProps = if(isrRelativePathEnabled) CSPMetaUtil.updateExternalRelativePath(externalProps) else externalProps + val updatedExternalProps = if(isrRelativePathEnabled) CSPMetaUtil.saveExternalRelativePath(externalProps) else externalProps request.setRequest(updatedExternalProps) ExternalPropsManager.saveProps(request) } diff --git a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala index 142e66a55..9951591b6 100644 --- a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala +++ b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala @@ -71,6 +71,21 @@ object CSPMetaUtil { result } + def saveExternalRelativePath(data: java.util.Map[String, AnyRef]): java.util.Map[String, AnyRef] = { + logger.info("CSPMetaUtil ::: saveExternalRelativePath util.Map[String, AnyRef] ::: data before url replace :: " + data) + val validCSPSource: List[String] = Platform.getStringList("cloudstorage.write_base_path", new java.util.ArrayList[String]()).asScala.toList + val basePaths: Array[String] = validCSPSource.map(source => source + java.io.File.separator + Platform.getString("cloud_storage_container", "")).toArray + val repArray = getReplacementData(basePaths, "CLOUD_STORAGE_BASE_PATH") + + val updatedData: java.util.Map[String, AnyRef] = new java.util.HashMap[String, AnyRef] + data.asScala.map(field => { + updatedData.put(field._1, StringUtils.replaceEach(field._2.asInstanceOf[String], basePaths, repArray).asInstanceOf[AnyRef]) + }).asJava + + logger.info("CSPMetaUtil ::: saveExternalRelativePath util.Map[String, AnyRef] ::: data after url replace :: " + updatedData) + updatedData + } + def updateExternalRelativePath(data: java.util.Map[String, AnyRef]): java.util.Map[String, AnyRef] = { logger.info("CSPMetaUtil ::: updateExternalRelativePath util.Map[String, AnyRef] ::: data before url replace :: " + data) From 849a184b7b4bf8a4d66780bf322bb0cb67154ef6 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Wed, 30 Nov 2022 16:36:10 +0530 Subject: [PATCH 161/490] Issue #KN-579 feat: Properties update --- schemas/relationalmetadata/1.0/schema.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/schemas/relationalmetadata/1.0/schema.json b/schemas/relationalmetadata/1.0/schema.json index b4c4476d2..cf62a6654 100644 --- a/schemas/relationalmetadata/1.0/schema.json +++ b/schemas/relationalmetadata/1.0/schema.json @@ -5,11 +5,11 @@ "type": "object", "required": [], "properties": { - "relName": { + "name": { "type": "string", "minLength": 5 }, - "relTrackable": { + "optional": { "type": "boolean", "default": false }, @@ -17,7 +17,7 @@ "type": "boolean", "default": false }, - "relScore": { + "score": { "type": "number" }, "keywords": { From aa2246cd2fc661fa45ed226665d7b9c427a10215 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Wed, 30 Nov 2022 17:30:18 +0530 Subject: [PATCH 162/490] Issue #KN-579 feat: Properties update --- .../src/test/scala/org/sunbird/managers/TestHierarchy.scala | 2 +- .../test/scala/org/sunbird/managers/TestUpdateHierarchy.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/content-api/hierarchy-manager/src/test/scala/org/sunbird/managers/TestHierarchy.scala b/content-api/hierarchy-manager/src/test/scala/org/sunbird/managers/TestHierarchy.scala index b32920743..ae34a5181 100644 --- a/content-api/hierarchy-manager/src/test/scala/org/sunbird/managers/TestHierarchy.scala +++ b/content-api/hierarchy-manager/src/test/scala/org/sunbird/managers/TestHierarchy.scala @@ -53,7 +53,7 @@ class TestHierarchy extends BaseSpec { request.put("rootId", "do_11283193441064550414") request.put("unitId", "do_11283193463014195215") request.put("children", util.Arrays.asList("do_11340096165525094411")) - request.put("relationalMetadata",mapAsJavaMap(Map("do_11340096165525094411" -> Map("relName" -> "Test Name RM", "keywords" -> Array("Overwriting content Keywords") )))) + request.put("relationalMetadata",mapAsJavaMap(Map("do_11340096165525094411" -> Map("name" -> "Test Name RM", "keywords" -> Array("Overwriting content Keywords") )))) request.put("mode","edit") val future = HierarchyManager.addLeafNodesToHierarchy(request) future.map(response => { diff --git a/content-api/hierarchy-manager/src/test/scala/org/sunbird/managers/TestUpdateHierarchy.scala b/content-api/hierarchy-manager/src/test/scala/org/sunbird/managers/TestUpdateHierarchy.scala index 205f71858..73b77c353 100644 --- a/content-api/hierarchy-manager/src/test/scala/org/sunbird/managers/TestUpdateHierarchy.scala +++ b/content-api/hierarchy-manager/src/test/scala/org/sunbird/managers/TestUpdateHierarchy.scala @@ -394,7 +394,7 @@ class TestUpdateHierarchy extends BaseSpec { " \t\"b9a50833-eff6-4ef5-a2a4-2413f2d51f6c\": {\n"+ " \t\t\"root\": false,\n"+ " \t\t\"children\": [\"do_31250856200414822416938\",\"do_11340096165525094411\"],\n"+ - " \t\t\"relationalMetadata\": {\n\"do_11340096165525094411\": {\n\"relName\": \"abc\"\n,\"keywords\": [\"test\"]}\n}" + + " \t\t\"relationalMetadata\": {\n\"do_11340096165525094411\": {\n\"name\": \"abc\"\n,\"keywords\": [\"test\"]}\n}" + " \t}\n"+ " }" JsonUtils.deserialize(hierarchyString, classOf[util.HashMap[String, AnyRef]]) From a5495e0fc9dfdf78ff944e9da8771b5141ab0a7c Mon Sep 17 00:00:00 2001 From: anilgupta Date: Wed, 30 Nov 2022 19:16:44 +0530 Subject: [PATCH 163/490] Issue #KN-439 fix: Fixed update scenario where response is return with metadata without reading it again. --- .../src/main/scala/org/sunbird/graph/GraphService.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/GraphService.scala b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/GraphService.scala index c931095f8..26afb68de 100644 --- a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/GraphService.scala +++ b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/GraphService.scala @@ -19,7 +19,7 @@ class GraphService { val metadata = CSPMetaUtil.updateRelativePath(node.getMetadata) node.setMetadata(metadata) } - NodeAsyncOperations.addNode(graphId, node) + NodeAsyncOperations.addNode(graphId, node).map(resNode => if(isrRelativePathEnabled) CSPMetaUtil.updateAbsolutePath(resNode) else resNode) } def upsertNode(graphId: String, node: Node, request: Request): Future[Node] = { @@ -27,11 +27,11 @@ class GraphService { val metadata = CSPMetaUtil.updateRelativePath(node.getMetadata) node.setMetadata(metadata) } - NodeAsyncOperations.upsertNode(graphId, node, request) + NodeAsyncOperations.upsertNode(graphId, node, request).map(resNode => if(isrRelativePathEnabled) CSPMetaUtil.updateAbsolutePath(resNode) else resNode) } def upsertRootNode(graphId: String, request: Request): Future[Node] = { - NodeAsyncOperations.upsertRootNode(graphId, request) + NodeAsyncOperations.upsertRootNode(graphId, request).map(resNode => if(isrRelativePathEnabled) CSPMetaUtil.updateAbsolutePath(resNode) else resNode) } def getNodeByUniqueId(graphId: String, nodeId: String, getTags: Boolean, request: Request): Future[Node] = { From 0f201712304c9f6ee0db11b94e158f8063061864 Mon Sep 17 00:00:00 2001 From: anilgupta Date: Wed, 30 Nov 2022 19:49:40 +0530 Subject: [PATCH 164/490] Issue #KN-439 test: Fixed update scenario where response is return with metadata without reading it again. --- .../src/main/scala/org/sunbird/graph/GraphService.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/GraphService.scala b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/GraphService.scala index 26afb68de..d555e8ff5 100644 --- a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/GraphService.scala +++ b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/GraphService.scala @@ -31,7 +31,7 @@ class GraphService { } def upsertRootNode(graphId: String, request: Request): Future[Node] = { - NodeAsyncOperations.upsertRootNode(graphId, request).map(resNode => if(isrRelativePathEnabled) CSPMetaUtil.updateAbsolutePath(resNode) else resNode) + NodeAsyncOperations.upsertRootNode(graphId, request) } def getNodeByUniqueId(graphId: String, nodeId: String, getTags: Boolean, request: Request): Future[Node] = { From 8fb207926864731acb788eb19168957e6eb2e37f Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Thu, 1 Dec 2022 17:07:32 +0530 Subject: [PATCH 165/490] Issue #KN-439 merge: csp-migration to release-5.2.0 (#900) * Issue #KN-439 fix: Fixed saveExternalRelativePath issue for ArrayList * Issue #KN-439 feat: Added the variable for relative_path_prefix Co-authored-by: anilgupta Co-authored-by: vinukumar-vs --- .../content-service/conf/application.conf | 1 + .../org/sunbird/graph/util/CSPMetaUtil.scala | 39 ++++++++++++------- .../taxonomy-service/conf/application.conf | 3 +- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/content-api/content-service/conf/application.conf b/content-api/content-service/conf/application.conf index 1d4696944..5c189d9a8 100644 --- a/content-api/content-service/conf/application.conf +++ b/content-api/content-service/conf/application.conf @@ -760,6 +760,7 @@ cloud_storage_container="" cloud_storage_endpoint="" cloudstorage.metadata.replace_absolute_path=false +cloudstorage.relative_path_prefix= "CONTENT_STORAGE_BASE_PATH" cloudstorage.read_base_path="https://sunbirddev.blob.core.windows.net" cloudstorage.write_base_path=["https://sunbirddev.blob.core.windows.net"] cloudstorage.metadata.list=["appIcon","posterImage","artifactUrl","downloadUrl","variants","previewUrl","pdfUrl", "streamingUrl", "toc_url"] diff --git a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala index 9951591b6..53ee8fd9b 100644 --- a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala +++ b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala @@ -13,13 +13,15 @@ object CSPMetaUtil { private[this] val logger = LoggerFactory.getLogger(classOf[CSPMetaUtil]) def updateAbsolutePath(data: java.util.Map[String, AnyRef]): java.util.Map[String, AnyRef] = { + logger.info("CSPMetaUtil ::: updateAbsolutePath util.Map[String, AnyRef] ::: data before url replace :: " + data) + val relativePathPrefix: String = Platform.getString("cloudstorage.relative_path_prefix", "") val cspMeta = Platform.getStringList("cloudstorage.metadata.list", new java.util.ArrayList[String]()).asScala.toList val absolutePath = Platform.getString("cloudstorage.read_base_path", "") + java.io.File.separator + Platform.getString("cloud_storage_container", "") if (MapUtils.isNotEmpty(data)) { val updatedMeta: java.util.Map[String, AnyRef] = new java.util.HashMap[String, AnyRef] data.asScala.map(x => if (cspMeta.contains(x._1)) - updatedMeta.put(x._1, x._2.asInstanceOf[String].replace("CLOUD_STORAGE_BASE_PATH", absolutePath)) + updatedMeta.put(x._1, x._2.asInstanceOf[String].replace(relativePathPrefix, absolutePath)) else updatedMeta.put(x._1, x._2) ).asJava updatedMeta @@ -39,13 +41,14 @@ object CSPMetaUtil { } def updateAbsolutePath(property: Property): Property = { + val relativePathPrefix: String = Platform.getString("cloudstorage.relative_path_prefix", "") val cspMeta = Platform.getStringList("cloudstorage.metadata.list", new java.util.ArrayList[String]()) val absolutePath = Platform.getString("cloudstorage.read_base_path", "") + java.io.File.separator + Platform.getString("cloud_storage_container", "") if(cspMeta.contains(property.getPropertyName)) { val value = property.getPropertyValue value match { case str: String => - property.setPropertyValue(str.replace("CLOUD_STORAGE_BASE_PATH", absolutePath)) + property.setPropertyValue(str.replace(relativePathPrefix, absolutePath)) case _ => } } @@ -54,10 +57,11 @@ object CSPMetaUtil { def updateRelativePath(data: java.util.Map[String, AnyRef]): java.util.Map[String, AnyRef] = { logger.info("CSPMetaUtil ::: updateRelativePath util.Map[String, AnyRef] ::: data before url replace :: " + data) + val relativePathPrefix: String = Platform.getString("cloudstorage.relative_path_prefix", "") val cspMeta: java.util.List[String] = Platform.getStringList("cloudstorage.metadata.list", new java.util.ArrayList[String]()) val validCSPSource: List[String] = Platform.getStringList("cloudstorage.write_base_path", new java.util.ArrayList[String]()).asScala.toList val basePaths: Array[String] = validCSPSource.map(source => source + java.io.File.separator + Platform.getString("cloud_storage_container", "")).toArray - val repArray = getReplacementData(basePaths, "CLOUD_STORAGE_BASE_PATH") + val repArray = getReplacementData(basePaths, relativePathPrefix) val result = if (MapUtils.isNotEmpty(data)) { val updatedMeta: java.util.Map[String, AnyRef] = new java.util.HashMap[String, AnyRef] data.asScala.map(x => @@ -73,13 +77,24 @@ object CSPMetaUtil { def saveExternalRelativePath(data: java.util.Map[String, AnyRef]): java.util.Map[String, AnyRef] = { logger.info("CSPMetaUtil ::: saveExternalRelativePath util.Map[String, AnyRef] ::: data before url replace :: " + data) + val relativePathPrefix: String = Platform.getString("cloudstorage.relative_path_prefix", "") val validCSPSource: List[String] = Platform.getStringList("cloudstorage.write_base_path", new java.util.ArrayList[String]()).asScala.toList val basePaths: Array[String] = validCSPSource.map(source => source + java.io.File.separator + Platform.getString("cloud_storage_container", "")).toArray - val repArray = getReplacementData(basePaths, "CLOUD_STORAGE_BASE_PATH") + val repArray = getReplacementData(basePaths, relativePathPrefix) val updatedData: java.util.Map[String, AnyRef] = new java.util.HashMap[String, AnyRef] - data.asScala.map(field => { - updatedData.put(field._1, StringUtils.replaceEach(field._2.asInstanceOf[String], basePaths, repArray).asInstanceOf[AnyRef]) + data.asScala.map(col => { + col._2 match { + case x: List[AnyRef] => { + val output = x.map(value => StringUtils.replaceEach(value.asInstanceOf[String], basePaths, repArray)) + updatedData.put(col._1, output) + } + case y: java.util.List[AnyRef] => { + val output = y.asScala.toList.map(value => StringUtils.replaceEach(value.asInstanceOf[String], basePaths, repArray)) + updatedData.put(col._1, output) + } + case _ => updatedData.put(col._1, StringUtils.replaceEach(col._2.asInstanceOf[String], basePaths, repArray).asInstanceOf[AnyRef]) + } }).asJava logger.info("CSPMetaUtil ::: saveExternalRelativePath util.Map[String, AnyRef] ::: data after url replace :: " + updatedData) @@ -88,16 +103,14 @@ object CSPMetaUtil { def updateExternalRelativePath(data: java.util.Map[String, AnyRef]): java.util.Map[String, AnyRef] = { logger.info("CSPMetaUtil ::: updateExternalRelativePath util.Map[String, AnyRef] ::: data before url replace :: " + data) + val relativePathPrefix: String = Platform.getString("cloudstorage.relative_path_prefix", "") + val validCSPSource: List[String] = Platform.getStringList("cloudstorage.write_base_path", new java.util.ArrayList[String]()).asScala.toList + val basePaths: Array[String] = validCSPSource.map(source => source + java.io.File.separator + Platform.getString("cloud_storage_container", "")).toArray + val repArray = getReplacementData(basePaths, relativePathPrefix) val values = data.get("values") val updatedValues = values match { - case x: List[AnyRef] => { - val validCSPSource: List[String] = Platform.getStringList("cloudstorage.write_base_path", new java.util.ArrayList[String]()).asScala.toList - val basePaths: Array[String] = validCSPSource.map(source => source + java.io.File.separator + Platform.getString("cloud_storage_container", "")).toArray - val repArray = getReplacementData(basePaths, "CLOUD_STORAGE_BASE_PATH") - - x.map(value => StringUtils.replaceEach(value.asInstanceOf[String], basePaths, repArray)) - } + case x: List[AnyRef] => x.map(value => StringUtils.replaceEach(value.asInstanceOf[String], basePaths, repArray)) case _ => values } diff --git a/taxonomy-api/taxonomy-service/conf/application.conf b/taxonomy-api/taxonomy-service/conf/application.conf index 73b8f5ec5..4c93e3ac8 100644 --- a/taxonomy-api/taxonomy-service/conf/application.conf +++ b/taxonomy-api/taxonomy-service/conf/application.conf @@ -318,7 +318,8 @@ languageCode { platform.language.codes=["as","bn","en","gu","hi","hoc","jun","ka","mai","mr","unx","or","san","sat","ta","te","urd"] objectcategorydefinition.keyspace=dev_category_store -cloudstorage.metadata.replace_absolute_path=true +cloudstorage.metadata.replace_absolute_path=false +cloudstorage.relative_path_prefix= "CONTENT_STORAGE_BASE_PATH" cloudstorage.read_base_path="https://sunbirddev.blob.core.windows.net" cloudstorage.write_base_path=["https://sunbirddev.blob.core.windows.net"] cloudstorage.metadata.list=["appIcon","posterImage","artifactUrl","downloadUrl","variants","previewUrl","pdfUrl", "streamingUrl", "toc_url"] From ac5978f556369aa5f87acfa7bb7a8857af2c7cd4 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Thu, 1 Dec 2022 18:52:30 +0530 Subject: [PATCH 166/490] Issue #KN-427 feat: Test case update --- .../org/sunbird/graph/util/CSPMetaUtil.scala | 16 +- .../src/test/resources/application.conf | 2 + .../sunbird/graph/util/CSPMetaUtilTest.scala | 187 ++++++++++++++++++ 3 files changed, 191 insertions(+), 14 deletions(-) create mode 100644 ontology-engine/graph-core_2.11/src/test/scala/org/sunbird/graph/util/CSPMetaUtilTest.scala diff --git a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala index 53ee8fd9b..2ee5b68f6 100644 --- a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala +++ b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala @@ -82,20 +82,8 @@ object CSPMetaUtil { val basePaths: Array[String] = validCSPSource.map(source => source + java.io.File.separator + Platform.getString("cloud_storage_container", "")).toArray val repArray = getReplacementData(basePaths, relativePathPrefix) - val updatedData: java.util.Map[String, AnyRef] = new java.util.HashMap[String, AnyRef] - data.asScala.map(col => { - col._2 match { - case x: List[AnyRef] => { - val output = x.map(value => StringUtils.replaceEach(value.asInstanceOf[String], basePaths, repArray)) - updatedData.put(col._1, output) - } - case y: java.util.List[AnyRef] => { - val output = y.asScala.toList.map(value => StringUtils.replaceEach(value.asInstanceOf[String], basePaths, repArray)) - updatedData.put(col._1, output) - } - case _ => updatedData.put(col._1, StringUtils.replaceEach(col._2.asInstanceOf[String], basePaths, repArray).asInstanceOf[AnyRef]) - } - }).asJava + val updatedObjString = StringUtils.replaceEach(JsonUtils.serialize(data), basePaths, repArray) + val updatedData = JsonUtils.deserialize(updatedObjString, classOf[java.util.Map[String, AnyRef]]) logger.info("CSPMetaUtil ::: saveExternalRelativePath util.Map[String, AnyRef] ::: data after url replace :: " + updatedData) updatedData diff --git a/ontology-engine/graph-core_2.11/src/test/resources/application.conf b/ontology-engine/graph-core_2.11/src/test/resources/application.conf index 6504a3c99..04889e1dd 100644 --- a/ontology-engine/graph-core_2.11/src/test/resources/application.conf +++ b/ontology-engine/graph-core_2.11/src/test/resources/application.conf @@ -487,7 +487,9 @@ languageCode { platform.language.codes=["as","bn","en","gu","hi","hoc","jun","ka","mai","mr","unx","or","san","sat","ta","te","urd"] objectcategorydefinition.keyspace=category_store +cloud_storage_container="sunbird-content-dev" cloudstorage.metadata.replace_absolute_path=true +cloudstorage.relative_path_prefix= "CONTENT_STORAGE_BASE_PATH" cloudstorage.read_base_path="https://sunbirddev.blob.core.windows.net" cloudstorage.write_base_path=["https://sunbirddev.blob.core.windows.net","https://obj.dev.sunbird.org"] cloudstorage.metadata.list=["appIcon","posterImage","artifactUrl","downloadUrl","variants","previewUrl","pdfUrl", "streamingUrl", "toc_url"] \ No newline at end of file diff --git a/ontology-engine/graph-core_2.11/src/test/scala/org/sunbird/graph/util/CSPMetaUtilTest.scala b/ontology-engine/graph-core_2.11/src/test/scala/org/sunbird/graph/util/CSPMetaUtilTest.scala new file mode 100644 index 000000000..c69bb2bfc --- /dev/null +++ b/ontology-engine/graph-core_2.11/src/test/scala/org/sunbird/graph/util/CSPMetaUtilTest.scala @@ -0,0 +1,187 @@ +package org.sunbird.graph.util + +import org.sunbird.graph.BaseSpec +import org.sunbird.graph.dac.model.Node + +import java.util + +class CSPMetaUtilTest extends BaseSpec { + + "saveExternalRelativePath" should "return map with relative Paths for question data" in { + val migratedData = CSPMetaUtil.saveExternalRelativePath(getQuestionData()) + assert(migratedData != null && migratedData.toString.contains("CONTENT_STORAGE_BASE_PATH")) + } + + "updateExternalRelativePath" should "return map with relative Paths" in { + val dataMap: util.HashMap[String, AnyRef] = new util.HashMap[String, AnyRef]() { + { + put("identifier", "do_1234567") + put("values", List("https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/assets/do_1132339274094346241120/test-874-kb.mp4")) + } + } + val migratedData = CSPMetaUtil.updateExternalRelativePath(dataMap) + assert(migratedData != null && migratedData.get("values").toString.contains("CONTENT_STORAGE_BASE_PATH")) + } + + "updateRelativePath" should "return map with relative Paths" in { + val migratedData = CSPMetaUtil.updateRelativePath(getNode.getMetadata) + assert(migratedData != null && migratedData.toString.contains("CONTENT_STORAGE_BASE_PATH")) + } + + "updateAbsolutePath" should "return map with relative Paths" in { + val migratedData = CSPMetaUtil.updateAbsolutePath(new util.ArrayList[Node]() { + add(getRelativeNode()) + }) + assert(migratedData != null && migratedData.get(0).getMetadata.toString.contains("https://sunbirddev.blob.core.windows.net/sunbird-content-dev")) +} + + def getReplaceData(): String = { + """{"ownershipType": ["createdBy"],"subject": ["Mathematics"],"channel": "0126825293972439041","organisation": ["Sunbird"], + |"language": ["English"],"mimeType": "application/vnd.ekstep.content-collection","objectType": "Content","gradeLevel": ["Class 4"], + |"primaryCategory": "Digital Textbook","children": [{"ownershipType": ["createdBy"],"parent": "do_1132828073514926081518","code": "do_1132828084877148161531", + |"keywords": [],"credentials": {"enabled": "No"},"channel": "0126825293972439041","description": "This chapter describes about human body", + |"language": ["English"],"mimeType": "application/vnd.ekstep.content-collection","idealScreenSize": "normal","createdOn": "2021-05-20T08:58:33.470+0000", + |"objectType": "Content","primaryCategory": "Textbook Unit","children": [{"ownershipType": ["createdBy"],"parent": "do_1132833371215462401716", + |"code": "do_1132828084876574721523","keywords": [],"credentials": {"enabled": "No"},"channel": "0126825293972439041", + |"description": "This section describes about various part of the body such as head, hands, legs etc.","language": ["English"], + |"mimeType": "application/vnd.ekstep.content-collection","idealScreenSize": "normal","createdOn": "2021-05-20T08:58:33.466+0000", + |"objectType": "Content","primaryCategory": "Textbook Unit","children": [{"ownershipType": ["createdBy"],"parent": "do_1132833371215134721712", + |"code": "do_1132828084876738561525","keywords": ["legs etc."],"credentials": {"enabled": "No"},"channel": "0126825293972439041","description": "xyz", + |"language": ["English"],"mimeType": "application/vnd.ekstep.content-collection","idealScreenSize": "normal","createdOn": "2021-05-20T08:58:33.475+0000", + |"objectType": "Content","primaryCategory": "Textbook Unit","children": [{"ownershipType": ["createdBy"],"parent": "do_1132833371215872001720", + |"previewUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/assets/do_1132339274094346241120/test-874-kb.mp4", + |"channel": "b00bc992ef25f1a9a8d63291e20efc8d", + |"downhttps://sunbirddev.blob.core.windows.netloadUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_1132339274094346241120/untitled-content_1616331971279_do_1132339274094346241120_14.0.ecar", + |"organisation": ["Sunbird"],"language": ["English"],"mimeType": "video/mp4","variants": {"spine": + |{"ecarUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_1132339274094346241120/untitled-content_1616331975047_do_1132339274094346241120_14.0_spine.ecar","size": 1381.0}},"objectType": "Content","primaryCategory": "Learning Resource","appId": "local.sunbird.portal","contentEncoding": "identity","artifactUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/assets/do_1132339274094346241120/test-874-kb.mp4", + |"lockKey": "34a029c4-ac81-4934-9792-11b7a57d6c13","sYS_INTERNAL_LAST_UPDATED_ON": "2021-03-21T13:20:24.579+0000","contentType": "Resource", + |"trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_1132339274094346241120","audience": ["Student"],"visibility": "Default", + |"consumerId": "273f3b18-5dda-4a27-984a-060c7cd398d3","index": 1,"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"], + |"lastPublishedBy": "8454cb21-3ce9-4e30-85b5-fade097880d8","version": 2,"license": "CC BY 4.0","prevState": "Live","size": 849897.0, + |"lastPublishedOn": "2021-03-21T13:06:11.272+0000","name": "Untitled Content","status": "Live","code": "a88b0257-670b-455b-98b8-6e359ebac009","credentials": {"enabled": "No"},"prevStatus": "Processing","description": "updated","streamingUrl": "https://sunbirddevmedia-inct.streaming.media.azure.net/9c0ebb33-af08-403f-afb4-eb24749f40a1/test-874-kb.ism/manifest(format=m3u8-aapl-v3)","idealScreenSize": "normal","createdOn": "2021-03-11T13:34:14.475+0000","contentDisposition": "inline","lastUpdatedOn": "2021-03-21T13:06:09.526+0000","dialcodeRequired": "No","lastStatusChangedOn": "2021-03-21T18:36:15.799+0530","createdFor": ["ORG_001"],"creator": "Reviewer User","os": ["All"],"se_FWIds": ["NCFCOPY"],"pkgVersion": 14.0,"versionKey": "1616331969523","idealScreenDensity": "hdpi","framework": "NCFCOPY","depth": 4,"s3Key": "ecar_files/do_1132339274094346241120/untitled-content_1616331971279_do_1132339274094346241120_14.0.ecar","createdBy": "95e4942d-cbe8-477d-aebd-ad8e6de4bfc8","compatibilityLevel": 1,"resourceType": "Learn"},{"ownershipType": ["createdBy"],"parent": "do_1132833371215872001720","unitIdentifiers": ["do_1132239562839900161634"],"copyright": "2021 MIT","previewUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/ecml/do_113223967141863424174-latest","plugins": [{"identifier": "org.sunbird.questionunit.quml","semanticVersion": "1.1"}],"subject": ["Hindi"],"channel": "01309282781705830427","downloadUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_113223967141863424174/esa_1614253812772_do_113223967141863424174_1.0.ecar","language": ["English"],"source": "https://dock.sunbirded.org/api/content/v1/read/do_1132239617341767681638","mimeType": "application/vnd.ekstep.ecml-archive","variants": {"spine": {"ecarUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_113223967141863424174/esa_1614253813394_do_113223967141863424174_1.0_spine.ecar","size": 24293.0}},"objectType": "Content","se_mediums": ["English"],"gradeLevel": ["Class 10"],"primaryCategory": "Exam Question","appId": "dev.dock.portal","contentEncoding": "gzip","artifactUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/do_113223967141863424174/artifact/1614253223147_do_1132239617341767681638.zip","sYS_INTERNAL_LAST_UPDATED_ON": "2021-02-25T11:50:16.032+0000","contentType": "Resource","se_gradeLevels": ["Class 10"],"trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_113223967141863424174","audience": ["Student"],"visibility": "Default","author": "anusha","maxQuestions": 1,"consumerId": "b7054510-3ca4-49fd-b373-b100b3f65e18","index": 2,"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"],"lastPublishedBy": "5a587cc1-e018-4859-a0a8-e842650b9d64","version": 2,"se_subjects": ["Hindi"],"license": "CC BY 4.0","prevState": "Review","size": 384798.0,"lastPublishedOn": "2021-02-25T11:50:12.771+0000","name": "esa","topic": ["तोप"],"status": "Live","code": "d19f43ce-753d-2c70-a9fd-70302af424a0","credentials": {"enabled": "No"},"prevStatus": "Processing","origin": "do_1132239617341767681638","streamingUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/ecml/do_113223967141863424174-latest","medium": ["English"],"idealScreenSize": "normal","createdOn": "2021-02-25T11:50:01.500+0000","se_boards": ["CBSE"],"processId": "2b00cca7-42d5-4e35-aa03-1c22dfe03de8","contentDisposition": "inline","lastUpdatedOn": "2021-02-25T11:50:12.678+0000","originData": {"identifier": "do_1132239617341767681638","repository": "https://dock.sunbirded.org/api/content/v1/read/do_1132239617341767681638"},"collectionId": "do_1132239562836049921627","dialcodeRequired": "No","editorVersion": 3,"lastStatusChangedOn": "2021-02-25T11:50:16.017+0000","creator": "anusha","os": ["All"],"questionCategories": ["SA"],"cloudStorageKey": "content/do_113223967141863424174/artifact/1614253223147_do_1132239617341767681638.zip","se_FWIds": ["ekstep_ncert_k-12"],"marks": "12","bloomsLevel": ["Apply"],"pkgVersion": 1.0,"versionKey": "1614253812678","idealScreenDensity": "hdpi","framework": "ekstep_ncert_k-12","depth": 4,"s3Key": "ecar_files/do_113223967141863424174/esa_1614253812772_do_113223967141863424174_1.0.ecar","lastSubmittedOn": "2021-02-25T11:50:11.539+0000","createdBy": "19ba0e4e-9285-4335-8dd0-f674bf03fa4d","se_topics": ["तोप"],"compatibilityLevel": 1,"itemSetPreviewUrl": "https://dockstorage.blob.core.windows.net/sunbird-content-dock/content/do_1132239617341767681638/artifact/do_1132239617341767681638_1614253222002.pdf","board": "CBSE","programId": "463cfa30-775c-11eb-8c56-93946e419809"}],"contentDisposition": "inline","lastUpdatedOn": "2021-05-20T08:58:33.475+0000","contentEncoding": "gzip","contentType": "TextBookUnit","dialcodeRequired": "Yes","identifier": "do_1132833371215872001720","lastStatusChangedOn": "2021-05-20T08:58:33.475+0000","audience": ["Student"],"os": ["All"],"visibility": "Parent","discussionForum": {"enabled": "Yes"},"index": 1,"mediaType": "content","osId": "org.ekstep.launcher","languageCode": ["en"],"version": 2,"versionKey": "1621501113475","license": "CC BY 4.0","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 3,"compatibilityLevel": 1,"name": "5.1.1 Key parts in the head","topic": [],"status": "Draft"},{"ownershipType": ["createdBy"],"parent": "do_1132833371215134721712","code": "do_1132828084876165121519","keywords": [],"credentials": {"enabled": "No"},"channel": "0126825293972439041","description": "","language": ["English"],"mimeType": "application/vnd.ekstep.content-collection","idealScreenSize": "normal","createdOn": "2021-05-20T08:58:33.473+0000","objectType": "Content","primaryCategory": "Textbook Unit","contentDisposition": "inline","lastUpdatedOn": "2021-05-20T08:58:33.473+0000","contentEncoding": "gzip","contentType": "TextBookUnit","dialcodeRequired": "No","identifier": "do_1132833371215708161718","lastStatusChangedOn": "2021-05-20T08:58:33.473+0000","audience": ["Student"],"os": ["All"],"visibility": "Parent","discussionForum": {"enabled": "Yes"},"index": 2,"mediaType": "content","osId": "org.ekstep.launcher","languageCode": ["en"],"version": 2,"versionKey": "1621501113473","license": "CC BY 4.0","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 3,"compatibilityLevel": 1,"name": "5.1.2 Other parts","topic": [],"status": "Draft"},{"ownershipType": ["createdBy"],"parent": "do_1132833371215134721712","unitIdentifiers": ["do_11323721176414617611924"],"copyright": "2021 MIT","organisationId": "e7328d77-42a7-44c8-84f4-8cfea235f07d","previewUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/ecml/do_1132372524622561281279-latest","plugins": [{"identifier": "org.sunbird.questionunit.quml","semanticVersion": "1.1"}],"subject": ["Mathematics"],"channel": "01309282781705830427","downloadUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_1132372524622561281279/untitled_1615875562931_do_1132372524622561281279_1.0.ecar","language": ["English"],"source": "https://dock.sunbirded.org/api/content/v1/read/do_11323724954450329611930","mimeType": "application/vnd.ekstep.ecml-archive","variants": {"spine": {"ecarUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_1132372524622561281279/untitled_1615875563539_do_1132372524622561281279_1.0_spine.ecar","size": 19563.0}},"objectType": "Content","se_mediums": ["English"],"gradeLevel": ["Class 5"],"primaryCategory": "Exam Question","appId": "dev.dock.portal","contentEncoding": "gzip","artifactUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/do_1132372524622561281279/artifact/1615875430184_do_11323724954450329611930.zip","sYS_INTERNAL_LAST_UPDATED_ON": "2021-03-16T06:19:26.162+0000","contentType": "Resource","se_gradeLevels": ["Class 5"],"trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_1132372524622561281279","audience": ["Student"],"visibility": "Default","author": "N18","maxQuestions": 1,"consumerId": "f73cfcc5-4d43-4fa0-8b81-46166c81bc2b","learningOutcome": ["identify the need to find area and perimeter of rectangle and square."],"index": 3,"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"],"lastPublishedBy": "5a587cc1-e018-4859-a0a8-e842650b9d64","version": 2,"se_subjects": ["Mathematics"],"license": "CC BY 4.0","prevState": "Review","size": 374996.0,"lastPublishedOn": "2021-03-16T06:19:22.931+0000","name": "Untitled","topic": ["Speed, Distance and Time"],"status": "Live","code": "2544c8b8-7946-b6c0-e1c7-ced4aee4ea8c","credentials": {"enabled": "No"},"prevStatus": "Processing","origin": "do_11323724954450329611930","streamingUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/ecml/do_1132372524622561281279-latest","medium": ["English"],"idealScreenSize": "normal","createdOn": "2021-03-16T06:19:04.712+0000","se_boards": ["CBSE"],"processId": "9995e013-a7c9-4da1-b2c9-2f59da33414a","contentDisposition": "inline","lastUpdatedOn": "2021-03-16T06:19:20.817+0000","originData": {"identifier": "do_11323724954450329611930","repository": "https://dock.sunbirded.org/api/content/v1/read/do_11323724954450329611930"},"collectionId": "do_11323721176353996811921","dialcodeRequired": "No","editorVersion": 3,"lastStatusChangedOn": "2021-03-16T06:19:26.147+0000","creator": "N18","os": ["All"],"questionCategories": ["MTF"],"cloudStorageKey": "content/do_1132372524622561281279/artifact/1615875430184_do_11323724954450329611930.zip","se_FWIds": ["ekstep_ncert_k-12"],"marks": "2","bloomsLevel": ["Apply"],"pkgVersion": 1.0,"versionKey": "1615875560817","idealScreenDensity": "hdpi","framework": "ekstep_ncert_k-12","depth": 3,"s3Key": "ecar_files/do_1132372524622561281279/untitled_1615875562931_do_1132372524622561281279_1.0.ecar","lastSubmittedOn": "2021-03-16T06:19:17.005+0000","createdBy": "60f91e9e-34ee-4f9f-a907-d312d0e8063e","se_topics": ["Speed, Distance and Time"],"compatibilityLevel": 1,"itemSetPreviewUrl": "https://dockstorage.blob.core.windows.net/sunbird-content-dock/content/do_11323724954450329611930/artifact/do_11323724954450329611930_1615875429226.pdf","board": "CBSE","programId": "800eb440-8613-11eb-a663-4f63bbe94184"}],"contentDisposition": "inline","lastUpdatedOn": "2021-05-20T08:58:33.466+0000","contentEncoding": "gzip","contentType": "TextBookUnit","dialcodeRequired": "Yes","identifier": "do_1132833371215134721712","lastStatusChangedOn": "2021-05-20T08:58:33.466+0000","audience": ["Student"],"os": ["All"],"visibility": "Parent","discussionForum": {"enabled": "Yes"},"index": 1,"mediaType": "content","osId": "org.ekstep.launcher","languageCode": ["en"],"version": 2,"versionKey": "1621501113466","license": "CC BY 4.0","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 2,"compatibilityLevel": 1,"name": "5.1 Parts of Body","topic": ["Role Of The Sense Organs"],"status": "Draft"},{"ownershipType": ["createdBy"],"parent": "do_1132833371215462401716","code": "do_1132828084877066241529","keywords": [],"credentials": {"enabled": "No"},"channel": "0126825293972439041","description": "","language": ["English"],"mimeType": "application/vnd.ekstep.content-collection","idealScreenSize": "normal","createdOn": "2021-05-20T08:58:33.476+0000","objectType": "Content","primaryCategory": "Textbook Unit","children": [{"ownershipType": ["createdBy"],"parent": "do_1132833371215953921722","code": "do_1132828084876492801521","keywords": ["test key","check"],"credentials": {"enabled": "No"},"channel": "0126825293972439041","description": "","language": ["English"],"mimeType": "application/vnd.ekstep.content-collection","idealScreenSize": "normal","createdOn": "2021-05-20T08:58:33.468+0000","objectType": "Content","primaryCategory": "Textbook Unit","children": [{"ownershipType": ["createdBy"],"parent": "do_1132833371215298561714","code": "do_1132828084876820481527","keywords": ["abcd","cgf"],"credentials": {"enabled": "No"},"channel": "0126825293972439041","description": "labeled new","language": ["English"],"mimeType": "application/vnd.ekstep.content-collection","idealScreenSize": "normal","createdOn": "2021-05-20T08:58:33.464+0000","objectType": "Content","primaryCategory": "Textbook Unit","children": [{"ownershipType": ["createdBy"],"parent": "do_1132833371214970881710","previewUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/assets/do_1132338069147811841118/test-874-kb.mp4","channel": "b00bc992ef25f1a9a8d63291e20efc8d","downloadUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_1132338069147811841118/untitled-content_1615468830522_do_1132338069147811841118_2.0.ecar","organisation": ["Sunbird"],"language": ["English"],"mimeType": "video/mp4","variants": {"spine": {"ecarUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_1132338069147811841118/untitled-content_1615468834470_do_1132338069147811841118_2.0_spine.ecar","size": 1361.0}},"objectType": "Content","primaryCategory": "Learning Resource","appId": "dev.sunbird.portal","contentEncoding": "identity","artifactUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/assets/do_1132338069147811841118/test-874-kb.mp4","lockKey": "d73707c8-9999-4fc9-9b34-0207f74faf43","sYS_INTERNAL_LAST_UPDATED_ON": "2021-03-12T08:10:31.335+0000","contentType": "Resource","trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_1132338069147811841118","audience": ["Student"],"visibility": "Default","consumerId": "273f3b18-5dda-4a27-984a-060c7cd398d3","index": 1,"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"],"lastPublishedBy": "8454cb21-3ce9-4e30-85b5-fade097880d8","version": 2,"license": "CC BY 4.0","prevState": "Live","size": 849876.0,"lastPublishedOn": "2021-03-11T13:20:30.514+0000","name": "Untitled Content","status": "Live","code": "9deb2c69-7240-472a-98e7-ed438e76262b","credentials": {"enabled": "No"},"prevStatus": "Processing","streamingUrl": "https://sunbirddevmedia-inct.streaming.media.azure.net/f17bccc5-cab3-4da8-a5eb-11d7211f1507/test-874-kb.ism/manifest(format=m3u8-aapl-v3)","idealScreenSize": "normal","createdOn": "2021-03-11T09:29:05.654+0000","contentDisposition": "inline","lastUpdatedOn": "2021-03-11T13:20:28.256+0000","dialcodeRequired": "No","lastStatusChangedOn": "2021-03-11T18:50:28.256+0530","createdFor": ["ORG_001"],"creator": "Reviewer User","os": ["All"],"se_FWIds": ["NCFCOPY"],"pkgVersion": 2.0,"versionKey": "1615455090358","idealScreenDensity": "hdpi","framework": "NCFCOPY","depth": 5,"s3Key": "ecar_files/do_1132338069147811841118/untitled-content_1615468830522_do_1132338069147811841118_2.0.ecar","createdBy": "95e4942d-cbe8-477d-aebd-ad8e6de4bfc8","compatibilityLevel": 1,"resourceType": "Learn"},{"ownershipType": ["createdBy"],"parent": "do_1132833371214970881710","unitIdentifiers": ["do_1132238266042040321422"],"copyright": "2021 MIT","previewUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/ecml/do_11322383952751820816-latest","plugins": [{"identifier": "org.sunbird.questionunit.quml","semanticVersion": "1.1"}],"subject": ["Hindi"],"channel": "01309282781705830427","downloadUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_11322383952751820816/sapractice_1614238238045_do_11322383952751820816_1.0.ecar","language": ["English"],"source": "https://dock.sunbirded.org/api/content/v1/read/do_1132238287156183041424","mimeType": "application/vnd.ekstep.ecml-archive","variants": {"spine": {"ecarUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_11322383952751820816/sapractice_1614238238800_do_11322383952751820816_1.0_spine.ecar","size": 13171.0}},"objectType": "Content","se_mediums": ["English"],"gradeLevel": ["Class 10"],"primaryCategory": "Exam Question","appId": "dev.dock.portal","contentEncoding": "gzip","artifactUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/do_11322383952751820816/artifact/1614237122171_do_1132238287156183041424.zip","sYS_INTERNAL_LAST_UPDATED_ON": "2021-02-25T07:30:44.916+0000","contentType": "Resource","se_gradeLevels": ["Class 10"],"trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_11322383952751820816","audience": ["Student"],"visibility": "Default","author": "anusha","maxQuestions": 1,"consumerId": "273f3b18-5dda-4a27-984a-060c7cd398d3","index": 2,"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"],"lastPublishedBy": "5a587cc1-e018-4859-a0a8-e842650b9d64","version": 2,"se_subjects": ["Hindi"],"license": "CC BY 4.0","prevState": "Review","size": 362236.0,"lastPublishedOn": "2021-02-25T07:30:38.043+0000","name": "sa:practice","status": "Live","code": "f239c77e-ed71-9133-0145-7468a92bce79","credentials": {"enabled": "No"},"prevStatus": "Processing","origin": "do_1132238287156183041424","streamingUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/ecml/do_11322383952751820816-latest","medium": ["English"],"idealScreenSize": "normal","createdOn": "2021-02-25T07:30:23.577+0000","se_boards": ["CBSE"],"processId": "04d5aec9-ed09-4a57-963d-9fa654fecf8d","contentDisposition": "inline","lastUpdatedOn": "2021-02-25T07:30:37.956+0000","originData": {"identifier": "do_1132238287156183041424","repository": "https://dock.sunbirded.org/api/content/v1/read/do_1132238287156183041424"},"collectionId": "do_1132238266036551681415","dialcodeRequired": "No","editorVersion": 3,"lastStatusChangedOn": "2021-02-25T07:30:44.908+0000","creator": "anusha","os": ["All"],"questionCategories": ["SA"],"cloudStorageKey": "content/do_11322383952751820816/artifact/1614237122171_do_1132238287156183041424.zip","se_FWIds": ["ekstep_ncert_k-12"],"marks": "12","pkgVersion": 1.0,"versionKey": "1614238237956","idealScreenDensity": "hdpi","framework": "ekstep_ncert_k-12","depth": 5,"s3Key": "ecar_files/do_11322383952751820816/sapractice_1614238238045_do_11322383952751820816_1.0.ecar","lastSubmittedOn": "2021-02-25T07:30:36.709+0000","createdBy": "19ba0e4e-9285-4335-8dd0-f674bf03fa4d","compatibilityLevel": 1,"itemSetPreviewUrl": "https://dockstorage.blob.core.windows.net/sunbird-content-dock/content/do_1132238287156183041424/artifact/do_1132238287156183041424_1614237121022.pdf","board": "CBSE","programId": "94564340-7737-11eb-96e0-29a9f8ed81cf"},{"ownershipType": ["createdBy"],"parent": "do_1132833371214970881710","unitIdentifiers": ["do_11322165488232038412588"],"copyright": "2021 MIT","previewUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/ecml/do_1132216902566133761410-latest","plugins": [{"identifier": "org.sunbird.questionunit.quml","semanticVersion": "1.1"}],"subject": ["Environmental Studies"],"channel": "01309282781705830427","downloadUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_1132216902566133761410/mcqmcq_1613975872529_do_1132216902566133761410_1.0.ecar","language": ["English"],"source": "https://dock.sunbirded.org/api/content/v1/read/do_11322168163282944012605","mimeType": "application/vnd.ekstep.ecml-archive","variants": {"spine": {"ecarUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_1132216902566133761410/mcqmcq_1613975873161_do_1132216902566133761410_1.0_spine.ecar","size": 17182.0}},"objectType": "Content","se_mediums": ["English"],"gradeLevel": ["Class 10"],"primaryCategory": "Exam Question","appId": "dev.dock.portal","contentEncoding": "gzip","artifactUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/do_1132216902566133761410/artifact/1613975740738_do_11322168163282944012605.zip","sYS_INTERNAL_LAST_UPDATED_ON": "2021-02-22T06:37:55.328+0000","contentType": "Resource","se_gradeLevels": ["Class 10"],"trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_1132216902566133761410","audience": ["Student"],"visibility": "Default","author": "color4","maxQuestions": 1,"consumerId": "7411b6bd-89f3-40ec-98d1-229dc64ce77d","learningOutcome": ["Understand the importance of values in life"],"index": 3,"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"],"lastPublishedBy": "ae94b68c-a535-4dce-8e7a-fb9662b0ad68","version": 2,"se_subjects": ["Environmental Studies"],"license": "CC BY 4.0","prevState": "Review","size": 370363.0,"lastPublishedOn": "2021-02-22T06:37:52.529+0000","name": "MCQMCQ","topic": ["Animals"],"status": "Live","code": "0cbae0f8-e3eb-1d31-e2e5-0337dc7d697d","credentials": {"enabled": "No"},"prevStatus": "Processing","origin": "do_11322168163282944012605","streamingUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/ecml/do_1132216902566133761410-latest","medium": ["English"],"idealScreenSize": "normal","createdOn": "2021-02-22T06:37:41.405+0000","se_boards": ["CBSE"],"processId": "fbcec2af-cb7a-4ed1-8683-ff04b475947e","contentDisposition": "inline","lastUpdatedOn": "2021-02-22T06:37:52.447+0000","originData": {"identifier": "do_11322168163282944012605","repository": "https://dock.sunbirded.org/api/content/v1/read/do_11322168163282944012605"},"collectionId": "do_11322165488181248012584","dialcodeRequired": "No","editorVersion": 3,"lastStatusChangedOn": "2021-02-22T06:37:55.314+0000","creator": "color4","os": ["All"],"questionCategories": ["MCQ"],"cloudStorageKey": "content/do_1132216902566133761410/artifact/1613975740738_do_11322168163282944012605.zip","se_FWIds": ["ekstep_ncert_k-12"],"marks": "1","bloomsLevel": ["Understand"],"pkgVersion": 1.0,"versionKey": "1613975872447","idealScreenDensity": "hdpi","framework": "ekstep_ncert_k-12","depth": 5,"s3Key": "ecar_files/do_1132216902566133761410/mcqmcq_1613975872529_do_1132216902566133761410_1.0.ecar","lastSubmittedOn": "2021-02-22T06:37:51.179+0000","createdBy": "0ce5b67e-b48e-489b-a818-e938e8bfc14b","se_topics": ["Animals"],"compatibilityLevel": 1,"itemSetPreviewUrl": "https://dockstorage.blob.core.windows.net/sunbird-content-dock/content/do_11322168163282944012605/artifact/do_11322168163282944012605_1613975739805.pdf","board": "CBSE","programId": "b2433a00-74cd-11eb-9f3c-f39a9ab9f5ce"}],"contentDisposition": "inline","lastUpdatedOn": "2021-05-20T08:58:33.464+0000","contentEncoding": "gzip","contentType": "TextBookUnit","dialcodeRequired": "Yes","identifier": "do_1132833371214970881710","lastStatusChangedOn": "2021-05-20T08:58:33.464+0000","audience": ["Student"],"os": ["All"],"visibility": "Parent","discussionForum": {"enabled": "Yes"},"index": 1,"mediaType": "content","osId": "org.ekstep.launcher","languageCode": ["en"],"version": 2,"versionKey": "1621501113464","license": "CC BY 4.0","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 4,"compatibilityLevel": 1,"name": "dsffgdg","topic": [],"status": "Draft"}],"contentDisposition": "inline","lastUpdatedOn": "2021-05-20T08:58:33.468+0000","contentEncoding": "gzip","contentType": "TextBookUnit","dialcodeRequired": "No","identifier": "do_1132833371215298561714","lastStatusChangedOn": "2021-05-20T08:58:33.468+0000","audience": ["Student"],"os": ["All"],"visibility": "Parent","discussionForum": {"enabled": "Yes"},"index": 1,"mediaType": "content","osId": "org.ekstep.launcher","languageCode": ["en"],"version": 2,"versionKey": "1621501113468","license": "CC BY 4.0","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 3,"compatibilityLevel": 1,"name": "5.2.1 Respiratory System","topic": ["Look and say","Role Of The Sense Organs"],"status": "Draft"}],"contentDisposition": "inline","lastUpdatedOn": "2021-05-20T08:58:33.476+0000","contentEncoding": "gzip","contentType": "TextBookUnit","dialcodeRequired": "No","identifier": "do_1132833371215953921722","lastStatusChangedOn": "2021-05-20T08:58:33.476+0000","audience": ["Student"],"os": ["All"],"visibility": "Parent","discussionForum": {"enabled": "Yes"},"index": 2,"mediaType": "content","osId": "org.ekstep.launcher","languageCode": ["en"],"version": 2,"versionKey": "1621501113476","license": "CC BY 4.0","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 2,"compatibilityLevel": 1,"name": "5.2 Organ Systems","topic": [],"status": "Draft"},{"ownershipType": ["createdBy"],"parent": "do_1132833371215462401716","copyright": "Sunbird","previewUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/assets/do_1132344630588948481134/test-874-kb.mp4","subject": ["Math"],"channel": "b00bc992ef25f1a9a8d63291e20efc8d","downloadUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_1132344630588948481134/untitled-content_1615535618825_do_1132344630588948481134_2.0.ecar","organisation": ["Sunbird"],"showNotification": true,"language": ["English"],"mimeType": "video/mp4","variants": {"spine": {"ecarUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_1132344630588948481134/untitled-content_1615535619590_do_1132344630588948481134_2.0_spine.ecar","size": 35301.0}},"objectType": "Content","se_mediums": ["English"],"gradeLevel": ["Grade 1"],"appIcon": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/do_1132344630588948481134/artifact/2a4b8abd789184932399d222d03d9b5c.thumb.jpg","primaryCategory": "Learning Resource","appId": "dev.sunbird.portal","contentEncoding": "identity","artifactUrl": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/assets/do_1132344630588948481134/test-874-kb.mp4","lockKey": "1d28d983-2704-44bd-803e-5feb4e62da62","sYS_INTERNAL_LAST_UPDATED_ON": "2021-03-12T08:10:34.367+0000","contentType": "Resource","se_gradeLevels": ["Grade 1"],"trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_1132344630588948481134","lastUpdatedBy": "95e4942d-cbe8-477d-aebd-ad8e6de4bfc8","audience": ["Student"],"visibility": "Default","consumerId": "273f3b18-5dda-4a27-984a-060c7cd398d3","index": 3,"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"],"lastPublishedBy": "8454cb21-3ce9-4e30-85b5-fade097880d8","version": 2,"se_subjects": ["Math"],"license": "CC BY 4.0","prevState": "Review","size": 883817.0,"lastPublishedOn": "2021-03-12T07:53:38.825+0000","name": "Untitled Content","status": "Live","code": "8851e754-6e20-44d4-9070-e1a9664163ad","credentials": {"enabled": "No"},"prevStatus": "Review","description": "updated desrciption","streamingUrl": "https://sunbirddevmedia-inct.streaming.media.azure.net/40ae07aa-069e-4056-8f2b-014bc9a2d21b/test-874-kb.ism/manifest(format=m3u8-aapl-v3)","medium": ["English"],"posterImage": "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/do_11299104587967692816/artifact/2a4b8abd789184932399d222d03d9b5c.jpg","idealScreenSize": "normal","createdOn": "2021-03-12T07:44:01.371+0000","se_boards": ["NCERT"],"copyrightYear": 2020,"contentDisposition": "inline","licenseterms": "By creating any type of content (resources, books, courses etc.) on DIKSHA, you consent to publish it under the Creative Commons License Framework. Please choose the applicable creative commons license you wish to apply to your content.","lastUpdatedOn": "2021-03-12T07:53:38.505+0000","dialcodeRequired": "No","lastStatusChangedOn": "2021-03-12T07:53:38.494+0000","createdFor": ["ORG_001"],"creator": "Reviewer User","os": ["All"],"se_FWIds": ["NCFCOPY"],"pkgVersion": 2.0,"versionKey": "1615535618583","idealScreenDensity": "hdpi","framework": "NCFCOPY","depth": 2,"s3Key": "ecar_files/do_1132344630588948481134/untitled-content_1615535618825_do_1132344630588948481134_2.0.ecar","lastSubmittedOn": "2021-03-12T07:53:10.005+0000","createdBy": "95e4942d-cbe8-477d-aebd-ad8e6de4bfc8","compatibilityLevel": 1,"board": "NCERT","resourceType": "Learn"}],"contentDisposition": "inline","lastUpdatedOn": "2021-05-20T08:58:33.470+0000","contentEncoding": "gzip","contentType": "TextBookUnit","dialcodeRequired": "No","identifier": "do_1132833371215462401716","lastStatusChangedOn": "2021-05-20T08:58:33.470+0000","audience": ["Student"],"os": ["All"],"visibility": "Parent","discussionForum": {"enabled": "Yes"},"index": 1,"mediaType": "content","osId": "org.ekstep.launcher","languageCode": ["en"],"version": 2,"versionKey": "1621501113470","license": "CC BY 4.0","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 1,"compatibilityLevel": 1,"name": "5. Human Body","topic": [],"status": "Draft"}],"appId": "dev.sunbird.portal","contentEncoding": "gzip","sYS_INTERNAL_LAST_UPDATED_ON": "2021-05-20T09:12:06.988+0000","contentType": "TextBook","trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_1132828073514926081518","audience": ["Student"],"visibility": "Default","consumerId": "01814e02-fc27-4165-ae53-3d1816e55817","childNodes": ["do_1132339274094346241120","do_1132833371215872001720","do_1132833371215134721712","do_1132833371215462401716","do_113223967141863424174","do_1132833371214970881710","do_1132833371215708161718","do_1132372524622561281279","do_1132338069147811841118","do_1132833371215298561714","do_1132833371215953921722","do_11322383952751820816","do_1132216902566133761410","do_1132344630588948481134"],"discussionForum": {"enabled": "Yes"},"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"],"version": 2,"license": "CC BY 4.0","name": "TestCSVUpload","status": "Draft","code": "org.sunbird.yhqB6L","credentials": {"enabled": "No"},"description": "Enter description for TextBook","medium": ["English"],"idealScreenSize": "normal","createdOn": "2021-05-19T15:00:44.279+0000","contentDisposition": "inline","additionalCategories": ["Textbook"],"lastUpdatedOn": "2021-05-20T07:10:32.805+0000","dialcodeRequired": "No","lastStatusChangedOn": "2021-05-19T15:00:44.279+0000","createdFor": ["0126825293972439041"],"creator": "Book Creator","os": ["All"],"versionKey": "1621501113536","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 0,"createdBy": "8454cb21-3ce9-4e30-85b5-fade097880d8","compatibilityLevel": 1,"userConsent": "Yes","board": "State (Tamil Nadu)","resourceType": "Book"}""".stripMargin + + } + + def getMigratedData(): String = { + s"""{identifier=do_1234567, hierarchy={"ownershipType": ["createdBy"],"subject": ["Mathematics"],"channel": "0126825293972439041","organisation": ["Sunbird"], + |"language": ["English"],"mimeType": "application/vnd.ekstep.content-collection","objectType": "Content","gradeLevel": ["Class 4"], + |"primaryCategory": "Digital Textbook","children": [{"ownershipType": ["createdBy"],"parent": "do_1132828073514926081518","code": "do_1132828084877148161531", + |"keywords": [],"credentials": {"enabled": "No"},"channel": "0126825293972439041","description": "This chapter describes about human body", + |"description": "This section describes about various part of the body such as head, hands, legs etc.","language": ["English"], + |"mimeType": "application/vnd.ekstep.content-collection","idealScreenSize": "normal","createdOn": "2021-05-20T08:58:33.466+0000", + |"objectType": "Content","primaryCategory": "Textbook Unit","children": [{"ownershipType": ["createdBy"],"parent": "do_1132833371215134721712", + |"code": "do_1132828084876738561525","keywords": ["legs etc."],"credentials": {"enabled": "No"},"channel": "0126825293972439041","description": "xyz", + |"language": ["English"],"mimeType": "application/vnd.ekstep.content-collection","idealScreenSize": "normal","createdOn": "2021-05-20T08:58:33.475+0000", + |"objectType": "Content","primaryCategory": "Textbook Unit","children": [{"ownershipType": ["createdBy"],"parent": "do_1132833371215872001720", + |"previewUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/content/assets/do_1132339274094346241120/test-874-kb.mp4", + |"channel": "b00bc992ef25f1a9a8d63291e20efc8d", + |"downhttps://sunbirddev.blob.core.windows.netloadUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/ecar_files/do_1132339274094346241120/untitled-content_1616331971279_do_1132339274094346241120_14.0.ecar", + |"organisation": ["Sunbird"],"language": ["English"],"mimeType": "video/mp4","variants": {"spine": + |{"ecarUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/ecar_files/do_1132339274094346241120/untitled-content_1616331975047_do_1132339274094346241120_14.0_spine.ecar","size": 1381.0}},"objectType": "Content","primaryCategory": "Learning Resource","appId": "local.sunbird.portal","contentEncoding": "identity","artifactUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/content/assets/do_1132339274094346241120/test-874-kb.mp4", + |"lockKey": "34a029c4-ac81-4934-9792-11b7a57d6c13","sYS_INTERNAL_LAST_UPDATED_ON": "2021-03-21T13:20:24.579+0000","contentType": "Resource", + |"trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_1132339274094346241120","audience": ["Student"],"visibility": "Default", + |"consumerId": "273f3b18-5dda-4a27-984a-060c7cd398d3","index": 1,"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"], + |"lastPublishedBy": "8454cb21-3ce9-4e30-85b5-fade097880d8","version": 2,"license": "CC BY 4.0","prevState": "Live","size": 849897.0, + |"lastPublishedOn": "2021-03-21T13:06:11.272+0000","name": "Untitled Content","status": "Live","code": "a88b0257-670b-455b-98b8-6e359ebac009","credentials": {"enabled": "No"},"prevStatus": "Processing","description": "updated","streamingUrl": "https://sunbirddevmedia-inct.streaming.media.azure.net/9c0ebb33-af08-403f-afb4-eb24749f40a1/test-874-kb.ism/manifest(format=m3u8-aapl-v3)","idealScreenSize": "normal","createdOn": "2021-03-11T13:34:14.475+0000","contentDisposition": "inline","lastUpdatedOn": "2021-03-21T13:06:09.526+0000","dialcodeRequired": "No","lastStatusChangedOn": "2021-03-21T18:36:15.799+0530","createdFor": ["ORG_001"],"creator": "Reviewer User","os": ["All"],"se_FWIds": ["NCFCOPY"],"pkgVersion": 14.0,"versionKey": "1616331969523","idealScreenDensity": "hdpi","framework": "NCFCOPY","depth": 4,"s3Key": "ecar_files/do_1132339274094346241120/untitled-content_1616331971279_do_1132339274094346241120_14.0.ecar","createdBy": "95e4942d-cbe8-477d-aebd-ad8e6de4bfc8","compatibilityLevel": 1,"resourceType": "Learn"},{"ownershipType": ["createdBy"],"parent": "do_1132833371215872001720","unitIdentifiers": ["do_1132239562839900161634"],"copyright": "2021 MIT","previewUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/content/ecml/do_113223967141863424174-latest","plugins": [{"identifier": "org.sunbird.questionunit.quml","semanticVersion": "1.1"}],"subject": ["Hindi"],"channel": "01309282781705830427","downloadUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/ecar_files/do_113223967141863424174/esa_1614253812772_do_113223967141863424174_1.0.ecar","language": ["English"],"source": "https://dock.sunbirded.org/api/content/v1/read/do_1132239617341767681638","mimeType": "application/vnd.ekstep.ecml-archive","variants": {"spine": {"ecarUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/ecar_files/do_113223967141863424174/esa_1614253813394_do_113223967141863424174_1.0_spine.ecar","size": 24293.0}},"objectType": "Content","se_mediums": ["English"],"gradeLevel": ["Class 10"],"primaryCategory": "Exam Question","appId": "dev.dock.portal","contentEncoding": "gzip","artifactUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/content/do_113223967141863424174/artifact/1614253223147_do_1132239617341767681638.zip","sYS_INTERNAL_LAST_UPDATED_ON": "2021-02-25T11:50:16.032+0000","contentType": "Resource","se_gradeLevels": ["Class 10"],"trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_113223967141863424174","audience": ["Student"],"visibility": "Default","author": "anusha","maxQuestions": 1,"consumerId": "b7054510-3ca4-49fd-b373-b100b3f65e18","index": 2,"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"],"lastPublishedBy": "5a587cc1-e018-4859-a0a8-e842650b9d64","version": 2,"se_subjects": ["Hindi"],"license": "CC BY 4.0","prevState": "Review","size": 384798.0,"lastPublishedOn": "2021-02-25T11:50:12.771+0000","name": "esa","topic": ["तोप"],"status": "Live","code": "d19f43ce-753d-2c70-a9fd-70302af424a0","credentials": {"enabled": "No"},"prevStatus": "Processing","origin": "do_1132239617341767681638","streamingUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/content/ecml/do_113223967141863424174-latest","medium": ["English"],"idealScreenSize": "normal","createdOn": "2021-02-25T11:50:01.500+0000","se_boards": ["CBSE"],"processId": "2b00cca7-42d5-4e35-aa03-1c22dfe03de8","contentDisposition": "inline","lastUpdatedOn": "2021-02-25T11:50:12.678+0000","originData": {"identifier": "do_1132239617341767681638","repository": "https://dock.sunbirded.org/api/content/v1/read/do_1132239617341767681638"},"collectionId": "do_1132239562836049921627","dialcodeRequired": "No","editorVersion": 3,"lastStatusChangedOn": "2021-02-25T11:50:16.017+0000","creator": "anusha","os": ["All"],"questionCategories": ["SA"],"cloudStorageKey": "content/do_113223967141863424174/artifact/1614253223147_do_1132239617341767681638.zip","se_FWIds": ["ekstep_ncert_k-12"],"marks": "12","bloomsLevel": ["Apply"],"pkgVersion": 1.0,"versionKey": "1614253812678","idealScreenDensity": "hdpi","framework": "ekstep_ncert_k-12","depth": 4,"s3Key": "ecar_files/do_113223967141863424174/esa_1614253812772_do_113223967141863424174_1.0.ecar","lastSubmittedOn": "2021-02-25T11:50:11.539+0000","createdBy": "19ba0e4e-9285-4335-8dd0-f674bf03fa4d","se_topics": ["तोप"],"compatibilityLevel": 1,"itemSetPreviewUrl": "https://dockstorage.blob.core.windows.net/sunbird-content-dock/content/do_1132239617341767681638/artifact/do_1132239617341767681638_1614253222002.pdf","board": "CBSE","programId": "463cfa30-775c-11eb-8c56-93946e419809"}],"contentDisposition": "inline","lastUpdatedOn": "2021-05-20T08:58:33.475+0000","contentEncoding": "gzip","contentType": "TextBookUnit","dialcodeRequired": "Yes","identifier": "do_1132833371215872001720","lastStatusChangedOn": "2021-05-20T08:58:33.475+0000","audience": ["Student"],"os": ["All"],"visibility": "Parent","discussionForum": {"enabled": "Yes"},"index": 1,"mediaType": "content","osId": "org.ekstep.launcher","languageCode": ["en"],"version": 2,"versionKey": "1621501113475","license": "CC BY 4.0","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 3,"compatibilityLevel": 1,"name": "5.1.1 Key parts in the head","topic": [],"status": "Draft"},{"ownershipType": ["createdBy"],"parent": "do_1132833371215134721712","code": "do_1132828084876165121519","keywords": [],"credentials": {"enabled": "No"},"channel": "0126825293972439041","description": "","language": ["English"],"mimeType": "application/vnd.ekstep.content-collection","idealScreenSize": "normal","createdOn": "2021-05-20T08:58:33.473+0000","objectType": "Content","primaryCategory": "Textbook Unit","contentDisposition": "inline","lastUpdatedOn": "2021-05-20T08:58:33.473+0000","contentEncoding": "gzip","contentType": "TextBookUnit","dialcodeRequired": "No","identifier": "do_1132833371215708161718","lastStatusChangedOn": "2021-05-20T08:58:33.473+0000","audience": ["Student"],"os": ["All"],"visibility": "Parent","discussionForum": {"enabled": "Yes"},"index": 2,"mediaType": "content","osId": "org.ekstep.launcher","languageCode": ["en"],"version": 2,"versionKey": "1621501113473","license": "CC BY 4.0","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 3,"compatibilityLevel": 1,"name": "5.1.2 Other parts","topic": [],"status": "Draft"},{"ownershipType": ["createdBy"],"parent": "do_1132833371215134721712","unitIdentifiers": ["do_11323721176414617611924"],"copyright": "2021 MIT","organisationId": "e7328d77-42a7-44c8-84f4-8cfea235f07d","previewUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/content/ecml/do_1132372524622561281279-latest","plugins": [{"identifier": "org.sunbird.questionunit.quml","semanticVersion": "1.1"}],"subject": ["Mathematics"],"channel": "01309282781705830427","downloadUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/ecar_files/do_1132372524622561281279/untitled_1615875562931_do_1132372524622561281279_1.0.ecar","language": ["English"],"source": "https://dock.sunbirded.org/api/content/v1/read/do_11323724954450329611930","mimeType": "application/vnd.ekstep.ecml-archive","variants": {"spine": {"ecarUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/ecar_files/do_1132372524622561281279/untitled_1615875563539_do_1132372524622561281279_1.0_spine.ecar","size": 19563.0}},"objectType": "Content","se_mediums": ["English"],"gradeLevel": ["Class 5"],"primaryCategory": "Exam Question","appId": "dev.dock.portal","contentEncoding": "gzip","artifactUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/content/do_1132372524622561281279/artifact/1615875430184_do_11323724954450329611930.zip","sYS_INTERNAL_LAST_UPDATED_ON": "2021-03-16T06:19:26.162+0000","contentType": "Resource","se_gradeLevels": ["Class 5"],"trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_1132372524622561281279","audience": ["Student"],"visibility": "Default","author": "N18","maxQuestions": 1,"consumerId": "f73cfcc5-4d43-4fa0-8b81-46166c81bc2b","learningOutcome": ["identify the need to find area and perimeter of rectangle and square."],"index": 3,"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"],"lastPublishedBy": "5a587cc1-e018-4859-a0a8-e842650b9d64","version": 2,"se_subjects": ["Mathematics"],"license": "CC BY 4.0","prevState": "Review","size": 374996.0,"lastPublishedOn": "2021-03-16T06:19:22.931+0000","name": "Untitled","topic": ["Speed, Distance and Time"],"status": "Live","code": "2544c8b8-7946-b6c0-e1c7-ced4aee4ea8c","credentials": {"enabled": "No"},"prevStatus": "Processing","origin": "do_11323724954450329611930","streamingUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/content/ecml/do_1132372524622561281279-latest","medium": ["English"],"idealScreenSize": "normal","createdOn": "2021-03-16T06:19:04.712+0000","se_boards": ["CBSE"],"processId": "9995e013-a7c9-4da1-b2c9-2f59da33414a","contentDisposition": "inline","lastUpdatedOn": "2021-03-16T06:19:20.817+0000","originData": {"identifier": "do_11323724954450329611930","repository": "https://dock.sunbirded.org/api/content/v1/read/do_11323724954450329611930"},"collectionId": "do_11323721176353996811921","dialcodeRequired": "No","editorVersion": 3,"lastStatusChangedOn": "2021-03-16T06:19:26.147+0000","creator": "N18","os": ["All"],"questionCategories": ["MTF"],"cloudStorageKey": "content/do_1132372524622561281279/artifact/1615875430184_do_11323724954450329611930.zip","se_FWIds": ["ekstep_ncert_k-12"],"marks": "2","bloomsLevel": ["Apply"],"pkgVersion": 1.0,"versionKey": "1615875560817","idealScreenDensity": "hdpi","framework": "ekstep_ncert_k-12","depth": 3,"s3Key": "ecar_files/do_1132372524622561281279/untitled_1615875562931_do_1132372524622561281279_1.0.ecar","lastSubmittedOn": "2021-03-16T06:19:17.005+0000","createdBy": "60f91e9e-34ee-4f9f-a907-d312d0e8063e","se_topics": ["Speed, Distance and Time"],"compatibilityLevel": 1,"itemSetPreviewUrl": "https://dockstorage.blob.core.windows.net/sunbird-content-dock/content/do_11323724954450329611930/artifact/do_11323724954450329611930_1615875429226.pdf","board": "CBSE","programId": "800eb440-8613-11eb-a663-4f63bbe94184"}],"contentDisposition": "inline","lastUpdatedOn": "2021-05-20T08:58:33.466+0000","contentEncoding": "gzip","contentType": "TextBookUnit","dialcodeRequired": "Yes","identifier": "do_1132833371215134721712","lastStatusChangedOn": "2021-05-20T08:58:33.466+0000","audience": ["Student"],"os": ["All"],"visibility": "Parent","discussionForum": {"enabled": "Yes"},"index": 1,"mediaType": "content","osId": "org.ekstep.launcher","languageCode": ["en"],"version": 2,"versionKey": "1621501113466","license": "CC BY 4.0","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 2,"compatibilityLevel": 1,"name": "5.1 Parts of Body","topic": ["Role Of The Sense Organs"],"status": "Draft"},{"ownershipType": ["createdBy"],"parent": "do_1132833371215462401716","code": "do_1132828084877066241529","keywords": [],"credentials": {"enabled": "No"},"channel": "0126825293972439041","description": "","language": ["English"],"mimeType": "application/vnd.ekstep.content-collection","idealScreenSize": "normal","createdOn": "2021-05-20T08:58:33.476+0000","objectType": "Content","primaryCategory": "Textbook Unit","children": [{"ownershipType": ["createdBy"],"parent": "do_1132833371215953921722","code": "do_1132828084876492801521","keywords": ["test key","check"],"credentials": {"enabled": "No"},"channel": "0126825293972439041","description": "","language": ["English"],"mimeType": "application/vnd.ekstep.content-collection","idealScreenSize": "normal","createdOn": "2021-05-20T08:58:33.468+0000","objectType": "Content","primaryCategory": "Textbook Unit","children": [{"ownershipType": ["createdBy"],"parent": "do_1132833371215298561714","code": "do_1132828084876820481527","keywords": ["abcd","cgf"],"credentials": {"enabled": "No"},"channel": "0126825293972439041","description": "labeled new","language": ["English"],"mimeType": "application/vnd.ekstep.content-collection","idealScreenSize": "normal","createdOn": "2021-05-20T08:58:33.464+0000","objectType": "Content","primaryCategory": "Textbook Unit","children": [{"ownershipType": ["createdBy"],"parent": "do_1132833371214970881710","previewUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/content/assets/do_1132338069147811841118/test-874-kb.mp4","channel": "b00bc992ef25f1a9a8d63291e20efc8d","downloadUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/ecar_files/do_1132338069147811841118/untitled-content_1615468830522_do_1132338069147811841118_2.0.ecar","organisation": ["Sunbird"],"language": ["English"],"mimeType": "video/mp4","variants": {"spine": {"ecarUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/ecar_files/do_1132338069147811841118/untitled-content_1615468834470_do_1132338069147811841118_2.0_spine.ecar","size": 1361.0}},"objectType": "Content","primaryCategory": "Learning Resource","appId": "dev.sunbird.portal","contentEncoding": "identity","artifactUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/content/assets/do_1132338069147811841118/test-874-kb.mp4","lockKey": "d73707c8-9999-4fc9-9b34-0207f74faf43","sYS_INTERNAL_LAST_UPDATED_ON": "2021-03-12T08:10:31.335+0000","contentType": "Resource","trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_1132338069147811841118","audience": ["Student"],"visibility": "Default","consumerId": "273f3b18-5dda-4a27-984a-060c7cd398d3","index": 1,"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"],"lastPublishedBy": "8454cb21-3ce9-4e30-85b5-fade097880d8","version": 2,"license": "CC BY 4.0","prevState": "Live","size": 849876.0,"lastPublishedOn": "2021-03-11T13:20:30.514+0000","name": "Untitled Content","status": "Live","code": "9deb2c69-7240-472a-98e7-ed438e76262b","credentials": {"enabled": "No"},"prevStatus": "Processing","streamingUrl": "https://sunbirddevmedia-inct.streaming.media.azure.net/f17bccc5-cab3-4da8-a5eb-11d7211f1507/test-874-kb.ism/manifest(format=m3u8-aapl-v3)","idealScreenSize": "normal","createdOn": "2021-03-11T09:29:05.654+0000","contentDisposition": "inline","lastUpdatedOn": "2021-03-11T13:20:28.256+0000","dialcodeRequired": "No","lastStatusChangedOn": "2021-03-11T18:50:28.256+0530","createdFor": ["ORG_001"],"creator": "Reviewer User","os": ["All"],"se_FWIds": ["NCFCOPY"],"pkgVersion": 2.0,"versionKey": "1615455090358","idealScreenDensity": "hdpi","framework": "NCFCOPY","depth": 5,"s3Key": "ecar_files/do_1132338069147811841118/untitled-content_1615468830522_do_1132338069147811841118_2.0.ecar","createdBy": "95e4942d-cbe8-477d-aebd-ad8e6de4bfc8","compatibilityLevel": 1,"resourceType": "Learn"},{"ownershipType": ["createdBy"],"parent": "do_1132833371214970881710","unitIdentifiers": ["do_1132238266042040321422"],"copyright": "2021 MIT","previewUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/content/ecml/do_11322383952751820816-latest","plugins": [{"identifier": "org.sunbird.questionunit.quml","semanticVersion": "1.1"}],"subject": ["Hindi"],"channel": "01309282781705830427","downloadUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/ecar_files/do_11322383952751820816/sapractice_1614238238045_do_11322383952751820816_1.0.ecar","language": ["English"],"source": "https://dock.sunbirded.org/api/content/v1/read/do_1132238287156183041424","mimeType": "application/vnd.ekstep.ecml-archive","variants": {"spine": {"ecarUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/ecar_files/do_11322383952751820816/sapractice_1614238238800_do_11322383952751820816_1.0_spine.ecar","size": 13171.0}},"objectType": "Content","se_mediums": ["English"],"gradeLevel": ["Class 10"],"primaryCategory": "Exam Question","appId": "dev.dock.portal","contentEncoding": "gzip","artifactUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/content/do_11322383952751820816/artifact/1614237122171_do_1132238287156183041424.zip","sYS_INTERNAL_LAST_UPDATED_ON": "2021-02-25T07:30:44.916+0000","contentType": "Resource","se_gradeLevels": ["Class 10"],"trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_11322383952751820816","audience": ["Student"],"visibility": "Default","author": "anusha","maxQuestions": 1,"consumerId": "273f3b18-5dda-4a27-984a-060c7cd398d3","index": 2,"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"],"lastPublishedBy": "5a587cc1-e018-4859-a0a8-e842650b9d64","version": 2,"se_subjects": ["Hindi"],"license": "CC BY 4.0","prevState": "Review","size": 362236.0,"lastPublishedOn": "2021-02-25T07:30:38.043+0000","name": "sa:practice","status": "Live","code": "f239c77e-ed71-9133-0145-7468a92bce79","credentials": {"enabled": "No"},"prevStatus": "Processing","origin": "do_1132238287156183041424","streamingUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/content/ecml/do_11322383952751820816-latest","medium": ["English"],"idealScreenSize": "normal","createdOn": "2021-02-25T07:30:23.577+0000","se_boards": ["CBSE"],"processId": "04d5aec9-ed09-4a57-963d-9fa654fecf8d","contentDisposition": "inline","lastUpdatedOn": "2021-02-25T07:30:37.956+0000","originData": {"identifier": "do_1132238287156183041424","repository": "https://dock.sunbirded.org/api/content/v1/read/do_1132238287156183041424"},"collectionId": "do_1132238266036551681415","dialcodeRequired": "No","editorVersion": 3,"lastStatusChangedOn": "2021-02-25T07:30:44.908+0000","creator": "anusha","os": ["All"],"questionCategories": ["SA"],"cloudStorageKey": "content/do_11322383952751820816/artifact/1614237122171_do_1132238287156183041424.zip","se_FWIds": ["ekstep_ncert_k-12"],"marks": "12","pkgVersion": 1.0,"versionKey": "1614238237956","idealScreenDensity": "hdpi","framework": "ekstep_ncert_k-12","depth": 5,"s3Key": "ecar_files/do_11322383952751820816/sapractice_1614238238045_do_11322383952751820816_1.0.ecar","lastSubmittedOn": "2021-02-25T07:30:36.709+0000","createdBy": "19ba0e4e-9285-4335-8dd0-f674bf03fa4d","compatibilityLevel": 1,"itemSetPreviewUrl": "https://dockstorage.blob.core.windows.net/sunbird-content-dock/content/do_1132238287156183041424/artifact/do_1132238287156183041424_1614237121022.pdf","board": "CBSE","programId": "94564340-7737-11eb-96e0-29a9f8ed81cf"},{"ownershipType": ["createdBy"],"parent": "do_1132833371214970881710","unitIdentifiers": ["do_11322165488232038412588"],"copyright": "2021 MIT","previewUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/content/ecml/do_1132216902566133761410-latest","plugins": [{"identifier": "org.sunbird.questionunit.quml","semanticVersion": "1.1"}],"subject": ["Environmental Studies"],"channel": "01309282781705830427","downloadUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/ecar_files/do_1132216902566133761410/mcqmcq_1613975872529_do_1132216902566133761410_1.0.ecar","language": ["English"],"source": "https://dock.sunbirded.org/api/content/v1/read/do_11322168163282944012605","mimeType": "application/vnd.ekstep.ecml-archive","variants": {"spine": {"ecarUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/ecar_files/do_1132216902566133761410/mcqmcq_1613975873161_do_1132216902566133761410_1.0_spine.ecar","size": 17182.0}},"objectType": "Content","se_mediums": ["English"],"gradeLevel": ["Class 10"],"primaryCategory": "Exam Question","appId": "dev.dock.portal","contentEncoding": "gzip","artifactUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/content/do_1132216902566133761410/artifact/1613975740738_do_11322168163282944012605.zip","sYS_INTERNAL_LAST_UPDATED_ON": "2021-02-22T06:37:55.328+0000","contentType": "Resource","se_gradeLevels": ["Class 10"],"trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_1132216902566133761410","audience": ["Student"],"visibility": "Default","author": "color4","maxQuestions": 1,"consumerId": "7411b6bd-89f3-40ec-98d1-229dc64ce77d","learningOutcome": ["Understand the importance of values in life"],"index": 3,"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"],"lastPublishedBy": "ae94b68c-a535-4dce-8e7a-fb9662b0ad68","version": 2,"se_subjects": ["Environmental Studies"],"license": "CC BY 4.0","prevState": "Review","size": 370363.0,"lastPublishedOn": "2021-02-22T06:37:52.529+0000","name": "MCQMCQ","topic": ["Animals"],"status": "Live","code": "0cbae0f8-e3eb-1d31-e2e5-0337dc7d697d","credentials": {"enabled": "No"},"prevStatus": "Processing","origin": "do_11322168163282944012605","streamingUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/content/ecml/do_1132216902566133761410-latest","medium": ["English"],"idealScreenSize": "normal","createdOn": "2021-02-22T06:37:41.405+0000","se_boards": ["CBSE"],"processId": "fbcec2af-cb7a-4ed1-8683-ff04b475947e","contentDisposition": "inline","lastUpdatedOn": "2021-02-22T06:37:52.447+0000","originData": {"identifier": "do_11322168163282944012605","repository": "https://dock.sunbirded.org/api/content/v1/read/do_11322168163282944012605"},"collectionId": "do_11322165488181248012584","dialcodeRequired": "No","editorVersion": 3,"lastStatusChangedOn": "2021-02-22T06:37:55.314+0000","creator": "color4","os": ["All"],"questionCategories": ["MCQ"],"cloudStorageKey": "content/do_1132216902566133761410/artifact/1613975740738_do_11322168163282944012605.zip","se_FWIds": ["ekstep_ncert_k-12"],"marks": "1","bloomsLevel": ["Understand"],"pkgVersion": 1.0,"versionKey": "1613975872447","idealScreenDensity": "hdpi","framework": "ekstep_ncert_k-12","depth": 5,"s3Key": "ecar_files/do_1132216902566133761410/mcqmcq_1613975872529_do_1132216902566133761410_1.0.ecar","lastSubmittedOn": "2021-02-22T06:37:51.179+0000","createdBy": "0ce5b67e-b48e-489b-a818-e938e8bfc14b","se_topics": ["Animals"],"compatibilityLevel": 1,"itemSetPreviewUrl": "https://dockstorage.blob.core.windows.net/sunbird-content-dock/content/do_11322168163282944012605/artifact/do_11322168163282944012605_1613975739805.pdf","board": "CBSE","programId": "b2433a00-74cd-11eb-9f3c-f39a9ab9f5ce"}],"contentDisposition": "inline","lastUpdatedOn": "2021-05-20T08:58:33.464+0000","contentEncoding": "gzip","contentType": "TextBookUnit","dialcodeRequired": "Yes","identifier": "do_1132833371214970881710","lastStatusChangedOn": "2021-05-20T08:58:33.464+0000","audience": ["Student"],"os": ["All"],"visibility": "Parent","discussionForum": {"enabled": "Yes"},"index": 1,"mediaType": "content","osId": "org.ekstep.launcher","languageCode": ["en"],"version": 2,"versionKey": "1621501113464","license": "CC BY 4.0","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 4,"compatibilityLevel": 1,"name": "dsffgdg","topic": [],"status": "Draft"}],"contentDisposition": "inline","lastUpdatedOn": "2021-05-20T08:58:33.468+0000","contentEncoding": "gzip","contentType": "TextBookUnit","dialcodeRequired": "No","identifier": "do_1132833371215298561714","lastStatusChangedOn": "2021-05-20T08:58:33.468+0000","audience": ["Student"],"os": ["All"],"visibility": "Parent","discussionForum": {"enabled": "Yes"},"index": 1,"mediaType": "content","osId": "org.ekstep.launcher","languageCode": ["en"],"version": 2,"versionKey": "1621501113468","license": "CC BY 4.0","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 3,"compatibilityLevel": 1,"name": "5.2.1 Respiratory System","topic": ["Look and say","Role Of The Sense Organs"],"status": "Draft"}],"contentDisposition": "inline","lastUpdatedOn": "2021-05-20T08:58:33.476+0000","contentEncoding": "gzip","contentType": "TextBookUnit","dialcodeRequired": "No","identifier": "do_1132833371215953921722","lastStatusChangedOn": "2021-05-20T08:58:33.476+0000","audience": ["Student"],"os": ["All"],"visibility": "Parent","discussionForum": {"enabled": "Yes"},"index": 2,"mediaType": "content","osId": "org.ekstep.launcher","languageCode": ["en"],"version": 2,"versionKey": "1621501113476","license": "CC BY 4.0","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 2,"compatibilityLevel": 1,"name": "5.2 Organ Systems","topic": [],"status": "Draft"},{"ownershipType": ["createdBy"],"parent": "do_1132833371215462401716","copyright": "Sunbird","previewUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/content/assets/do_1132344630588948481134/test-874-kb.mp4","subject": ["Math"],"channel": "b00bc992ef25f1a9a8d63291e20efc8d","downloadUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/ecar_files/do_1132344630588948481134/untitled-content_1615535618825_do_1132344630588948481134_2.0.ecar","organisation": ["Sunbird"],"showNotification": true,"language": ["English"],"mimeType": "video/mp4","variants": {"spine": {"ecarUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/ecar_files/do_1132344630588948481134/untitled-content_1615535619590_do_1132344630588948481134_2.0_spine.ecar","size": 35301.0}},"objectType": "Content","se_mediums": ["English"],"gradeLevel": ["Grade 1"],"appIcon": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/content/do_1132344630588948481134/artifact/2a4b8abd789184932399d222d03d9b5c.thumb.jpg","primaryCategory": "Learning Resource","appId": "dev.sunbird.portal","contentEncoding": "identity","artifactUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/content/assets/do_1132344630588948481134/test-874-kb.mp4","lockKey": "1d28d983-2704-44bd-803e-5feb4e62da62","sYS_INTERNAL_LAST_UPDATED_ON": "2021-03-12T08:10:34.367+0000","contentType": "Resource","se_gradeLevels": ["Grade 1"],"trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_1132344630588948481134","lastUpdatedBy": "95e4942d-cbe8-477d-aebd-ad8e6de4bfc8","audience": ["Student"],"visibility": "Default","consumerId": "273f3b18-5dda-4a27-984a-060c7cd398d3","index": 3,"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"],"lastPublishedBy": "8454cb21-3ce9-4e30-85b5-fade097880d8","version": 2,"se_subjects": ["Math"],"license": "CC BY 4.0","prevState": "Review","size": 883817.0,"lastPublishedOn": "2021-03-12T07:53:38.825+0000","name": "Untitled Content","status": "Live","code": "8851e754-6e20-44d4-9070-e1a9664163ad","credentials": {"enabled": "No"},"prevStatus": "Review","description": "updated desrciption","streamingUrl": "https://sunbirddevmedia-inct.streaming.media.azure.net/40ae07aa-069e-4056-8f2b-014bc9a2d21b/test-874-kb.ism/manifest(format=m3u8-aapl-v3)","medium": ["English"],"posterImage": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/content/do_11299104587967692816/artifact/2a4b8abd789184932399d222d03d9b5c.jpg","idealScreenSize": "normal","createdOn": "2021-03-12T07:44:01.371+0000","se_boards": ["NCERT"],"copyrightYear": 2020,"contentDisposition": "inline","licenseterms": "By creating any type of content (resources, books, courses etc.) on DIKSHA, you consent to publish it under the Creative Commons License Framework. Please choose the applicable creative commons license you wish to apply to your content.","lastUpdatedOn": "2021-03-12T07:53:38.505+0000","dialcodeRequired": "No","lastStatusChangedOn": "2021-03-12T07:53:38.494+0000","createdFor": ["ORG_001"],"creator": "Reviewer User","os": ["All"],"se_FWIds": ["NCFCOPY"],"pkgVersion": 2.0,"versionKey": "1615535618583","idealScreenDensity": "hdpi","framework": "NCFCOPY","depth": 2,"s3Key": "ecar_files/do_1132344630588948481134/untitled-content_1615535618825_do_1132344630588948481134_2.0.ecar","lastSubmittedOn": "2021-03-12T07:53:10.005+0000","createdBy": "95e4942d-cbe8-477d-aebd-ad8e6de4bfc8","compatibilityLevel": 1,"board": "NCERT","resourceType": "Learn"}],"contentDisposition": "inline","lastUpdatedOn": "2021-05-20T08:58:33.470+0000","contentEncoding": "gzip","contentType": "TextBookUnit","dialcodeRequired": "No","identifier": "do_1132833371215462401716","lastStatusChangedOn": "2021-05-20T08:58:33.470+0000","audience": ["Student"],"os": ["All"],"visibility": "Parent","discussionForum": {"enabled": "Yes"},"index": 1,"mediaType": "content","osId": "org.ekstep.launcher","languageCode": ["en"],"version": 2,"versionKey": "1621501113470","license": "CC BY 4.0","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 1,"compatibilityLevel": 1,"name": "5. Human Body","topic": [],"status": "Draft"}],"appId": "dev.sunbird.portal","contentEncoding": "gzip","sYS_INTERNAL_LAST_UPDATED_ON": "2021-05-20T09:12:06.988+0000","contentType": "TextBook","trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_1132828073514926081518","audience": ["Student"],"visibility": "Default","consumerId": "01814e02-fc27-4165-ae53-3d1816e55817","childNodes": ["do_1132339274094346241120","do_1132833371215872001720","do_1132833371215134721712","do_1132833371215462401716","do_113223967141863424174","do_1132833371214970881710","do_1132833371215708161718","do_1132372524622561281279","do_1132338069147811841118","do_1132833371215298561714","do_1132833371215953921722","do_11322383952751820816","do_1132216902566133761410","do_1132344630588948481134"],"discussionForum": {"enabled": "Yes"},"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"],"version": 2,"license": "CC BY 4.0","name": "TestCSVUpload","status": "Draft","code": "org.sunbird.yhqB6L","credentials": {"enabled": "No"},"description": "Enter description for TextBook","medium": ["English"],"idealScreenSize": "normal","createdOn": "2021-05-19T15:00:44.279+0000","contentDisposition": "inline","additionalCategories": ["Textbook"],"lastUpdatedOn": "2021-05-20T07:10:32.805+0000","dialcodeRequired": "No","lastStatusChangedOn": "2021-05-19T15:00:44.279+0000","createdFor": ["0126825293972439041"],"creator": "Book Creator","os": ["All"],"versionKey": "1621501113536","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 0,"createdBy": "8454cb21-3ce9-4e30-85b5-fade097880d8","compatibilityLevel": 1,"userConsent": "Yes","board": "State (Tamil Nadu)","resourceType": "Book"}} + |"primaryCategory": "Digital Textbook","children": [{"ownershipType": ["createdBy"],"parent": "do_1132828073514926081518","code": "do_1132828084877148161531", + |"keywords": [],"credentials": {"enabled": "No"},"channel": "0126825293972439041","description": "This chapter describes about human body", + |"language": ["English"],"mimeType": "application/vnd.ekstep.content-collection","idealScreenSize": "normal","createdOn": "2021-05-20T08:58:33.470+0000", + |"objectType": "Content","primaryCategory": "Textbook Unit","children": [{"ownershipType": ["createdBy"],"parent": "do_1132833371215462401716", + |"code": "do_1132828084876574721523","keywords": [],"credentials": {"enabled": "No"},"channel": "0126825293972439041", + |"description": "This section describes about various part of the body such as head, hands, legs etc.","language": ["English"], + |"mimeType": "application/vnd.ekstep.content-collection","idealScreenSize": "normal","createdOn": "2021-05-20T08:58:33.466+0000", + |"objectType": "Content","primaryCategory": "Textbook Unit","children": [{"ownershipType": ["createdBy"],"parent": "do_1132833371215134721712", + |"code": "do_1132828084876738561525","keywords": ["legs etc."],"credentials": {"enabled": "No"},"channel": "0126825293972439041","description": "xyz", + |"language": ["English"],"mimeType": "application/vnd.ekstep.content-collection","idealScreenSize": "normal","createdOn": "2021-05-20T08:58:33.475+0000", + |"objectType": "Content","primaryCategory": "Textbook Unit","children": [{"ownershipType": ["createdBy"],"parent": "do_1132833371215872001720", + |"previewUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/content/assets/do_1132339274094346241120/test-874-kb.mp4", + |"downhttps://sunbirddev.blob.core.windows.netloadUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/ecar_files/do_1132339274094346241120/untitled-content_1616331971279_do_1132339274094346241120_14.0.ecar", + |"organisation": ["Sunbird"],"language": ["English"],"mimeType": "video/mp4","variants": {"spine": + |{"ecarUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/ecar_files/do_1132339274094346241120/untitled-content_1616331975047_do_1132339274094346241120_14.0_spine.ecar","size": 1381.0}},"objectType": "Content","primaryCategory": "Learning Resource","appId": "local.sunbird.portal","contentEncoding": "identity","artifactUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/content/assets/do_1132339274094346241120/test-874-kb.mp4", + |"lockKey": "34a029c4-ac81-4934-9792-11b7a57d6c13","sYS_INTERNAL_LAST_UPDATED_ON": "2021-03-21T13:20:24.579+0000","contentType": "Resource", + |"trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_1132339274094346241120","audience": ["Student"],"visibility": "Default", + |"consumerId": "273f3b18-5dda-4a27-984a-060c7cd398d3","index": 1,"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"], + |"lastPublishedBy": "8454cb21-3ce9-4e30-85b5-fade097880d8","version": 2,"license": "CC BY 4.0","prevState": "Live","size": 849897.0, + |"lastPublishedOn": "2021-03-21T13:06:11.272+0000","name": "Untitled Content","status": "Live","code": "a88b0257-670b-455b-98b8-6e359ebac009","credentials": {"enabled": "No"},"prevStatus": "Processing","description": "updated","streamingUrl": "https://sunbirddevmedia-inct.streaming.media.azure.net/9c0ebb33-af08-403f-afb4-eb24749f40a1/test-874-kb.ism/manifest(format=m3u8-aapl-v3)","idealScreenSize": "normal","createdOn": "2021-03-11T13:34:14.475+0000","contentDisposition": "inline","lastUpdatedOn": "2021-03-21T13:06:09.526+0000","dialcodeRequired": "No","lastStatusChangedOn": "2021-03-21T18:36:15.799+0530","createdFor": ["ORG_001"],"creator": "Reviewer User","os": ["All"],"se_FWIds": ["NCFCOPY"],"pkgVersion": 14.0,"versionKey": "1616331969523","idealScreenDensity": "hdpi","framework": "NCFCOPY","depth": 4,"s3Key": "ecar_files/do_1132339274094346241120/untitled-content_1616331971279_do_1132339274094346241120_14.0.ecar","createdBy": "95e4942d-cbe8-477d-aebd-ad8e6de4bfc8","compatibilityLevel": 1,"resourceType": "Learn"},{"ownershipType": ["createdBy"],"parent": "do_1132833371215872001720","unitIdentifiers": ["do_1132239562839900161634"],"copyright": "2021 MIT","previewUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/content/ecml/do_113223967141863424174-latest","plugins": [{"identifier": "org.sunbird.questionunit.quml","semanticVersion": "1.1"}],"subject": ["Hindi"],"channel": "01309282781705830427","downloadUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/ecar_files/do_113223967141863424174/esa_1614253812772_do_113223967141863424174_1.0.ecar","language": ["English"],"source": "https://dock.sunbirded.org/api/content/v1/read/do_1132239617341767681638","mimeType": "application/vnd.ekstep.ecml-archive","variants": {"spine": {"ecarUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/ecar_files/do_113223967141863424174/esa_1614253813394_do_113223967141863424174_1.0_spine.ecar","size": 24293.0}},"objectType": "Content","se_mediums": ["English"],"gradeLevel": ["Class 10"],"primaryCategory": "Exam Question","appId": "dev.dock.portal","contentEncoding": "gzip","artifactUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/content/do_113223967141863424174/artifact/1614253223147_do_1132239617341767681638.zip","sYS_INTERNAL_LAST_UPDATED_ON": "2021-02-25T11:50:16.032+0000","contentType": "Resource","se_gradeLevels": ["Class 10"],"trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_113223967141863424174","audience": ["Student"],"visibility": "Default","author": "anusha","maxQuestions": 1,"consumerId": "b7054510-3ca4-49fd-b373-b100b3f65e18","index": 2,"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"],"lastPublishedBy": "5a587cc1-e018-4859-a0a8-e842650b9d64","version": 2,"se_subjects": ["Hindi"],"license": "CC BY 4.0","prevState": "Review","size": 384798.0,"lastPublishedOn": "2021-02-25T11:50:12.771+0000","name": "esa","topic": ["तोप"],"status": "Live","code": "d19f43ce-753d-2c70-a9fd-70302af424a0","credentials": {"enabled": "No"},"prevStatus": "Processing","origin": "do_1132239617341767681638","streamingUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/content/ecml/do_113223967141863424174-latest","medium": ["English"],"idealScreenSize": "normal","createdOn": "2021-02-25T11:50:01.500+0000","se_boards": ["CBSE"],"processId": "2b00cca7-42d5-4e35-aa03-1c22dfe03de8","contentDisposition": "inline","lastUpdatedOn": "2021-02-25T11:50:12.678+0000","originData": {"identifier": "do_1132239617341767681638","repository": "https://dock.sunbirded.org/api/content/v1/read/do_1132239617341767681638"},"collectionId": "do_1132239562836049921627","dialcodeRequired": "No","editorVersion": 3,"lastStatusChangedOn": "2021-02-25T11:50:16.017+0000","creator": "anusha","os": ["All"],"questionCategories": ["SA"],"cloudStorageKey": "content/do_113223967141863424174/artifact/1614253223147_do_1132239617341767681638.zip","se_FWIds": ["ekstep_ncert_k-12"],"marks": "12","bloomsLevel": ["Apply"],"pkgVersion": 1.0,"versionKey": "1614253812678","idealScreenDensity": "hdpi","framework": "ekstep_ncert_k-12","depth": 4,"s3Key": "ecar_files/do_113223967141863424174/esa_1614253812772_do_113223967141863424174_1.0.ecar","lastSubmittedOn": "2021-02-25T11:50:11.539+0000","createdBy": "19ba0e4e-9285-4335-8dd0-f674bf03fa4d","se_topics": ["तोप"],"compatibilityLevel": 1,"itemSetPreviewUrl": "https://dockstorage.blob.core.windows.net/sunbird-content-dock/content/do_1132239617341767681638/artifact/do_1132239617341767681638_1614253222002.pdf","board": "CBSE","programId": "463cfa30-775c-11eb-8c56-93946e419809"}],"contentDisposition": "inline","lastUpdatedOn": "2021-05-20T08:58:33.475+0000","contentEncoding": "gzip","contentType": "TextBookUnit","dialcodeRequired": "Yes","identifier": "do_1132833371215872001720","lastStatusChangedOn": "2021-05-20T08:58:33.475+0000","audience": ["Student"],"os": ["All"],"visibility": "Parent","discussionForum": {"enabled": "Yes"},"index": 1,"mediaType": "content","osId": "org.ekstep.launcher","languageCode": ["en"],"version": 2,"versionKey": "1621501113475","license": "CC BY 4.0","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 3,"compatibilityLevel": 1,"name": "5.1.1 Key parts in the head","topic": [],"status": "Draft"},{"ownershipType": ["createdBy"],"parent": "do_1132833371215134721712","code": "do_1132828084876165121519","keywords": [],"credentials": {"enabled": "No"},"channel": "0126825293972439041","description": "","language": ["English"],"mimeType": "application/vnd.ekstep.content-collection","idealScreenSize": "normal","createdOn": "2021-05-20T08:58:33.473+0000","objectType": "Content","primaryCategory": "Textbook Unit","contentDisposition": "inline","lastUpdatedOn": "2021-05-20T08:58:33.473+0000","contentEncoding": "gzip","contentType": "TextBookUnit","dialcodeRequired": "No","identifier": "do_1132833371215708161718","lastStatusChangedOn": "2021-05-20T08:58:33.473+0000","audience": ["Student"],"os": ["All"],"visibility": "Parent","discussionForum": {"enabled": "Yes"},"index": 2,"mediaType": "content","osId": "org.ekstep.launcher","languageCode": ["en"],"version": 2,"versionKey": "1621501113473","license": "CC BY 4.0","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 3,"compatibilityLevel": 1,"name": "5.1.2 Other parts","topic": [],"status": "Draft"},{"ownershipType": ["createdBy"],"parent": "do_1132833371215134721712","unitIdentifiers": ["do_11323721176414617611924"],"copyright": "2021 MIT","organisationId": "e7328d77-42a7-44c8-84f4-8cfea235f07d","previewUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/content/ecml/do_1132372524622561281279-latest","plugins": [{"identifier": "org.sunbird.questionunit.quml","semanticVersion": "1.1"}],"subject": ["Mathematics"],"channel": "01309282781705830427","downloadUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/ecar_files/do_1132372524622561281279/untitled_1615875562931_do_1132372524622561281279_1.0.ecar","language": ["English"],"source": "https://dock.sunbirded.org/api/content/v1/read/do_11323724954450329611930","mimeType": "application/vnd.ekstep.ecml-archive","variants": {"spine": {"ecarUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/ecar_files/do_1132372524622561281279/untitled_1615875563539_do_1132372524622561281279_1.0_spine.ecar","size": 19563.0}},"objectType": "Content","se_mediums": ["English"],"gradeLevel": ["Class 5"],"primaryCategory": "Exam Question","appId": "dev.dock.portal","contentEncoding": "gzip","artifactUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/content/do_1132372524622561281279/artifact/1615875430184_do_11323724954450329611930.zip","sYS_INTERNAL_LAST_UPDATED_ON": "2021-03-16T06:19:26.162+0000","contentType": "Resource","se_gradeLevels": ["Class 5"],"trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_1132372524622561281279","audience": ["Student"],"visibility": "Default","author": "N18","maxQuestions": 1,"consumerId": "f73cfcc5-4d43-4fa0-8b81-46166c81bc2b","learningOutcome": ["identify the need to find area and perimeter of rectangle and square."],"index": 3,"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"],"lastPublishedBy": "5a587cc1-e018-4859-a0a8-e842650b9d64","version": 2,"se_subjects": ["Mathematics"],"license": "CC BY 4.0","prevState": "Review","size": 374996.0,"lastPublishedOn": "2021-03-16T06:19:22.931+0000","name": "Untitled","topic": ["Speed, Distance and Time"],"status": "Live","code": "2544c8b8-7946-b6c0-e1c7-ced4aee4ea8c","credentials": {"enabled": "No"},"prevStatus": "Processing","origin": "do_11323724954450329611930","streamingUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/content/ecml/do_1132372524622561281279-latest","medium": ["English"],"idealScreenSize": "normal","createdOn": "2021-03-16T06:19:04.712+0000","se_boards": ["CBSE"],"processId": "9995e013-a7c9-4da1-b2c9-2f59da33414a","contentDisposition": "inline","lastUpdatedOn": "2021-03-16T06:19:20.817+0000","originData": {"identifier": "do_11323724954450329611930","repository": "https://dock.sunbirded.org/api/content/v1/read/do_11323724954450329611930"},"collectionId": "do_11323721176353996811921","dialcodeRequired": "No","editorVersion": 3,"lastStatusChangedOn": "2021-03-16T06:19:26.147+0000","creator": "N18","os": ["All"],"questionCategories": ["MTF"],"cloudStorageKey": "content/do_1132372524622561281279/artifact/1615875430184_do_11323724954450329611930.zip","se_FWIds": ["ekstep_ncert_k-12"],"marks": "2","bloomsLevel": ["Apply"],"pkgVersion": 1.0,"versionKey": "1615875560817","idealScreenDensity": "hdpi","framework": "ekstep_ncert_k-12","depth": 3,"s3Key": "ecar_files/do_1132372524622561281279/untitled_1615875562931_do_1132372524622561281279_1.0.ecar","lastSubmittedOn": "2021-03-16T06:19:17.005+0000","createdBy": "60f91e9e-34ee-4f9f-a907-d312d0e8063e","se_topics": ["Speed, Distance and Time"],"compatibilityLevel": 1,"itemSetPreviewUrl": "https://dockstorage.blob.core.windows.net/sunbird-content-dock/content/do_11323724954450329611930/artifact/do_11323724954450329611930_1615875429226.pdf","board": "CBSE","programId": "800eb440-8613-11eb-a663-4f63bbe94184"}],"contentDisposition": "inline","lastUpdatedOn": "2021-05-20T08:58:33.466+0000","contentEncoding": "gzip","contentType": "TextBookUnit","dialcodeRequired": "Yes","identifier": "do_1132833371215134721712","lastStatusChangedOn": "2021-05-20T08:58:33.466+0000","audience": ["Student"],"os": ["All"],"visibility": "Parent","discussionForum": {"enabled": "Yes"},"index": 1,"mediaType": "content","osId": "org.ekstep.launcher","languageCode": ["en"],"version": 2,"versionKey": "1621501113466","license": "CC BY 4.0","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 2,"compatibilityLevel": 1,"name": "5.1 Parts of Body","topic": ["Role Of The Sense Organs"],"status": "Draft"},{"ownershipType": ["createdBy"],"parent": "do_1132833371215462401716","code": "do_1132828084877066241529","keywords": [],"credentials": {"enabled": "No"},"channel": "0126825293972439041","description": "","language": ["English"],"mimeType": "application/vnd.ekstep.content-collection","idealScreenSize": "normal","createdOn": "2021-05-20T08:58:33.476+0000","objectType": "Content","primaryCategory": "Textbook Unit","children": [{"ownershipType": ["createdBy"],"parent": "do_1132833371215953921722","code": "do_1132828084876492801521","keywords": ["test key","check"],"credentials": {"enabled": "No"},"channel": "0126825293972439041","description": "","language": ["English"],"mimeType": "application/vnd.ekstep.content-collection","idealScreenSize": "normal","createdOn": "2021-05-20T08:58:33.468+0000","objectType": "Content","primaryCategory": "Textbook Unit","children": [{"ownershipType": ["createdBy"],"parent": "do_1132833371215298561714","code": "do_1132828084876820481527","keywords": ["abcd","cgf"],"credentials": {"enabled": "No"},"channel": "0126825293972439041","description": "labeled new","language": ["English"],"mimeType": "application/vnd.ekstep.content-collection","idealScreenSize": "normal","createdOn": "2021-05-20T08:58:33.464+0000","objectType": "Content","primaryCategory": "Textbook Unit","children": [{"ownershipType": ["createdBy"],"parent": "do_1132833371214970881710","previewUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/content/assets/do_1132338069147811841118/test-874-kb.mp4","channel": "b00bc992ef25f1a9a8d63291e20efc8d","downloadUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/ecar_files/do_1132338069147811841118/untitled-content_1615468830522_do_1132338069147811841118_2.0.ecar","organisation": ["Sunbird"],"language": ["English"],"mimeType": "video/mp4","variants": {"spine": {"ecarUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/ecar_files/do_1132338069147811841118/untitled-content_1615468834470_do_1132338069147811841118_2.0_spine.ecar","size": 1361.0}},"objectType": "Content","primaryCategory": "Learning Resource","appId": "dev.sunbird.portal","contentEncoding": "identity","artifactUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/content/assets/do_1132338069147811841118/test-874-kb.mp4","lockKey": "d73707c8-9999-4fc9-9b34-0207f74faf43","sYS_INTERNAL_LAST_UPDATED_ON": "2021-03-12T08:10:31.335+0000","contentType": "Resource","trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_1132338069147811841118","audience": ["Student"],"visibility": "Default","consumerId": "273f3b18-5dda-4a27-984a-060c7cd398d3","index": 1,"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"],"lastPublishedBy": "8454cb21-3ce9-4e30-85b5-fade097880d8","version": 2,"license": "CC BY 4.0","prevState": "Live","size": 849876.0,"lastPublishedOn": "2021-03-11T13:20:30.514+0000","name": "Untitled Content","status": "Live","code": "9deb2c69-7240-472a-98e7-ed438e76262b","credentials": {"enabled": "No"},"prevStatus": "Processing","streamingUrl": "https://sunbirddevmedia-inct.streaming.media.azure.net/f17bccc5-cab3-4da8-a5eb-11d7211f1507/test-874-kb.ism/manifest(format=m3u8-aapl-v3)","idealScreenSize": "normal","createdOn": "2021-03-11T09:29:05.654+0000","contentDisposition": "inline","lastUpdatedOn": "2021-03-11T13:20:28.256+0000","dialcodeRequired": "No","lastStatusChangedOn": "2021-03-11T18:50:28.256+0530","createdFor": ["ORG_001"],"creator": "Reviewer User","os": ["All"],"se_FWIds": ["NCFCOPY"],"pkgVersion": 2.0,"versionKey": "1615455090358","idealScreenDensity": "hdpi","framework": "NCFCOPY","depth": 5,"s3Key": "ecar_files/do_1132338069147811841118/untitled-content_1615468830522_do_1132338069147811841118_2.0.ecar","createdBy": "95e4942d-cbe8-477d-aebd-ad8e6de4bfc8","compatibilityLevel": 1,"resourceType": "Learn"},{"ownershipType": ["createdBy"],"parent": "do_1132833371214970881710","unitIdentifiers": ["do_1132238266042040321422"],"copyright": "2021 MIT","previewUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/content/ecml/do_11322383952751820816-latest","plugins": [{"identifier": "org.sunbird.questionunit.quml","semanticVersion": "1.1"}],"subject": ["Hindi"],"channel": "01309282781705830427","downloadUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/ecar_files/do_11322383952751820816/sapractice_1614238238045_do_11322383952751820816_1.0.ecar","language": ["English"],"source": "https://dock.sunbirded.org/api/content/v1/read/do_1132238287156183041424","mimeType": "application/vnd.ekstep.ecml-archive","variants": {"spine": {"ecarUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/ecar_files/do_11322383952751820816/sapractice_1614238238800_do_11322383952751820816_1.0_spine.ecar","size": 13171.0}},"objectType": "Content","se_mediums": ["English"],"gradeLevel": ["Class 10"],"primaryCategory": "Exam Question","appId": "dev.dock.portal","contentEncoding": "gzip","artifactUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/content/do_11322383952751820816/artifact/1614237122171_do_1132238287156183041424.zip","sYS_INTERNAL_LAST_UPDATED_ON": "2021-02-25T07:30:44.916+0000","contentType": "Resource","se_gradeLevels": ["Class 10"],"trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_11322383952751820816","audience": ["Student"],"visibility": "Default","author": "anusha","maxQuestions": 1,"consumerId": "273f3b18-5dda-4a27-984a-060c7cd398d3","index": 2,"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"],"lastPublishedBy": "5a587cc1-e018-4859-a0a8-e842650b9d64","version": 2,"se_subjects": ["Hindi"],"license": "CC BY 4.0","prevState": "Review","size": 362236.0,"lastPublishedOn": "2021-02-25T07:30:38.043+0000","name": "sa:practice","status": "Live","code": "f239c77e-ed71-9133-0145-7468a92bce79","credentials": {"enabled": "No"},"prevStatus": "Processing","origin": "do_1132238287156183041424","streamingUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/content/ecml/do_11322383952751820816-latest","medium": ["English"],"idealScreenSize": "normal","createdOn": "2021-02-25T07:30:23.577+0000","se_boards": ["CBSE"],"processId": "04d5aec9-ed09-4a57-963d-9fa654fecf8d","contentDisposition": "inline","lastUpdatedOn": "2021-02-25T07:30:37.956+0000","originData": {"identifier": "do_1132238287156183041424","repository": "https://dock.sunbirded.org/api/content/v1/read/do_1132238287156183041424"},"collectionId": "do_1132238266036551681415","dialcodeRequired": "No","editorVersion": 3,"lastStatusChangedOn": "2021-02-25T07:30:44.908+0000","creator": "anusha","os": ["All"],"questionCategories": ["SA"],"cloudStorageKey": "content/do_11322383952751820816/artifact/1614237122171_do_1132238287156183041424.zip","se_FWIds": ["ekstep_ncert_k-12"],"marks": "12","pkgVersion": 1.0,"versionKey": "1614238237956","idealScreenDensity": "hdpi","framework": "ekstep_ncert_k-12","depth": 5,"s3Key": "ecar_files/do_11322383952751820816/sapractice_1614238238045_do_11322383952751820816_1.0.ecar","lastSubmittedOn": "2021-02-25T07:30:36.709+0000","createdBy": "19ba0e4e-9285-4335-8dd0-f674bf03fa4d","compatibilityLevel": 1,"itemSetPreviewUrl": "https://dockstorage.blob.core.windows.net/sunbird-content-dock/content/do_1132238287156183041424/artifact/do_1132238287156183041424_1614237121022.pdf","board": "CBSE","programId": "94564340-7737-11eb-96e0-29a9f8ed81cf"},{"ownershipType": ["createdBy"],"parent": "do_1132833371214970881710","unitIdentifiers": ["do_11322165488232038412588"],"copyright": "2021 MIT","previewUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/content/ecml/do_1132216902566133761410-latest","plugins": [{"identifier": "org.sunbird.questionunit.quml","semanticVersion": "1.1"}],"subject": ["Environmental Studies"],"channel": "01309282781705830427","downloadUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/ecar_files/do_1132216902566133761410/mcqmcq_1613975872529_do_1132216902566133761410_1.0.ecar","language": ["English"],"source": "https://dock.sunbirded.org/api/content/v1/read/do_11322168163282944012605","mimeType": "application/vnd.ekstep.ecml-archive","variants": {"spine": {"ecarUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/ecar_files/do_1132216902566133761410/mcqmcq_1613975873161_do_1132216902566133761410_1.0_spine.ecar","size": 17182.0}},"objectType": "Content","se_mediums": ["English"],"gradeLevel": ["Class 10"],"primaryCategory": "Exam Question","appId": "dev.dock.portal","contentEncoding": "gzip","artifactUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/content/do_1132216902566133761410/artifact/1613975740738_do_11322168163282944012605.zip","sYS_INTERNAL_LAST_UPDATED_ON": "2021-02-22T06:37:55.328+0000","contentType": "Resource","se_gradeLevels": ["Class 10"],"trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_1132216902566133761410","audience": ["Student"],"visibility": "Default","author": "color4","maxQuestions": 1,"consumerId": "7411b6bd-89f3-40ec-98d1-229dc64ce77d","learningOutcome": ["Understand the importance of values in life"],"index": 3,"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"],"lastPublishedBy": "ae94b68c-a535-4dce-8e7a-fb9662b0ad68","version": 2,"se_subjects": ["Environmental Studies"],"license": "CC BY 4.0","prevState": "Review","size": 370363.0,"lastPublishedOn": "2021-02-22T06:37:52.529+0000","name": "MCQMCQ","topic": ["Animals"],"status": "Live","code": "0cbae0f8-e3eb-1d31-e2e5-0337dc7d697d","credentials": {"enabled": "No"},"prevStatus": "Processing","origin": "do_11322168163282944012605","streamingUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/content/ecml/do_1132216902566133761410-latest","medium": ["English"],"idealScreenSize": "normal","createdOn": "2021-02-22T06:37:41.405+0000","se_boards": ["CBSE"],"processId": "fbcec2af-cb7a-4ed1-8683-ff04b475947e","contentDisposition": "inline","lastUpdatedOn": "2021-02-22T06:37:52.447+0000","originData": {"identifier": "do_11322168163282944012605","repository": "https://dock.sunbirded.org/api/content/v1/read/do_11322168163282944012605"},"collectionId": "do_11322165488181248012584","dialcodeRequired": "No","editorVersion": 3,"lastStatusChangedOn": "2021-02-22T06:37:55.314+0000","creator": "color4","os": ["All"],"questionCategories": ["MCQ"],"cloudStorageKey": "content/do_1132216902566133761410/artifact/1613975740738_do_11322168163282944012605.zip","se_FWIds": ["ekstep_ncert_k-12"],"marks": "1","bloomsLevel": ["Understand"],"pkgVersion": 1.0,"versionKey": "1613975872447","idealScreenDensity": "hdpi","framework": "ekstep_ncert_k-12","depth": 5,"s3Key": "ecar_files/do_1132216902566133761410/mcqmcq_1613975872529_do_1132216902566133761410_1.0.ecar","lastSubmittedOn": "2021-02-22T06:37:51.179+0000","createdBy": "0ce5b67e-b48e-489b-a818-e938e8bfc14b","se_topics": ["Animals"],"compatibilityLevel": 1,"itemSetPreviewUrl": "https://dockstorage.blob.core.windows.net/sunbird-content-dock/content/do_11322168163282944012605/artifact/do_11322168163282944012605_1613975739805.pdf","board": "CBSE","programId": "b2433a00-74cd-11eb-9f3c-f39a9ab9f5ce"}],"contentDisposition": "inline","lastUpdatedOn": "2021-05-20T08:58:33.464+0000","contentEncoding": "gzip","contentType": "TextBookUnit","dialcodeRequired": "Yes","identifier": "do_1132833371214970881710","lastStatusChangedOn": "2021-05-20T08:58:33.464+0000","audience": ["Student"],"os": ["All"],"visibility": "Parent","discussionForum": {"enabled": "Yes"},"index": 1,"mediaType": "content","osId": "org.ekstep.launcher","languageCode": ["en"],"version": 2,"versionKey": "1621501113464","license": "CC BY 4.0","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 4,"compatibilityLevel": 1,"name": "dsffgdg","topic": [],"status": "Draft"}],"contentDisposition": "inline","lastUpdatedOn": "2021-05-20T08:58:33.468+0000","contentEncoding": "gzip","contentType": "TextBookUnit","dialcodeRequired": "No","identifier": "do_1132833371215298561714","lastStatusChangedOn": "2021-05-20T08:58:33.468+0000","audience": ["Student"],"os": ["All"],"visibility": "Parent","discussionForum": {"enabled": "Yes"},"index": 1,"mediaType": "content","osId": "org.ekstep.launcher","languageCode": ["en"],"version": 2,"versionKey": "1621501113468","license": "CC BY 4.0","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 3,"compatibilityLevel": 1,"name": "5.2.1 Respiratory System","topic": ["Look and say","Role Of The Sense Organs"],"status": "Draft"}],"contentDisposition": "inline","lastUpdatedOn": "2021-05-20T08:58:33.476+0000","contentEncoding": "gzip","contentType": "TextBookUnit","dialcodeRequired": "No","identifier": "do_1132833371215953921722","lastStatusChangedOn": "2021-05-20T08:58:33.476+0000","audience": ["Student"],"os": ["All"],"visibility": "Parent","discussionForum": {"enabled": "Yes"},"index": 2,"mediaType": "content","osId": "org.ekstep.launcher","languageCode": ["en"],"version": 2,"versionKey": "1621501113476","license": "CC BY 4.0","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 2,"compatibilityLevel": 1,"name": "5.2 Organ Systems","topic": [],"status": "Draft"},{"ownershipType": ["createdBy"],"parent": "do_1132833371215462401716","copyright": "Sunbird","previewUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/content/assets/do_1132344630588948481134/test-874-kb.mp4","subject": ["Math"],"channel": "b00bc992ef25f1a9a8d63291e20efc8d","downloadUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/ecar_files/do_1132344630588948481134/untitled-content_1615535618825_do_1132344630588948481134_2.0.ecar","organisation": ["Sunbird"],"showNotification": true,"language": ["English"],"mimeType": "video/mp4","variants": {"spine": {"ecarUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/ecar_files/do_1132344630588948481134/untitled-content_1615535619590_do_1132344630588948481134_2.0_spine.ecar","size": 35301.0}},"objectType": "Content","se_mediums": ["English"],"gradeLevel": ["Grade 1"],"appIcon": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/content/do_1132344630588948481134/artifact/2a4b8abd789184932399d222d03d9b5c.thumb.jpg","primaryCategory": "Learning Resource","appId": "dev.sunbird.portal","contentEncoding": "identity","artifactUrl": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/content/assets/do_1132344630588948481134/test-874-kb.mp4","lockKey": "1d28d983-2704-44bd-803e-5feb4e62da62","sYS_INTERNAL_LAST_UPDATED_ON": "2021-03-12T08:10:34.367+0000","contentType": "Resource","se_gradeLevels": ["Grade 1"],"trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_1132344630588948481134","lastUpdatedBy": "95e4942d-cbe8-477d-aebd-ad8e6de4bfc8","audience": ["Student"],"visibility": "Default","consumerId": "273f3b18-5dda-4a27-984a-060c7cd398d3","index": 3,"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"],"lastPublishedBy": "8454cb21-3ce9-4e30-85b5-fade097880d8","version": 2,"se_subjects": ["Math"],"license": "CC BY 4.0","prevState": "Review","size": 883817.0,"lastPublishedOn": "2021-03-12T07:53:38.825+0000","name": "Untitled Content","status": "Live","code": "8851e754-6e20-44d4-9070-e1a9664163ad","credentials": {"enabled": "No"},"prevStatus": "Review","description": "updated desrciption","streamingUrl": "https://sunbirddevmedia-inct.streaming.media.azure.net/40ae07aa-069e-4056-8f2b-014bc9a2d21b/test-874-kb.ism/manifest(format=m3u8-aapl-v3)","medium": ["English"],"posterImage": "CONTENT_STORAGE_BASE_PATHsunbird-content-dev/content/do_11299104587967692816/artifact/2a4b8abd789184932399d222d03d9b5c.jpg","idealScreenSize": "normal","createdOn": "2021-03-12T07:44:01.371+0000","se_boards": ["NCERT"],"copyrightYear": 2020,"contentDisposition": "inline","licenseterms": "By creating any type of content (resources, books, courses etc.) on DIKSHA, you consent to publish it under the Creative Commons License Framework. Please choose the applicable creative commons license you wish to apply to your content.","lastUpdatedOn": "2021-03-12T07:53:38.505+0000","dialcodeRequired": "No","lastStatusChangedOn": "2021-03-12T07:53:38.494+0000","createdFor": ["ORG_001"],"creator": "Reviewer User","os": ["All"],"se_FWIds": ["NCFCOPY"],"pkgVersion": 2.0,"versionKey": "1615535618583","idealScreenDensity": "hdpi","framework": "NCFCOPY","depth": 2,"s3Key": "ecar_files/do_1132344630588948481134/untitled-content_1615535618825_do_1132344630588948481134_2.0.ecar","lastSubmittedOn": "2021-03-12T07:53:10.005+0000","createdBy": "95e4942d-cbe8-477d-aebd-ad8e6de4bfc8","compatibilityLevel": 1,"board": "NCERT","resourceType": "Learn"}],"contentDisposition": "inline","lastUpdatedOn": "2021-05-20T08:58:33.470+0000","contentEncoding": "gzip","contentType": "TextBookUnit","dialcodeRequired": "No","identifier": "do_1132833371215462401716","lastStatusChangedOn": "2021-05-20T08:58:33.470+0000","audience": ["Student"],"os": ["All"],"visibility": "Parent","discussionForum": {"enabled": "Yes"},"index": 1,"mediaType": "content","osId": "org.ekstep.launcher","languageCode": ["en"],"version": 2,"versionKey": "1621501113470","license": "CC BY 4.0","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 1,"compatibilityLevel": 1,"name": "5. Human Body","topic": [],"status": "Draft"}],"appId": "dev.sunbird.portal","contentEncoding": "gzip","sYS_INTERNAL_LAST_UPDATED_ON": "2021-05-20T09:12:06.988+0000","contentType": "TextBook","trackable": {"enabled": "No","autoBatch": "No"},"identifier": "do_1132828073514926081518","audience": ["Student"],"visibility": "Default","consumerId": "01814e02-fc27-4165-ae53-3d1816e55817","childNodes": ["do_1132339274094346241120","do_1132833371215872001720","do_1132833371215134721712","do_1132833371215462401716","do_113223967141863424174","do_1132833371214970881710","do_1132833371215708161718","do_1132372524622561281279","do_1132338069147811841118","do_1132833371215298561714","do_1132833371215953921722","do_11322383952751820816","do_1132216902566133761410","do_1132344630588948481134"],"discussionForum": {"enabled": "Yes"},"mediaType": "content","osId": "org.ekstep.quiz.app","languageCode": ["en"],"version": 2,"license": "CC BY 4.0","name": "TestCSVUpload","status": "Draft","code": "org.sunbird.yhqB6L","credentials": {"enabled": "No"},"description": "Enter description for TextBook","medium": ["English"],"idealScreenSize": "normal","createdOn": "2021-05-19T15:00:44.279+0000","contentDisposition": "inline","additionalCategories": ["Textbook"],"lastUpdatedOn": "2021-05-20T07:10:32.805+0000","dialcodeRequired": "No","lastStatusChangedOn": "2021-05-19T15:00:44.279+0000","createdFor": ["0126825293972439041"],"creator": "Book Creator","os": ["All"],"versionKey": "1621501113536","idealScreenDensity": "hdpi","framework": "tn_k-12","depth": 0,"createdBy": "8454cb21-3ce9-4e30-85b5-fade097880d8","compatibilityLevel": 1,"userConsent": "Yes","board": "State (Tamil Nadu)","resourceType": "Book"}""".stripMargin + } + + def getQuestionData(): util.HashMap[String, AnyRef] = { + new util.HashMap[String, AnyRef]() { + put("mimeType","application/vnd.sunbird.question") + put("editorState", new util.HashMap[String, AnyRef](){ + put("answer","
\"b1d586453b4e0b1535bf55332347934e\"
") + put("question","
\"b1d586453b4e0b1535bf55332347934e\"
") + put("solutions", new util.ArrayList[AnyRef]() { + new util.HashMap[String, AnyRef](){ + put("id", "4eb6d17a-94e1-5742-5344-41201b049ee6") + put("type", "html") + put("value", "
\"b1d586453b4e0b1535bf55332347934e\"
") + } + }) + }) + put("answer", "
\"b1d586453b4e0b1535bf55332347934e\"
") + put("solutions", new util.ArrayList[AnyRef]() { + new util.HashMap[String, AnyRef](){ + put("id", "4eb6d17a-94e1-5742-5344-41201b049ee6") + put("type", "html") + put("value", "
\"b1d586453b4e0b1535bf55332347934e\"
") + } + }) + put("body", "
\"b1d586453b4e0b1535bf55332347934e\"
") + put("responseDeclaration", new util.HashMap[String, AnyRef](){ + put("response1", new util.HashMap[String, AnyRef](){ + put("type", "string") + }) + }) + } + } + + + private def getNode(): Node = { + val node = new Node() + node.setGraphId("domain") + node.setIdentifier("do_1234") + node.setNodeType("DATA_NODE") + node.setObjectType("Content") + node.setMetadata(new util.HashMap[String, AnyRef]() { + { + put("identifier", "do_1234") + put("mimeType", "application/pdf") + put("status", "Draft") + put("contentType", "Resource") + put("primaryCategory", "Learning Resource") + put("name", "Copy content") + put("artifactUrl", "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/assets/do_212959046431154176151/hindi3.pdf") + put("channel", "in.ekstep") + put("code", "xyz") + put("versionKey", "1234") + } + }) + node + } + + private def getRelativeNode(): Node = { + val node = new Node() + node.setGraphId("domain") + node.setIdentifier("do_1234") + node.setNodeType("DATA_NODE") + node.setObjectType("Content") + node.setMetadata(new util.HashMap[String, AnyRef]() { + { + put("identifier", "do_1234") + put("mimeType", "application/pdf") + put("status", "Draft") + put("contentType", "Resource") + put("primaryCategory", "Learning Resource") + put("name", "Copy content") + put("artifactUrl", "CONTENT_STORAGE_BASE_PATH/content/assets/do_212959046431154176151/hindi3.pdf") + put("channel", "in.ekstep") + put("code", "xyz") + put("versionKey", "1234") + } + }) + node + } +} + + From 0ade216123e3ef07c26b3a7c44284adfff9b0353 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Thu, 1 Dec 2022 19:19:49 +0530 Subject: [PATCH 167/490] Issue #KN-427 feat: Test case update --- .../src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala index 2ee5b68f6..7722d6269 100644 --- a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala +++ b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala @@ -17,7 +17,7 @@ object CSPMetaUtil { val relativePathPrefix: String = Platform.getString("cloudstorage.relative_path_prefix", "") val cspMeta = Platform.getStringList("cloudstorage.metadata.list", new java.util.ArrayList[String]()).asScala.toList val absolutePath = Platform.getString("cloudstorage.read_base_path", "") + java.io.File.separator + Platform.getString("cloud_storage_container", "") - if (MapUtils.isNotEmpty(data)) { + val returnData = if (MapUtils.isNotEmpty(data)) { val updatedMeta: java.util.Map[String, AnyRef] = new java.util.HashMap[String, AnyRef] data.asScala.map(x => if (cspMeta.contains(x._1)) @@ -26,6 +26,8 @@ object CSPMetaUtil { ).asJava updatedMeta } else data + logger.info("CSPMetaUtil ::: updateAbsolutePath util.Map[String, AnyRef] ::: updateAbsolutePath returnData :: " + returnData) + returnData } def updateAbsolutePath(node: Node): Node = { From ac7f27187432f9141eef216daceb8e501b345f77 Mon Sep 17 00:00:00 2001 From: anilgupta Date: Fri, 2 Dec 2022 11:54:42 +0530 Subject: [PATCH 168/490] Issue #KN-439 feat: Fixed ClassCast issue. --- .../src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala index 7722d6269..5ad22b5df 100644 --- a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala +++ b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala @@ -100,7 +100,7 @@ object CSPMetaUtil { val values = data.get("values") val updatedValues = values match { - case x: List[AnyRef] => x.map(value => StringUtils.replaceEach(value.asInstanceOf[String], basePaths, repArray)) + case x: List[AnyRef] => x.map(value => getBasePath("", value, basePaths, repArray)) case _ => values } From 07f6e1f8b96f188991c6672745a0d5245dee2257 Mon Sep 17 00:00:00 2001 From: anilgupta Date: Mon, 5 Dec 2022 10:35:58 +0530 Subject: [PATCH 169/490] Issue #KN-439 feat: Fixed the NullPointer issue --- .../scala/org/sunbird/graph/util/CSPMetaUtil.scala | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala index 5ad22b5df..9425e0166 100644 --- a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala +++ b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala @@ -20,9 +20,13 @@ object CSPMetaUtil { val returnData = if (MapUtils.isNotEmpty(data)) { val updatedMeta: java.util.Map[String, AnyRef] = new java.util.HashMap[String, AnyRef] data.asScala.map(x => - if (cspMeta.contains(x._1)) - updatedMeta.put(x._1, x._2.asInstanceOf[String].replace(relativePathPrefix, absolutePath)) - else updatedMeta.put(x._1, x._2) + if (cspMeta.contains(x._1)) { + x._2 match { + case value: String => + updatedMeta.put(x._1, value.replace(relativePathPrefix, absolutePath)) + case _ => updatedMeta.put(x._1, x._2) + } + } else updatedMeta.put(x._1, x._2) ).asJava updatedMeta } else data From aa54baa8207b4fc2bc04d0bac8d0ee7f7a1e5eda Mon Sep 17 00:00:00 2001 From: Kumar Gauraw Date: Mon, 5 Dec 2022 12:58:42 +0530 Subject: [PATCH 170/490] Issue #IQ-193 fix: updated code for cname --- .../org/sunbird/graph/GraphService.scala | 2 +- .../org/sunbird/graph/util/CSPMetaUtil.scala | 53 ++++++++++++++----- .../sunbird/graph/util/ScalaJsonUtil.scala | 37 +++++++++++++ 3 files changed, 78 insertions(+), 14 deletions(-) create mode 100644 ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/ScalaJsonUtil.scala diff --git a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/GraphService.scala b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/GraphService.scala index d555e8ff5..12be0be1b 100644 --- a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/GraphService.scala +++ b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/GraphService.scala @@ -57,7 +57,7 @@ class GraphService { def readExternalProps(request: Request, fields: List[String]): Future[Response] = { ExternalPropsManager.fetchProps(request, fields).map(res => if(isrRelativePathEnabled) { - val updatedResult = CSPMetaUtil.updateAbsolutePath(res.getResult) + val updatedResult = CSPMetaUtil.updateExternalAbsolutePath(res.getResult) val response = ResponseHandler.OK() response.putAll(updatedResult) response diff --git a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala index 5ad22b5df..7ef7643b3 100644 --- a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala +++ b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala @@ -1,5 +1,7 @@ package org.sunbird.graph.util +import java.util + import org.apache.commons.collections4.MapUtils import org.apache.commons.lang3.StringUtils import org.slf4j.LoggerFactory @@ -8,6 +10,7 @@ import org.sunbird.common.{JsonUtils, Platform} import org.sunbird.graph.dac.model.Node import scala.collection.JavaConverters._ +import scala.collection.immutable.Map object CSPMetaUtil { private[this] val logger = LoggerFactory.getLogger(classOf[CSPMetaUtil]) @@ -97,35 +100,59 @@ object CSPMetaUtil { val validCSPSource: List[String] = Platform.getStringList("cloudstorage.write_base_path", new java.util.ArrayList[String]()).asScala.toList val basePaths: Array[String] = validCSPSource.map(source => source + java.io.File.separator + Platform.getString("cloud_storage_container", "")).toArray val repArray = getReplacementData(basePaths, relativePathPrefix) - val values = data.get("values") val updatedValues = values match { case x: List[AnyRef] => x.map(value => getBasePath("", value, basePaths, repArray)) case _ => values } - data.put("values", updatedValues) logger.info("CSPMetaUtil ::: updateExternalRelativePath util.Map[String, AnyRef] ::: data after url replace :: " + data) data } + def updateExternalAbsolutePath(data: java.util.Map[String, AnyRef]): java.util.Map[String, AnyRef] = { + //No need to check the metadata fields because that will be taken care while writing data. + logger.info("CSPMetaUtil ::: updateExternalAbsolutePath util.Map[String, AnyRef] ::: data before url replace :: " + data) + val relativePathPrefix: String = Platform.getString("cloudstorage.relative_path_prefix", "") + //Not Implemented logic based on external field key, because while writing data it is not considered. + //val extFieldList = Platform.getStringList("cloudstorage.external_field_list", new java.util.ArrayList[String]()).asScala.toList + val absolutePath = Platform.getString("cloudstorage.read_base_path", "") + java.io.File.separator + Platform.getString("cloud_storage_container", "") + val returnData = if (MapUtils.isNotEmpty(data)) { + val updatedMeta: java.util.Map[String, AnyRef] = new java.util.HashMap[String, AnyRef] + data.asScala.map(x => updatedMeta.put(x._1, getBasePath(x._1, x._2, Array(relativePathPrefix), Array(absolutePath))) + ).asJava + updatedMeta + } else data + logger.info("CSPMetaUtil ::: updateExternalAbsolutePath util.Map[String, AnyRef] ::: data before url replace :: " + returnData) + returnData + } + private def getBasePath(key: String, value: AnyRef, oldPath: Array[String], newPath: Array[String]): AnyRef = { logger.info(s"CSPMetaUtil ::: getBasePath ::: Updating Path for Key : $key & Value : $value") val res = if (null != value) { value match { - case x: String => if (StringUtils.isNotBlank(x)) StringUtils.replaceEach(x, oldPath, newPath) else x - case y: Map[String, AnyRef] => { - val dStr = JsonUtils.serialize(y) - val result = StringUtils.replaceEach(dStr, oldPath, newPath) - val output: Map[String, AnyRef] = JsonUtils.deserialize(result, classOf[Map[String, AnyRef]]) - output + case p: String => if (StringUtils.isNotBlank(p)) StringUtils.replaceEach(p, oldPath, newPath) else p + case q: Map[String, AnyRef] => { + val updatedObjString = StringUtils.replaceEach(ScalaJsonUtil.serialize(q), oldPath, newPath) + val updatedData = ScalaJsonUtil.deserialize[Map[String, AnyRef]](updatedObjString) + updatedData + } + case r: java.util.Map[String, AnyRef] => { + val updatedObjString = StringUtils.replaceEach(JsonUtils.serialize(r), oldPath, newPath) + val updatedData = JsonUtils.deserialize(updatedObjString, classOf[java.util.List[AnyRef]]) + updatedData + } + case s: util.List[AnyRef] => { + val updatedObjString = StringUtils.replaceEach(JsonUtils.serialize(s), oldPath, newPath) + val updatedData = JsonUtils.deserialize(updatedObjString, classOf[java.util.List[AnyRef]]) + updatedData } - case z: java.util.Map[String, AnyRef] => { - val dStr = JsonUtils.serialize(z) - val result = StringUtils.replaceEach(dStr, oldPath, newPath) - val output: java.util.Map[String, AnyRef] = JsonUtils.deserialize(result, classOf[java.util.Map[String, AnyRef]]) - output + case t: List[AnyRef] => { + val updatedObjString = StringUtils.replaceEach(ScalaJsonUtil.serialize(t), oldPath, newPath) + val updatedData = ScalaJsonUtil.deserialize[List[AnyRef]](updatedObjString) + updatedData } + case _ => value } } else value logger.info(s"CSPMetaUtil ::: getBasePath ::: Updated Path for Key : $key & Updated Value is : $res") diff --git a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/ScalaJsonUtil.scala b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/ScalaJsonUtil.scala new file mode 100644 index 000000000..f2c0abd30 --- /dev/null +++ b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/ScalaJsonUtil.scala @@ -0,0 +1,37 @@ +package org.sunbird.graph.util + +import java.lang.reflect.{ParameterizedType, Type} + +import com.fasterxml.jackson.core.`type`.TypeReference +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.scala.DefaultScalaModule + +object ScalaJsonUtil { + + @transient val mapper = new ObjectMapper() + mapper.registerModule(DefaultScalaModule) + + @throws(classOf[Exception]) + def serialize(obj: AnyRef): String = { + mapper.writeValueAsString(obj); + } + + @throws(classOf[Exception]) + def deserialize[T: Manifest](value: String): T = mapper.readValue(value, typeReference[T]); + + private[this] def typeReference[T: Manifest] = new TypeReference[T] { + override def getType = typeFromManifest(manifest[T]) + } + + + private[this] def typeFromManifest(m: Manifest[_]): Type = { + if (m.typeArguments.isEmpty) { m.runtimeClass } + // $COVERAGE-OFF$Disabling scoverage as this code is impossible to test + else new ParameterizedType { + def getRawType = m.runtimeClass + def getActualTypeArguments = m.typeArguments.map(typeFromManifest).toArray + def getOwnerType = null + } + // $COVERAGE-ON$ + } +} From 9c0c5657ad8c65f15ddf871b2181d1b203d61536 Mon Sep 17 00:00:00 2001 From: Kumar Gauraw Date: Mon, 5 Dec 2022 13:31:12 +0530 Subject: [PATCH 171/490] Issue #IQ-193 fix: updated code --- .../src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala index 7ef7643b3..4f1e5910a 100644 --- a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala +++ b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala @@ -139,7 +139,7 @@ object CSPMetaUtil { } case r: java.util.Map[String, AnyRef] => { val updatedObjString = StringUtils.replaceEach(JsonUtils.serialize(r), oldPath, newPath) - val updatedData = JsonUtils.deserialize(updatedObjString, classOf[java.util.List[AnyRef]]) + val updatedData = JsonUtils.deserialize(updatedObjString, classOf[java.util.Map[String, AnyRef]]) updatedData } case s: util.List[AnyRef] => { From a784e4ae6191b912e447d8386820c98ccf55c8c2 Mon Sep 17 00:00:00 2001 From: Kumar Gauraw Date: Mon, 5 Dec 2022 15:45:44 +0530 Subject: [PATCH 172/490] Issue #IQ-193 feat: added test cases --- .../sunbird/graph/util/CSPMetaUtilTest.scala | 47 ++++++++++++++++- .../graph/util/ScalaJsonUtilTest.scala | 52 +++++++++++++++++++ 2 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 ontology-engine/graph-core_2.11/src/test/scala/org/sunbird/graph/util/ScalaJsonUtilTest.scala diff --git a/ontology-engine/graph-core_2.11/src/test/scala/org/sunbird/graph/util/CSPMetaUtilTest.scala b/ontology-engine/graph-core_2.11/src/test/scala/org/sunbird/graph/util/CSPMetaUtilTest.scala index c69bb2bfc..8056c3e5e 100644 --- a/ontology-engine/graph-core_2.11/src/test/scala/org/sunbird/graph/util/CSPMetaUtilTest.scala +++ b/ontology-engine/graph-core_2.11/src/test/scala/org/sunbird/graph/util/CSPMetaUtilTest.scala @@ -2,7 +2,7 @@ package org.sunbird.graph.util import org.sunbird.graph.BaseSpec import org.sunbird.graph.dac.model.Node - +import scala.collection.JavaConverters._ import java.util class CSPMetaUtilTest extends BaseSpec { @@ -35,6 +35,51 @@ class CSPMetaUtilTest extends BaseSpec { assert(migratedData != null && migratedData.get(0).getMetadata.toString.contains("https://sunbirddev.blob.core.windows.net/sunbird-content-dev")) } + "updateExternalRelativePath" should "return data having relative path" in { + val data = new java.util.HashMap[String, AnyRef]{{ + put("identifier", "do_213680293213650944125"); + put("values", List(new util.HashMap[String, AnyRef](){{ + put("default", "

Question Set

") + }}, new util.HashMap[String, AnyRef](){{ + put("identifier", "do_213680293213650944125"); + put("children",List(new util.HashMap[String, AnyRef](){{ + put("identifier", "do_123") + put("appIcon", "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/assets/do_21367965089393049618/b1d586453b4e0b1535bf55332347934e.jpg") + }}).asJava) + }}, + new util.ArrayList[util.HashMap[String, String]](){{ + add(new util.HashMap[String, String](){{ + put("src", "/asset/public/content/assets/do_21367965089393049618/b1d586453b4e0b1535bf55332347934e.jpg"); + put("basePath", "https://inquiry.sunbird.org") + }}); + add(new util.HashMap[String, String](){{ + put("src", "/asset/public/content/assets/do_21367965089393049618/b1d586453b4e0b1535bf55332347934e.jpg"); + put("basePath", "https://inquiry.sunbird.org") + }}) + }})); + put("fields", List("instructions", "hierarchy")) + }} + val output = CSPMetaUtil.updateExternalRelativePath(data) + assert(!output.toString.contains("https://sunbirddev.blob.core.windows.net/sunbird-content-dev")) + assert(output.toString.contains("CONTENT_STORAGE_BASE_PATH")) + } + + "updateExternalAbsolutePath" should "return data with absolute cloud path" in { + val data = new util.HashMap[String, AnyRef](){{ + put("identifier", "do_123") + put("hierarchy", new util.HashMap[String, AnyRef](){{ + put("identifier", "do_213680293213650944125"); + put("children",List(new util.HashMap[String, AnyRef](){{ + put("identifier", "do_123") + put("appIcon", "CONTENT_STORAGE_BASE_PATH/content/assets/do_21367965089393049618/b1d586453b4e0b1535bf55332347934e.jpg") + }}).asJava) + }}) + }} + val output = CSPMetaUtil.updateExternalAbsolutePath(data) + assert(output.toString.contains("https://sunbirddev.blob.core.windows.net/sunbird-content-dev")) + assert(!output.toString.contains("CONTENT_STORAGE_BASE_PATH")) + } + def getReplaceData(): String = { """{"ownershipType": ["createdBy"],"subject": ["Mathematics"],"channel": "0126825293972439041","organisation": ["Sunbird"], |"language": ["English"],"mimeType": "application/vnd.ekstep.content-collection","objectType": "Content","gradeLevel": ["Class 4"], diff --git a/ontology-engine/graph-core_2.11/src/test/scala/org/sunbird/graph/util/ScalaJsonUtilTest.scala b/ontology-engine/graph-core_2.11/src/test/scala/org/sunbird/graph/util/ScalaJsonUtilTest.scala new file mode 100644 index 000000000..59d634ea9 --- /dev/null +++ b/ontology-engine/graph-core_2.11/src/test/scala/org/sunbird/graph/util/ScalaJsonUtilTest.scala @@ -0,0 +1,52 @@ +package org.sunbird.graph.util + +import java.util + +import com.fasterxml.jackson.databind.exc.{InvalidDefinitionException, MismatchedInputException} +import org.apache.commons.lang3.StringUtils +import org.codehaus.jackson.JsonProcessingException +import org.scalatest.{FlatSpec, Matchers} + +class ScalaJsonUtilTest extends FlatSpec with Matchers { + + "serializing an empty object" should "Throw InvalidDefinitionException" in { + assertThrows[InvalidDefinitionException] { // Result type: Assertion + ScalaJsonUtil.serialize(new Object) + } + } + + "serializing an empty object" should "Throw JsonProcessingException" ignore { + assertThrows[JsonProcessingException] { // Result type: Assertion + ScalaJsonUtil.serialize(new util.HashMap()) + } + } + + "serializing a valid Map object" should "Should serialize the object" in { + val value: String = ScalaJsonUtil.serialize(Map("identifier" -> "do_1234", "status" -> "Draft")) + assert(StringUtils.equalsIgnoreCase(value, "{\"identifier\":\"do_1234\",\"status\":\"Draft\"}")) + } + + "serializing a valid List object" should "Should serialize the object" in { + val value: String = ScalaJsonUtil.serialize(List("identifier", "do_1234", "status", "Draft")) + assert(StringUtils.equalsIgnoreCase(value, "[\"identifier\",\"do_1234\",\"status\",\"Draft\"]")) + } + + "deserializing a stringified map" should "Should deserialize the string to map" in { + val value: Map[String, AnyRef] = ScalaJsonUtil.deserialize[Map[String, AnyRef]]("{\"identifier\":\"do_1234\",\"status\":\"Draft\"}") + assert(value != null) + assert(value.getOrElse("status", "").asInstanceOf[String] == "Draft") + } + + "deserializing a stringified list to map" should "Should throw Exception" in { + assertThrows[MismatchedInputException] { + ScalaJsonUtil.deserialize[Map[String, AnyRef]]("[\"identifier\",\"do_1234\",\"status\",\"Draft\"]") + } + } + + "deserializing a stringified list" should "Should deserialize the string to list" in { + val value:List[String] = ScalaJsonUtil.deserialize[List[String]]("[\"identifier\",\"do_1234\",\"status\",\"Draft\"]") + assert(value != null) + assert(value.size == 4) + } + +} From a775bc7b05e21d86a7355c9489174e3e056207d4 Mon Sep 17 00:00:00 2001 From: anilgupta Date: Tue, 6 Dec 2022 16:20:38 +0530 Subject: [PATCH 173/490] Issue #KN-439 chore: added the migrationVersion in asset, content and collection schema.json --- schemas/asset/1.0/schema.json | 3 +++ schemas/collection/1.0/schema.json | 3 +++ schemas/content/1.0/schema.json | 3 +++ 3 files changed, 9 insertions(+) diff --git a/schemas/asset/1.0/schema.json b/schemas/asset/1.0/schema.json index 5a4dc078b..973de6a5b 100644 --- a/schemas/asset/1.0/schema.json +++ b/schemas/asset/1.0/schema.json @@ -1261,6 +1261,9 @@ "items": { "type": "object" } + }, + "migrationVersion": { + "type": "number" } } } \ No newline at end of file diff --git a/schemas/collection/1.0/schema.json b/schemas/collection/1.0/schema.json index 2f897db5a..316dcf05b 100644 --- a/schemas/collection/1.0/schema.json +++ b/schemas/collection/1.0/schema.json @@ -1298,6 +1298,9 @@ "items": { "type": "object" } + }, + "migrationVersion": { + "type": "number" } } } diff --git a/schemas/content/1.0/schema.json b/schemas/content/1.0/schema.json index 9a0728d6b..38e82681d 100644 --- a/schemas/content/1.0/schema.json +++ b/schemas/content/1.0/schema.json @@ -1408,6 +1408,9 @@ "items": { "type": "object" } + }, + "migrationVersion": { + "type": "number" } } } From 6f4e31f764129bc6a1055cdb0202783e4422c2ee Mon Sep 17 00:00:00 2001 From: anilgupta Date: Wed, 7 Dec 2022 15:59:36 +0530 Subject: [PATCH 174/490] Issue #KN-439 chore: Added the more fields in metadata list --- content-api/content-service/conf/application.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content-api/content-service/conf/application.conf b/content-api/content-service/conf/application.conf index 5c189d9a8..72b9a64a9 100644 --- a/content-api/content-service/conf/application.conf +++ b/content-api/content-service/conf/application.conf @@ -763,4 +763,4 @@ cloudstorage.metadata.replace_absolute_path=false cloudstorage.relative_path_prefix= "CONTENT_STORAGE_BASE_PATH" cloudstorage.read_base_path="https://sunbirddev.blob.core.windows.net" cloudstorage.write_base_path=["https://sunbirddev.blob.core.windows.net"] -cloudstorage.metadata.list=["appIcon","posterImage","artifactUrl","downloadUrl","variants","previewUrl","pdfUrl", "streamingUrl", "toc_url"] +cloudstorage.metadata.list=["appIcon", "artifactUrl", "posterImage", "previewUrl", "thumbnail", "assetsMap", "certTemplate", "itemSetPreviewUrl", "grayScaleAppIcon", "sourceURL", "variants", "downloadUrl", "streamingUrl", "toc_url", "data", "question", "solutions", "editorState", "media", "pdfUrl"] From c4a7c0965ea1d6209fab098074cebfb5c82dbefa Mon Sep 17 00:00:00 2001 From: Gourav More Date: Fri, 16 Dec 2022 10:22:49 +0530 Subject: [PATCH 175/490] Issue #KN-667 fix: Search-service: Search API - Part 2 --- ...mposite Search API.postman_collection.json | 325 +++++++++++++----- ...Composite Search API.postman_test_run.json | 258 ++++++++++++-- 2 files changed, 463 insertions(+), 120 deletions(-) diff --git a/search-api/api-tests/Collections/Composite Search API.postman_collection.json b/search-api/api-tests/Collections/Composite Search API.postman_collection.json index a557142c2..792ae1489 100644 --- a/search-api/api-tests/Collections/Composite Search API.postman_collection.json +++ b/search-api/api-tests/Collections/Composite Search API.postman_collection.json @@ -21,10 +21,6 @@ "exec": [ "let jsonResponse = pm.response.json();", "", - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.have.status(200);", - "});", - "", "pm.test(\"Verify keys and data type of the response result\", () => {", " pm.expect(jsonResponse.result.content).to.be.a(\"array\");", "});", @@ -67,7 +63,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"request\": {\n \"filters\": {\n \"status\": [\n \"Live\"\n ],\n \"mimeType\": \"application/vnd.ekstep.ecml-archive\",\n \"contentType\": \"Resource\",\n \"objectType\": \"Content\"\n },\n \"sort_by\": {\n \"lastUpdatedOn\": \"desc\"\n }\n }\n}" + "raw": "{\n \"request\": {\n \"filters\": {\n \"status\": [\n \"Live\"\n ],\n \"mimeType\": \"application/vnd.ekstep.ecml-archive\",\n \"objectType\": \"Content\"\n },\n \"sort_by\": {\n \"lastUpdatedOn\": \"desc\"\n }\n }\n}" }, "url": { "raw": "{{host}}/action/composite/v3/search", @@ -308,92 +304,255 @@ "name": "Negative", "item": [ { - "name": "400 - Search filters missing", + "name": "500 Server Error", + "item": [ + { + "name": "500 - Property values are not in the right format", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "pm.test(\"Verify keys and data type of the response result\", () => {", + " pm.expect(jsonResponse.result).to.be.an(\"object\");", + "});", + "", + "// Params", + "pm.test(\"params.err should be \\\"ERR_SYSTEM_EXCEPTION\\\"\", () => {", + " pm.expect(jsonResponse.params.err).to.eql(\"ERR_SYSTEM_EXCEPTION\");", + "});", + "pm.test(\"params.errmsg should be \\\"Unsupported operation\\\"\", () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(\"Unsupported operation\");", + "});" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": { + "connection": true + } + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "X-Channel-Id", + "type": "text", + "value": "{{channel_id}}", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"filters\": {\n \"offset\": 1,\n \"limit\": 5,\n \"query\": \"\",\n \"sort_by\": {\n \"lastUpdatedOn\": \"desc\"\n }\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/action/composite/v3/search", + "host": [ + "{{host}}" + ], + "path": [ + "action", + "composite", + "v3", + "search" + ] + }, + "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + }, + { + "name": "500 - Property values is violating the schema definition(max length, min length etc)", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "// Params", + "pm.test(\"params.err should be \\\"ERR_SYSTEM_EXCEPTION\\\"\", () => {", + " pm.expect(jsonResponse.params.err).to.eql(\"ERR_SYSTEM_EXCEPTION\");", + "});", + "pm.test(\"params.errmsg should be \\\"Unsupported operation\\\"\", () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(\"Unsupported operation\");", + "});" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": { + "connection": true + } + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "X-Channel-Id", + "type": "text", + "value": "{{channel_id}}", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"filters\": {\n \"offset\": 1,\n \"limit\": 5,\n \"query\": \"\",\n \"sort_by\": {\n \"lastUpdatedOn\": \"desc\"\n }\n }\n }\n}" + }, + "url": { + "raw": "{{host}}/action/composite/v3/search", + "host": [ + "{{host}}" + ], + "path": [ + "action", + "composite", + "v3", + "search" + ] + }, + "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + }, + { + "name": "500 - parameter cannot be negative", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let jsonResponse = pm.response.json()", + "", + "// Params", + "pm.test(\"params.err should be \\\"ERR_SYSTEM_EXCEPTION\\\"\", () => {", + " pm.expect(jsonResponse.params.err).to.eql(\"ERR_SYSTEM_EXCEPTION\");", + "});", + "pm.test(\"params.errmsg should be \\\"[size] parameter cannot be negative, found [-1]\\\"\", () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(\"[size] parameter cannot be negative, found [-1]\");", + "});" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": { + "connection": true + } + }, + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Authenticated-User-token", + "value": "{{keycloak_access_token}}" + }, + { + "key": "Authorization", + "value": "{{kong_api_key}}" + }, + { + "key": "X-Channel-Id", + "type": "text", + "value": "{{channel_id}}", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"filters\": {\n \"objectType\": [\n \"AssessmentItem\"\n ],\n \"status\": [\n \"Live\"\n ]\n },\n \"sort_by\": {\n \"lastUpdatedOn\": \"desc\"\n },\n \"limit\": -1\n }\n}" + }, + "url": { + "raw": "{{host}}/action/composite/v3/search", + "host": [ + "{{host}}" + ], + "path": [ + "action", + "composite", + "v3", + "search" + ] + }, + "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [] + } + ], "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + }, { "listen": "test", "script": { + "type": "text/javascript", "exec": [ "let jsonResponse = pm.response.json()", "", "//Verification", - "pm.test(\"Status code is 400\", () => {", - " pm.response.to.have.status(400)", - "});", - "//code name contains a string", - "tests[\"Status code name has string \\\"Bad Request\\\"\"] = responseCode.name.has(\"Bad Request\");", - "", - "pm.test(\"Verify keys and data type of the response result\", () => {", - " pm.expect(jsonResponse.result).to.be.an(\"object\");", + "pm.test(\"Status code is 500\", () => {", + " pm.response.to.have.status(500)", "});", "", "//Validation", "// Response body", - "pm.test(\"responseCode should be \\\"CLIENT_ERROR\\\"\", () => {", - " pm.expect(jsonResponse.responseCode).to.eql(\"CLIENT_ERROR\");", + "pm.test(\"responseCode should be \\\"SERVER_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"SERVER_ERROR\");", "});", "", - "// Params", - "pm.test(\"params.err should be \\\"ERR_CONTENT_SEARCH_FIELDS_MISSING\\\"\", () => {", - " pm.expect(jsonResponse.params.err).to.eql(\"ERR_CONTENT_SEARCH_FIELDS_MISSING\");", - "});", - "pm.test(\"params.errmsg should be \\\"Required fields for search content are missing\\\"\", () => {", - " pm.expect(jsonResponse.params.errmsg).to.eql(\"Required fields for search content are missing\");", + "pm.test(\"Verify keys and data type of the response result\", () => {", + " pm.expect(jsonResponse.result).to.be.an(\"object\");", "});" - ], - "type": "text/javascript" + ] } } - ], - "protocolProfileBehavior": { - "disabledSystemHeaders": { - "connection": true - } - }, - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "X-Authenticated-User-token", - "value": "{{keycloak_access_token}}" - }, - { - "key": "Authorization", - "value": "{{kong_api_key}}" - }, - { - "key": "X-Channel-Id", - "value": "{{channel_id}}", - "type": "text", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"request\": {\n }\n}" - }, - "url": { - "raw": "{{host}}/action/composite/v3/search", - "host": [ - "{{host}}" - ], - "path": [ - "action", - "composite", - "v3", - "search" - ] - }, - "description": "This API is associated with batch creation\nThe endpoint for Creates a Batch is /batch/create\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." - }, - "response": [] + ] }, { - "name": "500 - parameter cannot be negative", + "name": "400 - Mandatory fields missing", "event": [ { "listen": "test", @@ -402,9 +561,11 @@ "let jsonResponse = pm.response.json()", "", "//Verification", - "pm.test(\"Status code is 500\", () => {", - " pm.response.to.have.status(500)", + "pm.test(\"Status code is 400\", () => {", + " pm.response.to.have.status(400)", "});", + "//code name contains a string", + "tests[\"Status code name has string \\\"Bad Request\\\"\"] = responseCode.name.has(\"Bad Request\");", "", "pm.test(\"Verify keys and data type of the response result\", () => {", " pm.expect(jsonResponse.result).to.be.an(\"object\");", @@ -412,16 +573,16 @@ "", "//Validation", "// Response body", - "pm.test(\"responseCode should be \\\"SERVER_ERROR\\\"\", () => {", - " pm.expect(jsonResponse.responseCode).to.eql(\"SERVER_ERROR\");", + "pm.test(\"responseCode should be \\\"CLIENT_ERROR\\\"\", () => {", + " pm.expect(jsonResponse.responseCode).to.eql(\"CLIENT_ERROR\");", "});", "", "// Params", - "pm.test(\"params.err should be \\\"ERR_SYSTEM_EXCEPTION\\\"\", () => {", - " pm.expect(jsonResponse.params.err).to.eql(\"ERR_SYSTEM_EXCEPTION\");", + "pm.test(\"params.err should be \\\"ERR_CONTENT_SEARCH_FIELDS_MISSING\\\"\", () => {", + " pm.expect(jsonResponse.params.err).to.eql(\"ERR_CONTENT_SEARCH_FIELDS_MISSING\");", "});", - "pm.test(\"params.errmsg should be \\\"[size] parameter cannot be negative, found [-1]\\\"\", () => {", - " pm.expect(jsonResponse.params.errmsg).to.eql(\"[size] parameter cannot be negative, found [-1]\");", + "pm.test(\"params.errmsg should be \\\"Required fields for search content are missing\\\"\", () => {", + " pm.expect(jsonResponse.params.errmsg).to.eql(\"Required fields for search content are missing\");", "});" ], "type": "text/javascript" @@ -450,14 +611,14 @@ }, { "key": "X-Channel-Id", - "value": "{{channel_id}}", "type": "text", + "value": "{{channel_id}}", "disabled": true } ], "body": { "mode": "raw", - "raw": "{\n \"request\": {\n \"filters\": {\n \"objectType\": [\n \"AssessmentItem\"\n ],\n \"status\": [\n \"Live\"\n ]\n },\n \"sort_by\": {\n \"lastUpdatedOn\": \"desc\"\n },\n \"limit\": -1\n }\n}" + "raw": "{\n \"request\": {\n }\n}" }, "url": { "raw": "{{host}}/action/composite/v3/search", diff --git a/search-api/api-tests/Reports/Composite Search API.postman_test_run.json b/search-api/api-tests/Reports/Composite Search API.postman_test_run.json index 913a356e0..3beb89b3e 100644 --- a/search-api/api-tests/Reports/Composite Search API.postman_test_run.json +++ b/search-api/api-tests/Reports/Composite Search API.postman_test_run.json @@ -1,18 +1,18 @@ { - "id": "320b48d5-cb8d-48c0-a7b9-3743821dbd86", + "id": "bad09758-306c-4b7f-8017-e721135c7c4e", "name": "Composite Search API", - "timestamp": "2022-11-18T04:22:08.826Z", + "timestamp": "2022-12-16T04:50:18.451Z", "collection_id": "65bcacf8-0c90-4f3f-874b-a5d2c01cdc36", "folder_id": 0, "environment_id": "43ba92f2-e81b-446b-8b2a-589126842629", - "totalPass": 59, + "totalPass": 81, "totalFail": 0, "results": [ { "id": "6c4437c8-62ec-4917-bc5a-838fd333b71c", "name": "200 - Get live Content list", "url": "https://dev.knowlg.sunbird.org//action/composite/v3/search", - "time": 2389, + "time": 441, "responseCode": { "code": 200, "name": "OK" @@ -77,7 +77,7 @@ } }, "times": [ - 2389 + 441 ], "allTests": [ { @@ -99,7 +99,7 @@ "id": "eaba0158-fbb9-480a-8971-e5f4aad1fc47", "name": "200 - Get live Assesment list", "url": "https://dev.knowlg.sunbird.org//action/composite/v3/search", - "time": 303, + "time": 295, "responseCode": { "code": 200, "name": "OK" @@ -164,7 +164,7 @@ } }, "times": [ - 303 + 295 ], "allTests": [ { @@ -186,7 +186,7 @@ "id": "97be2029-e5dc-4dcd-8736-9178818022cf", "name": "200 - Get live assets list", "url": "https://dev.knowlg.sunbird.org//action/composite/v3/search", - "time": 285, + "time": 287, "responseCode": { "code": 200, "name": "OK" @@ -251,7 +251,7 @@ } }, "times": [ - 285 + 287 ], "allTests": [ { @@ -270,13 +270,13 @@ ] }, { - "id": "74f75390-3349-4cff-b105-20788af46c95", - "name": "400 - Search filters missing", + "id": "0e58ccb3-8c25-4331-9dd3-10f63b6f8cb4", + "name": "500 - Property values are not in the right format", "url": "https://dev.knowlg.sunbird.org//action/composite/v3/search", - "time": 290, + "time": 148, "responseCode": { - "code": 400, - "name": "Bad Request" + "code": 500, + "name": "Internal Server Error" }, "tests": { "Content-Type header is present": true, @@ -285,12 +285,11 @@ "params.status should be \"failed\"": true, "id should be api.v1.search": true, "ver should be 1.0": true, - "Status code is 400": true, + "Status code is 500": true, + "responseCode should be \"SERVER_ERROR\"": true, "Verify keys and data type of the response result": true, - "responseCode should be \"CLIENT_ERROR\"": true, - "params.err should be \"ERR_CONTENT_SEARCH_FIELDS_MISSING\"": true, - "params.errmsg should be \"Required fields for search content are missing\"": true, - "Status code name has string \"Bad Request\"": true + "params.err should be \"ERR_SYSTEM_EXCEPTION\"": true, + "params.errmsg should be \"Unsupported operation\"": true }, "testPassFailCounts": { "Content-Type header is present": { @@ -317,7 +316,11 @@ "pass": 1, "fail": 0 }, - "Status code is 400": { + "Status code is 500": { + "pass": 1, + "fail": 0 + }, + "responseCode should be \"SERVER_ERROR\"": { "pass": 1, "fail": 0 }, @@ -325,25 +328,104 @@ "pass": 1, "fail": 0 }, - "responseCode should be \"CLIENT_ERROR\"": { + "params.err should be \"ERR_SYSTEM_EXCEPTION\"": { "pass": 1, "fail": 0 }, - "params.err should be \"ERR_CONTENT_SEARCH_FIELDS_MISSING\"": { + "params.errmsg should be \"Unsupported operation\"": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 148 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "id should be api.v1.search": true, + "ver should be 1.0": true, + "Status code is 500": true, + "responseCode should be \"SERVER_ERROR\"": true, + "Verify keys and data type of the response result": true, + "params.err should be \"ERR_SYSTEM_EXCEPTION\"": true, + "params.errmsg should be \"Unsupported operation\"": true + } + ] + }, + { + "id": "6ba750db-f029-4987-ae9b-54e35a4a807c", + "name": "500 - Property values is violating the schema definition(max length, min length etc)", + "url": "https://dev.knowlg.sunbird.org//action/composite/v3/search", + "time": 198, + "responseCode": { + "code": 500, + "name": "Internal Server Error" + }, + "tests": { + "Content-Type header is present": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "id should be api.v1.search": true, + "ver should be 1.0": true, + "Status code is 500": true, + "responseCode should be \"SERVER_ERROR\"": true, + "Verify keys and data type of the response result": true, + "params.err should be \"ERR_SYSTEM_EXCEPTION\"": true, + "params.errmsg should be \"Unsupported operation\"": true + }, + "testPassFailCounts": { + "Content-Type header is present": { "pass": 1, "fail": 0 }, - "params.errmsg should be \"Required fields for search content are missing\"": { + "Validate keys and data type of the response body": { "pass": 1, "fail": 0 }, - "Status code name has string \"Bad Request\"": { + "Verify keys and data type of the params in negative scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"failed\"": { + "pass": 1, + "fail": 0 + }, + "id should be api.v1.search": { + "pass": 1, + "fail": 0 + }, + "ver should be 1.0": { + "pass": 1, + "fail": 0 + }, + "Status code is 500": { + "pass": 1, + "fail": 0 + }, + "responseCode should be \"SERVER_ERROR\"": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the response result": { + "pass": 1, + "fail": 0 + }, + "params.err should be \"ERR_SYSTEM_EXCEPTION\"": { + "pass": 1, + "fail": 0 + }, + "params.errmsg should be \"Unsupported operation\"": { "pass": 1, "fail": 0 } }, "times": [ - 290 + 198 ], "allTests": [ { @@ -353,12 +435,11 @@ "params.status should be \"failed\"": true, "id should be api.v1.search": true, "ver should be 1.0": true, - "Status code is 400": true, + "Status code is 500": true, + "responseCode should be \"SERVER_ERROR\"": true, "Verify keys and data type of the response result": true, - "responseCode should be \"CLIENT_ERROR\"": true, - "params.err should be \"ERR_CONTENT_SEARCH_FIELDS_MISSING\"": true, - "params.errmsg should be \"Required fields for search content are missing\"": true, - "Status code name has string \"Bad Request\"": true + "params.err should be \"ERR_SYSTEM_EXCEPTION\"": true, + "params.errmsg should be \"Unsupported operation\"": true } ] }, @@ -366,7 +447,7 @@ "id": "8fb24515-12e0-430f-af59-7837ed777afb", "name": "500 - parameter cannot be negative", "url": "https://dev.knowlg.sunbird.org//action/composite/v3/search", - "time": 255, + "time": 173, "responseCode": { "code": 500, "name": "Internal Server Error" @@ -379,8 +460,8 @@ "id should be api.v1.search": true, "ver should be 1.0": true, "Status code is 500": true, - "Verify keys and data type of the response result": true, "responseCode should be \"SERVER_ERROR\"": true, + "Verify keys and data type of the response result": true, "params.err should be \"ERR_SYSTEM_EXCEPTION\"": true, "params.errmsg should be \"[size] parameter cannot be negative, found [-1]\"": true }, @@ -413,11 +494,11 @@ "pass": 1, "fail": 0 }, - "Verify keys and data type of the response result": { + "responseCode should be \"SERVER_ERROR\"": { "pass": 1, "fail": 0 }, - "responseCode should be \"SERVER_ERROR\"": { + "Verify keys and data type of the response result": { "pass": 1, "fail": 0 }, @@ -431,7 +512,7 @@ } }, "times": [ - 255 + 173 ], "allTests": [ { @@ -442,16 +523,109 @@ "id should be api.v1.search": true, "ver should be 1.0": true, "Status code is 500": true, - "Verify keys and data type of the response result": true, "responseCode should be \"SERVER_ERROR\"": true, + "Verify keys and data type of the response result": true, "params.err should be \"ERR_SYSTEM_EXCEPTION\"": true, "params.errmsg should be \"[size] parameter cannot be negative, found [-1]\"": true } ] + }, + { + "id": "74f75390-3349-4cff-b105-20788af46c95", + "name": "400 - Mandatory fields missing", + "url": "https://dev.knowlg.sunbird.org//action/composite/v3/search", + "time": 210, + "responseCode": { + "code": 400, + "name": "Bad Request" + }, + "tests": { + "Content-Type header is present": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "id should be api.v1.search": true, + "ver should be 1.0": true, + "Status code is 400": true, + "Verify keys and data type of the response result": true, + "responseCode should be \"CLIENT_ERROR\"": true, + "params.err should be \"ERR_CONTENT_SEARCH_FIELDS_MISSING\"": true, + "params.errmsg should be \"Required fields for search content are missing\"": true, + "Status code name has string \"Bad Request\"": true + }, + "testPassFailCounts": { + "Content-Type header is present": { + "pass": 1, + "fail": 0 + }, + "Validate keys and data type of the response body": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the params in negative scenario": { + "pass": 1, + "fail": 0 + }, + "params.status should be \"failed\"": { + "pass": 1, + "fail": 0 + }, + "id should be api.v1.search": { + "pass": 1, + "fail": 0 + }, + "ver should be 1.0": { + "pass": 1, + "fail": 0 + }, + "Status code is 400": { + "pass": 1, + "fail": 0 + }, + "Verify keys and data type of the response result": { + "pass": 1, + "fail": 0 + }, + "responseCode should be \"CLIENT_ERROR\"": { + "pass": 1, + "fail": 0 + }, + "params.err should be \"ERR_CONTENT_SEARCH_FIELDS_MISSING\"": { + "pass": 1, + "fail": 0 + }, + "params.errmsg should be \"Required fields for search content are missing\"": { + "pass": 1, + "fail": 0 + }, + "Status code name has string \"Bad Request\"": { + "pass": 1, + "fail": 0 + } + }, + "times": [ + 210 + ], + "allTests": [ + { + "Content-Type header is present": true, + "Validate keys and data type of the response body": true, + "Verify keys and data type of the params in negative scenario": true, + "params.status should be \"failed\"": true, + "id should be api.v1.search": true, + "ver should be 1.0": true, + "Status code is 400": true, + "Verify keys and data type of the response result": true, + "responseCode should be \"CLIENT_ERROR\"": true, + "params.err should be \"ERR_CONTENT_SEARCH_FIELDS_MISSING\"": true, + "params.errmsg should be \"Required fields for search content are missing\"": true, + "Status code name has string \"Bad Request\"": true + } + ] } ], "count": 1, - "totalTime": 3522, + "totalTime": 1752, "collection": { "requests": [ { @@ -467,12 +641,20 @@ "method": "POST" }, { - "id": "74f75390-3349-4cff-b105-20788af46c95", + "id": "0e58ccb3-8c25-4331-9dd3-10f63b6f8cb4", + "method": "POST" + }, + { + "id": "6ba750db-f029-4987-ae9b-54e35a4a807c", "method": "POST" }, { "id": "8fb24515-12e0-430f-af59-7837ed777afb", "method": "POST" + }, + { + "id": "74f75390-3349-4cff-b105-20788af46c95", + "method": "POST" } ] } From ec709a3bdbc27a847dbcd39f15855bfaafe0df50 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Mon, 26 Dec 2022 18:28:47 +0530 Subject: [PATCH 176/490] Issue #CO-173 debug: Adding debug statements --- .../graph/schema/CategoryDefinitionValidator.scala | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/CategoryDefinitionValidator.scala b/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/CategoryDefinitionValidator.scala index e99968aff..2b704a488 100644 --- a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/CategoryDefinitionValidator.scala +++ b/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/CategoryDefinitionValidator.scala @@ -3,7 +3,6 @@ package org.sunbird.graph.schema import java.io.{ByteArrayInputStream, File} import java.net.URI import java.util - import com.typesafe.config.{Config, ConfigFactory} import org.apache.commons.lang3.StringUtils import org.leadpony.justify.api.JsonSchema @@ -12,6 +11,7 @@ import org.sunbird.common.exception.{ResourceNotFoundException, ResponseCode, Se import org.sunbird.common.{JsonUtils, Platform} import org.sunbird.graph.OntologyEngineContext import org.sunbird.schema.impl.BaseSchemaValidator +import org.sunbird.telemetry.logger.TelemetryManager import scala.concurrent.duration.Duration import scala.concurrent.{Await, ExecutionContext} @@ -27,11 +27,14 @@ class CategoryDefinitionValidator(schemaName: String, version: String) extends B def loadSchema(ocd: ObjectCategoryDefinition)(implicit oec: OntologyEngineContext, ec: ExecutionContext): CategoryDefinitionValidator = { val categoryId: String = ObjectCategoryDefinitionMap.prepareCategoryId(ocd.categoryName, ocd.objectType, ocd.channel) + TelemetryManager.info("CategoryDefinitionValidator:: loadSchema:: ObjectCategoryDefinitionMap:: " + ObjectCategoryDefinitionMap) if(ObjectCategoryDefinitionMap.containsKey(categoryId) && null != ObjectCategoryDefinitionMap.get(categoryId)){ + TelemetryManager.info("CategoryDefinitionValidator:: loadSchema:: INSIDE IF BLOCK:: " ) this.schema = ObjectCategoryDefinitionMap.get(categoryId).getOrElse("schema", null).asInstanceOf[JsonSchema] this.config = ObjectCategoryDefinitionMap.get(categoryId).getOrElse("config", null).asInstanceOf[Config] } else { + TelemetryManager.info("CategoryDefinitionValidator:: loadSchema:: INSIDE ELSE BLOCK:: " ) val (schemaMap, configMap) = prepareSchema(ocd) this.schema = readSchema(new ByteArrayInputStream(JsonUtils.serialize(schemaMap).getBytes)) this.config = ConfigFactory.parseMap(configMap) @@ -42,6 +45,7 @@ class CategoryDefinitionValidator(schemaName: String, version: String) extends B def prepareSchema(ocd: ObjectCategoryDefinition)(implicit oec: OntologyEngineContext, ec: ExecutionContext): (java.util.Map[String, AnyRef], java.util.Map[String, AnyRef]) = { val categoryId: String = ObjectCategoryDefinitionMap.prepareCategoryId(ocd.categoryName, ocd.objectType, ocd.channel) + TelemetryManager.info("CategoryDefinitionValidator:: prepareSchema:: ObjectCategoryDefinitionMap:: " + ObjectCategoryDefinitionMap) val request: Request = new Request() val context = new util.HashMap[String, AnyRef]() context.put("schemaName", "objectcategorydefinition") @@ -55,12 +59,17 @@ class CategoryDefinitionValidator(schemaName: String, version: String) extends B if ("all".equalsIgnoreCase(ocd.channel)) throw new ResourceNotFoundException(resp.getParams.getErr, resp.getParams.getErrmsg + " " + resp.getResult) else { + TelemetryManager.info("CategoryDefinitionValidator:: prepareSchema:: INSIDE ELSE BLOCK:: " ) val updatedId = ObjectCategoryDefinitionMap.prepareCategoryId(ocd.categoryName, ocd.objectType, "all") + TelemetryManager.info("CategoryDefinitionValidator:: prepareSchema:: INSIDE ELSE BLOCK:: ObjectCategoryDefinitionMap:: " + ObjectCategoryDefinitionMap ) request.put("identifier", updatedId) val channelCatResp = Await.result(oec.graphService.readExternalProps(request, List("objectMetadata")), Duration.apply("30 seconds")) if(StringUtils.equalsAnyIgnoreCase(channelCatResp.getResponseCode.name(), ResponseCode.RESOURCE_NOT_FOUND.name())) { throw new ResourceNotFoundException(channelCatResp.getParams.getErr, channelCatResp.getParams.getErrmsg + " " + channelCatResp.getResult) - } else channelCatResp + } else { + TelemetryManager.info("CategoryDefinitionValidator:: prepareSchema:: INSIDE ELSE BLOCK:: channelCatResp:: " + channelCatResp ) + channelCatResp + } } } else throw new ServerException(resp.getParams.getErr, resp.getParams.getErrmsg + " " + resp.getResult) } else resp From 496fa6476d8551925415f6414e64d3c19e745e11 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Mon, 26 Dec 2022 18:52:26 +0530 Subject: [PATCH 177/490] Issue #CO-173 debug: Adding debug statements --- .../graph/schema/CategoryDefinitionValidator.scala | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/CategoryDefinitionValidator.scala b/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/CategoryDefinitionValidator.scala index 2b704a488..ba2dc0b79 100644 --- a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/CategoryDefinitionValidator.scala +++ b/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/CategoryDefinitionValidator.scala @@ -27,14 +27,14 @@ class CategoryDefinitionValidator(schemaName: String, version: String) extends B def loadSchema(ocd: ObjectCategoryDefinition)(implicit oec: OntologyEngineContext, ec: ExecutionContext): CategoryDefinitionValidator = { val categoryId: String = ObjectCategoryDefinitionMap.prepareCategoryId(ocd.categoryName, ocd.objectType, ocd.channel) - TelemetryManager.info("CategoryDefinitionValidator:: loadSchema:: ObjectCategoryDefinitionMap:: " + ObjectCategoryDefinitionMap) + println("CategoryDefinitionValidator:: loadSchema:: ObjectCategoryDefinitionMap:: " + ObjectCategoryDefinitionMap) if(ObjectCategoryDefinitionMap.containsKey(categoryId) && null != ObjectCategoryDefinitionMap.get(categoryId)){ - TelemetryManager.info("CategoryDefinitionValidator:: loadSchema:: INSIDE IF BLOCK:: " ) + println("CategoryDefinitionValidator:: loadSchema:: INSIDE IF BLOCK:: " ) this.schema = ObjectCategoryDefinitionMap.get(categoryId).getOrElse("schema", null).asInstanceOf[JsonSchema] this.config = ObjectCategoryDefinitionMap.get(categoryId).getOrElse("config", null).asInstanceOf[Config] } else { - TelemetryManager.info("CategoryDefinitionValidator:: loadSchema:: INSIDE ELSE BLOCK:: " ) + println("CategoryDefinitionValidator:: loadSchema:: INSIDE ELSE BLOCK:: " ) val (schemaMap, configMap) = prepareSchema(ocd) this.schema = readSchema(new ByteArrayInputStream(JsonUtils.serialize(schemaMap).getBytes)) this.config = ConfigFactory.parseMap(configMap) @@ -45,7 +45,7 @@ class CategoryDefinitionValidator(schemaName: String, version: String) extends B def prepareSchema(ocd: ObjectCategoryDefinition)(implicit oec: OntologyEngineContext, ec: ExecutionContext): (java.util.Map[String, AnyRef], java.util.Map[String, AnyRef]) = { val categoryId: String = ObjectCategoryDefinitionMap.prepareCategoryId(ocd.categoryName, ocd.objectType, ocd.channel) - TelemetryManager.info("CategoryDefinitionValidator:: prepareSchema:: ObjectCategoryDefinitionMap:: " + ObjectCategoryDefinitionMap) + println("CategoryDefinitionValidator:: prepareSchema:: ObjectCategoryDefinitionMap:: " + ObjectCategoryDefinitionMap) val request: Request = new Request() val context = new util.HashMap[String, AnyRef]() context.put("schemaName", "objectcategorydefinition") @@ -59,15 +59,15 @@ class CategoryDefinitionValidator(schemaName: String, version: String) extends B if ("all".equalsIgnoreCase(ocd.channel)) throw new ResourceNotFoundException(resp.getParams.getErr, resp.getParams.getErrmsg + " " + resp.getResult) else { - TelemetryManager.info("CategoryDefinitionValidator:: prepareSchema:: INSIDE ELSE BLOCK:: " ) + println("CategoryDefinitionValidator:: prepareSchema:: INSIDE ELSE BLOCK:: " ) val updatedId = ObjectCategoryDefinitionMap.prepareCategoryId(ocd.categoryName, ocd.objectType, "all") - TelemetryManager.info("CategoryDefinitionValidator:: prepareSchema:: INSIDE ELSE BLOCK:: ObjectCategoryDefinitionMap:: " + ObjectCategoryDefinitionMap ) + println("CategoryDefinitionValidator:: prepareSchema:: INSIDE ELSE BLOCK:: ObjectCategoryDefinitionMap:: " + ObjectCategoryDefinitionMap ) request.put("identifier", updatedId) val channelCatResp = Await.result(oec.graphService.readExternalProps(request, List("objectMetadata")), Duration.apply("30 seconds")) if(StringUtils.equalsAnyIgnoreCase(channelCatResp.getResponseCode.name(), ResponseCode.RESOURCE_NOT_FOUND.name())) { throw new ResourceNotFoundException(channelCatResp.getParams.getErr, channelCatResp.getParams.getErrmsg + " " + channelCatResp.getResult) } else { - TelemetryManager.info("CategoryDefinitionValidator:: prepareSchema:: INSIDE ELSE BLOCK:: channelCatResp:: " + channelCatResp ) + println("CategoryDefinitionValidator:: prepareSchema:: INSIDE ELSE BLOCK:: channelCatResp:: " + channelCatResp ) channelCatResp } } From 46492683b7ea017d146dc15e7c935319dd86d8b0 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Mon, 26 Dec 2022 19:17:41 +0530 Subject: [PATCH 178/490] Issue #CO-173 debug: Adding debug statements --- .../main/scala/org/sunbird/graph/GraphService.scala | 5 +++-- .../graph/schema/CategoryDefinitionValidator.scala | 10 +++++++--- pom.xml | 2 +- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/GraphService.scala b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/GraphService.scala index 12be0be1b..ef856d808 100644 --- a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/GraphService.scala +++ b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/GraphService.scala @@ -55,13 +55,14 @@ class GraphService { } def readExternalProps(request: Request, fields: List[String]): Future[Response] = { - ExternalPropsManager.fetchProps(request, fields).map(res => + ExternalPropsManager.fetchProps(request, fields).map(res => { + println("GraphService:: readExternalProps:: res.params:: " + res.getParams+ " || res.responseCode:: " + res.getResponseCode + " || res.result:: " + res.getResult) if(isrRelativePathEnabled) { val updatedResult = CSPMetaUtil.updateExternalAbsolutePath(res.getResult) val response = ResponseHandler.OK() response.putAll(updatedResult) response - } else res) + } else res}) } def saveExternalProps(request: Request): Future[Response] = { diff --git a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/CategoryDefinitionValidator.scala b/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/CategoryDefinitionValidator.scala index ba2dc0b79..308e678fa 100644 --- a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/CategoryDefinitionValidator.scala +++ b/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/CategoryDefinitionValidator.scala @@ -27,7 +27,7 @@ class CategoryDefinitionValidator(schemaName: String, version: String) extends B def loadSchema(ocd: ObjectCategoryDefinition)(implicit oec: OntologyEngineContext, ec: ExecutionContext): CategoryDefinitionValidator = { val categoryId: String = ObjectCategoryDefinitionMap.prepareCategoryId(ocd.categoryName, ocd.objectType, ocd.channel) - println("CategoryDefinitionValidator:: loadSchema:: ObjectCategoryDefinitionMap:: " + ObjectCategoryDefinitionMap) + println("CategoryDefinitionValidator:: loadSchema:: ObjectCategoryDefinitionMap:: " + ObjectCategoryDefinitionMap + " || categoryId:: " + categoryId) if(ObjectCategoryDefinitionMap.containsKey(categoryId) && null != ObjectCategoryDefinitionMap.get(categoryId)){ println("CategoryDefinitionValidator:: loadSchema:: INSIDE IF BLOCK:: " ) this.schema = ObjectCategoryDefinitionMap.get(categoryId).getOrElse("schema", null).asInstanceOf[JsonSchema] @@ -45,7 +45,7 @@ class CategoryDefinitionValidator(schemaName: String, version: String) extends B def prepareSchema(ocd: ObjectCategoryDefinition)(implicit oec: OntologyEngineContext, ec: ExecutionContext): (java.util.Map[String, AnyRef], java.util.Map[String, AnyRef]) = { val categoryId: String = ObjectCategoryDefinitionMap.prepareCategoryId(ocd.categoryName, ocd.objectType, ocd.channel) - println("CategoryDefinitionValidator:: prepareSchema:: ObjectCategoryDefinitionMap:: " + ObjectCategoryDefinitionMap) + println("CategoryDefinitionValidator:: prepareSchema:: ObjectCategoryDefinitionMap:: " + ObjectCategoryDefinitionMap + " || categoryId:: " + categoryId) val request: Request = new Request() val context = new util.HashMap[String, AnyRef]() context.put("schemaName", "objectcategorydefinition") @@ -54,6 +54,7 @@ class CategoryDefinitionValidator(schemaName: String, version: String) extends B request.put("identifier", categoryId) val response: Response = { val resp = Await.result(oec.graphService.readExternalProps(request, List("objectMetadata")), Duration.apply("30 seconds")) + println("CategoryDefinitionValidator:: prepareSchema:: resp:: " + resp.getResult) if (ResponseHandler.checkError(resp)) { if(StringUtils.equalsAnyIgnoreCase(resp.getResponseCode.name(), ResponseCode.RESOURCE_NOT_FOUND.name())) { if ("all".equalsIgnoreCase(ocd.channel)) @@ -61,9 +62,10 @@ class CategoryDefinitionValidator(schemaName: String, version: String) extends B else { println("CategoryDefinitionValidator:: prepareSchema:: INSIDE ELSE BLOCK:: " ) val updatedId = ObjectCategoryDefinitionMap.prepareCategoryId(ocd.categoryName, ocd.objectType, "all") - println("CategoryDefinitionValidator:: prepareSchema:: INSIDE ELSE BLOCK:: ObjectCategoryDefinitionMap:: " + ObjectCategoryDefinitionMap ) + println("CategoryDefinitionValidator:: prepareSchema:: INSIDE ELSE BLOCK:: ObjectCategoryDefinitionMap:: " + ObjectCategoryDefinitionMap + " || categoryId:: " + updatedId) request.put("identifier", updatedId) val channelCatResp = Await.result(oec.graphService.readExternalProps(request, List("objectMetadata")), Duration.apply("30 seconds")) + println("CategoryDefinitionValidator:: prepareSchema:: channelCatResp:: " + channelCatResp.getResult) if(StringUtils.equalsAnyIgnoreCase(channelCatResp.getResponseCode.name(), ResponseCode.RESOURCE_NOT_FOUND.name())) { throw new ResourceNotFoundException(channelCatResp.getParams.getErr, channelCatResp.getParams.getErrmsg + " " + channelCatResp.getResult) } else { @@ -78,10 +80,12 @@ class CategoryDefinitionValidator(schemaName: String, version: String) extends B } def populateSchema(response: Response, identifier: String) : (java.util.Map[String, AnyRef], java.util.Map[String, AnyRef]) = { + println("CategoryDefinitionValidator:: populateSchema:: response:: " + response.getResult + " || idenitfier:: " + identifier) val jsonString = getFileToString("schema.json") val schemaMap: java.util.Map[String, AnyRef] = JsonUtils.deserialize(jsonString, classOf[java.util.Map[String, AnyRef]]) val configMap: java.util.Map[String, AnyRef] = JsonUtils.deserialize(getFileToString("config.json"), classOf[java.util.Map[String, AnyRef]]) val objectMetadata = response.getResult.getOrDefault("objectMetadata", new util.HashMap[String, AnyRef]()).asInstanceOf[java.util.Map[String, AnyRef]] + println("CategoryDefinitionValidator:: populateSchema:: objectMetadata:: " + objectMetadata) val nodeSchema = JsonUtils.deserialize(objectMetadata.getOrDefault("schema", "{}").asInstanceOf[String], classOf[java.util.Map[String, AnyRef]]) schemaMap.getOrDefault("properties", new java.util.HashMap[String, AnyRef]()).asInstanceOf[java.util.Map[String, AnyRef]].putAll(nodeSchema.getOrDefault("properties", new java.util.HashMap[String, AnyRef]()).asInstanceOf[java.util.Map[String, AnyRef]]) schemaMap.getOrDefault("required", new java.util.ArrayList[String]()).asInstanceOf[java.util.List[String]].addAll(nodeSchema.getOrDefault("required", new java.util.ArrayList[String]()).asInstanceOf[java.util.List[String]]) diff --git a/pom.xml b/pom.xml index 0558992df..7810ee416 100644 --- a/pom.xml +++ b/pom.xml @@ -27,7 +27,7 @@ platform-core ontology-engine content-api - assessment-api + taxonomy-api platform-modules search-api From 967671c125b6a652254d8235a0545cb7ecd0da97 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Mon, 26 Dec 2022 19:31:47 +0530 Subject: [PATCH 179/490] Issue #CO-173 debug: Adding debug statements --- .../src/main/scala/org/sunbird/graph/GraphService.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/GraphService.scala b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/GraphService.scala index ef856d808..3f4b895c4 100644 --- a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/GraphService.scala +++ b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/GraphService.scala @@ -2,6 +2,7 @@ package org.sunbird.graph import org.sunbird.common.Platform import org.sunbird.common.dto.{Property, Request, Response, ResponseHandler} +import org.sunbird.common.exception.ResponseCode import org.sunbird.graph.dac.model.{Node, SearchCriteria} import org.sunbird.graph.external.ExternalPropsManager import org.sunbird.graph.service.operation.{GraphAsyncOperations, Neo4JBoltSearchOperations, NodeAsyncOperations, SearchAsyncOperations} @@ -57,7 +58,7 @@ class GraphService { def readExternalProps(request: Request, fields: List[String]): Future[Response] = { ExternalPropsManager.fetchProps(request, fields).map(res => { println("GraphService:: readExternalProps:: res.params:: " + res.getParams+ " || res.responseCode:: " + res.getResponseCode + " || res.result:: " + res.getResult) - if(isrRelativePathEnabled) { + if(isrRelativePathEnabled && res.getResponseCode != ResponseCode.RESOURCE_NOT_FOUND) { val updatedResult = CSPMetaUtil.updateExternalAbsolutePath(res.getResult) val response = ResponseHandler.OK() response.putAll(updatedResult) From fab5132dd35e96ca5d44f90a9c44f0db076a9da8 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Mon, 26 Dec 2022 19:48:31 +0530 Subject: [PATCH 180/490] Issue #CO-173 fix: Schema validation fix --- .../main/scala/org/sunbird/graph/GraphService.scala | 3 +-- .../graph/schema/CategoryDefinitionValidator.scala | 12 ------------ 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/GraphService.scala b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/GraphService.scala index 3f4b895c4..c87e74bda 100644 --- a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/GraphService.scala +++ b/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/GraphService.scala @@ -57,8 +57,7 @@ class GraphService { def readExternalProps(request: Request, fields: List[String]): Future[Response] = { ExternalPropsManager.fetchProps(request, fields).map(res => { - println("GraphService:: readExternalProps:: res.params:: " + res.getParams+ " || res.responseCode:: " + res.getResponseCode + " || res.result:: " + res.getResult) - if(isrRelativePathEnabled && res.getResponseCode != ResponseCode.RESOURCE_NOT_FOUND) { + if(isrRelativePathEnabled && res.getResponseCode == ResponseCode.OK) { val updatedResult = CSPMetaUtil.updateExternalAbsolutePath(res.getResult) val response = ResponseHandler.OK() response.putAll(updatedResult) diff --git a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/CategoryDefinitionValidator.scala b/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/CategoryDefinitionValidator.scala index 308e678fa..2543f39a9 100644 --- a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/CategoryDefinitionValidator.scala +++ b/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/CategoryDefinitionValidator.scala @@ -11,7 +11,6 @@ import org.sunbird.common.exception.{ResourceNotFoundException, ResponseCode, Se import org.sunbird.common.{JsonUtils, Platform} import org.sunbird.graph.OntologyEngineContext import org.sunbird.schema.impl.BaseSchemaValidator -import org.sunbird.telemetry.logger.TelemetryManager import scala.concurrent.duration.Duration import scala.concurrent.{Await, ExecutionContext} @@ -27,14 +26,11 @@ class CategoryDefinitionValidator(schemaName: String, version: String) extends B def loadSchema(ocd: ObjectCategoryDefinition)(implicit oec: OntologyEngineContext, ec: ExecutionContext): CategoryDefinitionValidator = { val categoryId: String = ObjectCategoryDefinitionMap.prepareCategoryId(ocd.categoryName, ocd.objectType, ocd.channel) - println("CategoryDefinitionValidator:: loadSchema:: ObjectCategoryDefinitionMap:: " + ObjectCategoryDefinitionMap + " || categoryId:: " + categoryId) if(ObjectCategoryDefinitionMap.containsKey(categoryId) && null != ObjectCategoryDefinitionMap.get(categoryId)){ - println("CategoryDefinitionValidator:: loadSchema:: INSIDE IF BLOCK:: " ) this.schema = ObjectCategoryDefinitionMap.get(categoryId).getOrElse("schema", null).asInstanceOf[JsonSchema] this.config = ObjectCategoryDefinitionMap.get(categoryId).getOrElse("config", null).asInstanceOf[Config] } else { - println("CategoryDefinitionValidator:: loadSchema:: INSIDE ELSE BLOCK:: " ) val (schemaMap, configMap) = prepareSchema(ocd) this.schema = readSchema(new ByteArrayInputStream(JsonUtils.serialize(schemaMap).getBytes)) this.config = ConfigFactory.parseMap(configMap) @@ -45,7 +41,6 @@ class CategoryDefinitionValidator(schemaName: String, version: String) extends B def prepareSchema(ocd: ObjectCategoryDefinition)(implicit oec: OntologyEngineContext, ec: ExecutionContext): (java.util.Map[String, AnyRef], java.util.Map[String, AnyRef]) = { val categoryId: String = ObjectCategoryDefinitionMap.prepareCategoryId(ocd.categoryName, ocd.objectType, ocd.channel) - println("CategoryDefinitionValidator:: prepareSchema:: ObjectCategoryDefinitionMap:: " + ObjectCategoryDefinitionMap + " || categoryId:: " + categoryId) val request: Request = new Request() val context = new util.HashMap[String, AnyRef]() context.put("schemaName", "objectcategorydefinition") @@ -54,22 +49,17 @@ class CategoryDefinitionValidator(schemaName: String, version: String) extends B request.put("identifier", categoryId) val response: Response = { val resp = Await.result(oec.graphService.readExternalProps(request, List("objectMetadata")), Duration.apply("30 seconds")) - println("CategoryDefinitionValidator:: prepareSchema:: resp:: " + resp.getResult) if (ResponseHandler.checkError(resp)) { if(StringUtils.equalsAnyIgnoreCase(resp.getResponseCode.name(), ResponseCode.RESOURCE_NOT_FOUND.name())) { if ("all".equalsIgnoreCase(ocd.channel)) throw new ResourceNotFoundException(resp.getParams.getErr, resp.getParams.getErrmsg + " " + resp.getResult) else { - println("CategoryDefinitionValidator:: prepareSchema:: INSIDE ELSE BLOCK:: " ) val updatedId = ObjectCategoryDefinitionMap.prepareCategoryId(ocd.categoryName, ocd.objectType, "all") - println("CategoryDefinitionValidator:: prepareSchema:: INSIDE ELSE BLOCK:: ObjectCategoryDefinitionMap:: " + ObjectCategoryDefinitionMap + " || categoryId:: " + updatedId) request.put("identifier", updatedId) val channelCatResp = Await.result(oec.graphService.readExternalProps(request, List("objectMetadata")), Duration.apply("30 seconds")) - println("CategoryDefinitionValidator:: prepareSchema:: channelCatResp:: " + channelCatResp.getResult) if(StringUtils.equalsAnyIgnoreCase(channelCatResp.getResponseCode.name(), ResponseCode.RESOURCE_NOT_FOUND.name())) { throw new ResourceNotFoundException(channelCatResp.getParams.getErr, channelCatResp.getParams.getErrmsg + " " + channelCatResp.getResult) } else { - println("CategoryDefinitionValidator:: prepareSchema:: INSIDE ELSE BLOCK:: channelCatResp:: " + channelCatResp ) channelCatResp } } @@ -80,12 +70,10 @@ class CategoryDefinitionValidator(schemaName: String, version: String) extends B } def populateSchema(response: Response, identifier: String) : (java.util.Map[String, AnyRef], java.util.Map[String, AnyRef]) = { - println("CategoryDefinitionValidator:: populateSchema:: response:: " + response.getResult + " || idenitfier:: " + identifier) val jsonString = getFileToString("schema.json") val schemaMap: java.util.Map[String, AnyRef] = JsonUtils.deserialize(jsonString, classOf[java.util.Map[String, AnyRef]]) val configMap: java.util.Map[String, AnyRef] = JsonUtils.deserialize(getFileToString("config.json"), classOf[java.util.Map[String, AnyRef]]) val objectMetadata = response.getResult.getOrDefault("objectMetadata", new util.HashMap[String, AnyRef]()).asInstanceOf[java.util.Map[String, AnyRef]] - println("CategoryDefinitionValidator:: populateSchema:: objectMetadata:: " + objectMetadata) val nodeSchema = JsonUtils.deserialize(objectMetadata.getOrDefault("schema", "{}").asInstanceOf[String], classOf[java.util.Map[String, AnyRef]]) schemaMap.getOrDefault("properties", new java.util.HashMap[String, AnyRef]()).asInstanceOf[java.util.Map[String, AnyRef]].putAll(nodeSchema.getOrDefault("properties", new java.util.HashMap[String, AnyRef]()).asInstanceOf[java.util.Map[String, AnyRef]]) schemaMap.getOrDefault("required", new java.util.ArrayList[String]()).asInstanceOf[java.util.List[String]].addAll(nodeSchema.getOrDefault("required", new java.util.ArrayList[String]()).asInstanceOf[java.util.List[String]]) From f5557e4526386219e7d7b7bd7ee530232d8414a3 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Mon, 26 Dec 2022 19:50:02 +0530 Subject: [PATCH 181/490] Issue #CO-173 fix: Schema validation fix --- .../sunbird/graph/schema/CategoryDefinitionValidator.scala | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/CategoryDefinitionValidator.scala b/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/CategoryDefinitionValidator.scala index 2543f39a9..951257c38 100644 --- a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/CategoryDefinitionValidator.scala +++ b/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/CategoryDefinitionValidator.scala @@ -59,9 +59,7 @@ class CategoryDefinitionValidator(schemaName: String, version: String) extends B val channelCatResp = Await.result(oec.graphService.readExternalProps(request, List("objectMetadata")), Duration.apply("30 seconds")) if(StringUtils.equalsAnyIgnoreCase(channelCatResp.getResponseCode.name(), ResponseCode.RESOURCE_NOT_FOUND.name())) { throw new ResourceNotFoundException(channelCatResp.getParams.getErr, channelCatResp.getParams.getErrmsg + " " + channelCatResp.getResult) - } else { - channelCatResp - } + } else channelCatResp } } else throw new ServerException(resp.getParams.getErr, resp.getParams.getErrmsg + " " + resp.getResult) } else resp From b970bcf67c118302b12d1a077ef6bd9ac9108262 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Mon, 26 Dec 2022 19:50:59 +0530 Subject: [PATCH 182/490] Issue #CO-173 fix: Schema validation fix --- .../org/sunbird/graph/schema/CategoryDefinitionValidator.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/CategoryDefinitionValidator.scala b/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/CategoryDefinitionValidator.scala index 951257c38..e99968aff 100644 --- a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/CategoryDefinitionValidator.scala +++ b/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/CategoryDefinitionValidator.scala @@ -3,6 +3,7 @@ package org.sunbird.graph.schema import java.io.{ByteArrayInputStream, File} import java.net.URI import java.util + import com.typesafe.config.{Config, ConfigFactory} import org.apache.commons.lang3.StringUtils import org.leadpony.justify.api.JsonSchema From ef050093a5af310a85c1a3a9d917a5d6e588ec51 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Fri, 6 Jan 2023 10:38:33 +0530 Subject: [PATCH 183/490] Issue #KN- fix: relational metadata backward compatibility fix --- schemas/relationalmetadata/1.0/schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/schemas/relationalmetadata/1.0/schema.json b/schemas/relationalmetadata/1.0/schema.json index cf62a6654..f7005fc3f 100644 --- a/schemas/relationalmetadata/1.0/schema.json +++ b/schemas/relationalmetadata/1.0/schema.json @@ -27,5 +27,5 @@ } } }, - "additionalProperties": false + "additionalProperties": true } \ No newline at end of file From 160984066777ad2836fe47b14a21ceda6b7d6df6 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Mon, 9 Jan 2023 21:01:40 +0530 Subject: [PATCH 184/490] Issue #KN-730 fix: QR Code and QRRequired inconsistency debug --- .../manager/CollectionCSVManager.scala | 17 +++++++++++++++-- .../managers/UpdateHierarchyManager.scala | 3 +++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/content-api/collection-csv-actors/src/main/scala/org.sunbird/collectioncsv/manager/CollectionCSVManager.scala b/content-api/collection-csv-actors/src/main/scala/org.sunbird/collectioncsv/manager/CollectionCSVManager.scala index 61c4fd97a..f97a9c56f 100644 --- a/content-api/collection-csv-actors/src/main/scala/org.sunbird/collectioncsv/manager/CollectionCSVManager.scala +++ b/content-api/collection-csv-actors/src/main/scala/org.sunbird/collectioncsv/manager/CollectionCSVManager.scala @@ -29,10 +29,15 @@ import scala.collection.mutable import scala.collection.mutable.ListBuffer import scala.concurrent.{ExecutionContext, Future} +import scala.collection.JavaConverters._ + object CollectionCSVManager extends CollectionInputFileReader { private val CONTENT_FOLDER = "cloud_storage.content.folder" val logger = Logger.getLogger("CollectionCSVManager") + val categoryMap: java.util.Map[String, AnyRef] = Platform.getAnyRef("contentTypeToPrimaryCategory", + new util.HashMap[String, AnyRef]()).asInstanceOf[java.util.Map[String, AnyRef]] + def getCode(code: String): String = {DigestUtils.md5Hex(code)} def validateInputData(inputFileExtension: String, csvRecords: util.List[CSVRecord], mode: String, collectionHierarchy: Map[String, AnyRef])(implicit oec: OntologyEngineContext, ec: ExecutionContext): List[Map[String, AnyRef]] = { @@ -323,9 +328,9 @@ object CollectionCSVManager extends CollectionInputFileReader { else try { s""""${nodeInfo(CollectionTOCConstants.IDENTIFIER).toString}": {"isNew": false,"root": false, "metadata": {"mimeType": "application/vnd.ekstep.content-collection", - |"contentType": "$collectionUnitType","name": ${JsonUtils.serialize(nodeInfo("name").toString.trim)}, + |"contentType": "$collectionUnitType","name": ${JsonUtils.serialize(nodeInfo("name").toString.trim)}, "primaryCategory": "${getPrimaryCategory(collectionUnitType)}", |"description": ${if(nodeInfo.contains(CollectionTOCConstants.DESCRIPTION)) JsonUtils.serialize(nodeInfo(CollectionTOCConstants.DESCRIPTION).toString) else JsonUtils.serialize("")}, - |"dialcodeRequired": "${nodeInfo(CollectionTOCConstants.DIAL_CODE_REQUIRED).toString}","dialcodes": "${nodeInfo(CollectionTOCConstants.DIAL_CODES).toString}", + |"dialcodeRequired": "${nodeInfo(CollectionTOCConstants.DIAL_CODE_REQUIRED).toString}","dialcodes": ["${nodeInfo(CollectionTOCConstants.DIAL_CODES).toString}"], |"code": "${nodeInfo(CollectionTOCConstants.IDENTIFIER).toString}","framework": "$frameworkID", |"keywords": ${if(nodeInfo.contains(CollectionTOCConstants.KEYWORDS) && nodeInfo(CollectionTOCConstants.KEYWORDS).asInstanceOf[List[String]].nonEmpty) nodeInfo(CollectionTOCConstants.KEYWORDS).asInstanceOf[List[String]].map(keyword=>JsonUtils.serialize(keyword)).mkString("[",",","]") else "[]"}, @@ -500,5 +505,13 @@ object CollectionCSVManager extends CollectionInputFileReader { }) } + private def getPrimaryCategory(contentType: String): String ={ + val primaryCategory = categoryMap.get(contentType) + if(primaryCategory.isInstanceOf[String]) + primaryCategory.asInstanceOf[String] + else + primaryCategory.asInstanceOf[util.List[String]].asScala.headOption.getOrElse("Learning Resource") + + } } diff --git a/content-api/hierarchy-manager/src/main/scala/org/sunbird/managers/UpdateHierarchyManager.scala b/content-api/hierarchy-manager/src/main/scala/org/sunbird/managers/UpdateHierarchyManager.scala index fbb73934a..dba9791fd 100644 --- a/content-api/hierarchy-manager/src/main/scala/org/sunbird/managers/UpdateHierarchyManager.scala +++ b/content-api/hierarchy-manager/src/main/scala/org/sunbird/managers/UpdateHierarchyManager.scala @@ -27,8 +27,11 @@ object UpdateHierarchyManager { def updateHierarchy(request: Request)(implicit oec: OntologyEngineContext, ec: ExecutionContext): Future[Response] = { validateRequest(request) val nodesModified: java.util.HashMap[String, AnyRef] = request.getRequest.get(HierarchyConstants.NODES_MODIFIED).asInstanceOf[java.util.HashMap[String, AnyRef]] + TelemetryManager.info("UpdateHierarchyManager:: updateHierarchy:: nodesModified: " + nodesModified) val hierarchy: java.util.HashMap[String, AnyRef] = request.getRequest.get(HierarchyConstants.HIERARCHY).asInstanceOf[java.util.HashMap[String, AnyRef]] + TelemetryManager.info("UpdateHierarchyManager:: updateHierarchy:: hierarchy: " + hierarchy) val rootId: String = getRootId(nodesModified, hierarchy) + TelemetryManager.info("UpdateHierarchyManager:: updateHierarchy:: rootId: " + rootId) request.getContext.put(HierarchyConstants.ROOT_ID, rootId) getValidatedRootNode(rootId, request).map(node => { getExistingHierarchy(request, node).map(existingHierarchy => { From 2f8a86dc6669f37f4b2f253bfa2ae96ad10aa6a1 Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Tue, 10 Jan 2023 20:05:58 +0530 Subject: [PATCH 185/490] Issue #KN-751 fix: Content and collection image publish issue --- .../org/sunbird/content/publish/mgr/PublishManager.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/publish/mgr/PublishManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/publish/mgr/PublishManager.scala index 16a4e2f71..f08ac0c3d 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/publish/mgr/PublishManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/publish/mgr/PublishManager.scala @@ -78,7 +78,7 @@ object PublishManager { } //objectData - objectData.put(ContentConstants.ID, identifier) + objectData.put(ContentConstants.ID, identifier.replace(".img","")) objectData.put(ContentConstants.VER, metadata.get(ContentConstants.VERSION_KEY)) //edata @@ -91,7 +91,7 @@ object PublishManager { instructionEventMetadata.put(ContentConstants.PACKAGE_VERSION, metadata.getOrDefault(ContentConstants.PACKAGE_VERSION,0.asInstanceOf[AnyRef])) instructionEventMetadata.put(ContentConstants.MIME_TYPE, metadata.get(ContentConstants.MIME_TYPE)) instructionEventMetadata.put(ContentConstants.LAST_PUBLISHED_BY, metadata.get(ContentConstants.LAST_PUBLISHED_BY)) - instructionEventMetadata.put(ContentConstants.IDENTIFIER, identifier) + instructionEventMetadata.put(ContentConstants.IDENTIFIER, identifier.replace(".img","")) instructionEventMetadata.put(ContentConstants.OBJECT_TYPE, objectType) edata.put(ContentConstants.METADATA, instructionEventMetadata) edata.put(ContentConstants.ACTION, ContentConstants.PUBLISH) From e4b8375ad78b908d6646b02e04cf5e9207a0d6fa Mon Sep 17 00:00:00 2001 From: Jayaprakash8887 Date: Wed, 18 Jan 2023 14:41:32 +0530 Subject: [PATCH 186/490] Issue #KN-754 feat: Postman DOCS publishing. --- .../License API(s).postman_collection.json | 1017 +++ ... Management API(s).postman_collection.json | 1179 ++++ ... Management API(s).postman_collection.json | 2921 +++++++++ ... Management API(s).postman_collection.json | 5667 +++++++++++++++++ .../Framework API(s).postman_collection.json | 4507 +++++++++++++ ...ct Category API(s).postman_collection.json | 1854 ++++++ 6 files changed, 17145 insertions(+) create mode 100644 content-api/api-tests/Collections/License API(s).postman_collection.json create mode 100644 content-api/api-tests/Collections/Sunbird Asset Management API(s).postman_collection.json create mode 100644 content-api/api-tests/Collections/Sunbird Collection Management API(s).postman_collection.json create mode 100644 content-api/api-tests/Collections/Sunbird Content Management API(s).postman_collection.json create mode 100644 taxonomy-api/api-tests/Collections/Framework API(s).postman_collection.json create mode 100644 taxonomy-api/api-tests/Collections/Object Category API(s).postman_collection.json diff --git a/content-api/api-tests/Collections/License API(s).postman_collection.json b/content-api/api-tests/Collections/License API(s).postman_collection.json new file mode 100644 index 000000000..8a013c354 --- /dev/null +++ b/content-api/api-tests/Collections/License API(s).postman_collection.json @@ -0,0 +1,1017 @@ +{ + "info": { + "_postman_id": "fb7f316f-8f4d-4ebf-a358-e1cc9ba3e8ed", + "name": "License API(s)", + "description": "The License API allows you to create licenses which can be tagged to content based on your requirements over the sunbird platform. Each endpoint serves a different purpose, so based on your requirement you can do the following: \n- The operations include CRUD (Create, Update, Read and Delete) operations\n- The URL for Sunbird License API(s) is `/license/v3`\n- The backend URL for the same is `/license/v3`. \n\n\nContact Support:\n Email: info@sunbird.org", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "license/v3", + "item": [ + { + "name": "Create License", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "{{apiKey}}", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"license\": {\n \"name\": \"CC BY 4.0\",\n \"url\": \"www.url.com\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/license/v3/create", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "license", + "v3", + "create" + ] + }, + "description": "This API is associated with creating License on the Sunbird Platform.\n - The endpoint for **Create License** is `/license/v3/create`\n - It points to content-service - `/license/v3/create`\n - It is mandatory to provide values for parameters marked with *. \n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "SUCCESS. The **Create License** operation was successful!", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:\n- application/json", + "key": "Content-Type", + "value": "esse et eu quis ut" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "esse et eu quis ut" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"license\": {\n \"name\": \"CC BY 4.0\",\n \"url\": \"www.url.com\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/license/v3/create", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "license", + "v3", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.license.create\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-12-18T06:48:10ZZ\",\n \"params\": {\n \"resmsgid\": \"5eb2e0f9-e27c-4d38-8353-e1720b0ed78a\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"cc-by-4.0\",\n \"node_id\": \"cc-by-4.0\"\n }\n}" + }, + { + "name": "BAD REQUEST. The **Create License** operation failed. You may have missed input for a mandatory parameter.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:\n- application/json", + "key": "Content-Type", + "value": "esse et eu quis ut" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "esse et eu quis ut" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"license\": {\n \"name\": \"CC BY 4.0\",\n \"url\": \"www.url.com\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/license/v3/create", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "license", + "v3", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.license.create\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-12-18T08:41:21ZZ\",\n \"params\": {\n \"resmsgid\": \"0ea93a27-b70d-4587-b280-29564175af86\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata name not set\"\n ]\n }\n}" + }, + { + "name": "'INTERNAL SERVER ERROR. We track these errors automatically and try to set it right at the earliest. Try refreshing the page. If the problem persists contact us at info@sunbird.org.'", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:\n- application/json", + "key": "Content-Type", + "value": "esse et eu quis ut" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "esse et eu quis ut" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"license\": {\n \"name\": \"CC BY 4.0\",\n \"url\": \"www.url.com\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/license/v3/create", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "license", + "v3", + "create" + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.license.create\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-10T08:51:51.647Z\",\n \"params\": {\n \"resmsgid\": \"f234a6f0-3ac4-11eb-b0a2-8d5c9f561887\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"SERVER_ERROR\",\n \"result\": {}\n}" + } + ] + }, + { + "name": "Update License", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "{{apiKey}}", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"license\": {\n \"description\": \"Updated description\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/license/v3/update/:License_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "license", + "v3", + "update", + ":License_Id" + ], + "variable": [ + { + "key": "License_Id", + "value": "esse et eu quis ut", + "description": "(Required) Please append a valid License Id To the Request URL" + } + ] + }, + "description": "This API is associated with updating license on the Sunbird Platform .\n - **_/Update/_** endpoint executes the \"Update License\" request based on parameters provided as metadata in the request body.\n - The endpoint for **Create License** is `/license/v3/update/{License_Id}`\n - It points to content-service - `/license/v3/update/{License_Id}`\n - {...}refers to a section of review endpoint URL path that needs to be replaced by appropriate Id.\n - It is mandatory to provide values for parameters marked with *.\n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "OK ! Successful operation. License update operation was successfuly executed.", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "esse et eu quis ut" + }, + { + "description": "(Required) All license APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "esse et eu quis ut" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"license\": {\n \"description\": \"Updated description\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/license/v3/update/:License_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "license", + "v3", + "update", + ":License_Id" + ], + "variable": [ + { + "key": "License_Id", + "value": "esse et eu quis ut", + "description": "(Required) Please append a valid License Id To the Request URL" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.license.update\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-12-18T12:10:36ZZ\",\n \"params\": {\n \"resmsgid\": \"18fa41bd-7b8e-4f8b-9028-d17879f60cc6\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"standard-youtube-license\",\n \"node_id\": \"standard-youtube-license\"\n }\n}" + }, + { + "name": "BAD REQUEST. The 'Update License ' operation failed ! The possible reason for failure is that you may have missed providing input for a mandatory parameter. ", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "esse et eu quis ut" + }, + { + "description": "(Required) All license APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "esse et eu quis ut" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"license\": {\n \"description\": \"Updated description\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/license/v3/update/:License_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "license", + "v3", + "update", + ":License_Id" + ], + "variable": [ + { + "key": "License_Id", + "value": "esse et eu quis ut", + "description": "(Required) Please append a valid License Id To the Request URL" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.license.update\",\n \"ver\": \"4.0\",\n \"ts\": \"2020-12-10T20:27:01ZZ\",\n \"params\": {\n \"resmsgid\": \"87eb99c6-bc10-406a-a6fe-13651f546ce1\",\n \"msgid\": null,\n \"err\": \"ERROR_RESTRICTED_PROP\",\n \"status\": \"failed\",\n \"errmsg\": \"Properties in list [identifier, name, status] are not allowed in request\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": null\n }\n}" + }, + { + "name": "RESOURCE NOT FOUND,**License Update** operation failed !The possible reason for failure is that you may have provided wrong license id.", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "esse et eu quis ut" + }, + { + "description": "(Required) All license APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "esse et eu quis ut" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"license\": {\n \"description\": \"Updated description\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/license/v3/update/:License_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "license", + "v3", + "update", + ":License_Id" + ], + "variable": [ + { + "key": "License_Id", + "value": "esse et eu quis ut", + "description": "(Required) Please append a valid License Id To the Request URL" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.license.update\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-12-18T12:13:57ZZ\",\n \"params\": {\n \"resmsgid\": \"d30ba7f5-916e-422f-8fcc-6a67b80acaea\",\n \"msgid\": null,\n \"err\": \"NOT_FOUND\",\n \"status\": \"failed\",\n \"errmsg\": \"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: standard-youtube-license1\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + }, + { + "name": "INTERNAL SERVER ERROR ! Looks like something went wrong! These errors are tracked automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "esse et eu quis ut" + }, + { + "description": "(Required) All license APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "esse et eu quis ut" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"license\": {\n \"description\": \"Updated description\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/license/v3/update/:License_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "license", + "v3", + "update", + ":License_Id" + ], + "variable": [ + { + "key": "License_Id", + "value": "esse et eu quis ut", + "description": "(Required) Please append a valid License Id To the Request URL" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.license.update\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-10T08:51:51.647Z\",\n \"params\": {\n \"resmsgid\": \"f234a6f0-3ac4-11eb-b0a2-8d5c9f561887\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"SERVER_ERROR\",\n \"result\": {}\n}" + } + ] + }, + { + "name": "Read License", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "{{apiKey}}", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/license/v3/read/:License_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "license", + "v3", + "read", + ":License_Id" + ], + "variable": [ + { + "key": "License_Id", + "value": "esse et eu quis ut", + "description": "(Required) Please append a valid License_Id To the Request URL" + } + ] + }, + "description": "This API is associated with viewing and reading out the master object category on the Sunbird Platform .\n - The **_/read/{License_Id}_** endpoint executes request for fetching the licenses to be viewed and read.\n - The endpoint for **Read License** is `/license/v3/read/{License_Id}`\n - It points to content-service - `/license/v3/read/{License_Id}`\n - {...}refers to a section of reject endpoint URL path that needs to be replaced by appropriate id.\n - You need to provide a valid object category id value in {License_Id} field of API URL.\n - It is mandatory to provide values for parameters marked with *. \n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "OK ! Successful operation.\"Read License\" operation was successfully executed. For all possible metadata, please [refer](https://github.com/project-sunbird/knowledge-platform/blob/release-3.5.0/schemas/license/1.0/schema.json)", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "esse et eu quis ut" + }, + { + "description": "(Required) All license APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "esse et eu quis ut" + } + ], + "url": { + "raw": "{{baseUrl}}/license/v3/read/:License_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "license", + "v3", + "read", + ":License_Id" + ], + "variable": [ + { + "key": "License_Id", + "value": "esse et eu quis ut", + "description": "(Required) Please append a valid License_Id To the Request URL" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.license.read\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-12-18T12:10:18ZZ\",\n \"params\": {\n \"resmsgid\": \"f7b3b400-cf43-49a3-ab14-a6d1a74e70b7\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"license\": {\n \"identifier\": \"standard-youtube-license\",\n \"lastStatusChangedOn\": \"2019-11-25T13:34:45.334+0000\",\n \"apoc_json\": \"{\\\"batch\\\": true}\",\n \"url\": \"www.url.com\",\n \"description\": \"This is the standard license of any Youtube content\",\n \"languageCode\": [],\n \"createdOn\": \"2019-11-25T13:34:45.334+0000\",\n \"objectType\": \"License\",\n \"apoc_text\": \"APOC\",\n \"versionKey\": \"1582725030824\",\n \"name\": \"Standard YouTube License\",\n \"lastUpdatedOn\": \"2020-02-26T13:50:30.824+0000\",\n \"apoc_num\": 1,\n \"status\": \"Live\"\n }\n }\n}" + }, + { + "name": "RESOURCE NOT FOUND,**License Read** operation failed !The possible reason for failure is that you may have provided wrong license id.", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "esse et eu quis ut" + }, + { + "description": "(Required) All license APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "esse et eu quis ut" + } + ], + "url": { + "raw": "{{baseUrl}}/license/v3/read/:License_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "license", + "v3", + "read", + ":License_Id" + ], + "variable": [ + { + "key": "License_Id", + "value": "esse et eu quis ut", + "description": "(Required) Please append a valid License_Id To the Request URL" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.license.read\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-12-18T12:21:51ZZ\",\n \"params\": {\n \"resmsgid\": \"f5b2202a-f349-4488-a7ba-e061cfc2a88b\",\n \"msgid\": null,\n \"err\": \"NOT_FOUND\",\n \"status\": \"failed\",\n \"errmsg\": \"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: standard-youtube-license1\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + }, + { + "name": "INTERNAL SERVER ERROR ! Looks like something went wrong! These errors are tracked automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "esse et eu quis ut" + }, + { + "description": "(Required) All license APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "esse et eu quis ut" + } + ], + "url": { + "raw": "{{baseUrl}}/license/v3/read/:License_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "license", + "v3", + "read", + ":License_Id" + ], + "variable": [ + { + "key": "License_Id", + "value": "esse et eu quis ut", + "description": "(Required) Please append a valid License_Id To the Request URL" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.license.read\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-10T08:51:51.647Z\",\n \"params\": {\n \"resmsgid\": \"f234a6f0-3ac4-11eb-b0a2-8d5c9f561887\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"SERVER_ERROR\",\n \"result\": {}\n}" + } + ] + }, + { + "name": "Retire Content", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "{{apiKey}}", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/license/v3/retire/:License_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "license", + "v3", + "retire", + ":License_Id" + ], + "variable": [ + { + "key": "License_Id", + "value": "esse et eu quis ut", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + }, + "description": "This API is associated with retiring license on the Sunbird Platform .\n - The **_/retire/{License_Id}_** endpoint executes \"Retire License\" request based on license Id to delete the specific license.'\n - The endpoint for **Retire License** is `/license/v3/retire/{License_Id}`\n - It points to content-service - `/license/v3/retire/{License_Id}`\n - {...}refers to a section of review endpoint URL path that needs to be replaced by appropriate Id.\n - It is mandatory to provide values for parameters.\n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "OK ! Successful operation.**License retire operation was successfully executed.", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "esse et eu quis ut" + } + ], + "url": { + "raw": "{{baseUrl}}/license/v3/retire/:License_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "license", + "v3", + "retire", + ":License_Id" + ], + "variable": [ + { + "key": "License_Id", + "value": "esse et eu quis ut", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.license.retire\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-12-11T12:05:52ZZ\",\n \"params\": {\n \"resmsgid\": \"ebd2655a-a69a-460c-9bcf-0f45f5938fac\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"standard-youtube-license\",\n \"node_id\": \"standard-youtube-license\"\n }\n}" + }, + { + "name": "ERR CONTENT RETIRE! License is already retired.", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "esse et eu quis ut" + } + ], + "url": { + "raw": "{{baseUrl}}/license/v3/retire/:License_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "license", + "v3", + "retire", + ":License_Id" + ], + "variable": [ + { + "key": "License_Id", + "value": "esse et eu quis ut", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.license.retire\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-12-11T12:01:24ZZ\",\n \"params\": {\n \"resmsgid\": \"ec20ec05-581c-4559-988a-449f3c5ae698\",\n \"msgid\": null,\n \"err\": \"ERR_CONTENT_RETIRE\",\n \"status\": \"failed\",\n \"errmsg\": \"Content with Identifier standard-youtube-license is already Retired.\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": null\n }\n}" + }, + { + "name": "RESOURCE NOT FOUND,**License Retire** operation failed !The possible reason for failure is that you may have provided wrong license id.", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "esse et eu quis ut" + } + ], + "url": { + "raw": "{{baseUrl}}/license/v3/retire/:License_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "license", + "v3", + "retire", + ":License_Id" + ], + "variable": [ + { + "key": "License_Id", + "value": "esse et eu quis ut", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.license.retire\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-12-11T11:56:35ZZ\",\n \"params\": {\n \"resmsgid\": \"d826e808-2866-4603-9b60-0a3130a37128\",\n \"msgid\": null,\n \"err\": \"NOT_FOUND\",\n \"status\": \"failed\",\n \"errmsg\": \"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: standard-youtube-license1\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + }, + { + "name": "INTERNAL SERVER ERROR ! Looks like something went wrong! These errors are tracked automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "esse et eu quis ut" + } + ], + "url": { + "raw": "{{baseUrl}}/license/v3/retire/:License_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "license", + "v3", + "retire", + ":License_Id" + ], + "variable": [ + { + "key": "License_Id", + "value": "esse et eu quis ut", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.license.retire\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-10T08:51:51.647Z\",\n \"params\": {\n \"resmsgid\": \"f234a6f0-3ac4-11eb-b0a2-8d5c9f561887\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"SERVER_ERROR\",\n \"result\": {}\n}" + } + ] + } + ] + } + ], + "variable": [ + { + "key": "baseUrl", + "value": "https://staging.sunbirded.org/api", + "type": "string" + } + ] +} \ No newline at end of file diff --git a/content-api/api-tests/Collections/Sunbird Asset Management API(s).postman_collection.json b/content-api/api-tests/Collections/Sunbird Asset Management API(s).postman_collection.json new file mode 100644 index 000000000..e1121bc32 --- /dev/null +++ b/content-api/api-tests/Collections/Sunbird Asset Management API(s).postman_collection.json @@ -0,0 +1,1179 @@ +{ + "info": { + "_postman_id": "8ca47646-464a-411a-b9be-7d741055618b", + "name": "Sunbird Asset Management API(s)", + "description": "The Asset Management API allows you to manage asset over the sunbird platform.Apis perform operations related to all the _Asset_ on the Sunbird Platform. - Each of the endpoints serve a different purpose, so which one to use depends on what you want to do - The operations include CRUD (Create, Update, Read and Upload) operations and other operations such as upload etc. - The URL for Sunbird Asset API(s) is `asset/v1` - [TestCases](https://www.getpostman.com/collections/fd62845fa723c00658ae)\n\nContact Support:\n Email: info@sunbird.org", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "1419050" + }, + "item": [ + { + "name": "asset", + "item": [ + { + "name": "v1", + "item": [ + { + "name": "Asset Create", + "request": { + "method": "POST", + "header": [ + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "reprehenderit aliquip" + }, + { + "description": "The Content Type entity is the media type of the resource.Possible", + "key": "Content-Type", + "value": "reprehenderit aliquip" + }, + { + "description": "(Required) Represents the authorized User ID to execute the API", + "key": "x-authenticated-user-token", + "value": "reprehenderit aliquip" + }, + { + "description": "(Required) Represents a physical location (e.g: state) uniquely to which the user belongs", + "key": "X-Channel-ID", + "value": "reprehenderit aliquip" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"asset\": {\n \"channel\": \"et qui officia non\",\n \"code\": \"tempor nostrud esse\",\n \"createdBy\": \"conseq\",\n \"creator\": \"in enim\",\n \"keywords\": \"occaecat ea\",\n \"language\": [\n \"proident id aliquip\",\n \"dolor\"\n ],\n \"mediaType\": \"dolore dolore sint dolor\",\n \"mimeType\": \"dolor irure\",\n \"name\": \"consequat in aute irure\",\n \"primaryCategory\": \"dolor pariatur\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/asset/v1/create", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "asset", + "v1", + "create" + ] + }, + "description": "This API is associated with creating asset on the Sunbird Platform.\n - The endpoint for **Create Asset** is `/asset/v1/create`\n - It points to knowledge-mw-service - `/v1/asset/create`\n - It is mandatory to provide values for parameters marked with *\n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "reprehenderit aliquip" + }, + { + "description": "The Content Type entity is the media type of the resource.Possible", + "key": "Content-Type", + "value": "reprehenderit aliquip" + }, + { + "description": "(Required) Represents the authorized User ID to execute the API", + "key": "x-authenticated-user-token", + "value": "reprehenderit aliquip" + }, + { + "description": "(Required) Represents a physical location (e.g: state) uniquely to which the user belongs", + "key": "X-Channel-ID", + "value": "reprehenderit aliquip" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"asset\": {\n \"channel\": \"eiusmod est ullamco dolor\",\n \"code\": \"officia incididunt culpa commodo\",\n \"createdBy\": \"ea sit laboris cupidatat\",\n \"creator\": \"et anim deserunt sint\",\n \"keywords\": \"dolore cill\",\n \"language\": [\n \"magna\",\n \"esse Excepteur cillum\"\n ],\n \"mediaType\": \"Duis proident id elit\",\n \"mimeType\": \"Lorem in aute nisi\",\n \"name\": \"sunt non proident Duis\",\n \"primaryCategory\": \"esse reprehenderit\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/asset/v1/create", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "asset", + "v1", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.asset.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2021-12-29T09:20:03ZZ\",\n \"params\": {\n \"resmsgid\": \"bc3f02f5-d550-42b0-904e-38eb671248ef\",\n \"status\": \"successful\"\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_113441184591495168151\",\n \"node_id\": \"do_113441184591495168151\",\n \"versionKey\": \"1640769603462\"\n }\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "reprehenderit aliquip" + }, + { + "description": "The Content Type entity is the media type of the resource.Possible", + "key": "Content-Type", + "value": "reprehenderit aliquip" + }, + { + "description": "(Required) Represents the authorized User ID to execute the API", + "key": "x-authenticated-user-token", + "value": "reprehenderit aliquip" + }, + { + "description": "(Required) Represents a physical location (e.g: state) uniquely to which the user belongs", + "key": "X-Channel-ID", + "value": "reprehenderit aliquip" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"asset\": {\n \"channel\": \"eiusmod est ullamco dolor\",\n \"code\": \"officia incididunt culpa commodo\",\n \"createdBy\": \"ea sit laboris cupidatat\",\n \"creator\": \"et anim deserunt sint\",\n \"keywords\": \"dolore cill\",\n \"language\": [\n \"magna\",\n \"esse Excepteur cillum\"\n ],\n \"mediaType\": \"Duis proident id elit\",\n \"mimeType\": \"Lorem in aute nisi\",\n \"name\": \"sunt non proident Duis\",\n \"primaryCategory\": \"esse reprehenderit\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/asset/v1/create", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "asset", + "v1", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.asset.create\",\n \"ver\": \"4.0\",\n \"ts\": \"2021-12-29T09:36:28ZZ\",\n \"params\": {\n \"resmsgid\": \"c2a8542a-3ed3-4b69-8a76-f2cc782191ed\",\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Required Metadata code not set\"\n ]\n }\n}" + } + ] + }, + { + "name": "Asset Update", + "request": { + "method": "PATCH", + "header": [ + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "reprehenderit aliquip" + }, + { + "description": "The Content Type entity is the media type of the resource.Possible", + "key": "Content-Type", + "value": "reprehenderit aliquip" + }, + { + "description": "(Required) Represents the authorized User ID to execute the API", + "key": "x-authenticated-user-token", + "value": "reprehenderit aliquip" + }, + { + "description": "(Required) Represents a physical location (e.g: state) uniquely to which the user belongs", + "key": "X-Channel-ID", + "value": "reprehenderit aliquip" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"asset\": {\n \"name\": \"test asset\",\n \"versionKey\": \"1640774434453\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/asset/v1/update/:Asset_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "asset", + "v1", + "update", + ":Asset_Id" + ], + "variable": [ + { + "key": "Asset_Id", + "value": "reprehenderit aliquip", + "description": "(Required) Please append a valid Asset Id To the Request URL" + } + ] + }, + "description": "This API is associated with updating Asset on the Sunbird Platform .\n - **_/Update/_** endpoint executes the \"Update Asset\" request based on parameters provided as metadata in the request body.\n - The endpoint for **Update Asset** is `/asset/v1/update`\n - It points to knowledge-mw-service - `/v1/asset/update`\n - {...}refers to a section of review endpoint URL path that needs to be replaced by appropriate Id.\n - It is mandatory to provide values for parameters marked with *.\n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "OK", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "reprehenderit aliquip" + }, + { + "description": "The Content Type entity is the media type of the resource.Possible", + "key": "Content-Type", + "value": "reprehenderit aliquip" + }, + { + "description": "(Required) Represents the authorized User ID to execute the API", + "key": "x-authenticated-user-token", + "value": "reprehenderit aliquip" + }, + { + "description": "(Required) Represents a physical location (e.g: state) uniquely to which the user belongs", + "key": "X-Channel-ID", + "value": "reprehenderit aliquip" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"asset\": {\n \"name\": \"test asset\",\n \"versionKey\": \"1640774434453\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/asset/v1/update/:Asset_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "asset", + "v1", + "update", + ":Asset_Id" + ], + "variable": [ + { + "key": "Asset_Id", + "value": "reprehenderit aliquip", + "description": "(Required) Please append a valid Asset Id To the Request URL" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.asset.update\",\n \"ver\": \"4.0\",\n \"ts\": \"2021-12-29T09:24:27ZZ\",\n \"params\": {\n \"resmsgid\": \"7302823d-669f-449e-a903-d03b5e3c307d\",\n \"status\": \"successful\"\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_113441184591495168151\",\n \"node_id\": \"do_113441184591495168151\",\n \"versionKey\": \"1640769867533\"\n }\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "reprehenderit aliquip" + }, + { + "description": "The Content Type entity is the media type of the resource.Possible", + "key": "Content-Type", + "value": "reprehenderit aliquip" + }, + { + "description": "(Required) Represents the authorized User ID to execute the API", + "key": "x-authenticated-user-token", + "value": "reprehenderit aliquip" + }, + { + "description": "(Required) Represents a physical location (e.g: state) uniquely to which the user belongs", + "key": "X-Channel-ID", + "value": "reprehenderit aliquip" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"asset\": {\n \"name\": \"test asset\",\n \"versionKey\": \"1640774434453\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/asset/v1/update/:Asset_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "asset", + "v1", + "update", + ":Asset_Id" + ], + "variable": [ + { + "key": "Asset_Id", + "value": "reprehenderit aliquip", + "description": "(Required) Please append a valid Asset Id To the Request URL" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.asset.update\",\n \"ver\": \"4.0\",\n \"ts\": \"2021-12-29T09:38:42ZZ\",\n \"params\": {\n \"resmsgid\": \"63da1ede-b594-473d-abd3-70ca45bce86c\",\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Invalid version Key\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {}\n}" + } + ] + }, + { + "name": "Asset Read", + "request": { + "method": "GET", + "header": [ + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "reprehenderit aliquip" + }, + { + "description": "The Content Type entity is the media type of the resource.Possible", + "key": "Content-Type", + "value": "reprehenderit aliquip" + }, + { + "description": "(Required) Represents the authorized User ID to execute the API", + "key": "x-authenticated-user-token", + "value": "reprehenderit aliquip" + }, + { + "description": "(Required) Represents a physical location (e.g: state) uniquely to which the user belongs", + "key": "X-Channel-ID", + "value": "reprehenderit aliquip" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/asset/v1/read/:Asset_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "asset", + "v1", + "read", + ":Asset_Id" + ], + "variable": [ + { + "key": "Asset_Id", + "value": "reprehenderit aliquip", + "description": "(Required) Please append a valid Asset Id To the Request URL" + } + ] + }, + "description": "This API is associated with viewing and reading out the asset on the Sunbird Platform .\n - The **_/read/{asset Id}_** endpoint executes request for fetching the asset to be viewed and read.\n - The endpoint for **Read Asset** is `/asset/v1/read`\n - It points to knowledge-mw-service - `/v1/asset/read`\n - {...}refers to a section of reject endpoint URL path that needs to be replaced by appropriate Id.\n - You need to provide a valid asset Id value in {Asset Id} field of API URL.\n - It is mandatory to provide values for parameters marked with *. \n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "OK", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "reprehenderit aliquip" + }, + { + "description": "The Content Type entity is the media type of the resource.Possible", + "key": "Content-Type", + "value": "reprehenderit aliquip" + }, + { + "description": "(Required) Represents the authorized User ID to execute the API", + "key": "x-authenticated-user-token", + "value": "reprehenderit aliquip" + }, + { + "description": "(Required) Represents a physical location (e.g: state) uniquely to which the user belongs", + "key": "X-Channel-ID", + "value": "reprehenderit aliquip" + } + ], + "url": { + "raw": "{{baseUrl}}/asset/v1/read/:Asset_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "asset", + "v1", + "read", + ":Asset_Id" + ], + "variable": [ + { + "key": "Asset_Id", + "value": "reprehenderit aliquip", + "description": "(Required) Please append a valid Asset Id To the Request URL" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.asset.read\",\n \"ver\": \"4.0\",\n \"ts\": \"2021-12-29T10:22:50ZZ\",\n \"params\": {\n \"resmsgid\": \"30114c34-e044-4e49-ad5e-89075ae503ce\",\n \"status\": \"successful\"\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"content\": {\n \"ownershipType\": [\n \"createdBy\"\n ],\n \"code\": \"f2f41233-1c34-cf28-7bf8-0987a1eee420\",\n \"keywords\": \"\",\n \"channel\": 1309282781705830400,\n \"downloadUrl\": \"https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/assets/do_113441146415767552142/file_example_mp4_640_3mg.mp4\",\n \"language\": [\n \"English\"\n ],\n \"mimeType\": \"video/mp4\",\n \"idealScreenSize\": \"normal\",\n \"createdOn\": \"2021-12-29T08:02:23.340+0000\",\n \"objectType\": \"Asset\",\n \"primaryCategory\": \"asset\",\n \"contentDisposition\": \"inline\",\n \"lastUpdatedOn\": \"2021-12-29T08:02:24.755+0000\",\n \"contentEncoding\": \"identity\",\n \"artifactUrl\": \"https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/assets/do_113441146415767552142/file_example_mp4_640_3mg.mp4\",\n \"contentType\": \"Resource\",\n \"dialcodeRequired\": false,\n \"identifier\": \"do_113441146415767552142\",\n \"lastStatusChangedOn\": \"2021-12-29T08:02:23.340+0000\",\n \"audience\": [\n \"Student\"\n ],\n \"creator\": \"N11\",\n \"os\": [\n \"All\"\n ],\n \"visibility\": \"Default\",\n \"consumerId\": \"bfe5883f-ac66-4744-a064-3ed88d986eba\",\n \"mediaType\": \"video\",\n \"osId\": \"org.ekstep.quiz.app\",\n \"languageCode\": [\n \"en\"\n ],\n \"version\": 2,\n \"versionKey\": \"1640764944755\",\n \"license\": \"CC BY 4.0\",\n \"idealScreenDensity\": \"hdpi\",\n \"framework\": \"NCF\",\n \"size\": 3114374,\n \"createdBy\": \"5a587cc1-e018-4859-a0a8-e842650b9d64\",\n \"compatibilityLevel\": 1,\n \"name\": \"file_example_MP4_640_3MG\",\n \"status\": \"Processing\"\n }\n }\n}" + } + ] + }, + { + "name": "Asset Upload", + "request": { + "method": "POST", + "header": [ + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "reprehenderit aliquip" + }, + { + "description": "The Content Type entity is the media type of the resource.Possible", + "key": "Content-Type", + "value": "reprehenderit aliquip" + }, + { + "description": "(Required) Represents the authorized User ID to execute the API", + "key": "x-authenticated-user-token", + "value": "reprehenderit aliquip" + }, + { + "description": "(Required) Represents a physical location (e.g: state) uniquely to which the user belongs", + "key": "X-Channel-ID", + "value": "reprehenderit aliquip" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/asset/v1/upload/:Asset_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "asset", + "v1", + "upload", + ":Asset_Id" + ], + "variable": [ + { + "key": "Asset_Id", + "value": "reprehenderit aliquip", + "description": "(Required) Please append a valid Asset Id To the Request URL" + } + ] + }, + "description": "This API is associated with uploading asset on the Sunbird Platform .\n - The**_/upload/{Asset Id}_** endpoint executes \"Upload Asset\" request based on parameters provided as metadata in the request body.\n - The endpoint for **Upload Asset** is `/asset/v1/upload`\n - It points to knowledge-mw-service - `/v1/asset/upload`\n - {...}refers to a section of upload endpoint URL path that needs to be replaced by appropriate Id.\n - You need to provide a valid asset Id value in {Asset Id} field of API URL.\n - It is mandatory to provide values for parameters marked with *. \n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "reprehenderit aliquip" + }, + { + "description": "The Content Type entity is the media type of the resource.Possible", + "key": "Content-Type", + "value": "reprehenderit aliquip" + }, + { + "description": "(Required) Represents the authorized User ID to execute the API", + "key": "x-authenticated-user-token", + "value": "reprehenderit aliquip" + }, + { + "description": "(Required) Represents a physical location (e.g: state) uniquely to which the user belongs", + "key": "X-Channel-ID", + "value": "reprehenderit aliquip" + } + ], + "url": { + "raw": "{{baseUrl}}/asset/v1/upload/:Asset_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "asset", + "v1", + "upload", + ":Asset_Id" + ], + "variable": [ + { + "key": "Asset_Id", + "value": "reprehenderit aliquip", + "description": "(Required) Please append a valid Asset Id To the Request URL" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.asset.upload\",\n \"ver\": \"4.0\",\n \"ts\": \"2021-12-28T12:18:32ZZ\",\n \"params\": {\n \"resmsgid\": \"1ee721a1-deb7-4e9c-8e7e-777a376f7844\",\n \"status\": \"successful\"\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_1134405639859486721110\",\n \"artifactUrl\": \"https://dockstorage.blob.core.windows.net/sunbird-content-dock/content/assets/do_1134405639859486721110/image1.jpeg\",\n \"content_url\": \"https://dockstorage.blob.core.windows.net/sunbird-content-dock/content/assets/do_1134405639859486721110/image1.jpeg\",\n \"node_id\": \"do_1134405639859486721110\",\n \"versionKey\": \"1640693912866\"\n }\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "reprehenderit aliquip" + }, + { + "description": "The Content Type entity is the media type of the resource.Possible", + "key": "Content-Type", + "value": "reprehenderit aliquip" + }, + { + "description": "(Required) Represents the authorized User ID to execute the API", + "key": "x-authenticated-user-token", + "value": "reprehenderit aliquip" + }, + { + "description": "(Required) Represents a physical location (e.g: state) uniquely to which the user belongs", + "key": "X-Channel-ID", + "value": "reprehenderit aliquip" + } + ], + "url": { + "raw": "{{baseUrl}}/asset/v1/upload/:Asset_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "asset", + "v1", + "upload", + ":Asset_Id" + ], + "variable": [ + { + "key": "Asset_Id", + "value": "reprehenderit aliquip", + "description": "(Required) Please append a valid Asset Id To the Request URL" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "application/json:" + } + ], + "cookie": [], + "body": "" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "reprehenderit aliquip" + }, + { + "description": "The Content Type entity is the media type of the resource.Possible", + "key": "Content-Type", + "value": "reprehenderit aliquip" + }, + { + "description": "(Required) Represents the authorized User ID to execute the API", + "key": "x-authenticated-user-token", + "value": "reprehenderit aliquip" + }, + { + "description": "(Required) Represents a physical location (e.g: state) uniquely to which the user belongs", + "key": "X-Channel-ID", + "value": "reprehenderit aliquip" + } + ], + "url": { + "raw": "{{baseUrl}}/asset/v1/upload/:Asset_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "asset", + "v1", + "upload", + ":Asset_Id" + ], + "variable": [ + { + "key": "Asset_Id", + "value": "reprehenderit aliquip", + "description": "(Required) Please append a valid Asset Id To the Request URL" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "application/json:" + } + ], + "cookie": [], + "body": "" + } + ] + }, + { + "name": "Asset Copy", + "request": { + "method": "POST", + "header": [ + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "reprehenderit aliquip" + }, + { + "description": "The Content Type entity is the media type of the resource.Possible", + "key": "Content-Type", + "value": "reprehenderit aliquip" + }, + { + "description": "(Required) Represents the authorized User ID to execute the API", + "key": "x-authenticated-user-token", + "value": "reprehenderit aliquip" + }, + { + "description": "(Required) Represents a physical location (e.g: state) uniquely to which the user belongs", + "key": "X-Channel-ID", + "value": "reprehenderit aliquip" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"publisher\": \"API_DOC_PUBLISHER\",\n \"lastPublishedBy\": \"API_DOC_PUBLISHER\",\n \"publishComment\": \"OK\",\n \"publishChecklist\": []\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/asset/v1/copy/:Asset_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "asset", + "v1", + "copy", + ":Asset_Id" + ], + "variable": [ + { + "key": "Asset_Id", + "value": "reprehenderit aliquip", + "description": "(Required) Please append a valid Asset Id To the Request URL" + } + ] + }, + "description": "This API is associated with copying a asset on the Sunbird Platform .\n - The **_/copy/_**endpoint executes request for copy asset, if asset is found appropriate.\n - The endpoint for **Copy Asset** is `/asset/v1/copy`\n - It points to knowledge-mw-service - `/v1/asset/copy`\n - It is mandatory to provide values for parameters marked with *.\n - Mandatory fields cannot be null or empty.." + }, + "response": [ + { + "name": "OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "reprehenderit aliquip" + }, + { + "description": "The Content Type entity is the media type of the resource.Possible", + "key": "Content-Type", + "value": "reprehenderit aliquip" + }, + { + "description": "(Required) Represents the authorized User ID to execute the API", + "key": "x-authenticated-user-token", + "value": "reprehenderit aliquip" + }, + { + "description": "(Required) Represents a physical location (e.g: state) uniquely to which the user belongs", + "key": "X-Channel-ID", + "value": "reprehenderit aliquip" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"sit culpa\",\n \"publishChecklist\": [\n \"ullamco\",\n \"consequat do ullamco esse labore\"\n ],\n \"publishComment\": \"anim amet consequat exercitation\",\n \"publisher\": \"mollit veniam est do\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/asset/v1/copy/:Asset_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "asset", + "v1", + "copy", + ":Asset_Id" + ], + "variable": [ + { + "key": "Asset_Id", + "value": "reprehenderit aliquip", + "description": "(Required) Please append a valid Asset Id To the Request URL" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.asset.copy\",\n \"ver\": \"4.0\",\n \"ts\": \"2021-12-30T11:42:05ZZ\",\n \"params\": {\n \"resmsgid\": \"ab112c9f-bbb0-4493-a32d-040243b14a09\",\n \"status\": \"successful\"\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"node_id\": {\n \"do_1134418461689282561147\": \"do_1134419621916180481174\"\n },\n \"versionKey\": \"1640864525352\"\n }\n}" + }, + { + "name": "Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "reprehenderit aliquip" + }, + { + "description": "The Content Type entity is the media type of the resource.Possible", + "key": "Content-Type", + "value": "reprehenderit aliquip" + }, + { + "description": "(Required) Represents the authorized User ID to execute the API", + "key": "x-authenticated-user-token", + "value": "reprehenderit aliquip" + }, + { + "description": "(Required) Represents a physical location (e.g: state) uniquely to which the user belongs", + "key": "X-Channel-ID", + "value": "reprehenderit aliquip" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"sit culpa\",\n \"publishChecklist\": [\n \"ullamco\",\n \"consequat do ullamco esse labore\"\n ],\n \"publishComment\": \"anim amet consequat exercitation\",\n \"publisher\": \"mollit veniam est do\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/asset/v1/copy/:Asset_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "asset", + "v1", + "copy", + ":Asset_Id" + ], + "variable": [ + { + "key": "Asset_Id", + "value": "reprehenderit aliquip", + "description": "(Required) Please append a valid Asset Id To the Request URL" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.asset.copy\",\n \"ver\": \"4.0\",\n \"ts\": \"2021-12-30T11:42:58ZZ\",\n \"params\": {\n \"resmsgid\": \"0a0c203d-4d8f-4a78-be67-1365fad3407e\",\n \"err\": \"NOT_FOUND\",\n \"status\": \"failed\",\n \"errmsg\": \"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: do_1134418461689282561142\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {}\n}" + } + ] + } + ] + }, + { + "name": "Asset Upload Url", + "request": { + "method": "POST", + "header": [ + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "reprehenderit aliquip" + }, + { + "description": "The Content Type entity is the media type of the resource.Possible", + "key": "Content-Type", + "value": "reprehenderit aliquip" + }, + { + "description": "(Required) Represents the authorized User ID to execute the API", + "key": "x-authenticated-user-token", + "value": "reprehenderit aliquip" + }, + { + "description": "(Required) Represents a physical location (e.g: state) uniquely to which the user belongs", + "key": "X-Channel-ID", + "value": "reprehenderit aliquip" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"asset\": {\n \"fileName\": \"flower6.jpeg\"\n }\n }\n}" + }, + "url": { + "raw": "{{baseUrl}}/asset/upload/url/:Asset_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "asset", + "upload", + "url", + ":Asset_Id" + ], + "variable": [ + { + "key": "Asset_Id", + "value": "reprehenderit aliquip", + "description": "(Required) Please append a valid Asset Id To the Request URL" + } + ] + }, + "description": "This API is associated with uploading asset on the Sunbird Platform .\n - The**_/upload/url/{Asset Id}_** endpoint executes \"Upload Asset URL\" request based on parameters provided as metadata in the request body.\n - The endpoint for **Upload Asset URL** is `/asset/v1/upload/url`\n - It points to knowledge-mw-service - `/v1/asset/upload/url`\n - {...}refers to a section of upload endpoint URL path that needs to be replaced by appropriate Id.\n - You need to provide a valid asset Id value in {Asset Id} field of API URL.\n - On Success api returns pre-signed-url link with come configured ttl against which file can be uploaded.\n - It is mandatory to provide values for parameters marked with *. \n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "reprehenderit aliquip" + }, + { + "description": "The Content Type entity is the media type of the resource.Possible", + "key": "Content-Type", + "value": "reprehenderit aliquip" + }, + { + "description": "(Required) Represents the authorized User ID to execute the API", + "key": "x-authenticated-user-token", + "value": "reprehenderit aliquip" + }, + { + "description": "(Required) Represents a physical location (e.g: state) uniquely to which the user belongs", + "key": "X-Channel-ID", + "value": "reprehenderit aliquip" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"asset\": {\n \"fileName\": \"flower6.jpeg\"\n }\n }\n}" + }, + "url": { + "raw": "{{baseUrl}}/asset/upload/url/:Asset_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "asset", + "upload", + "url", + ":Asset_Id" + ], + "variable": [ + { + "key": "Asset_Id", + "value": "reprehenderit aliquip", + "description": "(Required) Please append a valid Asset Id To the Request URL" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.asset.upload.url\",\n \"ver\": \"3.0\",\n \"ts\": \"2021-12-30T07:57:47ZZ\",\n \"params\": {\n \"resmsgid\": \"df673ca8-7d2c-46c9-b9e5-77e5602f9454\",\n \"status\": \"successful\"\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_1134418461689282561147\",\n \"url_expiry\": \"54000\",\n \"pre_signed_url\": \"https://dockstorage.blob.core.windows.net/sunbird-content-dock/content/assets/do_1134418461689282561147/flower6.jpeg?sv=2017-04-17&se=2021-12-30T22%3A57%3A47Z&sr=b&sp=w&sig=2pow25YLNLIM0hrMwVx4uy3aDu3BTNNbNdLI8X9Yt4M%3D\"\n }\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "reprehenderit aliquip" + }, + { + "description": "The Content Type entity is the media type of the resource.Possible", + "key": "Content-Type", + "value": "reprehenderit aliquip" + }, + { + "description": "(Required) Represents the authorized User ID to execute the API", + "key": "x-authenticated-user-token", + "value": "reprehenderit aliquip" + }, + { + "description": "(Required) Represents a physical location (e.g: state) uniquely to which the user belongs", + "key": "X-Channel-ID", + "value": "reprehenderit aliquip" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"asset\": {\n \"fileName\": \"flower6.jpeg\"\n }\n }\n}" + }, + "url": { + "raw": "{{baseUrl}}/asset/upload/url/:Asset_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "asset", + "upload", + "url", + ":Asset_Id" + ], + "variable": [ + { + "key": "Asset_Id", + "value": "reprehenderit aliquip", + "description": "(Required) Please append a valid Asset Id To the Request URL" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.asset.upload.url\",\n \"ver\": \"3.0\",\n \"ts\": \"2021-12-30T07:47:32ZZ\",\n \"params\": {\n \"resmsgid\": \"9b18aedd-c6d3-43bf-b967-d787e2054b60\",\n \"err\": \"ERR_CONTENT_BLANK_FILE_NAME\",\n \"status\": \"failed\",\n \"errmsg\": \"File name is blank\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {}\n}" + }, + { + "name": "Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "reprehenderit aliquip" + }, + { + "description": "The Content Type entity is the media type of the resource.Possible", + "key": "Content-Type", + "value": "reprehenderit aliquip" + }, + { + "description": "(Required) Represents the authorized User ID to execute the API", + "key": "x-authenticated-user-token", + "value": "reprehenderit aliquip" + }, + { + "description": "(Required) Represents a physical location (e.g: state) uniquely to which the user belongs", + "key": "X-Channel-ID", + "value": "reprehenderit aliquip" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"asset\": {\n \"fileName\": \"flower6.jpeg\"\n }\n }\n}" + }, + "url": { + "raw": "{{baseUrl}}/asset/upload/url/:Asset_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "asset", + "upload", + "url", + ":Asset_Id" + ], + "variable": [ + { + "key": "Asset_Id", + "value": "reprehenderit aliquip", + "description": "(Required) Please append a valid Asset Id To the Request URL" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.asset.upload.url\",\n \"ver\": \"3.0\",\n \"ts\": \"2021-12-30T07:59:12ZZ\",\n \"params\": {\n \"resmsgid\": \"256bbf94-1ddb-4f44-ab48-ffd04bd8c8a9\",\n \"err\": \"NOT_FOUND\",\n \"status\": \"failed\",\n \"errmsg\": \"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: do_1134418461689282561149\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {}\n}" + } + ] + } + ] + } + ], + "variable": [ + { + "key": "baseUrl", + "value": "https://staging.sunbirded.org/api", + "type": "string" + } + ] +} \ No newline at end of file diff --git a/content-api/api-tests/Collections/Sunbird Collection Management API(s).postman_collection.json b/content-api/api-tests/Collections/Sunbird Collection Management API(s).postman_collection.json new file mode 100644 index 000000000..d1231f7db --- /dev/null +++ b/content-api/api-tests/Collections/Sunbird Collection Management API(s).postman_collection.json @@ -0,0 +1,2921 @@ +{ + "info": { + "_postman_id": "f3122aff-72d4-4066-9373-9132b61dc0de", + "name": "Sunbird Collection Management API(s)", + "description": "The Collection Management API allows you to manage collection over the sunbird platform.Apis perform operations related to all the _Collection_ on the Sunbird Platform. \n- Each of the endpoints serve a different purpose, so which one to use depends on what you want to do\n- The operations include CRUD (Create, Update, Read) operations and other operations such as copy, flag, link Dialcode, etc. \n- The URL for Sunbird Content API(s) is `collection/v1` \n- [TestCases](https://www.getpostman.com/collections/40dde03073826216533a)\n\nContact Support:\n Email: info@sunbird.org", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "1419050" + }, + "item": [ + { + "name": "collection/v1", + "item": [ + { + "name": "flag", + "item": [ + { + "name": "Collection Flag", + "request": { + "method": "POST", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "magna" + }, + { + "description": "(Required) The Registered User ID used to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "magna" + }, + { + "description": "The unique ID provided to the State to which the user belongs.", + "key": "X-Channel-id", + "value": "magna" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "magna" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"flagReasons\": [\n \"Copyright Violation\"\n ],\n \"flaggedBy\": \"API-DOC-Tester\",\n \"versionKey\": \"{{versionKey}}\",\n \"flags\": [\n \"kp-flags\",\n \"newKp-flags\"\n ]\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/collection/v1/flag/:collection_id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "flag", + ":collection_id" + ], + "variable": [ + { + "key": "collection_id", + "value": "magna", + "description": "(Required) Please append a valid Collection Id To the Request URL" + } + ] + }, + "description": "This API is associated with flag Collection on the Sunbird Platform .\n - **_/Collection/_** endpoint executes the \"Flag Collection\" request based on parameters provided as metadata in the request body.\n - The endpoint for **Update Collection** is `/collection/v1/flag`\n - It points to knowledge-mw-service - `/v1/collection/flag`\n - {...}refers to a section of review endpoint URL path that needs to be replaced by appropriate Id.\n - It is mandatory to provide values for parameters marked with *.\n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "magna" + }, + { + "description": "(Required) The Registered User ID used to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "magna" + }, + { + "description": "The unique ID provided to the State to which the user belongs.", + "key": "X-Channel-id", + "value": "magna" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "magna" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"flagReasons\": [\n \"fugiat\",\n \"labore voluptate eiusmod\"\n ],\n \"flaggedBy\": \"labore e\",\n \"flags\": [\n \"mollit laborum\",\n \"p\"\n ],\n \"versionKey\": \"Ut nulla officia dolore\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/collection/v1/flag/:collection_id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "flag", + ":collection_id" + ], + "variable": [ + { + "key": "collection_id", + "value": "magna", + "description": "(Required) Please append a valid Collection Id To the Request URL" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.collection.flag\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-01-06T07:11:52ZZ\",\n \"params\": {\n \"resmsgid\": \"eb628dff-53c6-45f2-9789-0f193988f585\",\n \"status\": \"successful\"\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_1134467204859658241168\",\n \"node_id\": \"do_1134467204859658241168\",\n \"versionKey\": \"1641453112470\"\n }\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "magna" + }, + { + "description": "(Required) The Registered User ID used to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "magna" + }, + { + "description": "The unique ID provided to the State to which the user belongs.", + "key": "X-Channel-id", + "value": "magna" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "magna" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"flagReasons\": [\n \"fugiat\",\n \"labore voluptate eiusmod\"\n ],\n \"flaggedBy\": \"labore e\",\n \"flags\": [\n \"mollit laborum\",\n \"p\"\n ],\n \"versionKey\": \"Ut nulla officia dolore\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/collection/v1/flag/:collection_id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "flag", + ":collection_id" + ], + "variable": [ + { + "key": "collection_id", + "value": "magna", + "description": "(Required) Please append a valid Collection Id To the Request URL" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.collection.flag\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-01-06T07:08:12ZZ\",\n \"params\": {\n \"resmsgid\": \"aad90170-6b97-4d65-a980-b3184fe13142\",\n \"err\": \"ERR_CONTENT_NOT_FLAGGABLE\",\n \"status\": \"failed\",\n \"errmsg\": \"Unpublished Content do_11334223976733900811 cannot be flagged\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {}\n}" + } + ] + }, + { + "name": "Collection Flag Accept", + "request": { + "method": "POST", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "magna" + }, + { + "description": "(Required) The Registered User ID used to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "magna" + }, + { + "description": "The unique ID provided to the State to which the user belongs.", + "key": "X-Channel-id", + "value": "magna" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "magna" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"versionKey\": \"{{versionKey}}\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/collection/v1/flag/accept/:collection_id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "flag", + "accept", + ":collection_id" + ], + "variable": [ + { + "key": "collection_id", + "value": "magna", + "description": "(Required) Please append a valid Collection Id To the Request URL" + } + ] + }, + "description": "This API is associated with Flag Accept Collection on the Sunbird Platform .\n - **_/Collection/_** endpoint executes the \"Flag Accept Collection\" request based on parameters provided as metadata in the request body.\n - The endpoint for **Flag Accept Collection** is `/collection/v1/flag/accept`\n - It points to knowledge-mw-service - `/v1/collection/flag/accept`\n - {...}refers to a section of review endpoint URL path that needs to be replaced by appropriate Id.\n - It is mandatory to provide values for parameters marked with *.\n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "magna" + }, + { + "description": "(Required) The Registered User ID used to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "magna" + }, + { + "description": "The unique ID provided to the State to which the user belongs.", + "key": "X-Channel-id", + "value": "magna" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "magna" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"versionKey\": \"{{versionKey}}\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/collection/v1/flag/accept/:collection_id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "flag", + "accept", + ":collection_id" + ], + "variable": [ + { + "key": "collection_id", + "value": "magna", + "description": "(Required) Please append a valid Collection Id To the Request URL" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.collection.flag.accept\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-01-06T07:14:38ZZ\",\n \"params\": {\n \"resmsgid\": \"c7b2c6b0-ace7-4da5-8429-6f9d5f6b6d35\",\n \"status\": \"successful\"\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_1134467204859658241168\",\n \"node_id\": \"do_1134467204859658241168\",\n \"versionKey\": \"1641453278531\"\n }\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "magna" + }, + { + "description": "(Required) The Registered User ID used to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "magna" + }, + { + "description": "The unique ID provided to the State to which the user belongs.", + "key": "X-Channel-id", + "value": "magna" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "magna" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"versionKey\": \"{{versionKey}}\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/collection/v1/flag/accept/:collection_id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "flag", + "accept", + ":collection_id" + ], + "variable": [ + { + "key": "collection_id", + "value": "magna", + "description": "(Required) Please append a valid Collection Id To the Request URL" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.collection.flag.accept\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-01-06T07:06:24ZZ\",\n \"params\": {\n \"resmsgid\": \"40bd05d7-890e-4e3f-9ad3-dcde348e8fb9\",\n \"err\": \"ERR_INVALID_CONTENT\",\n \"status\": \"failed\",\n \"errmsg\": \"Invalid Flagged Content! Content Can Not Be Accepted.\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {}\n}" + } + ] + } + ] + }, + { + "name": "hierarchy", + "item": [ + { + "name": "Update Collection Hierarchy", + "request": { + "method": "PATCH", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "magna" + }, + { + "description": "(Required) The Registered User ID used to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "magna" + }, + { + "description": "The unique ID provided to the State to which the user belongs.", + "key": "X-Channel-id", + "value": "magna" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "magna" + }, + { + "key": "Accept", + "value": "application/json; charset=utf-8" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"data\": {\n \"nodesModified\": {\n \"do_113444715309432832183\": {\n \"root\": true,\n \"objectType\": \"Content\",\n \"metadata\": {\n \"appIcon\": \"\",\n \"name\": \"Untitled Collection\",\n \"description\": \"Enter description for Collection\",\n \"dialcodeRequired\": false,\n \"primaryCategory\": \"Content Playlist\",\n \"additionalCategories\": [],\n \"audience\": [\n \"Student\"\n ],\n \"mediumIds\": [\n \"ekstep_ncert_k-12_medium_english\"\n ],\n \"gradeLevelIds\": [\n \"ekstep_ncert_k-12_gradelevel_class1\"\n ],\n \"subjectIds\": [\n \"ekstep_ncert_k-12_subject_mathematics\"\n ],\n \"author\": \"N11\",\n \"copyright\": \"NIT123\",\n \"license\": \"CC BY 4.0\",\n \"contentType\": \"Collection\",\n \"attributions\": [],\n \"keywords\": [\n \"nnn\"\n ],\n \"boardIds\": [\n \"ekstep_ncert_k-12_board_cbse\"\n ],\n \"copyrightYear\": 2121\n },\n \"isNew\": false\n }\n },\n \"hierarchy\": {\n \"do_113444715309432832183\": {\n \"name\": \"Untitled Collection\",\n \"children\": [],\n \"root\": true\n }\n },\n \"lastUpdatedBy\": \"5a587cc1-e018-4859-a0a8-e842650b9d64\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/collection/v1/hierarchy/update", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "hierarchy", + "update" + ] + }, + "description": "This API is associated with updating collectio on the Sunbird Platform .\n - **_/Update/_** endpoint executes the \"Update Collection Hierarchy\" request based on parameters provided as metadata in the request body.\n - The endpoint for **Update Collection Hierarchy** is `/collection/v1/hierarchy/update`\n - It points to knowledge-mw-service - `/v1/collection/hierarchy/update`\n - {...}refers to a section of review endpoint URL path that needs to be replaced by appropriate Id.\n - It is mandatory to provide values for parameters marked with *.\n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "OK", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "magna" + }, + { + "description": "(Required) The Registered User ID used to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "magna" + }, + { + "description": "The unique ID provided to the State to which the user belongs.", + "key": "X-Channel-id", + "value": "magna" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "magna" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"data\": {\n \"nodesModified\": {\n \"do_113444715309432832183\": {\n \"root\": true,\n \"objectType\": \"Content\",\n \"metadata\": {\n \"appIcon\": \"\",\n \"name\": \"Untitled Collection\",\n \"description\": \"Enter description for Collection\",\n \"dialcodeRequired\": false,\n \"primaryCategory\": \"Content Playlist\",\n \"additionalCategories\": [],\n \"audience\": [\n \"Student\"\n ],\n \"mediumIds\": [\n \"ekstep_ncert_k-12_medium_english\"\n ],\n \"gradeLevelIds\": [\n \"ekstep_ncert_k-12_gradelevel_class1\"\n ],\n \"subjectIds\": [\n \"ekstep_ncert_k-12_subject_mathematics\"\n ],\n \"author\": \"N11\",\n \"copyright\": \"NIT123\",\n \"license\": \"CC BY 4.0\",\n \"contentType\": \"Collection\",\n \"attributions\": [],\n \"keywords\": [\n \"nnn\"\n ],\n \"boardIds\": [\n \"ekstep_ncert_k-12_board_cbse\"\n ],\n \"copyrightYear\": 2121\n },\n \"isNew\": false\n }\n },\n \"hierarchy\": {\n \"do_113444715309432832183\": {\n \"name\": \"Untitled Collection\",\n \"children\": [],\n \"root\": true\n }\n },\n \"lastUpdatedBy\": \"5a587cc1-e018-4859-a0a8-e842650b9d64\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/collection/v1/hierarchy/update", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "hierarchy", + "update" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=utf-8" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.hierarchy.update\",\n \"ver\": \"1.0\",\n \"ts\": \"2022-01-03T09:43:27.001Z\",\n \"params\": {\n \"resmsgid\": \"99def090-6c79-11ec-b3c3-d9afeab1a49c\",\n \"msgid\": \"fec10a19-18ca-743d-2594-701f84f47462\",\n \"status\": \"successful\"\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"content_id\": \"do_113444715309432832183\",\n \"identifiers\": {}\n }\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "magna" + }, + { + "description": "(Required) The Registered User ID used to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "magna" + }, + { + "description": "The unique ID provided to the State to which the user belongs.", + "key": "X-Channel-id", + "value": "magna" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "magna" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"data\": {\n \"nodesModified\": {\n \"do_113444715309432832183\": {\n \"root\": true,\n \"objectType\": \"Content\",\n \"metadata\": {\n \"appIcon\": \"\",\n \"name\": \"Untitled Collection\",\n \"description\": \"Enter description for Collection\",\n \"dialcodeRequired\": false,\n \"primaryCategory\": \"Content Playlist\",\n \"additionalCategories\": [],\n \"audience\": [\n \"Student\"\n ],\n \"mediumIds\": [\n \"ekstep_ncert_k-12_medium_english\"\n ],\n \"gradeLevelIds\": [\n \"ekstep_ncert_k-12_gradelevel_class1\"\n ],\n \"subjectIds\": [\n \"ekstep_ncert_k-12_subject_mathematics\"\n ],\n \"author\": \"N11\",\n \"copyright\": \"NIT123\",\n \"license\": \"CC BY 4.0\",\n \"contentType\": \"Collection\",\n \"attributions\": [],\n \"keywords\": [\n \"nnn\"\n ],\n \"boardIds\": [\n \"ekstep_ncert_k-12_board_cbse\"\n ],\n \"copyrightYear\": 2121\n },\n \"isNew\": false\n }\n },\n \"hierarchy\": {\n \"do_113444715309432832183\": {\n \"name\": \"Untitled Collection\",\n \"children\": [],\n \"root\": true\n }\n },\n \"lastUpdatedBy\": \"5a587cc1-e018-4859-a0a8-e842650b9d64\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/collection/v1/hierarchy/update", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "hierarchy", + "update" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=utf-8" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.hierarchy.update\",\n \"ver\": \"1.0\",\n \"ts\": \"2022-01-03T09:37:29.231Z\",\n \"params\": {\n \"resmsgid\": \"c49f91f0-6c78-11ec-b3c3-d9afeab1a49c\",\n \"status\": \"failed\",\n \"err\": \"ERR_CONTENT_HIERARCHY_UPDATE_FIELDS_MISSING\",\n \"errmsg\": \"Required fields for update hierarchy are missing\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {}\n}" + }, + { + "name": "Not Found", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "magna" + }, + { + "description": "(Required) The Registered User ID used to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "magna" + }, + { + "description": "The unique ID provided to the State to which the user belongs.", + "key": "X-Channel-id", + "value": "magna" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "magna" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"data\": {\n \"nodesModified\": {\n \"do_113444715309432832183\": {\n \"root\": true,\n \"objectType\": \"Content\",\n \"metadata\": {\n \"appIcon\": \"\",\n \"name\": \"Untitled Collection\",\n \"description\": \"Enter description for Collection\",\n \"dialcodeRequired\": false,\n \"primaryCategory\": \"Content Playlist\",\n \"additionalCategories\": [],\n \"audience\": [\n \"Student\"\n ],\n \"mediumIds\": [\n \"ekstep_ncert_k-12_medium_english\"\n ],\n \"gradeLevelIds\": [\n \"ekstep_ncert_k-12_gradelevel_class1\"\n ],\n \"subjectIds\": [\n \"ekstep_ncert_k-12_subject_mathematics\"\n ],\n \"author\": \"N11\",\n \"copyright\": \"NIT123\",\n \"license\": \"CC BY 4.0\",\n \"contentType\": \"Collection\",\n \"attributions\": [],\n \"keywords\": [\n \"nnn\"\n ],\n \"boardIds\": [\n \"ekstep_ncert_k-12_board_cbse\"\n ],\n \"copyrightYear\": 2121\n },\n \"isNew\": false\n }\n },\n \"hierarchy\": {\n \"do_113444715309432832183\": {\n \"name\": \"Untitled Collection\",\n \"children\": [],\n \"root\": true\n }\n },\n \"lastUpdatedBy\": \"5a587cc1-e018-4859-a0a8-e842650b9d64\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/collection/v1/hierarchy/update", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "hierarchy", + "update" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=utf-8" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.hierarchy.update\",\n \"ver\": \"1.0\",\n \"ts\": \"2022-01-03T09:36:03.730Z\",\n \"params\": {\n \"resmsgid\": \"91a92720-6c78-11ec-b3c3-d9afeab1a49c\",\n \"status\": \"failed\",\n \"err\": \"NOT_FOUND\",\n \"errmsg\": \"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: do_113444715309432832182\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {}\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "magna" + }, + { + "description": "(Required) The Registered User ID used to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "magna" + }, + { + "description": "The unique ID provided to the State to which the user belongs.", + "key": "X-Channel-id", + "value": "magna" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "magna" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"data\": {\n \"nodesModified\": {\n \"do_113444715309432832183\": {\n \"root\": true,\n \"objectType\": \"Content\",\n \"metadata\": {\n \"appIcon\": \"\",\n \"name\": \"Untitled Collection\",\n \"description\": \"Enter description for Collection\",\n \"dialcodeRequired\": false,\n \"primaryCategory\": \"Content Playlist\",\n \"additionalCategories\": [],\n \"audience\": [\n \"Student\"\n ],\n \"mediumIds\": [\n \"ekstep_ncert_k-12_medium_english\"\n ],\n \"gradeLevelIds\": [\n \"ekstep_ncert_k-12_gradelevel_class1\"\n ],\n \"subjectIds\": [\n \"ekstep_ncert_k-12_subject_mathematics\"\n ],\n \"author\": \"N11\",\n \"copyright\": \"NIT123\",\n \"license\": \"CC BY 4.0\",\n \"contentType\": \"Collection\",\n \"attributions\": [],\n \"keywords\": [\n \"nnn\"\n ],\n \"boardIds\": [\n \"ekstep_ncert_k-12_board_cbse\"\n ],\n \"copyrightYear\": 2121\n },\n \"isNew\": false\n }\n },\n \"hierarchy\": {\n \"do_113444715309432832183\": {\n \"name\": \"Untitled Collection\",\n \"children\": [],\n \"root\": true\n }\n },\n \"lastUpdatedBy\": \"5a587cc1-e018-4859-a0a8-e842650b9d64\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/collection/v1/hierarchy/update", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "hierarchy", + "update" + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=utf-8" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.hierarchy.update\",\n \"ver\": \"1.0\",\n \"ts\": \"2022-01-03T09:42:31.388Z\",\n \"params\": {\n \"resmsgid\": \"78b911c0-6c79-11ec-b3c3-d9afeab1a49c\",\n \"status\": \"failed\",\n \"err\": \"ERR_SYSTEM_EXCEPTION\",\n \"errmsg\": \"Something went wrong in server while processing the request\"\n },\n \"responseCode\": \"SERVER_ERROR\",\n \"result\": {}\n}" + } + ] + }, + { + "name": "Read Collection Hierarchy", + "request": { + "method": "GET", + "header": [ + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "magna" + }, + { + "description": "The Asset Type entity is the media type of the resource.Possible", + "key": "Content-Type", + "value": "magna" + }, + { + "description": "(Required) Represents the authorized User ID to execute the API", + "key": "x-authenticated-user-token", + "value": "magna" + }, + { + "description": "(Required) Represents a physical location (e.g: state) uniquely to which the user belongs", + "key": "X-Channel-ID", + "value": "magna" + }, + { + "key": "Accept", + "value": "application/json; charset=utf-8" + } + ], + "url": { + "raw": "{{baseUrl}}/collection/v1/hierarchy/:Collection_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "hierarchy", + ":Collection_Id" + ], + "variable": [ + { + "key": "Collection_Id", + "value": "magna", + "description": "(Required) Please append a valid collection Id To the Request URL" + } + ] + }, + "description": "This API is associated with viewing and reading out the collection on the Sunbird Platform .\n - The **_/hierarchy/{collectionId}_** endpoint executes request for fetching the collection to be viewed and read.\n - The endpoint for **Read Collection** is `/collection/v1/hierarchy`\n - It points to knowledge-mw-service - `/v1/collection/hierarchy`\n - {...}refers to a section of reject endpoint URL path that needs to be replaced by appropriate Id.\n - You need to provide a valid collection Id value in {Collection Id} field of API URL.\n - It is mandatory to provide values for parameters marked with *. \n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "OK", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "magna" + }, + { + "description": "The Asset Type entity is the media type of the resource.Possible", + "key": "Content-Type", + "value": "magna" + }, + { + "description": "(Required) Represents the authorized User ID to execute the API", + "key": "x-authenticated-user-token", + "value": "magna" + }, + { + "description": "(Required) Represents a physical location (e.g: state) uniquely to which the user belongs", + "key": "X-Channel-ID", + "value": "magna" + } + ], + "url": { + "raw": "{{baseUrl}}/collection/v1/hierarchy/:Collection_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "hierarchy", + ":Collection_Id" + ], + "variable": [ + { + "key": "Collection_Id", + "value": "magna", + "description": "(Required) Please append a valid collection Id To the Request URL" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "X-Frame-Options", + "value": "SAMEORIGIN", + "description": "" + }, + { + "key": "Strict-Transport-Security", + "value": "max-age=15552000; includeSubDomains", + "description": "" + }, + { + "key": "X-Content-Type-Options", + "value": "nosniff", + "description": "" + }, + { + "key": "request-time", + "value": "19", + "description": "" + }, + { + "key": "Connection", + "value": "keep-alive", + "description": "" + }, + { + "key": "X-Download-Options", + "value": "noopen", + "description": "" + }, + { + "key": "X-DNS-Prefetch-Control", + "value": "false", + "description": "" + }, + { + "key": "Content-Length", + "value": "8967", + "description": "" + }, + { + "key": "X-XSS-Protection", + "value": "1; mode=block", + "description": "" + }, + { + "key": "Date", + "value": "Tue, 04 Jan 2022 12:28:17 GMT", + "description": "" + }, + { + "key": "Content-Type", + "value": "application/json; charset=utf-8" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.collection.hierarchy.get\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-01-04T12:28:17ZZ\",\n \"params\": {\n \"resmsgid\": \"0541c308-4754-4b0c-8e0e-47467c3cc35c\",\n \"status\": \"successful\"\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"content\": {\n \"ownershipType\": [\n \"createdBy\"\n ],\n \"copyright\": \"NIT123\",\n \"keywords\": [\n \"Collection Test\"\n ],\n \"subject\": [\n \"Mathematics\"\n ],\n \"channel\": \"sunbird\",\n \"organisation\": [\n \"NIT\"\n ],\n \"language\": [\n \"English\"\n ],\n \"mimeType\": \"application/vnd.ekstep.content-collection\",\n \"objectType\": \"Collection\",\n \"chapterCountForContribution\": 1,\n \"gradeLevel\": [\n \"Class 1\"\n ],\n \"appIcon\": \"https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/do_11293020669537484811928/artifact/001_1578394365141.png\",\n \"primaryCategory\": \"Digital Textbook\",\n \"children\": [\n {\n \"ownershipType\": [\n \"createdBy\"\n ],\n \"parent\": \"do_1134445959309803521184\",\n \"code\": \"do_1132167447297474561289\",\n \"credentials\": {\n \"enabled\": false\n },\n \"origin\": \"do_1132167447297474561289\",\n \"channel\": \"sunbird\",\n \"language\": [\n \"English\"\n ],\n \"mimeType\": \"application/vnd.ekstep.content-collection\",\n \"idealScreenSize\": \"normal\",\n \"createdOn\": \"2022-01-03T05:00:26.831+0000\",\n \"objectType\": \"Content\",\n \"primaryCategory\": \"Content Playlist\",\n \"children\": [\n {\n \"ownershipType\": [\n \"createdBy\"\n ],\n \"parent\": \"do_1134445959317995521185\",\n \"code\": \"do_113333485887143936169\",\n \"credentials\": {\n \"enabled\": false\n },\n \"origin\": \"do_113333485887143936169\",\n \"channel\": \"sunbird\",\n \"language\": [\n \"English\"\n ],\n \"mimeType\": \"application/vnd.ekstep.content-collection\",\n \"idealScreenSize\": \"normal\",\n \"createdOn\": \"2022-01-03T05:00:26.834+0000\",\n \"objectType\": \"Content\",\n \"primaryCategory\": \"Textbook Unit\",\n \"contentDisposition\": \"inline\",\n \"lastUpdatedOn\": \"2022-01-03T05:00:26.834+0000\",\n \"contentEncoding\": \"gzip\",\n \"originData\": {\n \"channel\": 1309282781705830400\n },\n \"generateDIALCodes\": false,\n \"contentType\": \"TextBookUnit\",\n \"dialcodeRequired\": false,\n \"identifier\": \"do_1134445959318241281187\",\n \"lastStatusChangedOn\": \"2022-01-03T05:00:26.834+0000\",\n \"audience\": [\n \"Student\"\n ],\n \"os\": [\n \"All\"\n ],\n \"visibility\": \"Parent\",\n \"discussionForum\": {\n \"enabled\": true\n },\n \"index\": 1,\n \"mediaType\": \"content\",\n \"osId\": \"org.ekstep.launcher\",\n \"languageCode\": [\n \"en\"\n ],\n \"version\": 2,\n \"versionKey\": \"1641186026834\",\n \"allowedContentTypes\": [\n \"Demo Practice Question Set\",\n \"Content Playlist\",\n \"Course Assessment\",\n \"eTextbook\",\n \"Explanation Content\",\n \"Learning Resource\",\n \"Practice Question Set\",\n \"Teacher Resource\"\n ],\n \"license\": \"CC BY 4.0\",\n \"idealScreenDensity\": \"hdpi\",\n \"depth\": 2,\n \"compatibilityLevel\": 1,\n \"name\": \"Part 1\",\n \"openForContribution\": true,\n \"timeLimits\": {},\n \"programId\": \"fae8bbf0-6c51-11ec-ad7a-853bd8da76ad\",\n \"status\": \"Draft\",\n \"children\": [\n {\n \"ownershipType\": [\n \"createdBy\"\n ],\n \"unitIdentifiers\": [\n \"do_1134445959318241281187\"\n ],\n \"parent\": \"do_1134445959318241281187\",\n \"subject\": [\n \"Mathematics\"\n ],\n \"channel\": 1309282781705830400,\n \"language\": [\n \"English\"\n ],\n \"mimeType\": \"application/pdf\",\n \"objectType\": \"Content\",\n \"gradeLevel\": [\n \"Class 1\"\n ],\n \"primaryCategory\": \"eTextbook\",\n \"contentEncoding\": \"identity\",\n \"contentType\": \"eTextBook\",\n \"identifier\": \"do_1134453165632552961201\",\n \"audience\": [\n \"Student\"\n ],\n \"subjectIds\": [\n \"ekstep_ncert_k-12_subject_mathematics\"\n ],\n \"visibility\": \"Default\",\n \"author\": \"anusha\",\n \"index\": 1,\n \"mediaType\": \"content\",\n \"osId\": \"org.ekstep.quiz.app\",\n \"languageCode\": [\n \"en\"\n ],\n \"version\": 2,\n \"license\": \"CC BY 4.0\",\n \"name\": \"Untitled\",\n \"mediumIds\": [\n \"ekstep_ncert_k-12_medium_english\"\n ],\n \"status\": \"Draft\",\n \"code\": \"b1ef1747-b33a-d91a-93f0-7a0064f1f802\",\n \"interceptionPoints\": {},\n \"credentials\": {\n \"enabled\": false\n },\n \"medium\": [\n \"English\"\n ],\n \"idealScreenSize\": \"normal\",\n \"createdOn\": \"2022-01-04T05:26:34.539+0000\",\n \"contentDisposition\": \"inline\",\n \"lastUpdatedOn\": \"2022-01-04T05:26:34.989+0000\",\n \"collectionId\": \"do_1134445959309803521184\",\n \"dialcodeRequired\": false,\n \"lastStatusChangedOn\": \"2022-01-04T05:26:34.539+0000\",\n \"creator\": \"anusha\",\n \"os\": [\n \"All\"\n ],\n \"versionKey\": \"1641273994989\",\n \"idealScreenDensity\": \"hdpi\",\n \"framework\": \"ekstep_ncert_k-12\",\n \"boardIds\": [\n \"ekstep_ncert_k-12_board_cbse\"\n ],\n \"depth\": 3,\n \"createdBy\": \"19ba0e4e-9285-4335-8dd0-f674bf03fa4d\",\n \"compatibilityLevel\": 1,\n \"gradeLevelIds\": [\n \"ekstep_ncert_k-12_gradelevel_class1\"\n ],\n \"board\": \"CBSE\",\n \"programId\": \"fae8bbf0-6c51-11ec-ad7a-853bd8da76ad\",\n \"downloadUrl\": \"https://dockstorage.blob.core.windows.net/sunbird-content-dock/content/assets/do_1134453165632552961201/sample.pdf\",\n \"artifactUrl\": \"https://dockstorage.blob.core.windows.net/sunbird-content-dock/content/assets/do_1134453165632552961201/sample.pdf\",\n \"size\": 3028\n },\n {\n \"ownershipType\": [\n \"createdBy\"\n ],\n \"unitIdentifiers\": [\n \"do_1134445959318241281187\"\n ],\n \"parent\": \"do_1134445959318241281187\",\n \"copyright\": \"2021\",\n \"subject\": [\n \"Mathematics\"\n ],\n \"channel\": 1309282781705830400,\n \"downloadUrl\": \"https://dockstorage.blob.core.windows.net/sunbird-content-dock/content/assets/do_1134455063866736641416/sample.pdf\",\n \"language\": [\n \"English\"\n ],\n \"mimeType\": \"application/pdf\",\n \"objectType\": \"Content\",\n \"gradeLevel\": [\n \"Class 1\"\n ],\n \"primaryCategory\": \"eTextbook\",\n \"contentEncoding\": \"identity\",\n \"artifactUrl\": \"https://dockstorage.blob.core.windows.net/sunbird-content-dock/content/assets/do_1134455063866736641416/sample.pdf\",\n \"contentType\": \"eTextBook\",\n \"identifier\": \"do_1134455063866736641416\",\n \"audience\": [\n \"Student\"\n ],\n \"subjectIds\": [\n \"ekstep_ncert_k-12_subject_mathematics\"\n ],\n \"visibility\": \"Default\",\n \"author\": \"anusha\",\n \"index\": 2,\n \"mediaType\": \"content\",\n \"osId\": \"org.ekstep.quiz.app\",\n \"languageCode\": [\n \"en\"\n ],\n \"version\": 2,\n \"license\": \"CC BY 4.0\",\n \"size\": 3028,\n \"name\": \"test content\",\n \"mediumIds\": [\n \"ekstep_ncert_k-12_medium_english\"\n ],\n \"status\": \"Draft\",\n \"code\": \"9ab1557b-ea67-c31b-be52-e4073c5d8f1b\",\n \"interceptionPoints\": {},\n \"credentials\": {\n \"enabled\": false\n },\n \"medium\": [\n \"English\"\n ],\n \"idealScreenSize\": \"normal\",\n \"createdOn\": \"2022-01-04T11:52:46.343+0000\",\n \"contentDisposition\": \"inline\",\n \"lastUpdatedOn\": \"2022-01-04T11:53:13.077+0000\",\n \"collectionId\": \"do_1134445959309803521184\",\n \"dialcodeRequired\": false,\n \"lastStatusChangedOn\": \"2022-01-04T11:52:46.343+0000\",\n \"creator\": \"anusha\",\n \"os\": [\n \"All\"\n ],\n \"versionKey\": \"1641297193077\",\n \"idealScreenDensity\": \"hdpi\",\n \"framework\": \"ekstep_ncert_k-12\",\n \"boardIds\": [\n \"ekstep_ncert_k-12_board_cbse\"\n ],\n \"depth\": 3,\n \"createdBy\": \"19ba0e4e-9285-4335-8dd0-f674bf03fa4d\",\n \"compatibilityLevel\": 1,\n \"gradeLevelIds\": [\n \"ekstep_ncert_k-12_gradelevel_class1\"\n ],\n \"board\": \"CBSE\",\n \"programId\": \"fae8bbf0-6c51-11ec-ad7a-853bd8da76ad\"\n }\n ]\n }\n ],\n \"contentDisposition\": \"inline\",\n \"lastUpdatedOn\": \"2022-01-03T05:00:26.831+0000\",\n \"contentEncoding\": \"gzip\",\n \"originData\": {\n \"channel\": 1309282781705830400\n },\n \"contentType\": \"Collection\",\n \"dialcodeRequired\": false,\n \"trackable\": {\n \"enabled\": false,\n \"autoBatch\": false\n },\n \"identifier\": \"do_1134445959317995521185\",\n \"lastStatusChangedOn\": \"2022-01-03T05:00:26.831+0000\",\n \"audience\": [\n \"Student\"\n ],\n \"os\": [\n \"All\"\n ],\n \"visibility\": \"Parent\",\n \"index\": 1,\n \"mediaType\": \"content\",\n \"osId\": \"org.ekstep.launcher\",\n \"languageCode\": [\n \"en\"\n ],\n \"version\": 2,\n \"versionKey\": \"1641186026831\",\n \"allowedContentTypes\": [\n \"Demo Practice Question Set\",\n \"Content Playlist\",\n \"Course Assessment\",\n \"eTextbook\",\n \"Explanation Content\",\n \"Learning Resource\",\n \"Practice Question Set\",\n \"Teacher Resource\"\n ],\n \"license\": \"CC BY 4.0\",\n \"idealScreenDensity\": \"hdpi\",\n \"depth\": 1,\n \"compatibilityLevel\": 1,\n \"name\": \"Textbook Unit 1\",\n \"openForContribution\": true,\n \"timeLimits\": {},\n \"programId\": \"fae8bbf0-6c51-11ec-ad7a-853bd8da76ad\",\n \"status\": \"Draft\"\n }\n ],\n \"contentEncoding\": \"gzip\",\n \"collaborators\": [\n \"88ffb6eb-33bf-4f96-ad3a-75c15e5a04ff\",\n \"a4b4a783-d686-4f67-b079-512329c77f5e\",\n \"ce7f4172-f13a-47b7-804d-f194a2275538\"\n ],\n \"contentType\": \"Collection\",\n \"trackable\": {\n \"enabled\": false,\n \"autoBatch\": false\n },\n \"identifier\": \"do_1134445959309803521184\",\n \"audience\": [\n \"Student\"\n ],\n \"subjectIds\": [\n \"ekstep_ncert_k-12_subject_mathematics\"\n ],\n \"visibility\": \"Default\",\n \"consumerId\": \"028d6fb1-2d6f-4331-86aa-f7cf491a41e0\",\n \"childNodes\": [\n \"do_1134445959318241281187\",\n \"do_1134445959317995521185\",\n \"do_1134453165632552961201\",\n \"do_1134455063866736641416\"\n ],\n \"mediaType\": \"content\",\n \"osId\": \"org.ekstep.quiz.app\",\n \"languageCode\": [\n \"en\"\n ],\n \"version\": 2,\n \"allowedContentTypes\": [\n \"Demo Practice Question Set\",\n \"Content Playlist\",\n \"Course Assessment\",\n \"eTextbook\",\n \"Explanation Content\",\n \"Learning Resource\",\n \"Practice Question Set\",\n \"Teacher Resource\"\n ],\n \"license\": \"CC BY 4.0\",\n \"name\": \"Collection Test\",\n \"mediumIds\": [\n \"ekstep_ncert_k-12_medium_english\"\n ],\n \"attributions\": [\n \"Collection Test\"\n ],\n \"status\": \"Draft\",\n \"code\": \"do_1132167446143959041288\",\n \"credentials\": {\n \"enabled\": false\n },\n \"origin\": \"do_1132167446143959041288\",\n \"description\": \"Enter description for Collection\",\n \"medium\": [\n \"English\"\n ],\n \"idealScreenSize\": \"normal\",\n \"createdOn\": \"2022-01-03T05:00:26.734+0000\",\n \"copyrightYear\": 2021,\n \"contentDisposition\": \"inline\",\n \"additionalCategories\": [\n \"Textbook\"\n ],\n \"lastUpdatedOn\": \"2022-01-04T11:54:05.627+0000\",\n \"originData\": {\n \"channel\": 1309282781705830400\n },\n \"dialcodeRequired\": false,\n \"lastStatusChangedOn\": \"2022-01-03T05:00:26.734+0000\",\n \"createdFor\": [\n 1309282781705830400\n ],\n \"creator\": \"N11\",\n \"os\": [\n \"All\"\n ],\n \"chapterCount\": 1,\n \"versionKey\": \"1641297245627\",\n \"idealScreenDensity\": \"hdpi\",\n \"framework\": \"ekstep_ncert_k-12\",\n \"depth\": 0,\n \"boardIds\": [\n \"ekstep_ncert_k-12_board_cbse\"\n ],\n \"createdBy\": \"5a587cc1-e018-4859-a0a8-e842650b9d64\",\n \"compatibilityLevel\": 1,\n \"userConsent\": true,\n \"openForContribution\": true,\n \"timeLimits\": \"{}\",\n \"gradeLevelIds\": [\n \"ekstep_ncert_k-12_gradelevel_class1\"\n ],\n \"board\": \"CBSE\",\n \"programId\": \"fae8bbf0-6c51-11ec-ad7a-853bd8da76ad\",\n \"resourceType\": \"Collection\"\n }\n }\n}" + } + ] + }, + { + "name": "Add Collection Hierarchy", + "request": { + "method": "PATCH", + "header": [ + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "magna" + }, + { + "description": "The Asset Type entity is the media type of the resource.Possible", + "key": "Content-Type", + "value": "magna" + }, + { + "description": "(Required) Represents the authorized User ID to execute the API", + "key": "x-authenticated-user-token", + "value": "magna" + }, + { + "description": "(Required) Represents a physical location (e.g: state) uniquely to which the user belongs", + "key": "X-Channel-ID", + "value": "magna" + }, + { + "key": "Accept", + "value": "application/json; charset=utf-8" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"rootId\": \"do_1134445959309803521184\",\n \"unitId\": \"do_1134445959318241281187\",\n \"children\": [\n \"do_1134455063866736641416\"\n ]\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/collection/v1/hierarchy/add", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "hierarchy", + "add" + ] + }, + "description": "This API is associated with creating Collection on the Sunbird Platform.\n - The endpoint for **Create Collection** is `collection/v1/hierarchy/add`\n - It points to knowledge-mw-service - `/v1/collection/hierarchy/add`\n - It is mandatory to provide values for parameters marked with *\n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "OK", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "magna" + }, + { + "description": "The Asset Type entity is the media type of the resource.Possible", + "key": "Content-Type", + "value": "magna" + }, + { + "description": "(Required) Represents the authorized User ID to execute the API", + "key": "x-authenticated-user-token", + "value": "magna" + }, + { + "description": "(Required) Represents a physical location (e.g: state) uniquely to which the user belongs", + "key": "X-Channel-ID", + "value": "magna" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"children\": [\n \"consequat qui esse\",\n \"dolore m\"\n ],\n \"rootId\": \"nostrud tempor\",\n \"unitId\": \"nisi incididunt in do\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/collection/v1/hierarchy/add", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "hierarchy", + "add" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=utf-8" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.collection.hierarchy.add\",\n \"ver\": \"3.0\",\n \"ts\": \"2022-01-04T11:54:05ZZ\",\n \"params\": {\n \"resmsgid\": \"bb899cb6-e81a-4a7d-9e87-710aa1fba0ce\",\n \"status\": \"successful\"\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"rootId\": \"do_1134445959309803521184\",\n \"do_1134445959318241281187\": [\n \"do_1134455063866736641416\"\n ]\n }\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "magna" + }, + { + "description": "The Asset Type entity is the media type of the resource.Possible", + "key": "Content-Type", + "value": "magna" + }, + { + "description": "(Required) Represents the authorized User ID to execute the API", + "key": "x-authenticated-user-token", + "value": "magna" + }, + { + "description": "(Required) Represents a physical location (e.g: state) uniquely to which the user belongs", + "key": "X-Channel-ID", + "value": "magna" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"children\": [\n \"consequat qui esse\",\n \"dolore m\"\n ],\n \"rootId\": \"nostrud tempor\",\n \"unitId\": \"nisi incididunt in do\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/collection/v1/hierarchy/add", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "hierarchy", + "add" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=utf-8" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.collection.hierarchy.add\",\n \"ver\": \"3.0\",\n \"ts\": \"2022-01-04T12:16:21ZZ\",\n \"params\": {\n \"resmsgid\": \"4e3ab83b-e931-46fb-ad83-078c2731891c\",\n \"err\": \"ERR_BAD_REQUEST\",\n \"status\": \"failed\",\n \"errmsg\": \"unitId is mandatory\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {}\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "magna" + }, + { + "description": "The Asset Type entity is the media type of the resource.Possible", + "key": "Content-Type", + "value": "magna" + }, + { + "description": "(Required) Represents the authorized User ID to execute the API", + "key": "x-authenticated-user-token", + "value": "magna" + }, + { + "description": "(Required) Represents a physical location (e.g: state) uniquely to which the user belongs", + "key": "X-Channel-ID", + "value": "magna" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"children\": [\n \"consequat qui esse\",\n \"dolore m\"\n ],\n \"rootId\": \"nostrud tempor\",\n \"unitId\": \"nisi incididunt in do\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/collection/v1/hierarchy/add", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "hierarchy", + "add" + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=utf-8" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.collection.hierarchy.add\",\n \"ver\": \"3.0\",\n \"ts\": \"2022-01-04T12:22:29ZZ\",\n \"params\": {\n \"resmsgid\": \"112e8d10-9fb1-4b89-a1ad-333e3ee25819\",\n \"err\": \"ERR_SYSTEM_EXCEPTION\",\n \"status\": \"failed\",\n \"errmsg\": \"Something went wrong in server while processing the request\"\n },\n \"responseCode\": \"SERVER_ERROR\",\n \"result\": {}\n}" + } + ] + } + ] + }, + { + "name": "CSV TOC", + "item": [ + { + "name": "CSV TOC Upload", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource. Possible media types are:- \n - Application/json\n - Multipart/form-data\n - Application/x-www-form-urlencoded", + "key": "Content-Type", + "value": "application/json" + }, + { + "description": "(Required) Specify authorization key (format: Bearer api-key) received from administrator when performing given API request.", + "key": "X-Authenticated-User-Token", + "value": "{{authToken}}" + }, + { + "description": "(Required) Access token of registered user performing given API request.", + "key": "Authorization", + "value": "{{api-key}}" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "urlencoded", + "urlencoded": [ + { + "key": "fileUrl", + "value": "id enim non est Ut", + "description": "(Required) File which user want to upload" + } + ] + }, + "url": { + "raw": "{{baseUrl}}/collection/v1/import/:collection-id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "import", + ":collection-id" + ], + "variable": [ + { + "key": "collection-id", + "value": "consectetur minim incididunt", + "description": "(Required) Collection ID to upload. It should be unique and same as csv file identifier" + } + ] + }, + "description": "'This API is used to upload TOC with csv format on the Sunbird Platform.\n\nThe endpoint for TOC CSV upload is /collection/v1/import\n\nYou can find sample TOC CSV here:\n\n[Sample-collection-create-TOC](https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/collectioncreatecsvsample/artifact/collectioncreatecsvsample_1624340752201_createtoc.csv)\n\nor use following data with first line as header of csv and the rest is example of TOC data.\n\n\"Level 1 Folder*,Level 2 Folder,Level 3 Folder,Level 4 Folder,Description\n5. Human Body,,,,\n5. Human Body,5.1 Parts of Body,,,,\"\n\n\n[Sample-collection-update-TOC](https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/collectionupdatecsvsample/artifact/collectionupdatecsvsample_1624340789653_updatetoc.csv)\n\nor use following data with first line as header of csv and the rest is example of TOC data.\n\n\"Collection Name*,Folder Identifier*,Level 1 Folder*,Level 2 Folder,Level 3 Folder,Level 4 Folder,Description,Mapped Topics,Purpose of Content to be linked,Keywords,QR Code Required?,QR Code,Linked Content 1,Linked Content 2,Linked Content 3,Linked Content 4,Linked Content 5,Linked Content 6,Linked Content 7,Linked Content 8,Linked Content 9,Linked Content 10,Linked Content 11,Linked Content 12,Linked Content 13,Linked Content 14,Linked Content 15,Linked Content 16,Linked Content 17,Linked Content 18,Linked Content 19,Linked Content 20,Linked Content 21,Linked Content 22,Linked Content 23,Linked Content 24,Linked Content 26,Linked Content 27,Linked Content 8,Linked Content 29,Linked Content 30\nScience-10,id1,5. Human Body,,,,This chapter describes about human body,Human Body,\"head, eyes, nose, mouth\",Yes,MC8L1V,do_31260042816321126413012,,,,,,,,,,,,,,,,,,,,,,,,,,,,,\nScience-10,id2,5. Human Body,5.1 Parts of Body,,,\"This section describes about various part of the body such as head, hands, legs etc.\",Parts of Body,,No,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,\nScience-10,id3,5. Human Body,5.1 Parts of Body,5.1.1 Key parts in the head,,,Parts of Head,,Yes,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,\nScience-10,id4,5. Human Body,5.1 Parts of Body,5.1.2 Other parts,,,,,No,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,\nScience-10,id5,5. Human Body,5.2 Organ Systems,,,,Organ Systems,,No,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,\nScience-10,id6,5. Human Body,5.2 Organ Systems,5.2.1 Respiratory System,,,Respiratory System,,No,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,\"\n\n\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty.'" + }, + "response": [ + { + "name": "SUCCESS. The TOC was uploaded successfully.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource. Possible media types are:- \n - Application/json\n - Multipart/form-data\n - Application/x-www-form-urlencoded", + "key": "Content-Type", + "value": "application/json" + }, + { + "description": "(Required) Specify authorization key (format: Bearer api-key) received from administrator when performing given API request.", + "key": "X-Authenticated-User-Token", + "value": "{{authToken}}" + }, + { + "description": "(Required) Access token of registered user performing given API request.", + "key": "Authorization", + "value": "{{api-key}}" + } + ], + "body": { + "mode": "urlencoded", + "urlencoded": [ + { + "key": "fileUrl", + "value": "id enim non est Ut", + "description": "(Required) File which user want to upload" + } + ] + }, + "url": { + "raw": "{{baseUrl}}/collection/v1/import/:collection-id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "import", + ":collection-id" + ], + "variable": [ + { + "key": "collection-id", + "value": "consectetur minim incididunt", + "description": "(Required) Collection ID to upload. It should be unique and same as csv file identifier" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.collection.import\",\n \"ver\": \"v1\",\n \"ts\": \"2021-06-08 15:35:56:361+0000\",\n \"params\": {\n \"resmsgid\": null,\n \"msgid\": \"b3eb6ee7-108d-48b7-9f9f-0d96c55ac289\",\n \"err\": null,\n \"status\": \"success\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"response\": \"SUCCESS\"\n }\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource. Possible media types are:- \n - Application/json\n - Multipart/form-data\n - Application/x-www-form-urlencoded", + "key": "Content-Type", + "value": "application/json" + }, + { + "description": "(Required) Specify authorization key (format: Bearer api-key) received from administrator when performing given API request.", + "key": "X-Authenticated-User-Token", + "value": "{{authToken}}" + }, + { + "description": "(Required) Access token of registered user performing given API request.", + "key": "Authorization", + "value": "{{api-key}}" + } + ], + "body": { + "mode": "urlencoded", + "urlencoded": [ + { + "key": "fileUrl", + "value": "id enim non est Ut", + "description": "(Required) File which user want to upload" + } + ] + }, + "url": { + "raw": "{{baseUrl}}/collection/v1/import/:collection-id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "import", + ":collection-id" + ], + "variable": [ + { + "key": "collection-id", + "value": "consectetur minim incididunt", + "description": "(Required) Collection ID to upload. It should be unique and same as csv file identifier" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.collection.import\",\n \"ver\": \"v1\",\n \"ts\": \"2020-11-27 15:20:24:393+0000\",\n \"params\": {\n \"resmsgid\": null,\n \"msgid\": \"30527359-4723-4b59-b4c0-0b5b14f9a3e2\",\n \"err\": \"CSV_INVALID_COLLECTION_NAME\",\n \"status\": \"CSV_INVALID_COLLECTION_NAME\",\n \"errmsg\": \"Following rows have invalid “Collection Name”. Please correct and upload again.\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {}\n}" + }, + { + "name": "Resource Not Found", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource. Possible media types are:- \n - Application/json\n - Multipart/form-data\n - Application/x-www-form-urlencoded", + "key": "Content-Type", + "value": "application/json" + }, + { + "description": "(Required) Specify authorization key (format: Bearer api-key) received from administrator when performing given API request.", + "key": "X-Authenticated-User-Token", + "value": "{{authToken}}" + }, + { + "description": "(Required) Access token of registered user performing given API request.", + "key": "Authorization", + "value": "{{api-key}}" + } + ], + "body": { + "mode": "urlencoded", + "urlencoded": [ + { + "key": "fileUrl", + "value": "id enim non est Ut", + "description": "(Required) File which user want to upload" + } + ] + }, + "url": { + "raw": "{{baseUrl}}/collection/v1/import/:collection-id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "import", + ":collection-id" + ], + "variable": [ + { + "key": "collection-id", + "value": "consectetur minim incididunt", + "description": "(Required) Collection ID to upload. It should be unique and same as csv file identifier" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.collection.import\",\n \"ver\": \"v1\",\n \"ts\": \"2020-11-27 15:27:56:969+0000\",\n \"params\": {\n \"resmsgid\": null,\n \"msgid\": \"c148b326-7f46-4899-b0cb-31d563f225bc\",\n \"err\": \"RESOURCE_NOT_FOUND\",\n \"status\": \"RESOURCE_NOT_FOUND\",\n \"errmsg\": \"rootId do_11316025545317580812 does not exist\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {}\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource. Possible media types are:- \n - Application/json\n - Multipart/form-data\n - Application/x-www-form-urlencoded", + "key": "Content-Type", + "value": "application/json" + }, + { + "description": "(Required) Specify authorization key (format: Bearer api-key) received from administrator when performing given API request.", + "key": "X-Authenticated-User-Token", + "value": "{{authToken}}" + }, + { + "description": "(Required) Access token of registered user performing given API request.", + "key": "Authorization", + "value": "{{api-key}}" + } + ], + "body": { + "mode": "urlencoded", + "urlencoded": [ + { + "key": "fileUrl", + "value": "id enim non est Ut", + "description": "(Required) File which user want to upload" + } + ] + }, + "url": { + "raw": "{{baseUrl}}/collection/v1/import/:collection-id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "import", + ":collection-id" + ], + "variable": [ + { + "key": "collection-id", + "value": "consectetur minim incididunt", + "description": "(Required) Collection ID to upload. It should be unique and same as csv file identifier" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.collection.import\",\n \"ver\": \"v1\",\n \"ts\": \"2020-11-23 06:48:19:128+0000\",\n \"params\": {\n \"resmsgid\": null,\n \"msgid\": \"50f76a53-4710-4333-936f-c043031eec03\",\n \"err\": \"SERVER_ERROR\",\n \"status\": \"SERVER_ERROR\",\n \"errmsg\": \"SERVER_ERROR\"\n },\n \"responseCode\": \"SERVER_ERROR\",\n \"result\": {}\n}" + } + ] + }, + { + "name": "TOC Download", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource. Possible media types are:- \n - Application/json\n - Multipart/form-data\n - Application/x-www-form-urlencoded", + "key": "Content-Type", + "value": "application/json" + }, + { + "description": "(Required) Specify authorization key (format: Bearer api-key) received from administrator when performing given API request.", + "key": "x-authenticated-user-token", + "value": "{{authToken}}" + }, + { + "description": "(Required) Access token of registered user performing given API request.", + "key": "Authorization", + "value": "{{api-key}}" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/collection/v1/export/:collection-id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "export", + ":collection-id" + ], + "variable": [ + { + "key": "collection-id", + "value": "consectetur minim incididunt", + "description": "(Required) Collection ID which user want to download" + } + ] + }, + "description": "This API is used to download collection in csv format.\n\nThe endpoint for TOC download is /export\nThe fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [ + { + "name": "SUCCESS. The TOC was downloaded successfully.", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource. Possible media types are:- \n - Application/json\n - Multipart/form-data\n - Application/x-www-form-urlencoded", + "key": "Content-Type", + "value": "application/json" + }, + { + "description": "(Required) Specify authorization key (format: Bearer api-key) received from administrator when performing given API request.", + "key": "x-authenticated-user-token", + "value": "{{authToken}}" + }, + { + "description": "(Required) Access token of registered user performing given API request.", + "key": "Authorization", + "value": "{{api-key}}" + } + ], + "url": { + "raw": "{{baseUrl}}/collection/v1/export/:collection-id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "export", + ":collection-id" + ], + "variable": [ + { + "key": "collection-id", + "value": "consectetur minim incididunt", + "description": "(Required) Collection ID which user want to download" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.collection.export\",\n \"ver\": \"v1\",\n \"ts\": \"2020-11-27 15:47:33:106+0000\",\n \"params\": {\n \"resmsgid\": null,\n \"msgid\": \"ab202e68-bacd-4eee-a3b3-10d59b09c17f\",\n \"err\": null,\n \"status\": \"success\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"collection\": {\n \"tocUrl\": \"https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/collection/toc/do_11316025504283852811_test-27-nov_1606491356306.csv\",\n \"ttl\": \"86400\"\n }\n }\n}" + }, + { + "name": "Resource Not Found", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource. Possible media types are:- \n - Application/json\n - Multipart/form-data\n - Application/x-www-form-urlencoded", + "key": "Content-Type", + "value": "application/json" + }, + { + "description": "(Required) Specify authorization key (format: Bearer api-key) received from administrator when performing given API request.", + "key": "x-authenticated-user-token", + "value": "{{authToken}}" + }, + { + "description": "(Required) Access token of registered user performing given API request.", + "key": "Authorization", + "value": "{{api-key}}" + } + ], + "url": { + "raw": "{{baseUrl}}/collection/v1/export/:collection-id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "export", + ":collection-id" + ], + "variable": [ + { + "key": "collection-id", + "value": "consectetur minim incididunt", + "description": "(Required) Collection ID which user want to download" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.collection.export\",\n \"ver\": \"v1\",\n \"ts\": \"2020-11-27 15:49:18:113+0000\",\n \"params\": {\n \"resmsgid\": null,\n \"msgid\": \"1f4ed0b6-ab14-4644-8e91-0bed6b0cd756\",\n \"err\": \"RESOURCE_NOT_FOUND\",\n \"status\": \"RESOURCE_NOT_FOUND\",\n \"errmsg\": \"rootId do_11263298042220544900 does not exist\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {}\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource. Possible media types are:- \n - Application/json\n - Multipart/form-data\n - Application/x-www-form-urlencoded", + "key": "Content-Type", + "value": "application/json" + }, + { + "description": "(Required) Specify authorization key (format: Bearer api-key) received from administrator when performing given API request.", + "key": "x-authenticated-user-token", + "value": "{{authToken}}" + }, + { + "description": "(Required) Access token of registered user performing given API request.", + "key": "Authorization", + "value": "{{api-key}}" + } + ], + "url": { + "raw": "{{baseUrl}}/collection/v1/export/:collection-id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "export", + ":collection-id" + ], + "variable": [ + { + "key": "collection-id", + "value": "consectetur minim incididunt", + "description": "(Required) Collection ID which user want to download" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.collection.export\",\n \"ver\": \"v1\",\n \"ts\": \"2020-11-23 06:48:19:128+0000\",\n \"params\": {\n \"resmsgid\": null,\n \"msgid\": \"50f76a53-4710-4333-936f-c043031eec03\",\n \"err\": \"SERVER_ERROR\",\n \"status\": \"SERVER_ERROR\",\n \"errmsg\": \"SERVER_ERROR\"\n },\n \"responseCode\": \"SERVER_ERROR\",\n \"result\": {}\n}" + } + ] + } + ], + "description": "The Collection CSV TOC API resources perform operations related to download and upload of collection type of contents on the Sunbird Platform in csv format. The CSV TOC API(s) operations include uploads and downloads.\n\nThe URL for CSV TOC API(s) is /collection/v1/.\n\n- The backend URL for CSV TOC API(s) is collection/v4/.\n- [TestCases](https://desktop.postman.com/?desktopVersion=10.6.0&userId=1419050&teamId=304931)\n \n\nContact Support: \nEmail: [info@sunbird.org](https://desktop.postman.com/?desktopVersion=10.6.0&userId=1419050&teamId=304931)" + }, + { + "name": "Create Collection", + "request": { + "method": "POST", + "header": [ + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "magna" + }, + { + "description": "The Asset Type entity is the media type of the resource.Possible", + "key": "Content-Type", + "value": "magna" + }, + { + "description": "(Required) Represents the authorized User ID to execute the API", + "key": "x-authenticated-user-token", + "value": "magna" + }, + { + "description": "(Required) Represents a physical location (e.g: state) uniquely to which the user belongs", + "key": "X-Channel-ID", + "value": "magna" + }, + { + "key": "Accept", + "value": "application/json; charset=utf-8" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"collection\": {\n \"contentType\": \"voluptate magna aute laboris mollit\",\n \"createdBy\": \"ea dolor veni\",\n \"createdFor\": [\n \"irure sunt dolor ut\",\n \"nos\"\n ],\n \"creator\": \"reprehenderit dolore labore tempor\",\n \"description\": \"in aute dolore laborum e\",\n \"framework\": \"laborum ex culpa voluptate\",\n \"mimeType\": \"ad minim ullamco pariatur\",\n \"name\": \"commodo\",\n \"organisation\": [\n \"sunt voluptate eiusmod Excepteur dolore\",\n \"nulla\"\n ],\n \"primaryCategory\": \"eu pariatur occaecat sunt\",\n \"resourceType\": \"culpa irure\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/collection/v1/create", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "create" + ] + }, + "description": "This API is associated with creating Collection on the Sunbird Platform.\n - The endpoint for **Create Collection** is `/collection/v1/create`\n - It points to knowledge-mw-service - `/v1/collection/create`\n - It is mandatory to provide values for parameters marked with *\n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "magna" + }, + { + "description": "The Asset Type entity is the media type of the resource.Possible", + "key": "Content-Type", + "value": "magna" + }, + { + "description": "(Required) Represents the authorized User ID to execute the API", + "key": "x-authenticated-user-token", + "value": "magna" + }, + { + "description": "(Required) Represents a physical location (e.g: state) uniquely to which the user belongs", + "key": "X-Channel-ID", + "value": "magna" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"collection\": {\n \"contentType\": \"velit pariatur ea\",\n \"createdBy\": \"magn\",\n \"createdFor\": [\n \"ea culpa in do\",\n \"incididunt tempor ea\"\n ],\n \"creator\": \"mollit aute\",\n \"description\": \"officia aliquip elit\",\n \"framework\": \"qui incididunt aliquip exercitation\",\n \"mimeType\": \"est velit sunt voluptate\",\n \"name\": \"ad\",\n \"organisation\": [\n \"laborum non\",\n \"enim mollit ex\"\n ],\n \"primaryCategory\": \"sed Excepteur dolore officia qui\",\n \"resourceType\": \"minim\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/collection/v1/create", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=utf-8" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"1.0\",\n \"ts\": \"2022-01-03T09:05:31.875Z\",\n \"params\": {\n \"resmsgid\": \"4dca4330-6c74-11ec-b3c3-d9afeab1a49c\",\n \"msgid\": \"0340ddc8-1b95-30c5-4a1d-b5181ba88c5e\",\n \"status\": \"successful\"\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"content_id\": \"do_113444716395110400184\",\n \"versionKey\": \"1641200731833\"\n }\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "magna" + }, + { + "description": "The Asset Type entity is the media type of the resource.Possible", + "key": "Content-Type", + "value": "magna" + }, + { + "description": "(Required) Represents the authorized User ID to execute the API", + "key": "x-authenticated-user-token", + "value": "magna" + }, + { + "description": "(Required) Represents a physical location (e.g: state) uniquely to which the user belongs", + "key": "X-Channel-ID", + "value": "magna" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"collection\": {\n \"contentType\": \"velit pariatur ea\",\n \"createdBy\": \"magn\",\n \"createdFor\": [\n \"ea culpa in do\",\n \"incididunt tempor ea\"\n ],\n \"creator\": \"mollit aute\",\n \"description\": \"officia aliquip elit\",\n \"framework\": \"qui incididunt aliquip exercitation\",\n \"mimeType\": \"est velit sunt voluptate\",\n \"name\": \"ad\",\n \"organisation\": [\n \"laborum non\",\n \"enim mollit ex\"\n ],\n \"primaryCategory\": \"sed Excepteur dolore officia qui\",\n \"resourceType\": \"minim\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/collection/v1/create", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=utf-8" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"1.0\",\n \"ts\": \"2022-01-03T09:48:59.425Z\",\n \"params\": {\n \"resmsgid\": \"6002d110-6c7a-11ec-b3c3-d9afeab1a49c\",\n \"status\": \"failed\",\n \"err\": \"CLIENT_ERROR\",\n \"errmsg\": \"Validation Errors\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Metadata resourceType should be one of: [Read, Learn, Teach, Play, Test, Practice, Experiment, Collection, Book, Lesson Plan, Course, Theory, Worksheet, Practical]\"\n ]\n }\n}" + } + ] + }, + { + "name": "Collection Read", + "request": { + "method": "GET", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "magna" + }, + { + "description": "(Required) The Registered User ID used to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "magna" + }, + { + "description": "The unique ID provided to the State to which the user belongs.", + "key": "X-Channel-id", + "value": "magna" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "magna" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/collection/v1/read/:collection_id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "read", + ":collection_id" + ], + "variable": [ + { + "key": "collection_id", + "value": "magna", + "description": "(Required) Please append a valid Collection Id To the Request URL" + } + ] + }, + "description": "This API is associated with Read Collection on the Sunbird Platform .\n - **_/Collection/_** endpoint executes the \"Read Collection\" request based on parameters provided as metadata in the request body.\n - The endpoint for **Read Collection** is `/collection/v1/read`\n - It points to knowledge-mw-service - `/v1/collection/read`\n - {...}refers to a section of review endpoint URL path that needs to be replaced by appropriate Id.\n - It is mandatory to provide values for parameters marked with *.\n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "OK", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "magna" + }, + { + "description": "(Required) The Registered User ID used to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "magna" + }, + { + "description": "The unique ID provided to the State to which the user belongs.", + "key": "X-Channel-id", + "value": "magna" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "magna" + } + ], + "url": { + "raw": "{{baseUrl}}/collection/v1/read/:collection_id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "read", + ":collection_id" + ], + "variable": [ + { + "key": "collection_id", + "value": "magna", + "description": "(Required) Please append a valid Collection Id To the Request URL" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.collection.read\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-09T12:09:04.202Z\",\n \"params\": {\n \"resmsgid\": \"548b82a0-3a17-11eb-be5b-f9d249ba9049\",\n \"msgid\": \"5488ea90-3a17-11eb-be5b-f9d249ba9049\",\n \"status\": \"successful\"\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"content\": {\n \"ownershipType\": [\n \"createdBy\"\n ],\n \"code\": \"org.sunbird.ccG6ru\",\n \"credentials\": {\n \"enabled\": false\n },\n \"channel\": \"in.ekstep\",\n \"language\": [\n \"English\"\n ],\n \"mimeType\": \"application/pdf\",\n \"idealScreenSize\": \"normal\",\n \"createdOn\": \"2020-12-09T12:08:54.913+0000\",\n \"objectType\": \"Content\",\n \"primaryCategory\": \"Explanation Content\",\n \"contentDisposition\": \"inline\",\n \"lastUpdatedOn\": \"2020-12-09T12:08:54.913+0000\",\n \"contentEncoding\": \"identity\",\n \"contentType\": \"Resource\",\n \"dialcodeRequired\": false,\n \"identifier\": \"do_1131687689003827201864\",\n \"lastStatusChangedOn\": \"2020-12-09T12:08:54.913+0000\",\n \"audience\": [\n \"Student\"\n ],\n \"os\": [\n \"All\"\n ],\n \"visibility\": \"Default\",\n \"consumerId\": \"7411b6bd-89f3-40ec-98d1-229dc64ce77d\",\n \"mediaType\": \"content\",\n \"osId\": \"org.ekstep.quiz.app\",\n \"languageCode\": [\n \"en\"\n ],\n \"version\": 2,\n \"versionKey\": \"1607515734913\",\n \"license\": \"CC BY 4.0\",\n \"idealScreenDensity\": \"hdpi\",\n \"framework\": \"NCF\",\n \"createdBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\",\n \"compatibilityLevel\": 1,\n \"name\": \"API DOCUMENTATION CONTENT\",\n \"status\": \"Draft\"\n }\n }\n}" + } + ] + }, + { + "name": "Collection Private Read", + "request": { + "method": "GET", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "magna" + }, + { + "description": "(Required) The Registered User ID used to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "magna" + }, + { + "description": "The unique ID provided to the State to which the user belongs.", + "key": "X-Channel-id", + "value": "magna" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "magna" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/collection/v1/private/read/:collection_id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "private", + "read", + ":collection_id" + ], + "variable": [ + { + "key": "collection_id", + "value": "magna", + "description": "(Required) Please append a valid Collection Id To the Request URL" + } + ] + }, + "description": "This API is associated with read collection private on the Sunbird Platform .\n - **_/Collection/_** endpoint executes the \"Private Read Collection\" request based on parameters provided as metadata in the request body.\n - The endpoint for **Read Collection** is `/collection/v1/private/read`\n - It points to knowledge-mw-service - `/v1/collection/private/read`\n - {...}refers to a section of review endpoint URL path that needs to be replaced by appropriate Id.\n - It is mandatory to provide values for parameters marked with *.\n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "OK", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "magna" + }, + { + "description": "(Required) The Registered User ID used to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "magna" + }, + { + "description": "The unique ID provided to the State to which the user belongs.", + "key": "X-Channel-id", + "value": "magna" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "magna" + } + ], + "url": { + "raw": "{{baseUrl}}/collection/v1/private/read/:collection_id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "private", + "read", + ":collection_id" + ], + "variable": [ + { + "key": "collection_id", + "value": "magna", + "description": "(Required) Please append a valid Collection Id To the Request URL" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.collection.read\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-09T12:09:04.202Z\",\n \"params\": {\n \"resmsgid\": \"548b82a0-3a17-11eb-be5b-f9d249ba9049\",\n \"msgid\": \"5488ea90-3a17-11eb-be5b-f9d249ba9049\",\n \"status\": \"successful\"\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"content\": {\n \"ownershipType\": [\n \"createdBy\"\n ],\n \"code\": \"org.sunbird.ccG6ru\",\n \"credentials\": {\n \"enabled\": false\n },\n \"channel\": \"in.ekstep\",\n \"language\": [\n \"English\"\n ],\n \"mimeType\": \"application/pdf\",\n \"idealScreenSize\": \"normal\",\n \"createdOn\": \"2020-12-09T12:08:54.913+0000\",\n \"objectType\": \"Content\",\n \"primaryCategory\": \"Explanation Content\",\n \"contentDisposition\": \"inline\",\n \"lastUpdatedOn\": \"2020-12-09T12:08:54.913+0000\",\n \"contentEncoding\": \"identity\",\n \"contentType\": \"Resource\",\n \"dialcodeRequired\": false,\n \"identifier\": \"do_1131687689003827201864\",\n \"lastStatusChangedOn\": \"2020-12-09T12:08:54.913+0000\",\n \"audience\": [\n \"Student\"\n ],\n \"os\": [\n \"All\"\n ],\n \"visibility\": \"Default\",\n \"consumerId\": \"7411b6bd-89f3-40ec-98d1-229dc64ce77d\",\n \"mediaType\": \"content\",\n \"osId\": \"org.ekstep.quiz.app\",\n \"languageCode\": [\n \"en\"\n ],\n \"version\": 2,\n \"versionKey\": \"1607515734913\",\n \"license\": \"CC BY 4.0\",\n \"idealScreenDensity\": \"hdpi\",\n \"framework\": \"NCF\",\n \"createdBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\",\n \"compatibilityLevel\": 1,\n \"name\": \"API DOCUMENTATION CONTENT\",\n \"status\": \"Draft\"\n }\n }\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "magna" + }, + { + "description": "(Required) The Registered User ID used to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "magna" + }, + { + "description": "The unique ID provided to the State to which the user belongs.", + "key": "X-Channel-id", + "value": "magna" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "magna" + } + ], + "url": { + "raw": "{{baseUrl}}/collection/v1/private/read/:collection_id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "private", + "read", + ":collection_id" + ], + "variable": [ + { + "key": "collection_id", + "value": "magna", + "description": "(Required) Please append a valid Collection Id To the Request URL" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.collection.private.read\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-01-06T05:44:55ZZ\",\n \"params\": {\n \"resmsgid\": \"6ad2cc51-3878-4625-b2b7-89e5ab6b8cca\",\n \"err\": \"ERR_INVALID_CHANNEL\",\n \"status\": \"failed\",\n \"errmsg\": \"Please Provide Channel!\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {}\n}" + } + ] + }, + { + "name": "Collection Update", + "request": { + "method": "PATCH", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "magna" + }, + { + "description": "(Required) The Registered User ID used to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "magna" + }, + { + "description": "The unique ID provided to the State to which the user belongs.", + "key": "X-Channel-id", + "value": "magna" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "magna" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"collection\": {\n \"versionKey\": \"{{versionKey}}\",\n \"lastUpdatedBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\",\n \"description\": \"dsd\",\n \"name\": \"API DOCUMENTATION CONTENT\",\n \"mimeType\": \"application/vnd.ekstep.content-collection\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/collection/v1/update/:collection_id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "update", + ":collection_id" + ], + "variable": [ + { + "key": "collection_id", + "value": "magna", + "description": "(Required) Please append a valid Collection Id To the Request URL" + } + ] + }, + "description": "This API is associated with update Collection on the Sunbird Platform .\n - **_/Collection/_** endpoint executes the \"Update Collection\" request based on parameters provided as metadata in the request body.\n - The endpoint for **Update Collection** is `/collection/v1/update`\n - It points to knowledge-mw-service - `/v1/collection/update`\n - {...}refers to a section of review endpoint URL path that needs to be replaced by appropriate Id.\n - It is mandatory to provide values for parameters marked with *.\n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "OK", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "magna" + }, + { + "description": "(Required) The Registered User ID used to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "magna" + }, + { + "description": "The unique ID provided to the State to which the user belongs.", + "key": "X-Channel-id", + "value": "magna" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "magna" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"collection\": {\n \"versionKey\": \"{{versionKey}}\",\n \"lastUpdatedBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\",\n \"description\": \"dsd\",\n \"name\": \"API DOCUMENTATION CONTENT\",\n \"mimeType\": \"application/vnd.ekstep.content-collection\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/collection/v1/update/:collection_id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "update", + ":collection_id" + ], + "variable": [ + { + "key": "collection_id", + "value": "magna", + "description": "(Required) Please append a valid Collection Id To the Request URL" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.collection.update\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-01-06T07:01:16ZZ\",\n \"params\": {\n \"resmsgid\": \"5d0b07e7-be99-41a1-a702-ef10bdf93c61\",\n \"status\": \"successful\"\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_11334223976733900811\",\n \"node_id\": \"do_11334223976733900811\",\n \"versionKey\": \"1641452476524\"\n }\n}" + } + ] + }, + { + "name": "Collection Copy", + "request": { + "method": "POST", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "magna" + }, + { + "description": "(Required) The Registered User ID used to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "magna" + }, + { + "description": "The unique ID provided to the State to which the user belongs.", + "key": "X-Channel-id", + "value": "magna" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "magna" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"collection\": {\n \"createdBy\": \"Test\",\n \"createdFor\": [\n \"Sunbird\"\n ],\n \"organisation\": [\n \"Sunbird\"\n ],\n \"framework\": \"test\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/collection/v1/copy/:collection_id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "copy", + ":collection_id" + ], + "variable": [ + { + "key": "collection_id", + "value": "magna", + "description": "(Required) Please append a valid Collection Id To the Request URL" + } + ] + }, + "description": "This API is associated with copy Collection on the Sunbird Platform .\n - **_/Collection/_** endpoint executes the \"Copy Collection\" request based on parameters provided as metadata in the request body.\n - The endpoint for **Update Collection** is `/collection/v1/copy`\n - It points to knowledge-mw-service - `/v1/collection/copy`\n - {...}refers to a section of review endpoint URL path that needs to be replaced by appropriate Id.\n - It is mandatory to provide values for parameters marked with *.\n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "magna" + }, + { + "description": "(Required) The Registered User ID used to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "magna" + }, + { + "description": "The unique ID provided to the State to which the user belongs.", + "key": "X-Channel-id", + "value": "magna" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "magna" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"collection\": {\n \"createdBy\": \"laborum qui eiusmod sunt\",\n \"createdFor\": [\n \"et volup\",\n \"ullamco \"\n ],\n \"framework\": \"exercitation ex fugiat\",\n \"organisation\": [\n \"sunt ea mollit ullamco\",\n \"et\"\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/collection/v1/copy/:collection_id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "copy", + ":collection_id" + ], + "variable": [ + { + "key": "collection_id", + "value": "magna", + "description": "(Required) Please append a valid Collection Id To the Request URL" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.collection.copy\",\n \"ver\": \"3.0\",\n \"ts\": \"2022-01-06T07:27:47ZZ\",\n \"params\": {\n \"resmsgid\": \"38bc39fd-86ce-4ca0-9509-f11d7f761bf1\",\n \"status\": \"successful\"\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"node_id\": {\n \"do_1134417529722552321144\": \"do_1134467917040271361183\"\n },\n \"versionKey\": \"1641454067281\"\n }\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "magna" + }, + { + "description": "(Required) The Registered User ID used to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "magna" + }, + { + "description": "The unique ID provided to the State to which the user belongs.", + "key": "X-Channel-id", + "value": "magna" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "magna" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"collection\": {\n \"createdBy\": \"laborum qui eiusmod sunt\",\n \"createdFor\": [\n \"et volup\",\n \"ullamco \"\n ],\n \"framework\": \"exercitation ex fugiat\",\n \"organisation\": [\n \"sunt ea mollit ullamco\",\n \"et\"\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/collection/v1/copy/:collection_id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "copy", + ":collection_id" + ], + "variable": [ + { + "key": "collection_id", + "value": "magna", + "description": "(Required) Please append a valid Collection Id To the Request URL" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.collection.copy\",\n \"ver\": \"3.0\",\n \"ts\": \"2022-01-06T07:24:41ZZ\",\n \"params\": {\n \"resmsgid\": \"3b2ee619-4c41-4f3c-a6b1-0b31bba23dfb\",\n \"err\": \"ERR_INVALID_REQUEST\",\n \"status\": \"failed\",\n \"errmsg\": \"Cannot Copy content which is in retired status\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {}\n}" + } + ] + }, + { + "name": "Collection Reject", + "request": { + "method": "POST", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "magna" + }, + { + "description": "(Required) The Registered User ID used to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "magna" + }, + { + "description": "The unique ID provided to the State to which the user belongs.", + "key": "X-Channel-id", + "value": "magna" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "magna" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"collection\": {\n \"rejectComment\": \"Collection rejected\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/collection/v1/reject/:collection_id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "reject", + ":collection_id" + ], + "variable": [ + { + "key": "collection_id", + "value": "magna", + "description": "(Required) Please append a valid Collection Id To the Request URL" + } + ] + }, + "description": "This API is associated with reject Collection on the Sunbird Platform .\n - **_/Collection/_** endpoint executes the \"Reject Collection\" request based on parameters provided as metadata in the request body.\n - The endpoint for **Reject Collection** is `/collection/v1/reject`\n - It points to knowledge-mw-service - `/v1/collection/reject`\n - {...}refers to a section of review endpoint URL path that needs to be replaced by appropriate Id.\n - It is mandatory to provide values for parameters marked with *.\n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "OK", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "magna" + }, + { + "description": "(Required) The Registered User ID used to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "magna" + }, + { + "description": "The unique ID provided to the State to which the user belongs.", + "key": "X-Channel-id", + "value": "magna" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "magna" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"collection\": {\n \"rejectComment\": \"Collection rejected\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/collection/v1/reject/:collection_id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "reject", + ":collection_id" + ], + "variable": [ + { + "key": "collection_id", + "value": "magna", + "description": "(Required) Please append a valid Collection Id To the Request URL" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.collection.review.reject\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-01-06T07:04:33ZZ\",\n \"params\": {\n \"resmsgid\": \"1fdef75e-9854-4dbb-a829-efb03debde84\",\n \"status\": \"successful\"\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_11334223976733900811\",\n \"node_id\": \"do_11334223976733900811\"\n }\n}" + } + ] + }, + { + "name": "Collection Retire", + "request": { + "method": "DELETE", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "magna" + }, + { + "description": "(Required) The Registered User ID used to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "magna" + }, + { + "description": "The unique ID provided to the State to which the user belongs.", + "key": "X-Channel-id", + "value": "magna" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "magna" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/collection/v1/retire/:collection_id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "retire", + ":collection_id" + ], + "variable": [ + { + "key": "collection_id", + "value": "magna", + "description": "(Required) Please append a valid Collection Id To the Request URL" + } + ] + }, + "description": "This API is associated with Retire Collection on the Sunbird Platform .\n - **_/Collection/_** endpoint executes the \"Retire Collection\" request based on parameters provided as metadata in the request body.\n - The endpoint for **Retire Collection** is `/collection/v1/retire`\n - It points to knowledge-mw-service - `/v1/collection/retire`\n - {...}refers to a section of review endpoint URL path that needs to be replaced by appropriate Id.\n - It is mandatory to provide values for parameters marked with *.\n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "OK", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "magna" + }, + { + "description": "(Required) The Registered User ID used to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "magna" + }, + { + "description": "The unique ID provided to the State to which the user belongs.", + "key": "X-Channel-id", + "value": "magna" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "magna" + } + ], + "url": { + "raw": "{{baseUrl}}/collection/v1/retire/:collection_id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "retire", + ":collection_id" + ], + "variable": [ + { + "key": "collection_id", + "value": "magna", + "description": "(Required) Please append a valid Collection Id To the Request URL" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.collection.retire\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-01-06T06:51:00ZZ\",\n \"params\": {\n \"resmsgid\": \"280f83f7-4f69-4c22-bc93-33ebeb8823e8\",\n \"status\": \"successful\"\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_11334223976733900811\",\n \"node_id\": \"do_11334223976733900811\"\n }\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "magna" + }, + { + "description": "(Required) The Registered User ID used to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "magna" + }, + { + "description": "The unique ID provided to the State to which the user belongs.", + "key": "X-Channel-id", + "value": "magna" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "magna" + } + ], + "url": { + "raw": "{{baseUrl}}/collection/v1/retire/:collection_id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "retire", + ":collection_id" + ], + "variable": [ + { + "key": "collection_id", + "value": "magna", + "description": "(Required) Please append a valid Collection Id To the Request URL" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.collection.retire\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-01-06T06:56:42ZZ\",\n \"params\": {\n \"resmsgid\": \"0c79c484-c75f-4e77-b354-69c36a1caa7e\",\n \"err\": \"ERR_CONTENT_RETIRE\",\n \"status\": \"failed\",\n \"errmsg\": \"Content with Identifier do_11334223976733900811 is already Retired.\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {}\n}" + } + ] + }, + { + "name": "Collection Discard", + "request": { + "method": "DELETE", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "magna" + }, + { + "description": "(Required) The Registered User ID used to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "magna" + }, + { + "description": "The unique ID provided to the State to which the user belongs.", + "key": "X-Channel-id", + "value": "magna" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "magna" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/collection/v1/discard/:collection_id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "discard", + ":collection_id" + ], + "variable": [ + { + "key": "collection_id", + "value": "magna", + "description": "(Required) Please append a valid Collection Id To the Request URL" + } + ] + }, + "description": "This API is associated with Discard Collection on the Sunbird Platform .\n - **_/Collection/_** endpoint executes the \"Discard Collection\" request based on parameters provided as metadata in the request body.\n - The endpoint for **Discard Collection** is `/collection/v1/discard`\n - It points to knowledge-mw-service - `/v1/collection/discard`\n - {...}refers to a section of review endpoint URL path that needs to be replaced by appropriate Id.\n - It is mandatory to provide values for parameters marked with *.\n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "OK", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "magna" + }, + { + "description": "(Required) The Registered User ID used to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "magna" + }, + { + "description": "The unique ID provided to the State to which the user belongs.", + "key": "X-Channel-id", + "value": "magna" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "magna" + } + ], + "url": { + "raw": "{{baseUrl}}/collection/v1/discard/:collection_id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collection", + "v1", + "discard", + ":collection_id" + ], + "variable": [ + { + "key": "collection_id", + "value": "magna", + "description": "(Required) Please append a valid Collection Id To the Request URL" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.collection.discard\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-01-06T06:41:51ZZ\",\n \"params\": {\n \"resmsgid\": \"fc899cd5-958b-40ad-9a5c-6f31b65404b4\",\n \"status\": \"successful\"\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_1134467341514588161171\",\n \"message\": \"Draft version of the content with id : do_1134467341514588161171 is discarded\",\n \"node_id\": \"do_1134467341514588161171\"\n }\n}" + } + ] + } + ] + } + ], + "variable": [ + { + "key": "baseUrl", + "value": "https://staging.sunbirded.org/api", + "type": "string" + } + ] +} \ No newline at end of file diff --git a/content-api/api-tests/Collections/Sunbird Content Management API(s).postman_collection.json b/content-api/api-tests/Collections/Sunbird Content Management API(s).postman_collection.json new file mode 100644 index 000000000..9a7eddef8 --- /dev/null +++ b/content-api/api-tests/Collections/Sunbird Content Management API(s).postman_collection.json @@ -0,0 +1,5667 @@ +{ + "info": { + "_postman_id": "868e5465-d7df-4e57-9fe0-249b2e15cceb", + "name": "Sunbird Content Management API(s)", + "description": "The Content Management API allows you to manage content over the sunbird platform.Apis perform operations related to all the _Content_ on the Sunbird Platform.\n- Each of the endpoints serve a different purpose, so which one to use depends on what you want to do\n- The operations include CRUD (Create, Update, Read and Delete) operations and other operations such as upload, publish, flag, link Dialcode, etc.\n- The URL for Sunbird Content API(s) is `content/v2`\n- [TestCases](https://www.getpostman.com/collections/3c1817fcb4606ae53b9a)\n\nContact Support:\n Email: info@sunbird.org", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "1419050" + }, + "item": [ + { + "name": "content", + "item": [ + { + "name": "v2", + "item": [ + { + "name": "upload", + "item": [ + { + "name": "Upload Content", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json\n - Multipart/form-data\n - Application/x-www-form-urlencoded", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-Userid", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "file", + "description": "(Required) Content package file which you intend to upload", + "type": "file", + "src": [] + } + ] + }, + "url": { + "raw": "{{baseUrl}}/content/v2/upload/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "upload", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + }, + "description": "This API is associated with uploading content on the Sunbird Platform .\n - The**_/upload/{content Id}_** endpoint executes \"Upload Content\" request based on parameters provided as metadata in the request body.\n - The endpoint for **Upload Content** is `/content/v2/upload`\n - It points to knowledge-mw-service - `/v2/content/upload`\n - {...}refers to a section of upload endpoint URL path that needs to be replaced by appropriate Id.\n - You need to provide a valid content Id value in {Content Id} field of API URL.\n - It is mandatory to provide values for parameters marked with *.\n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "OK ! Successful operation.\"Content upload\" operation was successfully executed.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json\n - Multipart/form-data\n - Application/x-www-form-urlencoded", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-Userid", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "file", + "description": "(Required) Content package file which you intend to upload", + "type": "file", + "src": [] + } + ] + }, + "url": { + "raw": "{{baseUrl}}/content/v2/upload/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "upload", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.upload\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-12-10T21:00:16ZZ\",\n \"params\": {\n \"resmsgid\": \"4505a08c-9d0c-447b-8b3f-692e31f841fd\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_1131697376667402241315\",\n \"artifactUrl\": \"https://ekstep-public-dev.s3-ap-south-1.amazonaws.com/content/do_112547906582659072139/artifact/pdf_1531726896452.pdf\",\n \"content_url\": \"https://ekstep-public-dev.s3-ap-south-1.amazonaws.com/content/do_112547906582659072139/artifact/pdf_1531726896452.pdf\",\n \"node_id\": \"do_1131697376667402241315\",\n \"versionKey\": \"1607634016927\"\n }\n}" + }, + { + "name": "RESOURCE NOT FOUND,**Content Send for Review** operation failed !The possible reason for failure is that you may have provided wrong content id.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json\n - Multipart/form-data\n - Application/x-www-form-urlencoded", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-Userid", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "file", + "description": "(Required) Content package file which you intend to upload", + "type": "file", + "src": [] + } + ] + }, + "url": { + "raw": "{{baseUrl}}/content/v2/upload/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "upload", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.upload\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-12-10T21:21:20ZZ\",\n \"params\": {\n \"resmsgid\": \"58139c89-2852-4673-b414-2f867c91bbc1\",\n \"msgid\": null,\n \"err\": \"NOT_FOUND\",\n \"status\": \"failed\",\n \"errmsg\": \"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: do_11316973766674022413151\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + }, + { + "name": "INTERNAL SERVER ERROR ! Looks like something went wrong! These errors are tracked automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json\n - Multipart/form-data\n - Application/x-www-form-urlencoded", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-Userid", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "file", + "description": "(Required) Content package file which you intend to upload", + "type": "file", + "src": [] + } + ] + }, + "url": { + "raw": "{{baseUrl}}/content/v2/upload/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "upload", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"result\": {},\n \"id\": \"cupidatat reprehenderit dolor\",\n \"ver\": \"minim anim commodo\",\n \"ts\": \"commodo\",\n \"params\": {\n \"resmsgid\": \"pariatur repre\",\n \"msgid\": \"irure\",\n \"err\": \"consequat esse ea\",\n \"status\": \"esse\",\n \"errmsg\": \"aliqua laboris velit\"\n },\n \"responseCode\": \"reprehenderit dolor elit sunt dolore\"\n}" + } + ] + }, + { + "name": "Upload URL", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-Userid", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"fileName\": \"test.pdf\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/upload/url/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "upload", + "url", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + }, + "description": "This API is associated with uploading content on the Sunbird Platform .\n - The**_/upload/url/{content Id}_** endpoint executes \"Upload Content URL\" request based on parameters provided as metadata in the request body.\n - The endpoint for **Upload Content URL** is `/content/v2/upload/url`\n - It points to knowledge-mw-service - `/v2/content/upload/url`\n - {...}refers to a section of upload endpoint URL path that needs to be replaced by appropriate Id.\n - You need to provide a valid content Id value in {Content Id} field of API URL.\n - On Success api returns pre-signed-url link with come configured ttl against which file can be uploaded.\n - It is mandatory to provide values for parameters marked with *.\n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "OK ! Successful operation.\"Content upload\" operation was successfully executed.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-Userid", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"fileName\": \"test.pdf\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/upload/url/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "upload", + "url", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.upload.url\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-12-11T23:52:44ZZ\",\n \"params\": {\n \"resmsgid\": \"81e308fa-3e36-45ff-b295-e28e8f757dc7\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_11317053041892556811094\",\n \"url_expiry\": \"54000\",\n \"pre_signed_url\": \"https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/assets/do_11317053041892556811094/10-page-pdf.pdf?sv=2017-04-17&se=2020-12-12T14%3A52%3A44Z&sr=b&sp=w&sig=Tlt6uWI/6lMeupkEbsTAmGajmo1qzw8HeUHrVwOm6yk%3D\"\n }\n}" + }, + { + "name": "BAD REQUEST. The 'Upload Url' operation failed ! The possible reason for failure is that you may have missed providing input for a mandatory parameter.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-Userid", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"fileName\": \"test.pdf\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/upload/url/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "upload", + "url", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.upload.url\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-12-11T23:54:07ZZ\",\n \"params\": {\n \"resmsgid\": \"4edde64d-3116-4271-bcc1-a8ba0ec2d56b\",\n \"msgid\": null,\n \"err\": \"ERR_CONTENT_BLANK_FILE_NAME\",\n \"status\": \"failed\",\n \"errmsg\": \"File name is blank\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": null\n }\n}" + }, + { + "name": "RESOURCE NOT FOUND,**Content Id sent for generating upload url** operation failed !The possible reason for failure is that you may have provided wrong content id.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-Userid", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"fileName\": \"test.pdf\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/upload/url/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "upload", + "url", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.upload.url\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-11T23:48:26.694Z\",\n \"params\": {\n \"resmsgid\": \"5cf72660-3c0b-11eb-b0a2-8d5c9f561887\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": \"NOT_FOUND\",\n \"errmsg\": \"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: do_11254577204586086415\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {}\n}" + }, + { + "name": "INTERNAL SERVER ERROR ! Looks like something went wrong! These errors are tracked automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-Userid", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"fileName\": \"test.pdf\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/upload/url/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "upload", + "url", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"result\": {},\n \"id\": \"cupidatat reprehenderit dolor\",\n \"ver\": \"minim anim commodo\",\n \"ts\": \"commodo\",\n \"params\": {\n \"resmsgid\": \"pariatur repre\",\n \"msgid\": \"irure\",\n \"err\": \"consequat esse ea\",\n \"status\": \"esse\",\n \"errmsg\": \"aliqua laboris velit\"\n },\n \"responseCode\": \"reprehenderit dolor elit sunt dolore\"\n}" + } + ] + } + ] + }, + { + "name": "flag", + "item": [ + { + "name": "Flag Content", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-Userid", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/flag/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "flag", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + }, + "description": "This API is associated with setting Flag for content.\n - The **_/flag/{content Id}_** endpoint executes a request to set flagging status for content ,flag can either be set to \"Accepted\" or \"Rejected\".\n - The endpoint for **Flag Content** is `/content/v2/flag`\n - It points to knowledge-mw-service - `/v2/content/flag`\n - {...}refers to a section of reject endpoint URL path that needs to be replaced by appropriate Id.\n - You need to provide a valid content Id value in {Content Id} field of API URL.\n - It is mandatory to provide values for parameters marked with *.\n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "OK ! Successful operation.\"Content Flagging\" operation was successfully executed.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-Userid", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "body": { + "mode": "raw", + "raw": "{}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/flag/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "flag", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.flag\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-12-11T08:45:49ZZ\",\n \"params\": {\n \"resmsgid\": \"9c935e0c-b504-45e6-bd15-313f46c4613f\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_213143334457982976144\",\n \"node_id\": \"do_213143334457982976144\",\n \"versionKey\": \"1607676349236\"\n }\n}" + }, + { + "name": "ERR_CONTENT_NOT_FLAGGABLE! Looks like contentId is not in flaggable status.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-Userid", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "body": { + "mode": "raw", + "raw": "{}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/flag/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "flag", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.flag\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-12-11T08:44:53ZZ\",\n \"params\": {\n \"resmsgid\": \"2ff1945b-a114-44ca-929e-7d19a6f8a745\",\n \"msgid\": null,\n \"err\": \"ERR_CONTENT_NOT_FLAGGABLE\",\n \"status\": \"failed\",\n \"errmsg\": \"Unpublished Content do_213168073757138944196 cannot be flagged\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": null\n }\n}" + }, + { + "name": "RESOURCE NOT FOUND,**Content Send for Flag** operation failed !The possible reason for failure is that you may have provided wrong content id.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-Userid", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "body": { + "mode": "raw", + "raw": "{}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/flag/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "flag", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.flag\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-12-11T09:41:41ZZ\",\n \"params\": {\n \"resmsgid\": \"5b4f0b33-3941-4c18-b8bf-123c2e0348e6\",\n \"msgid\": null,\n \"err\": \"NOT_FOUND\",\n \"status\": \"failed\",\n \"errmsg\": \"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: do_2131433344579829761441\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + }, + { + "name": "INTERNAL SERVER ERROR ! Looks like something went wrong! These errors are tracked automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-Userid", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "body": { + "mode": "raw", + "raw": "{}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/flag/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "flag", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"result\": {},\n \"id\": \"cupidatat reprehenderit dolor\",\n \"ver\": \"minim anim commodo\",\n \"ts\": \"commodo\",\n \"params\": {\n \"resmsgid\": \"pariatur repre\",\n \"msgid\": \"irure\",\n \"err\": \"consequat esse ea\",\n \"status\": \"esse\",\n \"errmsg\": \"aliqua laboris velit\"\n },\n \"responseCode\": \"reprehenderit dolor elit sunt dolore\"\n}" + } + ] + }, + { + "name": "Accept Flag", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json\n - Multipart/form-data\n - Application/x-www-form-urlencoded", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-User-token", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"versionKey\": \"1507102327646\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/flag/accept/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "flag", + "accept", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + }, + "description": "This API is associated with setting Accept Flag for content under review process on the Sunbird Platform .\n - The **_/flag/accept/_**endpoint executes request for flagging content under review to \"Accept flag\", if content is found appropriate.\n - The endpoint for **Flag Accept Content** is `/content/v2/flag/accept`\n - It points to knowledge-mw-service - `/v2/content/flag/accept`\n - It is mandatory to provide values for parameters marked with *.\n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "OK ! Successful operation.\"Accept Flag\" operation was successfully executed.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json\n - Multipart/form-data\n - Application/x-www-form-urlencoded", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-User-token", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"versionKey\": \"1507102327646\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/flag/accept/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "flag", + "accept", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.flag.accept\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-11T11:40:01.464Z\",\n \"params\": {\n \"resmsgid\": \"9a9e4b80-3ba5-11eb-b0a2-8d5c9f561887\",\n \"msgid\": \"9a49e9f0-3ba5-11eb-b0a2-8d5c9f561887\",\n \"status\": \"successful\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_31221753157709004825531\",\n \"node_id\": \"do_31221753157709004825531\",\n \"versionKey\": \"1607686801161\"\n }\n}" + }, + { + "name": "BAD REQUEST. The \"Accept Flag\" operation failed ! The possible reason for failure is that you may have missed providing input for a mandatory parameter.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json\n - Multipart/form-data\n - Application/x-www-form-urlencoded", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-User-token", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"versionKey\": \"1507102327646\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/flag/accept/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "flag", + "accept", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.flag.accept\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-11T11:25:34.614Z\",\n \"params\": {\n \"resmsgid\": \"95ef7b60-3ba3-11eb-be5b-f9d249ba9049\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": \"ERR_INVALID_CONTENT\",\n \"errmsg\": \"Invalid Flagged Content! Content Can Not Be Accepted.\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {}\n}" + }, + { + "name": "RESOURCE NOT FOUND,**Content Send for Flag Accept** operation failed !The possible reason for failure is that you may have provided wrong content id.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json\n - Multipart/form-data\n - Application/x-www-form-urlencoded", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-User-token", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"versionKey\": \"1507102327646\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/flag/accept/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "flag", + "accept", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.flag.accept\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-11T11:24:42.903Z\",\n \"params\": {\n \"resmsgid\": \"771d0270-3ba3-11eb-be5b-f9d249ba9049\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": \"NOT_FOUND\",\n \"errmsg\": \"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: do_213143334457982976144\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {}\n}" + }, + { + "name": "INTERNAL SERVER ERROR ! Looks like something went wrong! These errors are tracked automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json\n - Multipart/form-data\n - Application/x-www-form-urlencoded", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-User-token", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"versionKey\": \"1507102327646\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/flag/accept/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "flag", + "accept", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"result\": {},\n \"id\": \"cupidatat reprehenderit dolor\",\n \"ver\": \"minim anim commodo\",\n \"ts\": \"commodo\",\n \"params\": {\n \"resmsgid\": \"pariatur repre\",\n \"msgid\": \"irure\",\n \"err\": \"consequat esse ea\",\n \"status\": \"esse\",\n \"errmsg\": \"aliqua laboris velit\"\n },\n \"responseCode\": \"reprehenderit dolor elit sunt dolore\"\n}" + } + ] + } + ] + }, + { + "name": "Create Content", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) Represents a physical location (e.g: state) uniquely to which the user belongs.", + "key": "x-channel-id", + "value": "ea laborum" + }, + { + "description": "(Required) Represents the authorized User ID to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "ea laborum" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "ea laborum" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"H5P Content\",\n \"code\": \"h5pcontent\",\n \"mimeType\": \"application/vnd.ekstep.h5p-archive\",\n \"primaryCategory\": \"Resource\",\n \"createdBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/create", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "create" + ] + }, + "description": "This API is associated with creating content on the Sunbird Platform.\n - The endpoint for **Create Content** is `/content/v2/create`\n - It points to knowledge-mw-service - `/v2/content/create`\n - It is mandatory to provide values for parameters marked with *.\n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "SUCCESS. The **Create Content** operation was successful!", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) Represents a physical location (e.g: state) uniquely to which the user belongs.", + "key": "x-channel-id", + "value": "ea laborum" + }, + { + "description": "(Required) Represents the authorized User ID to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "ea laborum" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"H5P Content\",\n \"code\": \"h5pcontent\",\n \"mimeType\": \"application/vnd.ekstep.h5p-archive\",\n \"primaryCategory\": \"Resource\",\n \"createdBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/create", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-12-10T20:24:45ZZ\",\n \"params\": {\n \"resmsgid\": \"3be02c4b-3324-41a3-afd8-60f6be0584d2\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_1131697204035993601314\",\n \"node_id\": \"do_1131697204035993601314\",\n \"versionKey\": \"1607631885207\"\n }\n}" + }, + { + "name": "'BAD REQUEST. The **Create Content** operation failed. You may have missed input for a mandatory parameter.'", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) Represents a physical location (e.g: state) uniquely to which the user belongs.", + "key": "x-channel-id", + "value": "ea laborum" + }, + { + "description": "(Required) Represents the authorized User ID to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "ea laborum" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"H5P Content\",\n \"code\": \"h5pcontent\",\n \"mimeType\": \"application/vnd.ekstep.h5p-archive\",\n \"primaryCategory\": \"Resource\",\n \"createdBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/create", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-10T08:50:29.786Z\",\n \"params\": {\n \"resmsgid\": \"c169a7a0-3ac4-11eb-b0a2-8d5c9f561887\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": \"ERR_CONTENT_CREATE_FIELDS_MISSING\",\n \"errmsg\": \"Required fields for create content are missing\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {}\n}" + }, + { + "name": "'INTERNAL SERVER ERROR. We track these errors automatically and try to set it right at the earliest. Try refreshing the page. If the problem persists contact us at info@sunbird.org.'", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) Represents a physical location (e.g: state) uniquely to which the user belongs.", + "key": "x-channel-id", + "value": "ea laborum" + }, + { + "description": "(Required) Represents the authorized User ID to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "ea laborum" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"name\": \"H5P Content\",\n \"code\": \"h5pcontent\",\n \"mimeType\": \"application/vnd.ekstep.h5p-archive\",\n \"primaryCategory\": \"Resource\",\n \"createdBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/create", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "create" + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.create\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-10T08:51:51.647Z\",\n \"params\": {\n \"resmsgid\": \"f234a6f0-3ac4-11eb-b0a2-8d5c9f561887\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"SERVER_ERROR\",\n \"result\": {}\n}" + } + ] + }, + { + "name": "Update Content", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "PATCH", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) The Registered User ID used to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "ea laborum" + }, + { + "description": "The unique ID provided to the State to which the user belongs.", + "key": "X-Channel-id", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"1607631400608\",\n \"description\": \"Updated description\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/update/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "update", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + }, + "description": "This API is associated with updating content on the Sunbird Platform .\n - **_/Update/_** endpoint executes the \"Update Content\" request based on parameters provided as metadata in the request body.\n - The endpoint for **Update Content** is `/content/v2/update`\n - It points to knowledge-mw-service - `/v2/content/update`\n - {...}refers to a section of review endpoint URL path that needs to be replaced by appropriate Id.\n - It is mandatory to provide values for parameters marked with *.\n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "OK ! Successful operation.Content update operation was successfuly executed.", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) The Registered User ID used to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "ea laborum" + }, + { + "description": "The unique ID provided to the State to which the user belongs.", + "key": "X-Channel-id", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"1607631400608\",\n \"description\": \"Updated description\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/update/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "update", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.update\",\n \"ver\": \"4.0\",\n \"ts\": \"2020-12-10T20:26:07ZZ\",\n \"params\": {\n \"resmsgid\": \"80aa9310-b749-411c-a13b-8d9f25af389f\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_1131697204035993601314\",\n \"node_id\": \"do_1131697204035993601314\",\n \"versionKey\": \"1607631967842\"\n }\n}" + }, + { + "name": "BAD REQUEST. The 'Update Content' operation failed ! The possible reason for failure is that you may have missed providing input for a mandatory parameter.", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) The Registered User ID used to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "ea laborum" + }, + { + "description": "The unique ID provided to the State to which the user belongs.", + "key": "X-Channel-id", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"1607631400608\",\n \"description\": \"Updated description\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/update/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "update", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.update\",\n \"ver\": \"4.0\",\n \"ts\": \"2020-12-10T20:27:01ZZ\",\n \"params\": {\n \"resmsgid\": \"87eb99c6-bc10-406a-a6fe-13651f546ce1\",\n \"msgid\": null,\n \"err\": \"ERR_INVALID_REQUEST\",\n \"status\": \"failed\",\n \"errmsg\": \"Please Provide Version Key!\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": null\n }\n}" + }, + { + "name": "RESOURCE NOT FOUND,**Content Update** operation failed !The possible reason for failure is that you may have provided wrong content id.", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) The Registered User ID used to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "ea laborum" + }, + { + "description": "The unique ID provided to the State to which the user belongs.", + "key": "X-Channel-id", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"1607631400608\",\n \"description\": \"Updated description\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/update/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "update", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.update\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-12-12T00:31:16ZZ\",\n \"params\": {\n \"resmsgid\": \"c29ffd8b-d105-4eeb-a875-deaeae2bf5b8\",\n \"msgid\": null,\n \"err\": \"NOT_FOUND\",\n \"status\": \"failed\",\n \"errmsg\": \"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: do_21317015414162227213604\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + }, + { + "name": "INTERNAL SERVER ERROR ! Looks like something went wrong! These errors are tracked automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) The Registered User ID used to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "ea laborum" + }, + { + "description": "The unique ID provided to the State to which the user belongs.", + "key": "X-Channel-id", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"versionKey\": \"1607631400608\",\n \"description\": \"Updated description\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/update/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "update", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"result\": {},\n \"id\": \"cupidatat reprehenderit dolor\",\n \"ver\": \"minim anim commodo\",\n \"ts\": \"commodo\",\n \"params\": {\n \"resmsgid\": \"pariatur repre\",\n \"msgid\": \"irure\",\n \"err\": \"consequat esse ea\",\n \"status\": \"esse\",\n \"errmsg\": \"aliqua laboris velit\"\n },\n \"responseCode\": \"reprehenderit dolor elit sunt dolore\"\n}" + } + ] + }, + { + "name": "Read Content", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "description": "The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/content/v2/read/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "read", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + }, + "description": "This API is associated with viewing and reading out the content on the Sunbird Platform .\n - The **_/read/{contentId}_** endpoint executes request for fetching the contents to be viewed and read.\n - The endpoint for **Read Content** is `/content/v2/read`\n - It points to knowledge-mw-service - `/v2/content/read`\n - {...}refers to a section of reject endpoint URL path that needs to be replaced by appropriate Id.\n - You need to provide a valid content Id value in {Content Id} field of API URL.\n - It is mandatory to provide values for parameters marked with *.\n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "OK ! Successful operation.\"Read content\" operation was successfully executed. For all possible metadata, please [refer](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + } + ], + "url": { + "raw": "{{baseUrl}}/content/v2/read/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "read", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.read\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-10T20:38:32.510Z\",\n \"params\": {\n \"resmsgid\": \"ab16e5e0-3b27-11eb-b0a2-8d5c9f561887\",\n \"msgid\": \"ab131550-3b27-11eb-b0a2-8d5c9f561887\",\n \"status\": \"successful\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"content\": {\n \"ownershipType\": [\n \"createdBy\"\n ],\n \"code\": \"org.sunbird.EHdZir\",\n \"credentials\": {\n \"enabled\": \"No\"\n },\n \"channel\": \"channel-1\",\n \"language\": [\n \"English\"\n ],\n \"mimeType\": \"application/vnd.ekstep.h5p-archive\",\n \"idealScreenSize\": \"normal\",\n \"createdOn\": \"2020-12-10T20:38:13.315+0000\",\n \"objectType\": \"Content\",\n \"primaryCategory\": \"Learning Resource\",\n \"contentDisposition\": \"inline\",\n \"lastUpdatedOn\": \"2020-12-10T20:38:13.315+0000\",\n \"contentEncoding\": \"gzip\",\n \"dialcodeRequired\": \"No\",\n \"trackable\": {\n \"enabled\": \"No\",\n \"autoBatch\": \"No\"\n },\n \"identifier\": \"do_21316972702362828813477\",\n \"lastStatusChangedOn\": \"2020-12-10T20:38:13.315+0000\",\n \"audience\": [\n \"Student\"\n ],\n \"os\": [\n \"All\"\n ],\n \"visibility\": \"Default\",\n \"consumerId\": \"2eaff3db-cdd1-42e5-a611-bebbf906e6cf\",\n \"mediaType\": \"content\",\n \"osId\": \"org.ekstep.quiz.app\",\n \"languageCode\": [\n \"en\"\n ],\n \"version\": 2,\n \"versionKey\": \"1607632693315\",\n \"license\": \"CC BY 4.0\",\n \"idealScreenDensity\": \"hdpi\",\n \"framework\": \"NCF\",\n \"createdBy\": \"874ed8a5-782e-4f6c-8f36-e0288455901e\",\n \"compatibilityLevel\": 1,\n \"name\": \"Test_h5p\",\n \"status\": \"Draft\"\n }\n }\n}" + }, + { + "name": "RESOURCE NOT FOUND,**Content Read** operation failed !The possible reason for failure is that you may have provided wrong content id.", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + } + ], + "url": { + "raw": "{{baseUrl}}/content/v2/read/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "read", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.read\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-10T20:37:58.223Z\",\n \"params\": {\n \"resmsgid\": \"96a71df0-3b27-11eb-b0a2-8d5c9f561887\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": \"NOT_FOUND\",\n \"errmsg\": \"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: do_1131697204035993601314\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + }, + { + "name": "INTERNAL SERVER ERROR ! Looks like something went wrong! These errors are tracked automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + } + ], + "url": { + "raw": "{{baseUrl}}/content/v2/read/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "read", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"result\": {},\n \"id\": \"cupidatat reprehenderit dolor\",\n \"ver\": \"minim anim commodo\",\n \"ts\": \"commodo\",\n \"params\": {\n \"resmsgid\": \"pariatur repre\",\n \"msgid\": \"irure\",\n \"err\": \"consequat esse ea\",\n \"status\": \"esse\",\n \"errmsg\": \"aliqua laboris velit\"\n },\n \"responseCode\": \"reprehenderit dolor elit sunt dolore\"\n}" + } + ] + }, + { + "name": "Submit for Review", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-User-token", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/review/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "review", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + }, + "description": ">- This API is associated with Submission of content for Review on the Sunbird Platform .\n - The**_/review/{content Id}_** endpoint executes \"Submit for Review\" request based on parameters provided as metadata in the request body.\n - The endpoint for **Review Content** is `/content/v2/review`\n - It points to knowledge-mw-service - `/v2/content/review`\n - {...}refers to a section of review endpoint URL path that needs to be replaced by appropriate Id.\n - You need to provide a valid content Id value in {Content Id} field of API URL.\n - It is mandatory to provide values for parameters marked with *.\n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "OK ! Successful operation.Content sent for review operation was successfully executed.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-User-token", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "body": { + "mode": "raw", + "raw": "{}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/review/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "review", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"ekstep.learning.content.review\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-12-10T21:06:26ZZ\",\n \"params\": {\n \"resmsgid\": \"186cf9a9-8094-4cdd-926f-b77a030921d1\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"node_id\": \"do_1131697376667402241315\",\n \"versionKey\": \"1607634386533\"\n }\n}" + }, + { + "name": "RESOURCE NOT FOUND,**Content Send for Review** operation failed !The possible reason for failure is that you may have provided wrong content id.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-User-token", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "body": { + "mode": "raw", + "raw": "{}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/review/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "review", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"ekstep.learning.content.review\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-12-10T21:06:54ZZ\",\n \"params\": {\n \"resmsgid\": \"944ac944-78d9-43e2-bfd5-337ab52b6552\",\n \"msgid\": null,\n \"err\": \"ERR_TAXONOMY_INVALID_CONTENT\",\n \"status\": \"failed\",\n \"errmsg\": \"Error! While Fetching the Content for Operation | [Content Id: do_11316973766674022413151]\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {}\n}" + }, + { + "name": "INTERNAL SERVER ERROR ! Looks like something went wrong! We track these errors automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-User-token", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "body": { + "mode": "raw", + "raw": "{}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/review/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "review", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"result\": {},\n \"id\": \"cupidatat reprehenderit dolor\",\n \"ver\": \"minim anim commodo\",\n \"ts\": \"commodo\",\n \"params\": {\n \"resmsgid\": \"pariatur repre\",\n \"msgid\": \"irure\",\n \"err\": \"consequat esse ea\",\n \"status\": \"esse\",\n \"errmsg\": \"aliqua laboris velit\"\n },\n \"responseCode\": \"reprehenderit dolor elit sunt dolore\"\n}" + } + ] + }, + { + "name": "Publish Content", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json\n - Multipart/form-data\n - Application/x-www-form-urlencoded", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) User Registered ID , which is used for logging In", + "key": "X-Authenticated-User-token", + "value": "ea laborum" + }, + { + "description": "(Required) Authorizing user in order to grant access.", + "key": "Authorization", + "value": "ea laborum" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Ekstep\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/publish/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "publish", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Content Id to be published" + } + ] + }, + "description": "This API is associated with publishing content on the Sunbird Platform .\n - **_/publish/_** endpoint executes the \"Publish Content\" request based on parameters provided as metadata in the request body.\n - The endpoint for **Publish Content** is `/content/v1/publish`\n - It points to knowledge-mw-service - `/v2/content/publish`\n - {...}refers to a section of review endpoint URL path that needs to be replaced by appropriate Id.\n - It is mandatory to provide values for parameters marked with *.\n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "OK ! Successful operation.**Content publish operation was successfully executed.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json\n - Multipart/form-data\n - Application/x-www-form-urlencoded", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) User Registered ID , which is used for logging In", + "key": "X-Authenticated-User-token", + "value": "ea laborum" + }, + { + "description": "(Required) Authorizing user in order to grant access.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Ekstep\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v1/publish/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v1", + "publish", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Content Id to be published" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.v3.publish\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-10T21:41:23.491Z\",\n \"params\": {\n \"resmsgid\": \"72c4ef30-3b30-11eb-b0a2-8d5c9f561887\",\n \"msgid\": \"72979da0-3b30-11eb-b0a2-8d5c9f561887\",\n \"status\": \"successful\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"content_id\": \"do_21316975736724684813479\",\n \"publishStatus\": \"Publish Operation for Content Id 'do_21316975736724684813479' Started Successfully!\"\n }\n}" + }, + { + "name": "RESOURCE NOT FOUND,**Content Send for Review** operation failed !The possible reason for failure is that you may have provided wrong content id.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json\n - Multipart/form-data\n - Application/x-www-form-urlencoded", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) User Registered ID , which is used for logging In", + "key": "X-Authenticated-User-token", + "value": "ea laborum" + }, + { + "description": "(Required) Authorizing user in order to grant access.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Ekstep\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v1/publish/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v1", + "publish", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Content Id to be published" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.v3.publish\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-10T21:51:39.870Z\",\n \"params\": {\n \"resmsgid\": \"e228e7e0-3b31-11eb-b0a2-8d5c9f561887\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": \"NOT_FOUND\",\n \"errmsg\": \"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: do_213169757367246848134791\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {}\n}" + }, + { + "name": "INTERNAL SERVER ERROR ! Looks like something went wrong! We track these errors automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json\n - Multipart/form-data\n - Application/x-www-form-urlencoded", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) User Registered ID , which is used for logging In", + "key": "X-Authenticated-User-token", + "value": "ea laborum" + }, + { + "description": "(Required) Authorizing user in order to grant access.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"lastPublishedBy\": \"Ekstep\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v1/publish/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v1", + "publish", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Content Id to be published" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"result\": {},\n \"id\": \"cupidatat reprehenderit dolor\",\n \"ver\": \"minim anim commodo\",\n \"ts\": \"commodo\",\n \"params\": {\n \"resmsgid\": \"pariatur repre\",\n \"msgid\": \"irure\",\n \"err\": \"consequat esse ea\",\n \"status\": \"esse\",\n \"errmsg\": \"aliqua laboris velit\"\n },\n \"responseCode\": \"reprehenderit dolor elit sunt dolore\"\n}" + } + ] + }, + { + "name": "Reject Content Review", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/reject/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "reject", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + }, + "description": "This API is associated with rejecting content under review.\n - The **_/reject/{contentId}_** endpoint executes request for rejecting content which is under review.\n - The endpoint for **Reject Content** is `/content/v2/reject`\n - It points to knowledge-mw-service - `/v2/content/reject`\n - {...}refers to a section of reject endpoint URL path that needs to be replaced by appropriate Id.\n - You need to provide a valid content Id value in {Content Id} field of API URL.\n - It is mandatory to provide values for parameters marked with *.\n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "OK ! Successful operation.\"Content reject\" operation was successfully executed.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "body": { + "mode": "raw", + "raw": "{}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/reject/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "reject", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.v3.reject\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-11T08:09:46.451Z\",\n \"params\": {\n \"resmsgid\": \"3b7c1230-3b88-11eb-b0a2-8d5c9f561887\",\n \"msgid\": \"3b692670-3b88-11eb-b0a2-8d5c9f561887\",\n \"status\": \"successful\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"node_id\": \"do_213168073757138944196\",\n \"versionKey\": \"1607674186357\"\n }\n}" + }, + { + "name": "ERROR CONTENT NOT IN REVIEW STATUS ! Looks like contentId provided is not in review status.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "body": { + "mode": "raw", + "raw": "{}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/reject/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "reject", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.v3.reject\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-11T08:03:51.807Z\",\n \"params\": {\n \"resmsgid\": \"6819b0f0-3b87-11eb-b0a2-8d5c9f561887\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": \"ERR_CONTENT_NOT_IN_REVIEW\",\n \"errmsg\": \"Content is not in review state for identifier: do_2131680942619115521243\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {}\n}" + }, + { + "name": "RESOURCE NOT FOUND ! Looks like contentId provided is not a valid content.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "body": { + "mode": "raw", + "raw": "{}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/reject/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "reject", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.v3.reject\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-11T08:08:30.454Z\",\n \"params\": {\n \"resmsgid\": \"0e2fd960-3b88-11eb-b0a2-8d5c9f561887\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": \"NOT_FOUND\",\n \"errmsg\": \"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: do_2123462720305561601147\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {}\n}" + }, + { + "name": "INTERNAL SERVER ERROR ! Looks like something went wrong! These errors are tracked automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "body": { + "mode": "raw", + "raw": "{}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/reject/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "reject", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"result\": {},\n \"id\": \"cupidatat reprehenderit dolor\",\n \"ver\": \"minim anim commodo\",\n \"ts\": \"commodo\",\n \"params\": {\n \"resmsgid\": \"pariatur repre\",\n \"msgid\": \"irure\",\n \"err\": \"consequat esse ea\",\n \"status\": \"esse\",\n \"errmsg\": \"aliqua laboris velit\"\n },\n \"responseCode\": \"reprehenderit dolor elit sunt dolore\"\n}" + } + ] + }, + { + "name": "Retire Content", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [ + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-User-token", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/content/v2/retire/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "retire", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + }, + "description": "This API is associated with retiring content on the Sunbird Platform .\n - The **_/retire/{contentId}_** endpoint executes \"Retire Content\" request based on content Id to delete the specific content.'\n - The endpoint for **Retire Content** is `/content/v2/retire`\n - It points to knowledge-mw-service - `/v2/content/retire`\n - {...}refers to a section of review endpoint URL path that needs to be replaced by appropriate Id.\n - It is mandatory to provide values for parameters.\n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "OK ! Successful operation.**Content deletion operation was successfully executed.", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-User-token", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "url": { + "raw": "{{baseUrl}}/content/v2/retire/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "retire", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.retire\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-12-11T12:05:52ZZ\",\n \"params\": {\n \"resmsgid\": \"ebd2655a-a69a-460c-9bcf-0f45f5938fac\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_11283183576849612818\",\n \"node_id\": \"do_11283183576849612818\"\n }\n}" + }, + { + "name": "ERR CONTENT RETIRE! Content is already retired.", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-User-token", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "url": { + "raw": "{{baseUrl}}/content/v2/retire/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "retire", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.retire\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-12-11T12:01:24ZZ\",\n \"params\": {\n \"resmsgid\": \"ec20ec05-581c-4559-988a-449f3c5ae698\",\n \"msgid\": null,\n \"err\": \"ERR_CONTENT_RETIRE\",\n \"status\": \"failed\",\n \"errmsg\": \"Content with Identifier do_11316298360130764811 is already Retired.\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": null\n }\n}" + }, + { + "name": "RESOURCE NOT FOUND,**Content Send for Flag Accept** operation failed !The possible reason for failure is that you may have provided wrong content id.", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-User-token", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "url": { + "raw": "{{baseUrl}}/content/v2/retire/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "retire", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.retire\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-12-11T11:56:35ZZ\",\n \"params\": {\n \"resmsgid\": \"d826e808-2866-4603-9b60-0a3130a37128\",\n \"msgid\": null,\n \"err\": \"NOT_FOUND\",\n \"status\": \"failed\",\n \"errmsg\": \"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: do_213143334457982976144\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + }, + { + "name": "INTERNAL SERVER ERROR ! Looks like something went wrong! These errors are tracked automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-User-token", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "url": { + "raw": "{{baseUrl}}/content/v2/retire/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "retire", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"result\": {},\n \"id\": \"cupidatat reprehenderit dolor\",\n \"ver\": \"minim anim commodo\",\n \"ts\": \"commodo\",\n \"params\": {\n \"resmsgid\": \"pariatur repre\",\n \"msgid\": \"irure\",\n \"err\": \"consequat esse ea\",\n \"status\": \"esse\",\n \"errmsg\": \"aliqua laboris velit\"\n },\n \"responseCode\": \"reprehenderit dolor elit sunt dolore\"\n}" + } + ] + }, + { + "name": "Content Discard", + "request": { + "method": "DELETE", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) The Registered User ID used to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "ea laborum" + }, + { + "description": "The unique ID provided to the State to which the user belongs.", + "key": "X-Channel-id", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/content/v2/discard/:content_id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "discard", + ":content_id" + ], + "variable": [ + { + "key": "content_id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + }, + "description": "This API is associated with Discard Content on the Sunbird Platform .\n - **_/Content/_** endpoint executes the \"Discard Content\" request based on parameters provided as metadata in the request body.\n - The endpoint for **Discard Content** is `/Content/v2/discard`\n - It points to knowledge-mw-service - `/v2/Content/discard`\n - {...}refers to a section of review endpoint URL path that needs to be replaced by appropriate Id.\n - It is mandatory to provide values for parameters marked with *.\n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "OK", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) The Registered User ID used to execute the API.", + "key": "X-Authenticated-User-Token", + "value": "ea laborum" + }, + { + "description": "The unique ID provided to the State to which the user belongs.", + "key": "X-Channel-id", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "url": { + "raw": "{{baseUrl}}/content/v2/discard/:content_id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "discard", + ":content_id" + ], + "variable": [ + { + "key": "content_id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.discard\",\n \"ver\": \"4.0\",\n \"ts\": \"2022-01-06T06:41:51ZZ\",\n \"params\": {\n \"resmsgid\": \"fc899cd5-958b-40ad-9a5c-6f31b65404b4\",\n \"status\": \"successful\"\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"do_1134467341514588161171\",\n \"message\": \"Draft version of the content with id : do_1134467341514588161171 is discarded\",\n \"node_id\": \"do_1134467341514588161171\"\n }\n}" + } + ] + }, + { + "name": "Copy Content", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-User-token", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"createdBy\": \"user1\",\n \"createdFor\": [\n \"user2\"\n ],\n \"organisation\": [\n \"sunbird\"\n ],\n \"framework\": \"NCF\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/copy/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "copy", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + }, + "description": "This API is associated with copying a content on the Sunbird Platform .\n - The **_/copy/_**endpoint executes request for copy content, if content is found appropriate.\n - The endpoint for **Copy Content** is `/content/v2/copy`\n - It points to knowledge-mw-service - `/v2/content/copy`\n - It is mandatory to provide values for parameters marked with *.\n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "OK ! Successful operation.\"Copy\" operation was successfully executed.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-User-token", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"createdBy\": \"user1\",\n \"createdFor\": [\n \"user2\"\n ],\n \"organisation\": [\n \"sunbird\"\n ],\n \"framework\": \"NCF\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/copy/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "copy", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.copy\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-11T23:28:25.343Z\",\n \"params\": {\n \"resmsgid\": \"90e788f0-3c08-11eb-b0a2-8d5c9f561887\",\n \"msgid\": \"90b3f5d0-3c08-11eb-b0a2-8d5c9f561887\",\n \"status\": \"successful\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"node_id\": {\n \"do_21316884557433241613378\": \"do_21317051846683852813814\"\n },\n \"versionKey\": \"1607729305036\"\n }\n}" + }, + { + "name": "BAD REQUEST. The \"Copy Content\" operation failed ! The possible reason for failure is that you may have missed providing input for a mandatory parameter.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-User-token", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"createdBy\": \"user1\",\n \"createdFor\": [\n \"user2\"\n ],\n \"organisation\": [\n \"sunbird\"\n ],\n \"framework\": \"NCF\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/copy/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "copy", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.copy\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-11T23:27:31.708Z\",\n \"params\": {\n \"resmsgid\": \"70ef7bc0-3c08-11eb-b0a2-8d5c9f561887\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": \"ERR_INVALID_REQUEST\",\n \"errmsg\": \"Please provide valid value for List(createdBy, createdFor, organisation, framework)\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": null\n }\n}" + }, + { + "name": "RESOURCE NOT FOUND,**Content Send for Copy** operation failed !The possible reason for failure is that you may have provided wrong content id.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-User-token", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"createdBy\": \"user1\",\n \"createdFor\": [\n \"user2\"\n ],\n \"organisation\": [\n \"sunbird\"\n ],\n \"framework\": \"NCF\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/copy/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "copy", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.copy\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-11T23:28:36.863Z\",\n \"params\": {\n \"resmsgid\": \"97c558f0-3c08-11eb-b0a2-8d5c9f561887\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": \"NOT_FOUND\",\n \"errmsg\": \"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: do_213168845574332416133781\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + }, + { + "name": "INTERNAL SERVER ERROR ! Looks like something went wrong! These errors are tracked automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-User-token", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"createdBy\": \"user1\",\n \"createdFor\": [\n \"user2\"\n ],\n \"organisation\": [\n \"sunbird\"\n ],\n \"framework\": \"NCF\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/copy/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "copy", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"result\": {},\n \"id\": \"cupidatat reprehenderit dolor\",\n \"ver\": \"minim anim commodo\",\n \"ts\": \"commodo\",\n \"params\": {\n \"resmsgid\": \"pariatur repre\",\n \"msgid\": \"irure\",\n \"err\": \"consequat esse ea\",\n \"status\": \"esse\",\n \"errmsg\": \"aliqua laboris velit\"\n },\n \"responseCode\": \"reprehenderit dolor elit sunt dolore\"\n}" + } + ] + }, + { + "name": "Import Content", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": [\n {\n \"source\": \"https://vdn.diksha.gov.in/api/content/v2/read/do_4313124780355543040126730\",\n \"metadata\": {},\n \"collection\": [\n {\n \"identifier\": \"do_3131112587361648641337\",\n \"unitId\": \"do_3131112587369922561425\"\n }\n ]\n }\n ]\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/import", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "import" + ] + }, + "description": "This API is associated with importing a content on the Sunbird Platform from different environment.\n - The **_/import/_**endpoint executes request for impoting content, if content is found appropriate.\n - The endpoint for **Import Content** is `/content/v2/import`\n - It points to content-service - `/content/v3/import`\n - It is mandatory to provide values for parameters marked with *.\n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "OK ! Successful operation.\"Import\" operation was successfully executed.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": [\n {\n \"source\": \"https://vdn.diksha.gov.in/api/content/v2/read/do_4313124780355543040126730\",\n \"metadata\": {},\n \"collection\": [\n {\n \"identifier\": \"do_3131112587361648641337\",\n \"unitId\": \"do_3131112587369922561425\"\n }\n ]\n }\n ]\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/import", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "import" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.import\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-12-17T16:29:11ZZ\",\n \"params\": {\n \"resmsgid\": \"7cce8934-0a2f-46bf-a0ee-be5f49874cae\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"processId\": \"80a765c7-2202-4b3f-b473-448b5c2cd20e\"\n }\n}" + }, + { + "name": "BAD REQUEST. The \"Import Content\" operation failed ! The possible reason for failure is that you may have missed providing input for a mandatory parameter.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": [\n {\n \"source\": \"https://vdn.diksha.gov.in/api/content/v2/read/do_4313124780355543040126730\",\n \"metadata\": {},\n \"collection\": [\n {\n \"identifier\": \"do_3131112587361648641337\",\n \"unitId\": \"do_3131112587369922561425\"\n }\n ]\n }\n ]\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/import", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "import" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.import\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-12-17T16:30:16ZZ\",\n \"params\": {\n \"resmsgid\": \"31f88a39-2514-4d86-b424-240166e315be\",\n \"msgid\": null,\n \"err\": \"ERR_REQUIRED_PROPS_VALIDATION\",\n \"status\": \"failed\",\n \"errmsg\": \"Validation Failed! Mandatory Properties Are [name, code, mimeType, primaryCategory, artifactUrl, framework]\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": null\n }\n}" + }, + { + "name": "INTERNAL SERVER ERROR ! Looks like something went wrong! These errors are tracked automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": [\n {\n \"source\": \"https://vdn.diksha.gov.in/api/content/v2/read/do_4313124780355543040126730\",\n \"metadata\": {},\n \"collection\": [\n {\n \"identifier\": \"do_3131112587361648641337\",\n \"unitId\": \"do_3131112587369922561425\"\n }\n ]\n }\n ]\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v2/import", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v2", + "import" + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"result\": {},\n \"id\": \"cupidatat reprehenderit dolor\",\n \"ver\": \"minim anim commodo\",\n \"ts\": \"commodo\",\n \"params\": {\n \"resmsgid\": \"pariatur repre\",\n \"msgid\": \"irure\",\n \"err\": \"consequat esse ea\",\n \"status\": \"esse\",\n \"errmsg\": \"aliqua laboris velit\"\n },\n \"responseCode\": \"reprehenderit dolor elit sunt dolore\"\n}" + } + ] + } + ] + }, + { + "name": "v1", + "item": [ + { + "name": "lock", + "item": [ + { + "name": "lock list", + "request": { + "method": "POST", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource", + "key": "Content-Type", + "value": "application/json" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "ipsum voluptate qui" + }, + { + "key": "Accept", + "value": "application/json; charset=utf-8" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"filters\": {\n \"resourceId\": [\n \"do_11319479631000371211\",\n \"do_11319851826161254418\",\n \"do_113107812356022272164\",\n \"do_1131983093687828481353\",\n \"do_113197939102253056140\",\n \"do_113197917149233152139\",\n \"do_113197913444737024138\",\n \"do_113197844509425664130\",\n \"do_113197903995772928137\"\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/lock/v1/list", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "lock", + "v1", + "list" + ] + }, + "description": "This API searches for a list of Locked resource by a user or system or service.\n - The **_lock/v1/list_** endpoint searches the list of Locked resource.\n - All fields marked with an * are mandatory\n - Required fields cannot be null or empty\n - Backend Route: http://knowledge-mw-service:5000/v1/lock/list" + }, + "response": [ + { + "name": "This represents the list response success", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource", + "key": "Content-Type", + "value": "application/json" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "ipsum voluptate qui" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"filters\": {\n \"resourceId\": [\n \"do_11319479631000371211\",\n \"do_11319851826161254418\",\n \"do_113107812356022272164\",\n \"do_1131983093687828481353\",\n \"do_113197939102253056140\",\n \"do_113197917149233152139\",\n \"do_113197913444737024138\",\n \"do_113197844509425664130\",\n \"do_113197903995772928137\"\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/lock/v1/list", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "lock", + "v1", + "list" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=utf-8" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.lock.list\",\n \"ver\": 1,\n \"ts\": \"2021-01-21T09:44:03.248Z\",\n \"params\": {\n \"resmsgid\": \"32228b00-5bcd-11eb-9b0c-abcfbdf41bc3\",\n \"msgid\": \"3247615a-0095-9236-9b6b-357d7b130b6e\",\n \"status\": \"successful\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"count\": 1,\n \"data\": [\n {\n \"lockId\": \"1d767439-a517-4433-bf46-fb0cca148d31\",\n \"resourceId\": \"do_11319479631000371211\",\n \"resourceType\": \"Content\",\n \"resourceInfo\": \"{\\\"contentType\\\":\\\"TextBook\\\",\\\"framework\\\":\\\"ekstep_ncert_k-12\\\",\\\"identifier\\\":\\\"do_11319479631000371211\\\",\\\"mimeType\\\":\\\"application/vnd.ekstep.content-collection\\\"}\",\n \"createdBy\": \"5a587cc1-e018-4859-a0a8-e842650b9d64\",\n \"creatorInfo\": \"{\\\"name\\\":\\\"N11\\\",\\\"id\\\":\\\"5a587cc1-e018-4859-a0a8-e842650b9d64\\\"}\",\n \"createdOn\": \"2021-01-21T09:22:12.577Z\",\n \"deviceId\": \"ad8ed16beb09e7c91534c2af80a1a41e\",\n \"expiresAt\": \"2021-01-21T10:26:27.591Z\"\n }\n ]\n }\n}" + }, + { + "name": "This represents the Internal Server Error", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource", + "key": "Content-Type", + "value": "application/json" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "ipsum voluptate qui" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"filters\": {\n \"resourceId\": [\n \"do_11319479631000371211\",\n \"do_11319851826161254418\",\n \"do_113107812356022272164\",\n \"do_1131983093687828481353\",\n \"do_113197939102253056140\",\n \"do_113197917149233152139\",\n \"do_113197913444737024138\",\n \"do_113197844509425664130\",\n \"do_113197903995772928137\"\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/lock/v1/list", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "lock", + "v1", + "list" + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.lock.list\",\n \"ver\": 1,\n \"ts\": \"2021-01-21T12:59:12.456Z\",\n \"params\": {\n \"resmsgid\": \"755de480-5be8-11eb-9b0c-abcfbdf41bc3\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": \"ERR_LISTING_LOCK_FAILED\",\n \"errmsg\": \"Listing lock failed\"\n },\n \"responseCode\": \"SERVER_ERROR\",\n \"result\": {}\n}" + } + ] + }, + { + "name": "Lock retire", + "request": { + "method": "DELETE", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource", + "key": "Content-Type", + "value": "application/json" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "ipsum voluptate qui" + }, + { + "key": "Accept", + "value": "application/json; charset=utf-8" + } + ], + "url": { + "raw": "{{baseUrl}}/lock/v1/retire", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "lock", + "v1", + "retire" + ] + }, + "description": "This API used to retire the Locked resource by a user or system or service.\n - The **_lock/v1/retire_** endpoint used to retire the Locked resource by a user or system or service.\n - All fields marked with an * are mandatory\n - Required fields cannot be null or empty\n - Backend Route: http://knowledge-mw-service:5000/v1/lock/retire" + }, + "response": [ + { + "name": "This represents the retire success response", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource", + "key": "Content-Type", + "value": "application/json" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "ipsum voluptate qui" + } + ], + "url": { + "raw": "{{baseUrl}}/lock/v1/retire", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "lock", + "v1", + "retire" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=utf-8" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.lock.retire\",\n \"ver\": 1,\n \"ts\": \"2021-01-21T11:07:24.864Z\",\n \"params\": {\n \"resmsgid\": \"d7549400-5bd8-11eb-9b0c-abcfbdf41bc3\",\n \"msgid\": \"eb729276-769e-8db2-477a-91548152908b\",\n \"status\": \"successful\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {}\n}" + }, + { + "name": "This represents the retire Bad Request", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource", + "key": "Content-Type", + "value": "application/json" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "ipsum voluptate qui" + } + ], + "url": { + "raw": "{{baseUrl}}/lock/v1/retire", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "lock", + "v1", + "retire" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.lock.retire\",\n \"ver\": 1,\n \"ts\": \"2021-01-21T11:08:43.618Z\",\n \"params\": {\n \"resmsgid\": \"0645ca40-5bd9-11eb-9b0c-abcfbdf41bc3\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": \"ERR_LOCK_RETIRING_FAILED\",\n \"errmsg\": \"Resource is not locked\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {}\n}" + } + ] + }, + { + "name": "Lock create", + "request": { + "method": "POST", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource", + "key": "Content-Type", + "value": "application/json" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "ipsum voluptate qui" + }, + { + "key": "Accept", + "value": "application/json; charset=utf-8" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"resourceId\": \"do_11319479631000371211\",\n \"resourceType\": \"Content\",\n \"resourceInfo\": \"{\\\"contentType\\\":\\\"TextBook\\\",\\\"framework\\\":\\\"ekstep_ncert_k-12\\\",\\\"identifier\\\":\\\"do_11319479631000371211\\\",\\\"mimeType\\\":\\\"application/vnd.ekstep.content-collection\\\"}\",\n \"creatorInfo\": \"{\\\"name\\\":\\\"N11\\\",\\\"id\\\":\\\"5a587cc1-e018-4859-a0a8-e842650b9d64\\\"}\",\n \"createdBy\": \"5a587cc1-e018-4859-a0a8-e842650b9d64\",\n \"isRootOrgAdmin\": true\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/lock/v1/create", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "lock", + "v1", + "create" + ] + }, + "description": "This API used to create the Lock resource by a user or system or service.\n - The **_lock/v1/create_** endpoint search the bulk job approve of the content.\n - All fields marked with an * are mandatory\n - Required fields cannot be null or empty\n - Backend Route: http://knowledge-mw-service:5000/v1/lock/create" + }, + "response": [ + { + "name": "This represents the create response body 200", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource", + "key": "Content-Type", + "value": "application/json" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "ipsum voluptate qui" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"resourceId\": \"do_11319479631000371211\",\n \"resourceType\": \"Content\",\n \"resourceInfo\": \"{\\\"contentType\\\":\\\"TextBook\\\",\\\"framework\\\":\\\"ekstep_ncert_k-12\\\",\\\"identifier\\\":\\\"do_11319479631000371211\\\",\\\"mimeType\\\":\\\"application/vnd.ekstep.content-collection\\\"}\",\n \"creatorInfo\": \"{\\\"name\\\":\\\"N11\\\",\\\"id\\\":\\\"5a587cc1-e018-4859-a0a8-e842650b9d64\\\"}\",\n \"createdBy\": \"5a587cc1-e018-4859-a0a8-e842650b9d64\",\n \"isRootOrgAdmin\": true\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/lock/v1/create", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "lock", + "v1", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=utf-8" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.lock.create\",\n \"ver\": 1,\n \"ts\": \"2021-01-21T11:14:46.643Z\",\n \"params\": {\n \"resmsgid\": \"dea6b430-5bd9-11eb-9b0c-abcfbdf41bc3\",\n \"msgid\": \"33e7b5d4-7662-bab6-1c06-72e71b8b8221\",\n \"status\": \"successful\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"lockKey\": \"bad55f24-8081-4d03-baab-364296062b47\",\n \"expiresAt\": \"2021-01-21T12:14:46.594Z\",\n \"expiresIn\": 60\n }\n}" + }, + { + "name": "This represents the create response Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource", + "key": "Content-Type", + "value": "application/json" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "ipsum voluptate qui" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"resourceId\": \"do_11319479631000371211\",\n \"resourceType\": \"Content\",\n \"resourceInfo\": \"{\\\"contentType\\\":\\\"TextBook\\\",\\\"framework\\\":\\\"ekstep_ncert_k-12\\\",\\\"identifier\\\":\\\"do_11319479631000371211\\\",\\\"mimeType\\\":\\\"application/vnd.ekstep.content-collection\\\"}\",\n \"creatorInfo\": \"{\\\"name\\\":\\\"N11\\\",\\\"id\\\":\\\"5a587cc1-e018-4859-a0a8-e842650b9d64\\\"}\",\n \"createdBy\": \"5a587cc1-e018-4859-a0a8-e842650b9d64\",\n \"isRootOrgAdmin\": true\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/lock/v1/create", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "lock", + "v1", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.lock.create\",\n \"ver\": 1,\n \"ts\": \"2021-01-21T13:09:44.935Z\",\n \"params\": {\n \"resmsgid\": \"ee5a8770-5be9-11eb-9b0c-abcfbdf41bc3\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": \"ERR_LOCK_CREATION_FIELDS_MISSING\",\n \"errmsg\": \"\\\"resourceInfo\\\" is required\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {}\n}" + } + ] + }, + { + "name": "Lock Refresh", + "request": { + "method": "PATCH", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource", + "key": "Content-Type", + "value": "application/json" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "ipsum voluptate qui" + }, + { + "key": "Accept", + "value": "application/json; charset=utf-8" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"resourceId\": \"do_11316513466772684811649\",\n \"resourceType\": \"Content\",\n \"lockId\": \"0e6b1631-93e6-4726-9dcf-0debdba37aa1\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/lock/v1/refresh", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "lock", + "v1", + "refresh" + ] + }, + "description": "This API used to refresh the Locked resource by a user or system or service.\n - The **_lock/v1/refresh_** endpoint refresh the Locked resource by a user or system or service.\n - All fields marked with an * are mandatory\n - Required fields cannot be null or empty\n - Backend Route: http://knowledge-mw-service:5000/v1/lock/refresh" + }, + "response": [ + { + "name": "This represents the refresh response 200", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource", + "key": "Content-Type", + "value": "application/json" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "ipsum voluptate qui" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"resourceId\": \"do_11316513466772684811649\",\n \"resourceType\": \"Content\",\n \"lockId\": \"0e6b1631-93e6-4726-9dcf-0debdba37aa1\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/lock/v1/refresh", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "lock", + "v1", + "refresh" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=utf-8" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.v1.refresh\",\n \"ver\": 1,\n \"ts\": \"2021-01-21T11:24:31.981Z\",\n \"params\": {\n \"resmsgid\": \"3b8a31d0-5bdb-11eb-9b0c-abcfbdf41bc3\",\n \"msgid\": \"3a33a280-5bdb-11eb-9b0c-abcfbdf41bc3\",\n \"status\": \"successful\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"lockKey\": \"0e6b1631-93e6-4726-9dcf-0debdba37aa1\",\n \"expiresAt\": \"2021-01-21T12:24:29.738Z\",\n \"expiresIn\": 60\n }\n}" + }, + { + "name": "This represents the refresh response for Bad Request", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource", + "key": "Content-Type", + "value": "application/json" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "ipsum voluptate qui" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"resourceId\": \"do_11316513466772684811649\",\n \"resourceType\": \"Content\",\n \"lockId\": \"0e6b1631-93e6-4726-9dcf-0debdba37aa1\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/lock/v1/refresh", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "lock", + "v1", + "refresh" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.v1.refresh\",\n \"ver\": 1,\n \"ts\": \"2021-01-21T11:27:46.193Z\",\n \"params\": {\n \"resmsgid\": \"af4c9810-5bdb-11eb-9b0c-abcfbdf41bc3\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": \"ERR_LOCK_REFRESHING_FIELDS_MISSING\",\n \"errmsg\": \"\\\"resourceId\\\" is required\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {}\n}" + } + ] + } + ], + "description": "This services explains the design of lock service and types of locks that are possible with mechanisms to lock a resource and unlock it with different possibilities.\n\nContact Support: \nEmail: [info@sunbird.org](https://desktop.postman.com/?desktopVersion=10.6.0&userId=1419050&teamId=304931)" + }, + { + "name": "Read Private Content", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "description": "The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) Represents a physical location (e.g: state) uniquely to which the user belongs.", + "key": "x-channel-id", + "value": "ea laborum" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "ea laborum" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/content/v1/private/read/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v1", + "private", + "read", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + }, + "description": "This API is associated with viewing and reading out the private and public contents on the Sunbird Platform.\n - The **_/private/read/{contentId}_** endpoint executes request for fetching the private and public contents to be viewed and read.\n - The endpoint for **Read Private Content** is `/content/v1/private/read`\n - It points to content-service - `/content/v4/private/read`\n - {...}refers to a section of reject endpoint URL path that needs to be replaced by appropriate Id.\n - You need to provide a valid content Id value in {Content Id} field of API URL.\n - It is mandatory to provide values for parameters marked with *.\n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "OK ! Successful operation.\"Read Private content\" operation was successfully executed. For all possible metadata, please [refer](https://github.com/project-sunbird/knowledge-platform/blob/master/schemas/content/1.0/schema.json)", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) Represents a physical location (e.g: state) uniquely to which the user belongs.", + "key": "x-channel-id", + "value": "ea laborum" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "url": { + "raw": "{{baseUrl}}/content/v1/private/read/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v1", + "private", + "read", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.private.read\",\n \"ver\": \"4.0\",\n \"ts\": \"2021-09-20T20:12:52Z+05:30\",\n \"params\": {\n \"resmsgid\": \"a31dbba1-c3bf-4c3d-a028-f3e5b7de4b57\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"content\": {\n \"ownershipType\": [\n \"createdBy\"\n ],\n \"code\": \"name\",\n \"interceptionPoints\": {},\n \"credentials\": {\n \"enabled\": \"No\"\n },\n \"channel\": \"abc-123\",\n \"language\": [\n \"English\"\n ],\n \"mimeType\": \"application/pdf\",\n \"idealScreenSize\": \"normal\",\n \"createdOn\": \"2021-08-30T13:11:08.485+0530\",\n \"objectType\": \"Content\",\n \"primaryCategory\": \"Learning Resource\",\n \"contentDisposition\": \"inline\",\n \"lastUpdatedOn\": \"2021-08-30T13:11:08.485+0530\",\n \"contentEncoding\": \"identity\",\n \"dialcodeRequired\": \"No\",\n \"trackable\": {\n \"enabled\": \"No\",\n \"autoBatch\": \"No\"\n },\n \"identifier\": \"do_11335549352263680012\",\n \"lastStatusChangedOn\": \"2021-08-30T13:11:08.485+0530\",\n \"audience\": [\n \"Student\"\n ],\n \"os\": [\n \"All\"\n ],\n \"visibility\": \"Private\",\n \"discussionForum\": {\n \"enabled\": \"No\"\n },\n \"mediaType\": \"content\",\n \"osId\": \"org.ekstep.quiz.app\",\n \"languageCode\": [\n \"en\"\n ],\n \"version\": 2,\n \"versionKey\": \"1630309268485\",\n \"license\": \"CC BY 4.0\",\n \"idealScreenDensity\": \"hdpi\",\n \"framework\": \"NCF\",\n \"compatibilityLevel\": 1,\n \"name\": \"Untitled Resource\",\n \"status\": \"Draft\"\n }\n }\n}" + }, + { + "name": "RESOURCE NOT FOUND,**Content Private Read** operation failed !The possible reason for failure is that you may have provided wrong content id.", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) Represents a physical location (e.g: state) uniquely to which the user belongs.", + "key": "x-channel-id", + "value": "ea laborum" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "url": { + "raw": "{{baseUrl}}/content/v1/private/read/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v1", + "private", + "read", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.private.read\",\n \"ver\": \"4.0\",\n \"ts\": \"2021-09-20T21:07:50Z+05:30\",\n \"params\": {\n \"resmsgid\": \"a8bb809b-c2e1-4749-b05b-b2cd7dc1ca49\",\n \"msgid\": null,\n \"err\": \"NOT_FOUND\",\n \"status\": \"failed\",\n \"errmsg\": \"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: do_11335549352263680013\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + }, + { + "name": "INTERNAL SERVER ERROR ! Looks like something went wrong! These errors are tracked automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) Represents a physical location (e.g: state) uniquely to which the user belongs.", + "key": "x-channel-id", + "value": "ea laborum" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "url": { + "raw": "{{baseUrl}}/content/v1/private/read/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v1", + "private", + "read", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "ea laborum", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"result\": {},\n \"id\": \"cupidatat reprehenderit dolor\",\n \"ver\": \"minim anim commodo\",\n \"ts\": \"commodo\",\n \"params\": {\n \"resmsgid\": \"pariatur repre\",\n \"msgid\": \"irure\",\n \"err\": \"consequat esse ea\",\n \"status\": \"esse\",\n \"errmsg\": \"aliqua laboris velit\"\n },\n \"responseCode\": \"reprehenderit dolor elit sunt dolore\"\n}" + } + ] + }, + { + "name": "Search Content", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "description": "The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"filters\": {\n \"objectType\": \"Content\",\n \"status\": []\n },\n \"limit\": 1\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v1/search", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v1", + "search" + ] + }, + "description": "This API is associated with Searching Object created as part of sourcing object creation on the Sunbird Platform.\n - The endpoint for **Content Search** is `/content/v1/search`\n - It points to knowledge-mw-service - `/v1/search`\n - It is mandatory to provide values for parameters marked with *.\n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "SUCCESS. The **Content Search** operation was successful!", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"filters\": {\n \"objectType\": \"Content\",\n \"status\": []\n },\n \"limit\": 1\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v1/search", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v1", + "search" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.search\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-22T06:24:45.025Z\",\n \"params\": {\n \"resmsgid\": \"62160510-441e-11eb-9b0c-abcfbdf41bc3\",\n \"msgid\": \"620ae180-441e-11eb-9b0c-abcfbdf41bc3\",\n \"status\": \"successful\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"count\": 480465,\n \"content\": [\n {\n \"code\": \"org.ekstep.jun16.story.test05\",\n \"subject\": [\n \"literacy\"\n ],\n \"flags\": [\n \"Test case\"\n ],\n \"channel\": \"in.ekstep\",\n \"downloadUrl\": \"https://ekstep-public-dev.s3-ap-south-1.amazonaws.com/ecar_files/org.ekstep.jun16.story.test05/vrgaatiil-upkrmclassroom-activities_1498725323304_org.ekstep.jun16.story.test05_1.0.ecar\",\n \"description\": \"शेर का साथी हाथी\",\n \"lastFlaggedOn\": \"2017-12-27T13:30:48.942+0000\",\n \"language\": [\n \"English\"\n ],\n \"variants\": {\n \"spine\": {\n \"ecarUrl\": \"https://ekstep-public-dev.s3-ap-south-1.amazonaws.com/ecar_files/org.ekstep.jun16.story.test05/vrgaatiil-upkrmclassroom-activities_1498725324677_org.ekstep.jun16.story.test05_1.0_spine.ecar\",\n \"size\": 851\n }\n },\n \"mimeType\": \"application/vnd.ekstep.ecml-archive\",\n \"flaggedBy\": [\n \"Test case\"\n ],\n \"idealScreenSize\": \"normal\",\n \"createdOn\": \"2017-06-29T07:44:15.875+0000\",\n \"objectType\": \"Content\",\n \"collections\": [\n \"do_11228062262625075214\"\n ],\n \"appId\": \"ekstep_portal\",\n \"contentDisposition\": \"inline\",\n \"contentEncoding\": \"gzip\",\n \"artifactUrl\": \"https://ekstep-public-dev.s3-ap-south-1.amazonaws.com/content/org.ekstep.jun16.story.test05/artifact/1485166711340_do_30102464_1498725273215.zip\",\n \"lastUpdatedOn\": \"2017-12-27T13:30:48.968+0000\",\n \"SYS_INTERNAL_LAST_UPDATED_ON\": \"2018-01-09T18:41:32.368+0000\",\n \"primaryCategory\": \"Story\",\n \"owner\": \"EkStep\",\n \"lastUpdatedBy\": \"Test case\",\n \"identifier\": \"org.ekstep.jun16.story.test05\",\n \"audience\": [\n \"Learner\"\n ],\n \"flagReasons\": [\n \"Copyright Violation\"\n ],\n \"visibility\": \"default\",\n \"os\": [\n \"All\"\n ],\n \"consumerId\": \"72e54829-6402-4cf0-888e-9b30733c1b88\",\n \"mediaType\": \"content\",\n \"osId\": \"org.ekstep.quiz.app\",\n \"graph_id\": \"domain\",\n \"nodeType\": \"DATA_NODE\",\n \"pkgVersion\": 1,\n \"versionKey\": \"1515523292368\",\n \"prevState\": \"Draft\",\n \"idealScreenDensity\": \"hdpi\",\n \"dialcodes\": [\n \"DAKDF\",\n \"FSDFDSA\"\n ],\n \"s3Key\": \"ecar_files/org.ekstep.jun16.story.test05/vrgaatiil-upkrmclassroom-activities_1498725323304_org.ekstep.jun16.story.test05_1.0.ecar\",\n \"size\": 9983654,\n \"lastPublishedOn\": \"2017-06-29T08:35:23.302+0000\",\n \"compatibilityLevel\": 1,\n \"name\": \"\\tवर्गातील उपक्रम(Classroom Activities)\",\n \"resourceType\": \"Story\",\n \"status\": \"Flagged\",\n \"node_id\": 105761\n }\n ]\n }\n}" + }, + { + "name": "BAD REQUEST. The **Content Search** operation failed. You may have missed input for a mandatory parameter - Content-Type in header", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"filters\": {\n \"objectType\": \"Content\",\n \"status\": []\n },\n \"limit\": 1\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v1/search", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v1", + "search" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.search\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-21T11:46:50.400Z\",\n \"params\": {\n \"resmsgid\": \"367e7a00-4382-11eb-9b0c-abcfbdf41bc3\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": \"ERR_CONTENT_SEARCH_FIELDS_MISSING\",\n \"errmsg\": \"Required fields for search content are missing\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {}\n}" + }, + { + "name": "'INTERNAL SERVER ERROR. We track these errors automatically and try to set it right at the earliest. Try refreshing the page. If the problem persists contact us at info@sunbird.org.'", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"filters\": {\n \"objectType\": \"Content\",\n \"status\": []\n },\n \"limit\": 1\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v1/search", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v1", + "search" + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.search\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-10T08:51:51.647Z\",\n \"params\": {\n \"resmsgid\": \"f234a6f0-3ac4-11eb-b0a2-8d5c9f561887\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"SERVER_ERROR\",\n \"result\": {}\n}" + } + ] + }, + { + "name": "Url Fetch Metadata", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "ea laborum" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"url\": \"https://github.com/project-sunbird/sunbird.org-docs\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v1/fetchmeta", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v1", + "fetchmeta" + ] + }, + "description": "This API is associated with fetching url metadata details on the Sunbird Platform.\n - The endpoint for **Create Content** is `/content/v1/fetchmeta`\n - It points to knowledge-mw-service - `/v1/url/fetchmeta`\n - It is mandatory to provide values for parameters marked with *.\n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "SUCCESS. The **URL Fetch Metadata** operation was successful!", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"url\": \"https://github.com/project-sunbird/sunbird.org-docs\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v1/fetchmeta", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v1", + "fetchmeta" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"ts\": \"2021-02-02T08:38:20.329Z\",\n \"params\": {\n \"resmsgid\": \"00edd990-6532-11eb-9dac-65b700f4c009\",\n \"msgid\": null,\n \"status\": \"successful\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"url\": \"https://github.com/project-sunbird/sunbird.org-docs\",\n \"canonical\": \"https://github.com/project-sunbird/sunbird.org-docs\",\n \"title\": \"project-sunbird/sunbird.org-docs\",\n \"image\": \"https://avatars.githubusercontent.com/u/26705730?s=400&v=4\",\n \"author\": \"\",\n \"description\": \"Repository for documentation on sunbird.org. Contribute to project-sunbird/sunbird.org-docs development by creating an account on GitHub.\",\n \"keywords\": \"\",\n \"source\": \"github.com\",\n \"og:url\": \"https://github.com/project-sunbird/sunbird.org-docs\",\n \"og:locale\": \"\",\n \"og:locale:alternate\": \"\",\n \"og:title\": \"project-sunbird/sunbird.org-docs\",\n \"og:type\": \"object\",\n \"og:description\": \"Repository for documentation on sunbird.org. Contribute to project-sunbird/sunbird.org-docs development by creating an account on GitHub.\",\n \"og:determiner\": \"\",\n \"og:site_name\": \"GitHub\",\n \"og:image\": \"https://avatars.githubusercontent.com/u/26705730?s=400&v=4\",\n \"og:image:secure_url\": \"\",\n \"og:image:type\": \"\",\n \"og:image:width\": \"\",\n \"og:image:height\": \"\",\n \"price\": \"\",\n \"priceCurrency\": \"\",\n \"availability\": \"\"\n }\n}" + }, + { + "name": "'BAD REQUEST. The **URL Fetch Metadata** operation failed. You may have missed input for a mandatory parameter.'", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"url\": \"https://github.com/project-sunbird/sunbird.org-docs\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v1/fetchmeta", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v1", + "fetchmeta" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"ts\": \"2021-02-02T08:53:31.808Z\",\n \"params\": {\n \"resmsgid\": \"20368200-6534-11eb-9dac-65b700f4c009\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": \"ERR_FETCH_URLMETA_MISSING\",\n \"errmsg\": \"Required fields for fetching url meta data are missing\"\n },\n \"responseCode\": \"CLIENT_ERROR\"\n}" + }, + { + "name": "'INTERNAL SERVER ERROR. We track these errors automatically and try to set it right at the earliest. Try refreshing the page. If the problem persists contact us at info@sunbird.org.'", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:-\n - Application/json", + "key": "Content-Type", + "value": "ea laborum" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "ea laborum" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"url\": \"https://github.com/project-sunbird/sunbird.org-docs\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v1/fetchmeta", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v1", + "fetchmeta" + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"ts\": \"2021-02-02T08:57:49.620Z\",\n \"params\": {\n \"resmsgid\": \"b9e17f40-6534-11eb-9dac-65b700f4c009\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": \"ERR_FETCH_URLMETA_FAILED\",\n \"errmsg\": \"Unable to load the url metadata\"\n }\n}" + } + ] + }, + { + "name": "Reject Flag", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "nisi amet " + }, + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-User-token", + "value": "nisi amet " + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "nisi amet " + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v1/flag/reject/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v1", + "flag", + "reject", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "nisi amet ", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + }, + "description": "This API is associated with setting Reject Flag for content under review process on the Sunbird Platform .\n - The **_/flag/reject/_**endpoint executes request for flagging content under review to \"Reject flag\", if content is found in-appropriate.\n - The endpoint for **Flag Reject Content** is `/content/v1/flag/reject`\n - It points to knowledge-mw-service - `/v1/content/flag/reject`\n - It is mandatory to provide values for parameters marked with *. \n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "OK ! Successful operation.\"Reject Flag\" operation was successfully executed.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "nisi amet " + }, + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-User-token", + "value": "nisi amet " + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "nisi amet " + } + ], + "body": { + "mode": "raw", + "raw": "{}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v1/flag/reject/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v1", + "flag", + "reject", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "nisi amet ", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"result\": {\n \"node_id\": \"ex reprehenderit magna eiusmod\",\n \"content_id\": \"adipisicing velit ullamco\",\n \"versionKey\": \"pari\"\n },\n \"id\": \"cupidatat dolore elit\",\n \"ver\": \"nostrud exercitation dolore labore minim\",\n \"ts\": \"nisi\",\n \"params\": {\n \"resmsgid\": \"ea officia voluptate\",\n \"msgid\": \"fugiat consequat\",\n \"err\": \"do non minim\",\n \"status\": \"in enim\",\n \"errmsg\": \"mollit eu quis\"\n },\n \"responseCode\": \"consectetur voluptate\"\n}" + }, + { + "name": "ERR CONTENT NOT FLAGGED! Looks like contentId is not in flagged status.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "nisi amet " + }, + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-User-token", + "value": "nisi amet " + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "nisi amet " + } + ], + "body": { + "mode": "raw", + "raw": "{}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v1/flag/reject/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v1", + "flag", + "reject", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "nisi amet ", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"result\": {},\n \"id\": \"voluptate culpa Excepteur minim\",\n \"ver\": \"Duis\",\n \"ts\": \"exercitation cupidatat aute repr\",\n \"params\": {\n \"resmsgid\": \"do Duis cupidatat nostrud\",\n \"msgid\": \"laboris veniam \",\n \"err\": \"ut ad pariatur adipisicing enim\",\n \"status\": \"sit sunt aliqua\",\n \"errmsg\": \"elit dolore irure\"\n },\n \"responseCode\": \"occaecat\"\n}" + }, + { + "name": "RESOURCE NOT FOUND,**Content Send for Flag Reject** operation failed !The possible reason for failure is that you may have provided wrong content id.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "nisi amet " + }, + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-User-token", + "value": "nisi amet " + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "nisi amet " + } + ], + "body": { + "mode": "raw", + "raw": "{}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v1/flag/reject/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v1", + "flag", + "reject", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "nisi amet ", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"result\": {},\n \"id\": \"voluptate culpa Excepteur minim\",\n \"ver\": \"Duis\",\n \"ts\": \"exercitation cupidatat aute repr\",\n \"params\": {\n \"resmsgid\": \"do Duis cupidatat nostrud\",\n \"msgid\": \"laboris veniam \",\n \"err\": \"ut ad pariatur adipisicing enim\",\n \"status\": \"sit sunt aliqua\",\n \"errmsg\": \"elit dolore irure\"\n },\n \"responseCode\": \"occaecat\"\n}" + }, + { + "name": "INTERNAL SERVER ERROR ! Looks like something went wrong! These errors are tracked automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "nisi amet " + }, + { + "description": "(Required) The Registered User ID to execute the API.", + "key": "X-Authenticated-User-token", + "value": "nisi amet " + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "nisi amet " + } + ], + "body": { + "mode": "raw", + "raw": "{}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/content/v1/flag/reject/:Content_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "content", + "v1", + "flag", + "reject", + ":Content_Id" + ], + "variable": [ + { + "key": "Content_Id", + "value": "nisi amet ", + "description": "(Required) Please append a valid Content Id To the Request URL" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"result\": {},\n \"id\": \"voluptate culpa Excepteur minim\",\n \"ver\": \"Duis\",\n \"ts\": \"exercitation cupidatat aute repr\",\n \"params\": {\n \"resmsgid\": \"do Duis cupidatat nostrud\",\n \"msgid\": \"laboris veniam \",\n \"err\": \"ut ad pariatur adipisicing enim\",\n \"status\": \"sit sunt aliqua\",\n \"errmsg\": \"elit dolore irure\"\n },\n \"responseCode\": \"occaecat\"\n}" + } + ] + } + ] + } + ] + } + ], + "variable": [ + { + "key": "baseUrl", + "value": "https://staging.sunbirded.org/api", + "type": "string" + } + ] +} \ No newline at end of file diff --git a/taxonomy-api/api-tests/Collections/Framework API(s).postman_collection.json b/taxonomy-api/api-tests/Collections/Framework API(s).postman_collection.json new file mode 100644 index 000000000..078758705 --- /dev/null +++ b/taxonomy-api/api-tests/Collections/Framework API(s).postman_collection.json @@ -0,0 +1,4507 @@ +{ + "info": { + "_postman_id": "c7518aae-df91-4f6e-818b-31f6d9d2780b", + "name": "Framework API(s)", + "description": "The Framework API resources is basically a supporting data structure which serves as a template \n- It can be used in places where the structure remains the same but only the usage changes as per the requirement.\n- These resources are used to create, update, read, search, the objects like channels, frameworks, catergories, terms\n- The Hierarchy is as: Framework-->Category-->Terms\n - Category: An abstract classification of the terms partitioned according to requirement\n - Terms: Refers to a keyword or a phrase to descibe a Concept\n- [TestCases](https://www.getpostman.com/collections/10a84f4c27c0b45f690b)", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "channel/v1", + "item": [ + { + "name": "Create Channel", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "{{apiKey}}", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"channel\": {\n \"name\": \"Channel Name\",\n \"code\": \"channel-id\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/channel/v1/create", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "channel", + "v1", + "create" + ] + }, + "description": "This API is used for creating a channel \n - The endpoint for **Create Channel** is `/channel/v1/create`\n - It points to knowledge-mw-service - `/v1/channel/create`\n - The fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [ + { + "name": "OK ! Successful operation.\"Create channel\" operation was successfully executed.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource\n Possible media types can be:- \n - application/json", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require bearer authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"channel\": {\n \"name\": \"Channel Name\",\n \"code\": \"channel-id\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/channel/v1/create", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "channel", + "v1", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.channel.create\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-14T08:27:27.810Z\",\n \"params\": {\n \"resmsgid\": \"3357da20-3de6-11eb-9ff3-67c41480f6aa\",\n \"msgid\": \"3353bb70-3de6-11eb-9ff3-67c41480f6aa\",\n \"status\": \"successful\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"channel-id\",\n \"node_id\": \"channel-id\"\n }\n}" + }, + { + "name": "BAD REQUEST. The \"Create Channel\" operation failed ! The possible reason for failure is that you may have missed providing input for a mandatory parameter or providing some wrong request.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource\n Possible media types can be:- \n - application/json", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require bearer authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"channel\": {\n \"name\": \"Channel Name\",\n \"code\": \"channel-id\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/channel/v1/create", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "channel", + "v1", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.channel.create\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-14T08:26:15.914Z\",\n \"params\": {\n \"resmsgid\": \"087d64a0-3de6-11eb-9ff3-67c41480f6aa\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": \"CONSTRAINT_VALIDATION_FAILED\",\n \"errmsg\": \"Object already exists with identifier: channel-id\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": null\n }\n}" + }, + { + "name": "INTERNAL SERVER ERROR ! Looks like something went wrong! These errors are tracked automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource\n Possible media types can be:- \n - application/json", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require bearer authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"channel\": {\n \"name\": \"Channel Name\",\n \"code\": \"channel-id\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/channel/v1/create", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "channel", + "v1", + "create" + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "cookie": [], + "body": "" + } + ] + }, + { + "name": "Get a Channel", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "{{apiKey}}", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/channel/v1/read/:ID", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "channel", + "v1", + "read", + ":ID" + ], + "variable": [ + { + "key": "ID", + "value": "Ut enim offici", + "description": "(Required) Unique identifier of the channel to get" + } + ] + }, + "description": "This API is associated with fetching a channel by a specific channel ID\n - The endpoint for **Get a Channel** is `/channel/v1/read/{ID}`\n - It points to knowledge-mw-service - `/v1/channel/read/{ID}` \n - The fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [ + { + "name": "OK ! Successful operation. **Channel Read** operation was successfully executed. \\n Please refer for different additional [metadata](https://github.com/project-sunbird/knowledge-platform/blob/release-3.5.0/schemas/content/1.0/schema.json)", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource\n Possible media types can be:- \n \n - application/json\n - multipart/form-data\n - application/x-www-form-urlencoded", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + } + ], + "url": { + "raw": "{{baseUrl}}/channel/v1/read/:ID", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "channel", + "v1", + "read", + ":ID" + ], + "variable": [ + { + "key": "ID", + "value": "Ut enim offici", + "description": "(Required) Unique identifier of the channel to get" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.channel.read\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-12-14T08:33:50ZZ\",\n \"params\": {\n \"resmsgid\": \"02c742d2-57e1-4441-aa31-0ce339c3917b\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"channel\": {\n \"identifier\": \"channel-405\",\n \"lastStatusChangedOn\": \"2020-12-14T08:27:49.490+0000\",\n \"code\": \"channel-405\",\n \"consumerId\": \"7411b6bd-89f3-40ec-98d1-229dc64ce77d\",\n \"assetAdditionalCategories\": [],\n \"autoCreateBatch\": \"Enabled\",\n \"languageCode\": [],\n \"suggested_frameworks\": [\n {\n \"identifier\": \"NCF\",\n \"code\": \"NCF\",\n \"name\": \"NCF\",\n \"objectType\": \"Framework\"\n }\n ],\n \"createdOn\": \"2020-12-14T08:27:49.490+0000\",\n \"objectType\": \"Channel\",\n \"versionKey\": \"1607934825088\",\n \"collectionPrimaryCategories\": [\n \"Content Playlist\",\n \"Course\",\n \"Digital Textbook\",\n \"Explanation Content\"\n ],\n \"contentPrimaryCategories\": [\n \"Course Assessment\",\n \"eTextbook\",\n \"Explanation Content\",\n \"Learning Resource\",\n \"Practice Question Set\",\n \"Teacher Resource\"\n ],\n \"name\": \"Channel without Default License\",\n \"lastUpdatedOn\": \"2020-12-14T08:33:45.088+0000\",\n \"defaultCourseFramework\": \"TPD\",\n \"collectionAdditionalCategories\": [\n \"Textbook\",\n \"Lesson Plan\",\n \"TV Lesson\"\n ],\n \"assetPrimaryCategories\": [\n \"Asset\",\n \"CertAsset\",\n \"Certificate Template\"\n ],\n \"contentAdditionalCategories\": [\n \"Classroom Teaching Video\",\n \"Concept Map\",\n \"Curiosity Question Set\",\n \"Experiential Resource\",\n \"Explanation Video\",\n \"Focus Spot\",\n \"Learning Outcome Definition\",\n \"Lesson Plan\",\n \"Marking Scheme Rubric\",\n \"Pedagogy Flow\",\n \"Previous Board Exam Papers\",\n \"TV Lesson\",\n \"Textbook\"\n ],\n \"status\": \"Live\",\n \"defaultFramework\": \"NCF\"\n }\n }\n}" + }, + { + "name": "RESOURCE NOT FOUND. The **Channel Read** operation failed ! The possible reason for failure is that you may have passed invalid channel id.", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource\n Possible media types can be:- \n \n - application/json\n - multipart/form-data\n - application/x-www-form-urlencoded", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + } + ], + "url": { + "raw": "{{baseUrl}}/channel/v1/read/:ID", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "channel", + "v1", + "read", + ":ID" + ], + "variable": [ + { + "key": "ID", + "value": "Ut enim offici", + "description": "(Required) Unique identifier of the channel to get" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.channel.read\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-12-14T08:36:08ZZ\",\n \"params\": {\n \"resmsgid\": \"e54ff3af-4026-420e-9412-0fcd3eba1674\",\n \"msgid\": null,\n \"err\": \"NOT_FOUND\",\n \"status\": \"failed\",\n \"errmsg\": \"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: channel-407\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + }, + { + "name": "INTERNAL SERVER ERROR ! Looks like something went wrong! These errors are tracked automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource\n Possible media types can be:- \n \n - application/json\n - multipart/form-data\n - application/x-www-form-urlencoded", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + } + ], + "url": { + "raw": "{{baseUrl}}/channel/v1/read/:ID", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "channel", + "v1", + "read", + ":ID" + ], + "variable": [ + { + "key": "ID", + "value": "Ut enim offici", + "description": "(Required) Unique identifier of the channel to get" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "cookie": [], + "body": "" + } + ] + }, + { + "name": "Update Channel", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "{{apiKey}}", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"channel\": {\n \"description\": \"Updated channel description\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/channel/v1/update/:ID", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "channel", + "v1", + "update", + ":ID" + ], + "variable": [ + { + "key": "ID", + "value": "Ut enim offici", + "description": "(Required) Unique identifier of the channel to be updated" + } + ] + }, + "description": "This API is associated with updating a specific channel\n - The endpoint for **Update Channel** is `/channel/v1/update/{ID}`\n - It points to knowledge-mw-service - `/v1/channel/update/{ID}`\n - The fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [ + { + "name": "OK ! Successful operation.\"Update channel by ID\" operation was successfully executed.", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.\n Possible media types can be:- \n - application/json\n - multipart/form-data\n - application/x-www-form-urlencoded", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"channel\": {\n \"description\": \"Updated channel description\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/channel/v1/update/:ID", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "channel", + "v1", + "update", + ":ID" + ], + "variable": [ + { + "key": "ID", + "value": "Ut enim offici", + "description": "(Required) Unique identifier of the channel to be updated" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.channel.update\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-14T08:29:24.724Z\",\n \"params\": {\n \"resmsgid\": \"79078340-3de6-11eb-9ff3-67c41480f6aa\",\n \"msgid\": \"78fef7c0-3de6-11eb-9ff3-67c41480f6aa\",\n \"status\": \"successful\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"channel-405\",\n \"node_id\": \"channel-405\"\n }\n}" + }, + { + "name": "BAD REQUEST. The **Update Channel\" operation failed ! The possible reason for failure is that you may have missed providing input for a mandatory parameter.", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.\n Possible media types can be:- \n - application/json\n - multipart/form-data\n - application/x-www-form-urlencoded", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"channel\": {\n \"description\": \"Updated channel description\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/channel/v1/update/:ID", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "channel", + "v1", + "update", + ":ID" + ], + "variable": [ + { + "key": "ID", + "value": "Ut enim offici", + "description": "(Required) Unique identifier of the channel to be updated" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"ts\": 79777367,\n \"id\": \"Lorem in dolore ex ullamco\",\n \"params\": {\n \"msgid\": \"in mollit commodo\",\n \"resmsgid\": \"fugiat deserunt Lorem\",\n \"status\": \"failed\",\n \"err\": \"ut dolor et repreh\",\n \"errmsg\": \"ut enim \"\n },\n \"ver\": \"reprehenderit laboris\",\n \"responseCode\": \"proident qui minim\",\n \"result\": {}\n}" + }, + { + "name": "RESOURCE NOT FOUND. The \"update channel by ID\" operation failed ! The possible reason for failure is that you may have passed invalid channel id.", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.\n Possible media types can be:- \n - application/json\n - multipart/form-data\n - application/x-www-form-urlencoded", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"channel\": {\n \"description\": \"Updated channel description\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/channel/v1/update/:ID", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "channel", + "v1", + "update", + ":ID" + ], + "variable": [ + { + "key": "ID", + "value": "Ut enim offici", + "description": "(Required) Unique identifier of the channel to be updated" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.channel.update\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-14T08:30:44.219Z\",\n \"params\": {\n \"resmsgid\": \"a8697cb0-3de6-11eb-9ff3-67c41480f6aa\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": \"NOT_FOUND\",\n \"errmsg\": \"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: channel-406\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + }, + { + "name": "INTERNAL SERVER ERROR ! Looks like something went wrong! These errors are tracked automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.\n Possible media types can be:- \n - application/json\n - multipart/form-data\n - application/x-www-form-urlencoded", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"channel\": {\n \"description\": \"Updated channel description\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/channel/v1/update/:ID", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "channel", + "v1", + "update", + ":ID" + ], + "variable": [ + { + "key": "ID", + "value": "Ut enim offici", + "description": "(Required) Unique identifier of the channel to be updated" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "cookie": [], + "body": "" + } + ] + } + ] + }, + { + "name": "framework/v1", + "item": [ + { + "name": "framework", + "item": [ + { + "name": "Create a Framework", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "{{apiKey}}", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "description": "(Required) It is the Unique Id to identify the root organisation to which the user belongs", + "key": "X-Channel-Id", + "value": "Ut enim offici" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"framework\": {\n \"name\": \"Framework Name\",\n \"code\": \"dummy_framework\",\n \"channels\": [\n {\n \"identifier\": \"in.ekstep\"\n }\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/create", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "create" + ] + }, + "description": "This API is associated with creating a new framework on the Sunbird Platform.\n - The endpoint for **Create a Framework** is `/framework/v1/create`\n - It points to knowledge-mw-service - `/v1/framework/create`\n - The fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [ + { + "name": "OK ! Successful operation.\"create framework\" operation was successfully executed.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource\n Possible media types can be:- \n - application/json", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + }, + { + "description": "(Required) It is the Unique Id to identify the root organisation to which the user belongs", + "key": "X-Channel-Id", + "value": "Ut enim offici" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"framework\": {\n \"name\": \"Framework Name\",\n \"code\": \"dummy_framework\",\n \"channels\": [\n {\n \"identifier\": \"in.ekstep\"\n }\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/create", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.framework.create\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-14T09:49:06.794Z\",\n \"params\": {\n \"resmsgid\": \"9b5d7ca0-3df1-11eb-9ff3-67c41480f6aa\",\n \"msgid\": \"9b4a90e0-3df1-11eb-9ff3-67c41480f6aa\",\n \"status\": \"successful\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"node_id\": \"dummy_framework\",\n \"versionKey\": \"1607939346678\"\n }\n}" + }, + { + "name": "BAD REQUEST. The \"create framework\" operation failed ! The possible reason for failure is that you may have missed providing input for a mandatory parameter.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource\n Possible media types can be:- \n - application/json", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + }, + { + "description": "(Required) It is the Unique Id to identify the root organisation to which the user belongs", + "key": "X-Channel-Id", + "value": "Ut enim offici" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"framework\": {\n \"name\": \"Framework Name\",\n \"code\": \"dummy_framework\",\n \"channels\": [\n {\n \"identifier\": \"in.ekstep\"\n }\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/create", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.framework.create\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-14T09:50:45.249Z\",\n \"params\": {\n \"resmsgid\": \"d60c8710-3df1-11eb-9ff3-67c41480f6aa\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": \"CONSTRAINT_VALIDATION_FAILED\",\n \"errmsg\": \"Object already exists with identifier: dummy_framework\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {}\n}" + }, + { + "name": "INTERNAL SERVER ERROR ! Looks like something went wrong! These errors are tracked automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource\n Possible media types can be:- \n - application/json", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + }, + { + "description": "(Required) It is the Unique Id to identify the root organisation to which the user belongs", + "key": "X-Channel-Id", + "value": "Ut enim offici" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"framework\": {\n \"name\": \"Framework Name\",\n \"code\": \"dummy_framework\",\n \"channels\": [\n {\n \"identifier\": \"in.ekstep\"\n }\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/create", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "create" + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "cookie": [], + "body": "" + } + ] + }, + { + "name": "Get a Framework", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "{{apiKey}}", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/framework/v1/read/:ID", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "read", + ":ID" + ], + "variable": [ + { + "key": "ID", + "value": "Ut enim offici", + "description": "(Required) The identifier of the framework to be fetched" + } + ] + }, + "description": "This API is associated with fetching a specific framework\n - The endpoint for **Get a Framework** is `/framework/v1/read/{ID}`\n - It points to knowledge-mw-service - `/v1/framework/read/{ID}`\n - The fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [ + { + "name": "OK ! Successful operation.\"read framework\" operation was successfully executed.", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + } + ], + "url": { + "raw": "{{baseUrl}}/framework/v1/read/:ID", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "read", + ":ID" + ], + "variable": [ + { + "key": "ID", + "value": "Ut enim offici", + "description": "(Required) The identifier of the framework to be fetched" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.framework.read\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-14T19:51:24ZZ\",\n \"params\": {\n \"resmsgid\": \"28f10a2a-ce6c-4dbe-a733-4c193013e84b\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"framework\": {\n \"identifier\": \"dummy_framework\",\n \"code\": \"Dumy framework updated\",\n \"name\": \"Framework Name\",\n \"description\": \"Dumy framework updated\",\n \"categories\": [\n {\n \"identifier\": \"dummy_framework_subject\",\n \"code\": \"subject\",\n \"terms\": [\n {\n \"identifier\": \"dummy_framework_subject_english\",\n \"code\": \"english\",\n \"translations\": null,\n \"name\": \"English\",\n \"description\": \"English\",\n \"index\": 1,\n \"category\": \"subject\",\n \"status\": \"Live\"\n }\n ],\n \"translations\": null,\n \"name\": \"Subject\",\n \"description\": \"Updated description\",\n \"index\": 1,\n \"status\": \"Live\"\n },\n {\n \"identifier\": \"dummy_framework_medium\",\n \"code\": \"medium\",\n \"translations\": null,\n \"name\": \"Medium\",\n \"description\": \"Medium\",\n \"index\": 2,\n \"status\": \"Live\"\n }\n ],\n \"type\": \"K-12\",\n \"objectType\": \"Framework\"\n }\n }\n}" + }, + { + "name": "BAD REQUEST. The \"read framework\" operation failed ! The possible reason for failure is that you may have missed providing input for a mandatory parameter.", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + } + ], + "url": { + "raw": "{{baseUrl}}/framework/v1/read/:ID", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "read", + ":ID" + ], + "variable": [ + { + "key": "ID", + "value": "Ut enim offici", + "description": "(Required) The identifier of the framework to be fetched" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"ts\": 87966182,\n \"id\": \"laboris q\",\n \"params\": {\n \"msgid\": \"do est non incididunt\",\n \"resmsgid\": \"sint Excepteur sunt\",\n \"status\": \"successful\",\n \"err\": \"nisi magna eu\",\n \"errmsg\": \"velit\"\n },\n \"ver\": \"cupidatat exercitation elit\",\n \"responseCode\": \"do et cillum\",\n \"result\": {}\n}" + }, + { + "name": "RESOURCE NOT FOUND. The \"read framework\" operation failed ! The possible reason for failure is that you may have passed invalid framework id.", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + } + ], + "url": { + "raw": "{{baseUrl}}/framework/v1/read/:ID", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "read", + ":ID" + ], + "variable": [ + { + "key": "ID", + "value": "Ut enim offici", + "description": "(Required) The identifier of the framework to be fetched" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.framework.read\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-14T10:00:12.540Z\",\n \"params\": {\n \"resmsgid\": \"282e43c0-3df3-11eb-9ff3-67c41480f6aa\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": \"ERR_DATA_NOT_FOUND\",\n \"errmsg\": \"Data not found with id : dummy_framework1\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {}\n}" + }, + { + "name": "INTERNAL SERVER ERROR ! Looks like something went wrong! These errors are tracked automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + } + ], + "url": { + "raw": "{{baseUrl}}/framework/v1/read/:ID", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "read", + ":ID" + ], + "variable": [ + { + "key": "ID", + "value": "Ut enim offici", + "description": "(Required) The identifier of the framework to be fetched" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "cookie": [], + "body": "" + } + ] + }, + { + "name": "Update Framework", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "{{apiKey}}", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "PATCH", + "header": [ + { + "description": "(Required) It is the Unique Id to identify the root organisation to which the user belongs", + "key": "X-Channel-Id", + "value": "Ut enim offici" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"framework\": {\n \"description\": \"Dumy framework updated\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/update/:ID", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "update", + ":ID" + ], + "variable": [ + { + "key": "ID", + "value": "Ut enim offici", + "description": "(Required) The identifier of the framework to be updated" + } + ] + }, + "description": "This API is associated with updating a framework\n - The endpoint for **Update Framework** is `/framework/v1/update/{ID}`\n - It points to knowledge-mw-service - `/v1/framework/update/{ID}`\n - The fieldsmarked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [ + { + "name": "OK ! Successful operation.\"update framework\" operation was successfully executed.", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource\n Possible media types can be:- \n - application/json\n - multipart/form-data\n - application/x-www-form-urlencoded", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + }, + { + "description": "(Required) It is the Unique Id to identify the root organisation to which the user belongs", + "key": "X-Channel-Id", + "value": "Ut enim offici" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"framework\": {\n \"description\": \"Dumy framework updated\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/update/:ID", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "update", + ":ID" + ], + "variable": [ + { + "key": "ID", + "value": "Ut enim offici", + "description": "(Required) The identifier of the framework to be updated" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.framework.update\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-14T09:57:20.317Z\",\n \"params\": {\n \"resmsgid\": \"c1871ed0-3df2-11eb-9ff3-67c41480f6aa\",\n \"msgid\": \"c17ff2e0-3df2-11eb-9ff3-67c41480f6aa\",\n \"status\": \"successful\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"node_id\": \"dummy_framework\",\n \"versionKey\": \"1607939840281\"\n }\n}" + }, + { + "name": "BAD REQUEST. The \"update framework\" operation failed ! The possible reason for failure is that you may have missed providing input for a mandatory parameter.", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource\n Possible media types can be:- \n - application/json\n - multipart/form-data\n - application/x-www-form-urlencoded", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + }, + { + "description": "(Required) It is the Unique Id to identify the root organisation to which the user belongs", + "key": "X-Channel-Id", + "value": "Ut enim offici" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"framework\": {\n \"description\": \"Dumy framework updated\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/update/:ID", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "update", + ":ID" + ], + "variable": [ + { + "key": "ID", + "value": "Ut enim offici", + "description": "(Required) The identifier of the framework to be updated" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"ts\": 24700756,\n \"id\": \"ut culpa\",\n \"params\": {\n \"msgid\": \"esse consequat\",\n \"resmsgid\": \"commodo aliqua magna Excepteur\",\n \"status\": \"failed\",\n \"err\": \"dolor laborum in ullamco\",\n \"errmsg\": \"laborum laboris sunt\"\n },\n \"ver\": \"laboris ut qu\",\n \"responseCode\": \"exercitation adipisicing in aliqua\",\n \"result\": {}\n}" + }, + { + "name": "RESOURCE NOT FOUND. The \"update framework\" operation failed ! The possible reason for failure is that you may have passed invalid framework id.", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource\n Possible media types can be:- \n - application/json\n - multipart/form-data\n - application/x-www-form-urlencoded", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + }, + { + "description": "(Required) It is the Unique Id to identify the root organisation to which the user belongs", + "key": "X-Channel-Id", + "value": "Ut enim offici" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"framework\": {\n \"description\": \"Dumy framework updated\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/update/:ID", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "update", + ":ID" + ], + "variable": [ + { + "key": "ID", + "value": "Ut enim offici", + "description": "(Required) The identifier of the framework to be updated" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.framework.update\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-14T09:57:50.352Z\",\n \"params\": {\n \"resmsgid\": \"d36e1900-3df2-11eb-9ff3-67c41480f6aa\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": \"ERR_FRAMEWORK_NOT_FOUND\",\n \"errmsg\": \"Framework Not Found With Id : dummy_framework1\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {}\n}" + }, + { + "name": "INTERNAL SERVER ERROR ! Looks like something went wrong! These errors are tracked automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource\n Possible media types can be:- \n - application/json\n - multipart/form-data\n - application/x-www-form-urlencoded", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + }, + { + "description": "(Required) It is the Unique Id to identify the root organisation to which the user belongs", + "key": "X-Channel-Id", + "value": "Ut enim offici" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"framework\": {\n \"description\": \"Dumy framework updated\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/update/:ID", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "update", + ":ID" + ], + "variable": [ + { + "key": "ID", + "value": "Ut enim offici", + "description": "(Required) The identifier of the framework to be updated" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "cookie": [], + "body": "" + } + ] + }, + { + "name": "List Existing Frameworks", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "{{apiKey}}", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"search\": {\n \"status\": \"Live\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/list", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "list" + ] + }, + "description": "This API is associated with listing of all existing frameworks\n - The endpoint for **List Existing Frameworks** is `/framework/v1/list`\n - It points to knowledge-mw-service - `/v1/framework/list`\n - The fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [ + { + "name": "OK ! Successful operation.\"list framework\" operation was successfully executed.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource\n Possible media types can be:- \n - application/json", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"search\": {\n \"status\": \"Live\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/list", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "list" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.framework.list\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-14T10:02:06.928Z\",\n \"params\": {\n \"resmsgid\": \"6c5c7d00-3df3-11eb-9ff3-67c41480f6aa\",\n \"msgid\": \"6c38ef70-3df3-11eb-9ff3-67c41480f6aa\",\n \"status\": \"successful\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"frameworks\": [\n {\n \"owner\": \"in.ekstep\",\n \"identifier\": \"NCERT\",\n \"code\": \"NCERT\",\n \"apoc_json\": \"{\\\"batch\\\": true}\",\n \"consumerId\": \"a6654129-b58d-4dd8-9cf2-f8f3c2f458bc\",\n \"channel\": \"in.ekstep\",\n \"description\": \"NCERT \",\n \"type\": \"K-12\",\n \"createdOn\": \"2018-01-18T17:24:50.850+0000\",\n \"apoc_text\": \"APOC\",\n \"versionKey\": \"1525253065583\",\n \"channels\": [\n {\n \"identifier\": \"in.ekstep\",\n \"name\": \"Ekstep\",\n \"objectType\": \"Channel\",\n \"relation\": \"hasSequenceMember\",\n \"description\": \"Channel for in.ekstep\",\n \"status\": \"Live\"\n }\n ],\n \"appId\": \"ekstep_portal\",\n \"name\": \"NCERT\",\n \"lastUpdatedOn\": \"2018-05-02T09:24:25.583+0000\",\n \"categories\": [\n {\n \"identifier\": \"ncert_board\",\n \"name\": \"Boards\",\n \"objectType\": \"CategoryInstance\",\n \"relation\": \"hasSequenceMember\",\n \"description\": \"\",\n \"index\": 5,\n \"status\": \"Retired\"\n }\n ],\n \"status\": \"Live\",\n \"apoc_num\": 1\n }\n ],\n \"count\": 1\n }\n}" + }, + { + "name": "BAD REQUEST. The \"list framework\" operation failed ! The possible reason for failure is that you may have missed providing input for a mandatory parameter.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource\n Possible media types can be:- \n - application/json", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"search\": {\n \"status\": \"Live\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/list", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "list" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.framework.list\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-14T18:07:11.141Z\",\n \"params\": {\n \"resmsgid\": \"2fd2f550-3e37-11eb-9ff3-67c41480f6aa\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": \"ERR_INVALID_SEARCH_REQUEST\",\n \"errmsg\": \"Invalid Search Request\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {}\n}" + }, + { + "name": "INTERNAL SERVER ERROR ! Looks like something went wrong! These errors are tracked automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource\n Possible media types can be:- \n - application/json", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"search\": {\n \"status\": \"Live\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/list", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "list" + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "cookie": [], + "body": "" + } + ] + }, + { + "name": "Publish Framework", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "{{apiKey}}", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "description": "(Required) It is the Unique Id to identify the root organisation to which the user belongs", + "key": "X-Channel-Id", + "value": "Ut enim offici" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/publish/:ID", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "publish", + ":ID" + ], + "variable": [ + { + "key": "ID", + "value": "Ut enim offici", + "description": "(Required) The identifier of the framework to be published" + } + ] + }, + "description": "This API is associated with publishing a framework\n - The endpoint for **Publish Framework** is `/framework/v1/publish/{ID}`\n - It points to knowledge-mw-service - `/v1/framework/publish/{ID}`\n - The fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [ + { + "name": "OK ! Successful operation.\"Publish framework\" operation was successfully executed.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "The Content Type entity is the media type of the resource.\n Possible media types can be:- \n - application/json", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + }, + { + "description": "(Required) It is the Unique Id to identify the root organisation to which the user belongs", + "key": "X-Channel-Id", + "value": "Ut enim offici" + } + ], + "body": { + "mode": "raw", + "raw": "{}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/publish/:ID", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "publish", + ":ID" + ], + "variable": [ + { + "key": "ID", + "value": "Ut enim offici", + "description": "(Required) The identifier of the framework to be published" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.framework.publish\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-14T10:07:54.648Z\",\n \"params\": {\n \"resmsgid\": \"3b9e5980-3df4-11eb-9ff3-67c41480f6aa\",\n \"msgid\": \"3b9cabd0-3df4-11eb-9ff3-67c41480f6aa\",\n \"status\": \"successful\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"publishStatus\": \"Publish Operation for Framework Id 'dummy_framework' Started Successfully!\"\n }\n}" + }, + { + "name": "BAD REQUEST. The \"Publish Framework\" operation failed ! The possible reason for failure is that you may have missed providing input for a mandatory parameter.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "The Content Type entity is the media type of the resource.\n Possible media types can be:- \n - application/json", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + }, + { + "description": "(Required) It is the Unique Id to identify the root organisation to which the user belongs", + "key": "X-Channel-Id", + "value": "Ut enim offici" + } + ], + "body": { + "mode": "raw", + "raw": "{}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/publish/:ID", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "publish", + ":ID" + ], + "variable": [ + { + "key": "ID", + "value": "Ut enim offici", + "description": "(Required) The identifier of the framework to be published" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.framework.publish\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-14T10:08:19.872Z\",\n \"params\": {\n \"resmsgid\": \"4aa73a00-3df4-11eb-9ff3-67c41480f6aa\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": \"ERR_INVALID_FRAMEOWRK_ID\",\n \"errmsg\": \"Invalid Framework Id. Framework doesn't exist.\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {}\n}" + }, + { + "name": "INTERNAL SERVER ERROR ! Looks like something went wrong! These errors are tracked automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "The Content Type entity is the media type of the resource.\n Possible media types can be:- \n - application/json", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + }, + { + "description": "(Required) It is the Unique Id to identify the root organisation to which the user belongs", + "key": "X-Channel-Id", + "value": "Ut enim offici" + } + ], + "body": { + "mode": "raw", + "raw": "{}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/publish/:ID", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "publish", + ":ID" + ], + "variable": [ + { + "key": "ID", + "value": "Ut enim offici", + "description": "(Required) The identifier of the framework to be published" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "cookie": [], + "body": "" + } + ] + }, + { + "name": "Copy Framework", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "{{apiKey}}", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "description": "(Required) It is the Unique Id to identify the root organisation to which the user belongs", + "key": "X-Channel-Id", + "value": "Ut enim offici" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"framework\": {\n \"code\": \"NCERTCOPY11\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/copy/:ID", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "copy", + ":ID" + ], + "variable": [ + { + "key": "ID", + "value": "Ut enim offici", + "description": "(Required) The identifier of the framework to be copied" + } + ] + }, + "description": "This API is associated with copying a framework\n - The endpoint for **Copy Framework** is `/framework/v1/copy/{ID}`\n - It points to knowledge-mw-service - `v1/framework/copy/{ID}`\n - The fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [ + { + "name": "OK ! Successful operation.\"Copy framework\" operation was successfully executed.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.\n Possible media types can be:- \n - application/json", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + }, + { + "description": "(Required) It is the Unique Id to identify the root organisation to which the user belongs", + "key": "X-Channel-Id", + "value": "Ut enim offici" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"framework\": {\n \"code\": \"NCERTCOPY11\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/copy/:ID", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "copy", + ":ID" + ], + "variable": [ + { + "key": "ID", + "value": "Ut enim offici", + "description": "(Required) The identifier of the framework to be copied" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.framework.copy\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-14T10:46:44.287Z\",\n \"params\": {\n \"resmsgid\": \"a8310cf0-3df9-11eb-9ff3-67c41480f6aa\",\n \"msgid\": \"a6b2aa50-3df9-11eb-9ff3-67c41480f6aa\",\n \"status\": \"successful\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"node_id\": \"NCERTCOPY11\"\n }\n}" + }, + { + "name": "BAD REQUEST. The \"Copy fFramework\" operation failed ! The possible reason for failure is that you may have missed providing input for a mandatory parameter.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.\n Possible media types can be:- \n - application/json", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + }, + { + "description": "(Required) It is the Unique Id to identify the root organisation to which the user belongs", + "key": "X-Channel-Id", + "value": "Ut enim offici" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"framework\": {\n \"code\": \"NCERTCOPY11\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/copy/:ID", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "copy", + ":ID" + ], + "variable": [ + { + "key": "ID", + "value": "Ut enim offici", + "description": "(Required) The identifier of the framework to be copied" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.framework.copy\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-14T10:47:22.578Z\",\n \"params\": {\n \"resmsgid\": \"bf03cb20-3df9-11eb-9ff3-67c41480f6aa\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": \"ERR_FRAMEWORK_EXISTS\",\n \"errmsg\": \"Framework with code: NCERTCOPY11, already exists.\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {}\n}" + }, + { + "name": "RESOURCE NOT FOUND. The \"Copy fFramework\" operation failed ! The possible reason for failure is that you may have missed invlalid framework id.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.\n Possible media types can be:- \n - application/json", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + }, + { + "description": "(Required) It is the Unique Id to identify the root organisation to which the user belongs", + "key": "X-Channel-Id", + "value": "Ut enim offici" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"framework\": {\n \"code\": \"NCERTCOPY11\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/copy/:ID", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "copy", + ":ID" + ], + "variable": [ + { + "key": "ID", + "value": "Ut enim offici", + "description": "(Required) The identifier of the framework to be copied" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.framework.copy\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-14T10:47:47.865Z\",\n \"params\": {\n \"resmsgid\": \"ce164890-3df9-11eb-9ff3-67c41480f6aa\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": \"ERR_DATA_NOT_FOUND\",\n \"errmsg\": \"Data not found with id : NCERT101100\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {}\n}" + }, + { + "name": "INTERNAL SERVER ERROR ! Looks like something went wrong! These errors are tracked automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.\n Possible media types can be:- \n - application/json", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + }, + { + "description": "(Required) It is the Unique Id to identify the root organisation to which the user belongs", + "key": "X-Channel-Id", + "value": "Ut enim offici" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"framework\": {\n \"code\": \"NCERTCOPY11\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/copy/:ID", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "copy", + ":ID" + ], + "variable": [ + { + "key": "ID", + "value": "Ut enim offici", + "description": "(Required) The identifier of the framework to be copied" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "cookie": [], + "body": "" + } + ] + } + ] + }, + { + "name": "category", + "item": [ + { + "name": "Create Category", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "{{apiKey}}", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"category\": {\n \"name\": \"Subject\",\n \"code\": \"subject\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/category/create?framework=Ut enim offici", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "category", + "create" + ], + "query": [ + { + "key": "framework", + "value": "Ut enim offici", + "description": "(Required) The code of the framework under which category has to be created" + } + ] + }, + "description": "This API is associated with creating a category under a framework\n - The endpoint for **Create Category** is `/framework/v1/category/create`\n - It points to knowledge-mw-service - `/v1/framework/category/create`\n - The fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [ + { + "name": "OK ! Successful operation.\"create category\" operation was successfully executed.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource\n Possible media types can be:- \n - application/json", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"category\": {\n \"name\": \"Subject\",\n \"code\": \"subject\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/category/create?framework=Ut enim offici", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "category", + "create" + ], + "query": [ + { + "key": "framework", + "value": "Ut enim offici" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.category.create\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-14T19:32:01.754Z\",\n \"params\": {\n \"resmsgid\": \"0a10dba0-3e43-11eb-9ff3-67c41480f6aa\",\n \"msgid\": \"0a05df20-3e43-11eb-9ff3-67c41480f6aa\",\n \"status\": \"successful\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"node_id\": \"dummy_framework_medium\",\n \"versionKey\": \"1607974321700\"\n }\n}" + }, + { + "name": "BAD REQUEST. The \"create category\" operation failed ! The possible reason for failure is that you may have missed providing input for a mandatory parameter.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource\n Possible media types can be:- \n - application/json", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"category\": {\n \"name\": \"Subject\",\n \"code\": \"subject\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/category/create?framework=Ut enim offici", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "category", + "create" + ], + "query": [ + { + "key": "framework", + "value": "Ut enim offici" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.category.create\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-14T19:30:50.114Z\",\n \"params\": {\n \"resmsgid\": \"df5d7620-3e42-11eb-9ff3-67c41480f6aa\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": \"CONSTRAINT_VALIDATION_FAILED\",\n \"errmsg\": \"Object already exists with identifier: dummy_framework_subject\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {}\n}" + }, + { + "name": "INTERNAL SERVER ERROR ! Looks like something went wrong! These errors are tracked automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource\n Possible media types can be:- \n - application/json", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"category\": {\n \"name\": \"Subject\",\n \"code\": \"subject\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/category/create?framework=Ut enim offici", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "category", + "create" + ], + "query": [ + { + "key": "framework", + "value": "Ut enim offici" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "cookie": [], + "body": "" + } + ] + }, + { + "name": "Fetch Category", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "{{apiKey}}", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/framework/v1/category/read/:code?framework=Ut enim offici", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "category", + "read", + ":code" + ], + "query": [ + { + "key": "framework", + "value": "Ut enim offici", + "description": "(Required) Name of the framework under which category exists. Its an abstract data structure which serves as a template for categories and terms" + } + ], + "variable": [ + { + "key": "code", + "value": "Ut enim offici", + "description": "(Required) The name/code of the category to be fetched" + } + ] + }, + "description": "This API is associated with fetching a category on Sunbird Platform\n - The endpoint for **Fetch Category** is `/framework/v1/category/read/{code}`\n - It points to knowledge-mw-service - `/v1/framework/category/read/{code}`\n - The fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [ + { + "name": "OK ! Successful operation.\"fetch category\" operation was successfully executed.", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "The Content Type entity is the media type of the resource\n Possible media types can be:- \n - application/json", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + } + ], + "url": { + "raw": "{{baseUrl}}/framework/v1/category/read/:code?framework=Ut enim offici", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "category", + "read", + ":code" + ], + "query": [ + { + "key": "framework", + "value": "Ut enim offici" + } + ], + "variable": [ + { + "key": "code", + "value": "Ut enim offici", + "description": "(Required) The name/code of the category to be fetched" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.category.read\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-14T19:34:41.261Z\",\n \"params\": {\n \"resmsgid\": \"6923b1d0-3e43-11eb-9ff3-67c41480f6aa\",\n \"msgid\": \"691e3390-3e43-11eb-9ff3-67c41480f6aa\",\n \"status\": \"successful\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"category\": {\n \"identifier\": \"dummy_framework_subject\",\n \"lastStatusChangedOn\": \"2020-12-14T19:30:32.392+0000\",\n \"code\": \"subject\",\n \"frameworks\": [\n {\n \"identifier\": \"dummy_framework\",\n \"name\": \"Framework Name\",\n \"objectType\": \"Framework\",\n \"relation\": \"hasSequenceMember\",\n \"description\": \"Dumy framework updated\",\n \"status\": \"Live\"\n }\n ],\n \"consumerId\": \"7411b6bd-89f3-40ec-98d1-229dc64ce77d\",\n \"channel\": \"in.ekstep\",\n \"description\": \"Subject\",\n \"domains\": [],\n \"createdOn\": \"2020-12-14T19:30:32.392+0000\",\n \"versionKey\": \"1607974232392\",\n \"terms\": [],\n \"appId\": \"@ignore@\",\n \"name\": \"Subject\",\n \"lastUpdatedOn\": \"2020-12-14T19:30:32.392+0000\",\n \"status\": \"Live\"\n }\n }\n}" + }, + { + "name": "BAD REQUEST. The \"fetch category\" operation failed ! The possible reason for failure is that you may have missed providing input for a mandatory parameter.", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "The Content Type entity is the media type of the resource\n Possible media types can be:- \n - application/json", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + } + ], + "url": { + "raw": "{{baseUrl}}/framework/v1/category/read/:code?framework=Ut enim offici", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "category", + "read", + ":code" + ], + "query": [ + { + "key": "framework", + "value": "Ut enim offici" + } + ], + "variable": [ + { + "key": "code", + "value": "Ut enim offici", + "description": "(Required) The name/code of the category to be fetched" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.category.read\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-14T19:35:21.540Z\",\n \"params\": {\n \"resmsgid\": \"8125c840-3e43-11eb-9ff3-67c41480f6aa\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": \"ERR_INVALID_FRAMEWORK_ID\",\n \"errmsg\": \"Invalid FrameworkId: dummy_framework1 for Categoryinstance \"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {}\n}" + }, + { + "name": "INTERNAL SERVER ERROR ! Looks like something went wrong! These errors are tracked automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "The Content Type entity is the media type of the resource\n Possible media types can be:- \n - application/json", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + } + ], + "url": { + "raw": "{{baseUrl}}/framework/v1/category/read/:code?framework=Ut enim offici", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "category", + "read", + ":code" + ], + "query": [ + { + "key": "framework", + "value": "Ut enim offici" + } + ], + "variable": [ + { + "key": "code", + "value": "Ut enim offici", + "description": "(Required) The name/code of the category to be fetched" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "cookie": [], + "body": "" + } + ] + }, + { + "name": "Update Category", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "{{apiKey}}", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"category\": {\n \"description\": \"Updated description\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/category/update/:code?framework=Ut enim offici", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "category", + "update", + ":code" + ], + "query": [ + { + "key": "framework", + "value": "Ut enim offici", + "description": "(Required) Represents the framework to which the category belongs" + } + ], + "variable": [ + { + "key": "code", + "value": "Ut enim offici", + "description": "(Required) The name/code of the category to be updated" + } + ] + }, + "description": "This API is associated with updating a category in an existing framework\n - The endpoint for **Update Category** is `/framework/v1/category/update/{code}`\n - It points to knowledge-mw-service - `/v1/framework/category/update/{code}`\n - The fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [ + { + "name": "OK ! Successful operation.\"update category\" operation was successfully executed.", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.\n Possible media types can be:- \n - application/json", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"category\": {\n \"description\": \"Updated description\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/category/update/:code?framework=Ut enim offici", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "category", + "update", + ":code" + ], + "query": [ + { + "key": "framework", + "value": "Ut enim offici" + } + ], + "variable": [ + { + "key": "code", + "value": "Ut enim offici", + "description": "(Required) The name/code of the category to be updated" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.category.update\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-14T19:37:49.082Z\",\n \"params\": {\n \"resmsgid\": \"d916e7a0-3e43-11eb-9ff3-67c41480f6aa\",\n \"msgid\": \"d90f4680-3e43-11eb-9ff3-67c41480f6aa\",\n \"status\": \"successful\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"node_id\": \"dummy_framework_subject\",\n \"versionKey\": \"1607974669043\"\n }\n}" + }, + { + "name": "BAD REQUEST. The \"update category\" operation failed ! The possible reason for failure is that you may have missed providing input for a mandatory parameter.", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.\n Possible media types can be:- \n - application/json", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"category\": {\n \"description\": \"Updated description\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/category/update/:code?framework=Ut enim offici", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "category", + "update", + ":code" + ], + "query": [ + { + "key": "framework", + "value": "Ut enim offici" + } + ], + "variable": [ + { + "key": "code", + "value": "Ut enim offici", + "description": "(Required) The name/code of the category to be updated" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.category.update\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-14T19:38:29.662Z\",\n \"params\": {\n \"resmsgid\": \"f146ebe0-3e43-11eb-9ff3-67c41480f6aa\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": \"ERR_INVALID_FRAMEWORK_ID\",\n \"errmsg\": \"Invalid FrameworkId: dummy_framework1 for Categoryinstance \"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {}\n}" + }, + { + "name": "INTERNAL SERVER ERROR ! Looks like something went wrong! These errors are tracked automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.\n Possible media types can be:- \n - application/json", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"category\": {\n \"description\": \"Updated description\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/category/update/:code?framework=Ut enim offici", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "category", + "update", + ":code" + ], + "query": [ + { + "key": "framework", + "value": "Ut enim offici" + } + ], + "variable": [ + { + "key": "code", + "value": "Ut enim offici", + "description": "(Required) The name/code of the category to be updated" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "cookie": [], + "body": "" + } + ] + }, + { + "name": "Search Categories", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "{{apiKey}}", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"search\": {}\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/category/search?framework=Ut enim offici", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "category", + "search" + ], + "query": [ + { + "key": "framework", + "value": "Ut enim offici", + "description": "(Required) Represents the framework in which category can be searched" + } + ] + }, + "description": "This API is associated with searching a category\n - The endpoint for **Search Categories** is `/framework/v1/category/search`\n - It points to knowledge-mw-service - `/v1/framework/category/search`\n - The fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [ + { + "name": "OK ! Successful operation.\"search category\" operation was successfully executed.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource\n Possible media types can be:- \n - application/json", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"search\": {}\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/category/search?framework=Ut enim offici", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "category", + "search" + ], + "query": [ + { + "key": "framework", + "value": "Ut enim offici" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.category.search\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-14T19:40:23.864Z\",\n \"params\": {\n \"resmsgid\": \"3558c380-3e44-11eb-9ff3-67c41480f6aa\",\n \"msgid\": \"3555b640-3e44-11eb-9ff3-67c41480f6aa\",\n \"status\": \"successful\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"count\": 2,\n \"categories\": [\n {\n \"identifier\": \"dummy_framework_subject\",\n \"lastStatusChangedOn\": \"2020-12-14T19:30:32.392+0000\",\n \"code\": \"subject\",\n \"frameworks\": [\n {\n \"identifier\": \"dummy_framework\",\n \"name\": \"Framework Name\",\n \"objectType\": \"Framework\",\n \"relation\": \"hasSequenceMember\",\n \"description\": \"Dumy framework updated\",\n \"status\": \"Live\"\n }\n ],\n \"consumerId\": \"7411b6bd-89f3-40ec-98d1-229dc64ce77d\",\n \"channel\": \"in.ekstep\",\n \"description\": \"Updated description\",\n \"domains\": [],\n \"createdOn\": \"2020-12-14T19:30:32.392+0000\",\n \"versionKey\": \"1607974669043\",\n \"terms\": [],\n \"appId\": \"dev.sunbird.portal\",\n \"name\": \"Subject\",\n \"lastUpdatedOn\": \"2020-12-14T19:37:49.043+0000\",\n \"status\": \"Live\"\n },\n {\n \"identifier\": \"dummy_framework_medium\",\n \"lastStatusChangedOn\": \"2020-12-14T19:32:01.700+0000\",\n \"code\": \"medium\",\n \"frameworks\": [\n {\n \"identifier\": \"dummy_framework\",\n \"name\": \"Framework Name\",\n \"objectType\": \"Framework\",\n \"relation\": \"hasSequenceMember\",\n \"description\": \"Dumy framework updated\",\n \"status\": \"Live\"\n }\n ],\n \"consumerId\": \"7411b6bd-89f3-40ec-98d1-229dc64ce77d\",\n \"channel\": \"in.ekstep\",\n \"description\": \"Medium\",\n \"domains\": [],\n \"createdOn\": \"2020-12-14T19:32:01.700+0000\",\n \"versionKey\": \"1607974321700\",\n \"terms\": [],\n \"appId\": \"@ignore@\",\n \"name\": \"Medium\",\n \"lastUpdatedOn\": \"2020-12-14T19:32:01.700+0000\",\n \"status\": \"Live\"\n }\n ]\n }\n}" + }, + { + "name": "BAD REQUEST. The \"search category\" operation failed ! The possible reason for failure is that you may have missed providing input for a mandatory parameter.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource\n Possible media types can be:- \n - application/json", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"search\": {}\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/category/search?framework=Ut enim offici", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "category", + "search" + ], + "query": [ + { + "key": "framework", + "value": "Ut enim offici" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.category.search\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-14T19:41:28.846Z\",\n \"params\": {\n \"resmsgid\": \"5c143ae0-3e44-11eb-9ff3-67c41480f6aa\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": \"ERR_INVALID_FRAMEWORK_ID\",\n \"errmsg\": \"Invalid FrameworkId: dummy_framework1 for Categoryinstance \"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {}\n}" + }, + { + "name": "INTERNAL SERVER ERROR ! Looks like something went wrong! These errors are tracked automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource\n Possible media types can be:- \n - application/json", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"search\": {}\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/category/search?framework=Ut enim offici", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "category", + "search" + ], + "query": [ + { + "key": "framework", + "value": "Ut enim offici" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "cookie": [], + "body": "" + } + ] + } + ] + }, + { + "name": "term", + "item": [ + { + "name": "Create Term", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "{{apiKey}}", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"term\": {\n \"code\": \"english\",\n \"name\": \"English\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/term/create?framework=Ut enim offici&category=Ut enim offici", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "term", + "create" + ], + "query": [ + { + "key": "framework", + "value": "Ut enim offici", + "description": "(Required) Represents the framework in which the term is to be created" + }, + { + "key": "category", + "value": "Ut enim offici", + "description": "(Required) Represents the category in which the term is to be created" + } + ] + }, + "description": "This API is associated with creating a term under a framework category\n - The endpoint for **Create Term** is `/framework/v1/term/create`\n - It points to knowledge-mw-service - `/v1/framework/term/create`\n - The fields marked with an asterisk (*) are mandatory. They cannot be null or empty.\"" + }, + "response": [ + { + "name": "OK ! Successful operation.\"create term\" operation was successfully executed.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.\n Possible media types can be:- \n - application/json", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"term\": {\n \"code\": \"english\",\n \"name\": \"English\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/term/create?framework=Ut enim offici&category=Ut enim offici", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "term", + "create" + ], + "query": [ + { + "key": "framework", + "value": "Ut enim offici" + }, + { + "key": "category", + "value": "Ut enim offici" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.term.create\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-14T19:44:02.348Z\",\n \"params\": {\n \"resmsgid\": \"b792c6c0-3e44-11eb-9ff3-67c41480f6aa\",\n \"msgid\": \"b783f9b0-3e44-11eb-9ff3-67c41480f6aa\",\n \"status\": \"successful\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"node_id\": [\n \"dummy_framework_subject_english\"\n ]\n }\n}" + }, + { + "name": "BAD REQUEST. The \"create term\" operation failed ! The possible reason for failure is that you may have missed providing input for a mandatory parameter.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.\n Possible media types can be:- \n - application/json", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"term\": {\n \"code\": \"english\",\n \"name\": \"English\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/term/create?framework=Ut enim offici&category=Ut enim offici", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "term", + "create" + ], + "query": [ + { + "key": "framework", + "value": "Ut enim offici" + }, + { + "key": "category", + "value": "Ut enim offici" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.term.create\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-14T19:44:49.263Z\",\n \"params\": {\n \"resmsgid\": \"d3896ff0-3e44-11eb-9ff3-67c41480f6aa\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": \"ERR_INVALID_CATEGORY_ID\",\n \"errmsg\": \"Please provide valid category.\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {}\n}" + }, + { + "name": "INTERNAL SERVER ERROR ! Looks like something went wrong! These errors are tracked automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.\n Possible media types can be:- \n - application/json", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"term\": {\n \"code\": \"english\",\n \"name\": \"English\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/term/create?framework=Ut enim offici&category=Ut enim offici", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "term", + "create" + ], + "query": [ + { + "key": "framework", + "value": "Ut enim offici" + }, + { + "key": "category", + "value": "Ut enim offici" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "cookie": [], + "body": "" + } + ] + }, + { + "name": "Fetch Term", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "{{apiKey}}", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/framework/v1/term/read/:code?framework=Ut enim offici&category=Ut enim offici", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "term", + "read", + ":code" + ], + "query": [ + { + "key": "framework", + "value": "Ut enim offici", + "description": "(Required) Represents the framework id in which the term can be fetched" + }, + { + "key": "category", + "value": "Ut enim offici", + "description": "(Required) Represents the category in which the term can be fetched" + } + ], + "variable": [ + { + "key": "code", + "value": "Ut enim offici", + "description": "(Required) The name/code of the term to be fetched" + } + ] + }, + "description": "This API is associated with fetching the available terms under a specific category and a framework on Sunbird Platform.\n - The endpoint for **Fetch Term** is `/framework/v1/term/read/{code}`\n - It points to knowledge-mw-service - `/v1/framework/term/read/{code}`\n - The fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [ + { + "name": "OK ! Successful operation.\"Fetch term\" operation was successfully executed.", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "The Content Type entity is the media type of the resource.\n Possible media types can be:- \n - application/json", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + } + ], + "url": { + "raw": "{{baseUrl}}/framework/v1/term/read/:code?framework=Ut enim offici&category=Ut enim offici", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "term", + "read", + ":code" + ], + "query": [ + { + "key": "framework", + "value": "Ut enim offici" + }, + { + "key": "category", + "value": "Ut enim offici" + } + ], + "variable": [ + { + "key": "code", + "value": "Ut enim offici", + "description": "(Required) The name/code of the term to be fetched" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.term.read\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-17T07:24:51.258Z\",\n \"params\": {\n \"resmsgid\": \"f380c1a0-4038-11eb-9b0c-abcfbdf41bc3\",\n \"msgid\": \"f358c740-4038-11eb-9b0c-abcfbdf41bc3\",\n \"status\": \"successful\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"term\": {\n \"associations\": [],\n \"identifier\": \"dummy_framework_subject_english\",\n \"lastStatusChangedOn\": \"2020-12-14T19:44:02.260+0000\",\n \"code\": \"english\",\n \"consumerId\": \"7411b6bd-89f3-40ec-98d1-229dc64ce77d\",\n \"channel\": \"in.ekstep\",\n \"description\": \"English\",\n \"createdOn\": \"2020-12-14T19:44:02.260+0000\",\n \"versionKey\": \"1607975299669\",\n \"children\": [],\n \"appId\": \"dev.sunbird.portal\",\n \"name\": \"English\",\n \"lastUpdatedOn\": \"2020-12-14T19:48:19.669+0000\",\n \"categories\": [\n {\n \"identifier\": \"dummy_framework_subject\",\n \"name\": \"Subject\",\n \"objectType\": \"CategoryInstance\",\n \"relation\": \"hasSequenceMember\",\n \"description\": \"Updated description\",\n \"status\": \"Live\"\n }\n ],\n \"category\": \"subject\",\n \"status\": \"Live\"\n }\n }\n}" + }, + { + "name": "BAD REQUEST. The \"Fetch term\" operation failed ! The possible reason for failure is that you may have missed providing input for a mandatory parameter.", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "The Content Type entity is the media type of the resource.\n Possible media types can be:- \n - application/json", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + } + ], + "url": { + "raw": "{{baseUrl}}/framework/v1/term/read/:code?framework=Ut enim offici&category=Ut enim offici", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "term", + "read", + ":code" + ], + "query": [ + { + "key": "framework", + "value": "Ut enim offici" + }, + { + "key": "category", + "value": "Ut enim offici" + } + ], + "variable": [ + { + "key": "code", + "value": "Ut enim offici", + "description": "(Required) The name/code of the term to be fetched" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.term.read\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-17T07:54:24.246Z\",\n \"params\": {\n \"resmsgid\": \"14494160-403d-11eb-9b0c-abcfbdf41bc3\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": \"ERR_INVALID_CATEGORY_ID\",\n \"errmsg\": \"Please provide valid category.\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {}\n}" + }, + { + "name": "INTERNAL SERVER ERROR ! Looks like something went wrong! These errors are tracked automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "The Content Type entity is the media type of the resource.\n Possible media types can be:- \n - application/json", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + } + ], + "url": { + "raw": "{{baseUrl}}/framework/v1/term/read/:code?framework=Ut enim offici&category=Ut enim offici", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "term", + "read", + ":code" + ], + "query": [ + { + "key": "framework", + "value": "Ut enim offici" + }, + { + "key": "category", + "value": "Ut enim offici" + } + ], + "variable": [ + { + "key": "code", + "value": "Ut enim offici", + "description": "(Required) The name/code of the term to be fetched" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "cookie": [], + "body": "" + } + ] + }, + { + "name": "Update Term", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "{{apiKey}}", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"term\": {\n \"name\": \"English\",\n \"description\": \"English\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/term/update{{code}}?framework=Ut enim offici&category=Ut enim offici", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "term", + "update{{code}}" + ], + "query": [ + { + "key": "framework", + "value": "Ut enim offici", + "description": "(Required) Represents the framework in which the terms is to be updated" + }, + { + "key": "category", + "value": "Ut enim offici", + "description": "(Required) Represents the category in which the term is to be updated" + } + ] + }, + "description": "This API is associated with updating a term under a category in a framework on Sunbird Platform\n - The endpoint for **Update Term** is `/framework/v1/term/update{code}`\n - It points to knowledge-mw-service - `/v1/framework/term/update/{code}`\n - The fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [ + { + "name": "OK ! Successful operation.\"update term\" operation was successfully executed.", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource\n Possible media types can be:- \n - application/json", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.'", + "key": "Authorization", + "value": "Ut enim offici" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"term\": {\n \"name\": \"English\",\n \"description\": \"English\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/term/update{{code}}?framework=Ut enim offici&category=Ut enim offici", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "term", + "update{{code}}" + ], + "query": [ + { + "key": "framework", + "value": "Ut enim offici" + }, + { + "key": "category", + "value": "Ut enim offici" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.term.update\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-14T19:48:19.754Z\",\n \"params\": {\n \"resmsgid\": \"50ffd0a0-3e45-11eb-9ff3-67c41480f6aa\",\n \"msgid\": \"50f04040-3e45-11eb-9ff3-67c41480f6aa\",\n \"status\": \"successful\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"node_id\": \"dummy_framework_subject_english\",\n \"versionKey\": \"1607975299669\"\n }\n}" + }, + { + "name": "BAD REQUEST. The \"update term\" operation failed ! The possible reason for failure is that you may have missed providing input for a mandatory parameter.", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource\n Possible media types can be:- \n - application/json", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.'", + "key": "Authorization", + "value": "Ut enim offici" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"term\": {\n \"name\": \"English\",\n \"description\": \"English\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/term/update{{code}}?framework=Ut enim offici&category=Ut enim offici", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "term", + "update{{code}}" + ], + "query": [ + { + "key": "framework", + "value": "Ut enim offici" + }, + { + "key": "category", + "value": "Ut enim offici" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.term.update\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-14T19:49:03.927Z\",\n \"params\": {\n \"resmsgid\": \"6b541470-3e45-11eb-9ff3-67c41480f6aa\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": \"ERR_INVALID_CATEGORY_ID\",\n \"errmsg\": \"Please provide valid category.\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {}\n}" + }, + { + "name": "INTERNAL SERVER ERROR ! Looks like something went wrong! These errors are tracked automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource\n Possible media types can be:- \n - application/json", + "key": "Content-Type", + "value": "Ut enim offici" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.'", + "key": "Authorization", + "value": "Ut enim offici" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"term\": {\n \"name\": \"English\",\n \"description\": \"English\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/term/update{{code}}?framework=Ut enim offici&category=Ut enim offici", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "term", + "update{{code}}" + ], + "query": [ + { + "key": "framework", + "value": "Ut enim offici" + }, + { + "key": "category", + "value": "Ut enim offici" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "cookie": [], + "body": "" + } + ] + }, + { + "name": "Search Term", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "{{apiKey}}", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {}\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/term/search?framework=Ut enim offici&category=Ut enim offici", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "term", + "search" + ], + "query": [ + { + "key": "framework", + "value": "Ut enim offici", + "description": "(Required) Represents the framework in which the term is to be searched" + }, + { + "key": "category", + "value": "Ut enim offici", + "description": "(Required) Represents the category in which the term is to be searched" + } + ] + }, + "description": "This API is associated with searching the existing terms under a category in a framework on Sunbird Platform\n - The endpoint for **Search Term** is `/framework/v1/term/search`\n - It points to knowledge-mw-service - `/v1/framework/term/search`\n - The fields marked with an asterisk (*) are mandatory. They cannot be null or empty." + }, + "response": [ + { + "name": "OK ! Successful operation.\"Search term operation was successfully executed.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {}\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/term/search?framework=Ut enim offici&category=Ut enim offici", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "term", + "search" + ], + "query": [ + { + "key": "framework", + "value": "Ut enim offici" + }, + { + "key": "category", + "value": "Ut enim offici" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.term.search\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-17T08:12:44.816Z\",\n \"params\": {\n \"resmsgid\": \"a446e900-403f-11eb-9b0c-abcfbdf41bc3\",\n \"msgid\": \"a4447800-403f-11eb-9b0c-abcfbdf41bc3\",\n \"status\": \"successful\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"terms\": [\n {\n \"associations\": [],\n \"identifier\": \"dummy_framework_subject_english\",\n \"lastStatusChangedOn\": \"2020-12-14T19:44:02.260+0000\",\n \"code\": \"english\",\n \"consumerId\": \"7411b6bd-89f3-40ec-98d1-229dc64ce77d\",\n \"channel\": \"in.ekstep\",\n \"description\": \"English\",\n \"createdOn\": \"2020-12-14T19:44:02.260+0000\",\n \"versionKey\": \"1607975299669\",\n \"children\": [],\n \"appId\": \"dev.sunbird.portal\",\n \"name\": \"English\",\n \"lastUpdatedOn\": \"2020-12-14T19:48:19.669+0000\",\n \"categories\": [\n {\n \"identifier\": \"dummy_framework_subject\",\n \"name\": \"Subject\",\n \"objectType\": \"CategoryInstance\",\n \"relation\": \"hasSequenceMember\",\n \"description\": \"Updated description\",\n \"status\": \"Live\"\n }\n ],\n \"category\": \"subject\",\n \"status\": \"Live\"\n }\n ],\n \"count\": 1\n }\n}" + }, + { + "name": "BAD REQUEST. The \"search term\" operation failed ! The possible reason for failure is that you may have missed providing input for a mandatory parameter.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {}\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/term/search?framework=Ut enim offici&category=Ut enim offici", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "term", + "search" + ], + "query": [ + { + "key": "framework", + "value": "Ut enim offici" + }, + { + "key": "category", + "value": "Ut enim offici" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.term.search\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-17T08:13:50.811Z\",\n \"params\": {\n \"resmsgid\": \"cb9cf2b0-403f-11eb-9b0c-abcfbdf41bc3\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": \"ERR_INVALID_CATEGORY_ID\",\n \"errmsg\": \"Please provide valid category.\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {}\n}" + }, + { + "name": "INTERNAL SERVER ERROR ! Looks like something went wrong! These errors are tracked automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "Ut enim offici" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {}\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/framework/v1/term/search?framework=Ut enim offici&category=Ut enim offici", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "framework", + "v1", + "term", + "search" + ], + "query": [ + { + "key": "framework", + "value": "Ut enim offici" + }, + { + "key": "category", + "value": "Ut enim offici" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "cookie": [], + "body": "" + } + ] + } + ] + } + ] + } + ], + "variable": [ + { + "key": "baseUrl", + "value": "https://staging.open-sunbird.org/api", + "type": "string" + }, + { + "key": "code", + "value": "Ut enim offici", + "type": "any", + "description": "(Required) The name/code of the term to be update" + } + ] +} \ No newline at end of file diff --git a/taxonomy-api/api-tests/Collections/Object Category API(s).postman_collection.json b/taxonomy-api/api-tests/Collections/Object Category API(s).postman_collection.json new file mode 100644 index 000000000..2acc96161 --- /dev/null +++ b/taxonomy-api/api-tests/Collections/Object Category API(s).postman_collection.json @@ -0,0 +1,1854 @@ +{ + "info": { + "_postman_id": "768d5a4c-2ca3-4918-a1ce-7851b72803d9", + "name": "Object Category API(s)", + "description": "The Object Category Definition API resources mainly allows you to set the rules for creating different type of objects for a particular category. For creating the definition for each object category, an object category has to be created. Each endpoint serves a different purpose, and thus based on your requirement you can do the following: \n- The APIs include CRUD (Create, Update, Read and Delete) operations. \n- You have to create a master category before setting the rules for the same.\n\n\nContact Support:\n Email: info@sunbird.org", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "object/category", + "item": [ + { + "name": "v1", + "item": [ + { + "name": "Create Object Category", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "{{apiKey}}", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"objectCategory\": {\n \"name\": \"Explanation Content\",\n \"description\": \"Content that can be used to Explain\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/object/category/v1/create", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "object", + "category", + "v1", + "create" + ] + }, + "description": "This API is associated with creating Object Category on the Sunbird Platform.\n - The endpoint for **Create Object Category** is `/object/category/v1/create`\n - It points to taxonomy-service - `/object/category/v4/create`\n - It is mandatory to provide values for parameters marked with *. \n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "SUCCESS. The **Create Object Category** operation was successful!", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:\n- application/json", + "key": "Content-Type", + "value": "sit" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "sit" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"objectCategory\": {\n \"name\": \"Explanation Content\",\n \"description\": \"Content that can be used to Explain\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/object/category/v1/create", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "object", + "category", + "v1", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.object.category.create\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-12-10T20:24:45ZZ\",\n \"params\": {\n \"resmsgid\": \"3be02c4b-3324-41a3-afd8-60f6be0584d2\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"obj-cat:classroom-teaching-video\"\n }\n}" + }, + { + "name": "BAD REQUEST. The **Create Object Category** operation failed. You may have missed input for a mandatory parameter.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:\n- application/json", + "key": "Content-Type", + "value": "sit" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "sit" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"objectCategory\": {\n \"name\": \"Explanation Content\",\n \"description\": \"Content that can be used to Explain\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/object/category/v1/create", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "object", + "category", + "v1", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.object.category.create\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-10T08:50:29.786Z\",\n \"params\": {\n \"resmsgid\": \"c169a7a0-3ac4-11eb-b0a2-8d5c9f561887\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": \"ERR_NAME_SET_AS_IDENTIFIER\",\n \"errmsg\": \"Name will be set as identifier\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {}\n}" + }, + { + "name": "INTERNAL SERVER ERROR. We track these errors automatically and try to set it right at the earliest. Try refreshing the page. If the problem persists contact us at info@sunbird.org.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:\n- application/json", + "key": "Content-Type", + "value": "sit" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "sit" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"objectCategory\": {\n \"name\": \"Explanation Content\",\n \"description\": \"Content that can be used to Explain\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/object/category/v1/create", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "object", + "category", + "v1", + "create" + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.object.category.create\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-10T08:51:51.647Z\",\n \"params\": {\n \"resmsgid\": \"f234a6f0-3ac4-11eb-b0a2-8d5c9f561887\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"SERVER_ERROR\",\n \"result\": {}\n}" + } + ] + }, + { + "name": "Update Object Category", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "{{apiKey}}", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"objectCategory\": {\n \"description\": \"Updated description\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/object/category/v1/update/:ObjectCategory_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "object", + "category", + "v1", + "update", + ":ObjectCategory_Id" + ], + "variable": [ + { + "key": "ObjectCategory_Id", + "value": "sit", + "description": "(Required) Please append a valid Object Category Id To the Request URL" + } + ] + }, + "description": "This API is associated with updating object category on the Sunbird Platform .\n - **_/Update/_** endpoint executes the \"Update Object Category\" request based on parameters provided as metadata in the request body.\n - The endpoint for **Update Object Category** is `/object/category/v1/update/{ObjectCategory_Id}`\n - It points to taxonomy-service - `/object/category/v4/update/{ObjectCategory_Id}`\n - {...}refers to a section of review endpoint URL path that needs to be replaced by appropriate Id.\n - It is mandatory to provide values for parameters marked with *.\n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "OK ! Successful operation. Object Category update operation was successfuly executed.", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "sit" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "sit" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"objectCategory\": {\n \"description\": \"Updated description\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/object/category/v1/update/:ObjectCategory_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "object", + "category", + "v1", + "update", + ":ObjectCategory_Id" + ], + "variable": [ + { + "key": "ObjectCategory_Id", + "value": "sit", + "description": "(Required) Please append a valid Object Category Id To the Request URL" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.object.category.update\",\n \"ver\": \"4.0\",\n \"ts\": \"2020-12-10T20:26:07ZZ\",\n \"params\": {\n \"resmsgid\": \"80aa9310-b749-411c-a13b-8d9f25af389f\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"obj-cat:classroom-teaching-video\"\n }\n}" + }, + { + "name": "BAD REQUEST. The 'Update Object Category ' operation failed ! The possible reason for failure is that you may have missed providing input for a mandatory parameter. ", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "sit" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "sit" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"objectCategory\": {\n \"description\": \"Updated description\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/object/category/v1/update/:ObjectCategory_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "object", + "category", + "v1", + "update", + ":ObjectCategory_Id" + ], + "variable": [ + { + "key": "ObjectCategory_Id", + "value": "sit", + "description": "(Required) Please append a valid Object Category Id To the Request URL" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.object.category.update\",\n \"ver\": \"4.0\",\n \"ts\": \"2020-12-10T20:27:01ZZ\",\n \"params\": {\n \"resmsgid\": \"87eb99c6-bc10-406a-a6fe-13651f546ce1\",\n \"msgid\": null,\n \"err\": \"ERROR_RESTRICTED_PROP\",\n \"status\": \"failed\",\n \"errmsg\": \"Properties in list [identifier, name, status] are not allowed in request\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": null\n }\n}" + }, + { + "name": "RESOURCE NOT FOUND,** Object Category Update** operation failed !The possible reason for failure is that you may have provided wrong object category id.", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "sit" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "sit" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"objectCategory\": {\n \"description\": \"Updated description\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/object/category/v1/update/:ObjectCategory_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "object", + "category", + "v1", + "update", + ":ObjectCategory_Id" + ], + "variable": [ + { + "key": "ObjectCategory_Id", + "value": "sit", + "description": "(Required) Please append a valid Object Category Id To the Request URL" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.object.category.update\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-12-17T20:16:23ZZ\",\n \"params\": {\n \"resmsgid\": \"34d3161a-aaa8-417e-9cbd-d77bad54441c\",\n \"msgid\": null,\n \"err\": \"NOT_FOUND\",\n \"status\": \"failed\",\n \"errmsg\": \"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: obj-cat:classroom-teaching-video1\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + }, + { + "name": "INTERNAL SERVER ERROR ! Looks like something went wrong! These errors are tracked automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "sit" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "sit" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"objectCategory\": {\n \"description\": \"Updated description\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/object/category/v1/update/:ObjectCategory_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "object", + "category", + "v1", + "update", + ":ObjectCategory_Id" + ], + "variable": [ + { + "key": "ObjectCategory_Id", + "value": "sit", + "description": "(Required) Please append a valid Object Category Id To the Request URL" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.object.category.update\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-10T08:51:51.647Z\",\n \"params\": {\n \"resmsgid\": \"f234a6f0-3ac4-11eb-b0a2-8d5c9f561887\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"SERVER_ERROR\",\n \"result\": {}\n}" + } + ] + }, + { + "name": "Read ObjectCategory", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "{{apiKey}}", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/object/category/v1/read/:ObjectCategory_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "object", + "category", + "v1", + "read", + ":ObjectCategory_Id" + ], + "variable": [ + { + "key": "ObjectCategory_Id", + "value": "sit", + "description": "(Required) Please append a valid ObjectCategory Id To the Request URL" + } + ] + }, + "description": "This API is associated with viewing and reading out the master object category on the Sunbird Platform .\n - The **_/read/{ObjectCategory_Id}_** endpoint executes request for fetching the contents to be viewed and read.\n - The endpoint for **Read Object Category** is `/object/category/v1/read/{ObjectCategory_Id}`\n - It points to taxonomy-service - `/object/category/v4/read/{ObjectCategory_Id}`\n - {...}refers to a section of reject endpoint URL path that needs to be replaced by appropriate id.\n - You need to provide a valid object category id value in {ObjectCategory Id} field of API URL.\n - It is mandatory to provide values for parameters marked with *. \n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "OK ! Successful operation.\"Read Object Category\" operation was successfully executed. For all possible metadata, please [refer](https://github.com/project-sunbird/knowledge-platform/blob/release-3.5.0/schemas/objectcategory/1.0/schema.json)", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "The Content Type entity is the media type of the resource. Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "sit" + } + ], + "url": { + "raw": "{{baseUrl}}/object/category/v1/read/:ObjectCategory_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "object", + "category", + "v1", + "read", + ":ObjectCategory_Id" + ], + "variable": [ + { + "key": "ObjectCategory_Id", + "value": "sit", + "description": "(Required) Please append a valid ObjectCategory Id To the Request URL" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.object.category.read\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-12-17T09:32:09ZZ\",\n \"params\": {\n \"resmsgid\": \"fa4ebf30-bb78-4448-9636-ee2a27bf75e5\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"objectCategory\": {\n \"identifier\": \"obj-cat:checking-account\",\n \"lastStatusChangedOn\": \"2020-12-17T09:09:55.687+0000\",\n \"consumerId\": \"7411b6bd-89f3-40ec-98d1-229dc64ce77d\",\n \"name\": \"Checking Account\",\n \"channel\": \"in.ekstep\",\n \"lastUpdatedOn\": \"2020-12-17T09:14:56.660+0000\",\n \"description\": \"Textbook Unit\",\n \"languageCode\": [],\n \"createdOn\": \"2020-12-17T09:09:55.687+0000\",\n \"objectType\": \"ObjectCategory\",\n \"versionKey\": \"1608196496660\",\n \"status\": \"Live\"\n }\n }\n}" + }, + { + "name": "RESOURCE NOT FOUND,**Object Category Read** operation failed !The possible reason for failure is that you may have provided wrong object category id.", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "The Content Type entity is the media type of the resource. Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "sit" + } + ], + "url": { + "raw": "{{baseUrl}}/object/category/v1/read/:ObjectCategory_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "object", + "category", + "v1", + "read", + ":ObjectCategory_Id" + ], + "variable": [ + { + "key": "ObjectCategory_Id", + "value": "sit", + "description": "(Required) Please append a valid ObjectCategory Id To the Request URL" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.content.read\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-10T20:37:58.223Z\",\n \"params\": {\n \"resmsgid\": \"96a71df0-3b27-11eb-b0a2-8d5c9f561887\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": \"NOT_FOUND\",\n \"errmsg\": \"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: do_1131697204035993601314\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + }, + { + "name": "INTERNAL SERVER ERROR ! Looks like something went wrong! These errors are tracked automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "The Content Type entity is the media type of the resource. Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "sit" + } + ], + "url": { + "raw": "{{baseUrl}}/object/category/v1/read/:ObjectCategory_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "object", + "category", + "v1", + "read", + ":ObjectCategory_Id" + ], + "variable": [ + { + "key": "ObjectCategory_Id", + "value": "sit", + "description": "(Required) Please append a valid ObjectCategory Id To the Request URL" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.object.category.read\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-10T08:51:51.647Z\",\n \"params\": {\n \"resmsgid\": \"f234a6f0-3ac4-11eb-b0a2-8d5c9f561887\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"SERVER_ERROR\",\n \"result\": {}\n}" + } + ] + } + ] + }, + { + "name": "definition/v1", + "item": [ + { + "name": "read", + "item": [ + { + "name": "Get Object Category Definition", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "{{apiKey}}", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"objectCategoryDefinition\": {\n \"name\": \"learning resource\",\n \"objectType\": \"Content\",\n \"channel\": \"013085024460783616158023\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/object/category/definition/v1/read", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "object", + "category", + "definition", + "v1", + "read" + ] + }, + "description": "This API is associated with creating Object Category Definition on the Sunbird Platform.\n - The endpoint for **Create Object Category Definition** is `/object/category/definition/v1/read`\n - It points to taxonomy-service - `/object/category/definition/v4/read`\n - It is mandatory to provide values for parameters marked with *. \n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "SUCCESS. The **Create Object Category Definition** operation was successful!", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource. Possible media types can be:\n - application/json", + "key": "Content-Type", + "value": "sit" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "sit" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"objectCategoryDefinition\": {\n \"name\": \"learning resource\",\n \"objectType\": \"Content\",\n \"channel\": \"013085024460783616158023\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/object/category/definition/v1/read", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "object", + "category", + "definition", + "v1", + "read" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.object.category.definition.read\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-12-14T14:01:36ZZ\",\n \"params\": {\n \"resmsgid\": \"8c94cbfa-7bb9-4d81-97f5-6cd1bdbe0ed6\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"objectCategoryDefinition\": {\n \"identifier\": \"obj-cat:learning-resource_content_013085024460783616158023\",\n \"objectMetadata\": {\n \"config\": {},\n \"schema\": {}\n },\n \"languageCode\": [],\n \"forms\": {}\n }\n }\n}" + }, + { + "name": "BAD REQUEST. The **Get Object Category Definition** operation failed. You may have missed input for a mandatory parameter.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource. Possible media types can be:\n - application/json", + "key": "Content-Type", + "value": "sit" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "sit" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"objectCategoryDefinition\": {\n \"name\": \"learning resource\",\n \"objectType\": \"Content\",\n \"channel\": \"013085024460783616158023\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/object/category/definition/v1/read", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "object", + "category", + "definition", + "v1", + "read" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.object.category.definition.read\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-12-17T19:40:33ZZ\",\n \"params\": {\n \"resmsgid\": \"e1d9d400-bbda-483f-8184-0f506a3649b4\",\n \"msgid\": null,\n \"err\": \"ERR_INVALID_REQUEST\",\n \"status\": \"failed\",\n \"errmsg\": \"Please provide required properties!\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": null\n }\n}" + }, + { + "name": "RESOURCE NOT FOUND,**ObjectCategoryDefinition Read** operation failed !The possible reason for failure is that you may have provided wrong content id.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource. Possible media types can be:\n - application/json", + "key": "Content-Type", + "value": "sit" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "sit" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"objectCategoryDefinition\": {\n \"name\": \"learning resource\",\n \"objectType\": \"Content\",\n \"channel\": \"013085024460783616158023\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/object/category/definition/v1/read", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "object", + "category", + "definition", + "v1", + "read" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.object.category.definition.read\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-10T20:37:58.223Z\",\n \"params\": {\n \"resmsgid\": \"96a71df0-3b27-11eb-b0a2-8d5c9f561887\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": \"NOT_FOUND\",\n \"errmsg\": \"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: do_1131697204035993601314\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + }, + { + "name": "INTERNAL SERVER ERROR. We track these errors automatically and try to set it right at the earliest. Try refreshing the page. If the problem persists contact us at info@sunbird.org.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource. Possible media types can be:\n - application/json", + "key": "Content-Type", + "value": "sit" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "sit" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"objectCategoryDefinition\": {\n \"name\": \"learning resource\",\n \"objectType\": \"Content\",\n \"channel\": \"013085024460783616158023\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/object/category/definition/v1/read", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "object", + "category", + "definition", + "v1", + "read" + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.object.category.definition.create\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-10T08:51:51.647Z\",\n \"params\": {\n \"resmsgid\": \"f234a6f0-3ac4-11eb-b0a2-8d5c9f561887\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"SERVER_ERROR\",\n \"result\": {}\n}" + } + ] + }, + { + "name": "Read ObjectCategoryDefinition", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "{{apiKey}}", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/object/category/definition/v1/read/:ObjectCategoryDefinition_Id?fields=sit", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "object", + "category", + "definition", + "v1", + "read", + ":ObjectCategoryDefinition_Id" + ], + "query": [ + { + "key": "fields", + "value": "sit", + "description": "List of specific properties to be fetched." + } + ], + "variable": [ + { + "key": "ObjectCategoryDefinition_Id", + "value": "sit", + "description": "(Required) Please append a valid ObjectCategoryDefinition Id To the Request URL" + } + ] + }, + "description": "This API is associated with viewing and reading out the master object category on the Sunbird Platform .\n - The **_/read/{objectCategoryDefinition_Id}_** endpoint executes request for fetching the contents to be viewed and read.\n - The endpoint for **Update Object Category Definition** is `/object/category/definition/v1/read/{ID}`\n - It points to taxonomy-service - `/object/category/definition/v4/read/{ID}`\n - {...}refers to a section of reject endpoint URL path that needs to be replaced by appropriate Id.\n - You need to provide a valid object category definition Id value in {objectCategoryDefinition_Id} field of API URL.\n - It is mandatory to provide values for parameters marked with *. \n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "OK ! Successful operation.\"Read content\" operation was successfully executed. For all possible metadata, please [refer](https://github.com/project-sunbird/knowledge-platform/blob/release-3.5.0/schemas/objectcategorydefinition/1.0/schema.json)", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "sit" + } + ], + "url": { + "raw": "{{baseUrl}}/object/category/definition/v1/read/:ObjectCategoryDefinition_Id?fields=sit", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "object", + "category", + "definition", + "v1", + "read", + ":ObjectCategoryDefinition_Id" + ], + "query": [ + { + "key": "fields", + "value": "sit" + } + ], + "variable": [ + { + "key": "ObjectCategoryDefinition_Id", + "value": "sit", + "description": "(Required) Please append a valid ObjectCategoryDefinition Id To the Request URL" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.object.category.definition.read\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-12-17T13:25:16ZZ\",\n \"params\": {\n \"resmsgid\": \"e61aee2f-8099-4978-a624-6605360218e1\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"objectCategoryDefinition\": {\n \"identifier\": \"obj-cat:checking-account\",\n \"lastStatusChangedOn\": \"2020-12-17T09:09:55.687+0000\",\n \"consumerId\": \"7411b6bd-89f3-40ec-98d1-229dc64ce77d\",\n \"name\": \"Checking Account\",\n \"channel\": \"in.ekstep\",\n \"lastUpdatedOn\": \"2020-12-17T11:15:37.740+0000\",\n \"description\": \"Textbook Unit\",\n \"createdOn\": \"2020-12-17T09:09:55.687+0000\",\n \"objectType\": \"ObjectCategory\",\n \"versionKey\": \"1608203737740\",\n \"status\": \"Live\",\n \"objectMetadata\": {\n \"config\": {},\n \"schema\": {}\n },\n \"languageCode\": [],\n \"forms\": {}\n }\n }\n}" + }, + { + "name": "RESOURCE NOT FOUND,**ObjectCategoryDefinition Read** operation failed !The possible reason for failure is that you may have provided wrong content id.", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "sit" + } + ], + "url": { + "raw": "{{baseUrl}}/object/category/definition/v1/read/:ObjectCategoryDefinition_Id?fields=sit", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "object", + "category", + "definition", + "v1", + "read", + ":ObjectCategoryDefinition_Id" + ], + "query": [ + { + "key": "fields", + "value": "sit" + } + ], + "variable": [ + { + "key": "ObjectCategoryDefinition_Id", + "value": "sit", + "description": "(Required) Please append a valid ObjectCategoryDefinition Id To the Request URL" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.object.category.definition.read\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-10T20:37:58.223Z\",\n \"params\": {\n \"resmsgid\": \"96a71df0-3b27-11eb-b0a2-8d5c9f561887\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": \"NOT_FOUND\",\n \"errmsg\": \"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: do_1131697204035993601314\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + }, + { + "name": "INTERNAL SERVER ERROR ! Looks like something went wrong! These errors are tracked automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "The Content Type entity is the media type of the resource.Possible media types can be:- \n - Application/json", + "key": "Content-Type", + "value": "sit" + } + ], + "url": { + "raw": "{{baseUrl}}/object/category/definition/v1/read/:ObjectCategoryDefinition_Id?fields=sit", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "object", + "category", + "definition", + "v1", + "read", + ":ObjectCategoryDefinition_Id" + ], + "query": [ + { + "key": "fields", + "value": "sit" + } + ], + "variable": [ + { + "key": "ObjectCategoryDefinition_Id", + "value": "sit", + "description": "(Required) Please append a valid ObjectCategoryDefinition Id To the Request URL" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.object.category.create\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-10T08:51:51.647Z\",\n \"params\": {\n \"resmsgid\": \"f234a6f0-3ac4-11eb-b0a2-8d5c9f561887\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"SERVER_ERROR\",\n \"result\": {}\n}" + } + ] + } + ] + }, + { + "name": "Create Object Category Definition", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "{{apiKey}}", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"objectCategoryDefinition\": {\n \"categoryId\": \"obj-cat:practice-question-set\",\n \"targetObjectType\": \"Content\",\n \"objectMetadata\": {\n \"config\": {},\n \"schema\": {}\n }\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/object/category/definition/v1/create", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "object", + "category", + "definition", + "v1", + "create" + ] + }, + "description": "This API is associated with creating Object Category Definition on the Sunbird Platform.\n - The endpoint for **Create Object Category Definition** is `/object/category/definition/v1/create`\n - It points to taxonomy-service - `/object/category/definition/v4/create`\n - It is mandatory to provide values for parameters marked with *. \n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "SUCCESS. The **Create Object Category Definition** operation was successful!", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource. Possible media types can be:\n - application/json", + "key": "Content-Type", + "value": "sit" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "sit" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"objectCategoryDefinition\": {\n \"categoryId\": \"obj-cat:practice-question-set\",\n \"targetObjectType\": \"Content\",\n \"objectMetadata\": {\n \"config\": {},\n \"schema\": {}\n }\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/object/category/definition/v1/create", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "object", + "category", + "definition", + "v1", + "create" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.object.category.definition.create\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-12-10T20:24:45ZZ\",\n \"params\": {\n \"resmsgid\": \"3be02c4b-3324-41a3-afd8-60f6be0584d2\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"obj-cat:explanation-content_content_all\"\n }\n}" + }, + { + "name": "BAD REQUEST. The **Create Object Category Definition** operation failed. You may have missed input for a mandatory parameter.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource. Possible media types can be:\n - application/json", + "key": "Content-Type", + "value": "sit" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "sit" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"objectCategoryDefinition\": {\n \"categoryId\": \"obj-cat:practice-question-set\",\n \"targetObjectType\": \"Content\",\n \"objectMetadata\": {\n \"config\": {},\n \"schema\": {}\n }\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/object/category/definition/v1/create", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "object", + "category", + "definition", + "v1", + "create" + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.object.category.definition.create\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-12-17T06:44:49ZZ\",\n \"params\": {\n \"resmsgid\": \"c4fa577f-6e15-4991-b251-94f7b2a897bf\",\n \"msgid\": null,\n \"err\": \"CLIENT_ERROR\",\n \"status\": \"failed\",\n \"errmsg\": \"Invalid request\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": [\n \"Invalid Props are : [xyz]\"\n ]\n }\n}" + }, + { + "name": "INTERNAL SERVER ERROR. We track these errors automatically and try to set it right at the earliest. Try refreshing the page. If the problem persists contact us at info@sunbird.org.", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource. Possible media types can be:\n - application/json", + "key": "Content-Type", + "value": "sit" + }, + { + "description": "(Required) To make use of the API, you require authorization. Raise a request to the administrator for the use of the API. You will receive the authorization key. Specify the key received, here.", + "key": "Authorization", + "value": "sit" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"objectCategoryDefinition\": {\n \"categoryId\": \"obj-cat:practice-question-set\",\n \"targetObjectType\": \"Content\",\n \"objectMetadata\": {\n \"config\": {},\n \"schema\": {}\n }\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/object/category/definition/v1/create", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "object", + "category", + "definition", + "v1", + "create" + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.object.category.definition.create\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-10T08:51:51.647Z\",\n \"params\": {\n \"resmsgid\": \"f234a6f0-3ac4-11eb-b0a2-8d5c9f561887\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"SERVER_ERROR\",\n \"result\": {}\n}" + } + ] + }, + { + "name": "Update Object Category Definition", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "{{apiKey}}", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"description\": \"Updated description\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/object/category/definition/v1/update/:ObjectCategoryDefinition_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "object", + "category", + "definition", + "v1", + "update", + ":ObjectCategoryDefinition_Id" + ], + "variable": [ + { + "key": "ObjectCategoryDefinition_Id", + "value": "sit", + "description": "(Required) Please append a valid Object Category Definition Id To the Request URL" + } + ] + }, + "description": "This API is associated with updating object category on the Sunbird Platform .\n - **_/Update/_** endpoint executes the \"Update Object Category Definition\" request based on parameters provided as metadata in the request body.\n - The endpoint for **Update Object Category Definition** is `/object/category/definition/v1/update/{ID}`\n - It points to taxonomy-service - `/object/category/definition/v4/update/{ID}`\n - {...}refers to a section of review endpoint URL path that needs to be replaced by appropriate Id.\n - It is mandatory to provide values for parameters marked with *.\n - Mandatory fields cannot be null or empty." + }, + "response": [ + { + "name": "OK ! Successful operation. Object Category Definition update operation was successfuly executed.", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:\n - Application/json", + "key": "Content-Type", + "value": "sit" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "sit" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"description\": \"Updated description\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/object/category/definition/v1/update/:ObjectCategoryDefinition_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "object", + "category", + "definition", + "v1", + "update", + ":ObjectCategoryDefinition_Id" + ], + "variable": [ + { + "key": "ObjectCategoryDefinition_Id", + "value": "sit", + "description": "(Required) Please append a valid Object Category Definition Id To the Request URL" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.object.category.definition.update\",\n \"ver\": \"4.0\",\n \"ts\": \"2020-12-10T20:26:07ZZ\",\n \"params\": {\n \"resmsgid\": \"80aa9310-b749-411c-a13b-8d9f25af389f\",\n \"msgid\": null,\n \"err\": null,\n \"status\": \"successful\",\n \"errmsg\": null\n },\n \"responseCode\": \"OK\",\n \"result\": {\n \"identifier\": \"obj-cat:explanation-content_content_all\"\n }\n}" + }, + { + "name": "BAD REQUEST. The 'Update Object Category Definition ' operation failed ! The possible reason for failure is that you may have missed providing input for a mandatory parameter. ", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:\n - Application/json", + "key": "Content-Type", + "value": "sit" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "sit" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"description\": \"Updated description\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/object/category/definition/v1/update/:ObjectCategoryDefinition_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "object", + "category", + "definition", + "v1", + "update", + ":ObjectCategoryDefinition_Id" + ], + "variable": [ + { + "key": "ObjectCategoryDefinition_Id", + "value": "sit", + "description": "(Required) Please append a valid Object Category Definition Id To the Request URL" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.object.category.definition.update\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-12-17T11:15:46ZZ\",\n \"params\": {\n \"resmsgid\": \"d2ed46be-6846-42e4-b761-c8acdff5954a\",\n \"msgid\": null,\n \"err\": \"ERROR_RESTRICTED_PROP\",\n \"status\": \"failed\",\n \"errmsg\": \"Properties in list [identifier, categoryId, targetObjectType, channel, status, objectType] are not allowed in request\"\n },\n \"responseCode\": \"CLIENT_ERROR\",\n \"result\": {\n \"messages\": null\n }\n}" + }, + { + "name": "RESOURCE NOT FOUND,** Object Category Definition Update** operation failed !The possible reason for failure is that you may have provided wrong content id.", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:\n - Application/json", + "key": "Content-Type", + "value": "sit" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "sit" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"description\": \"Updated description\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/object/category/definition/v1/update/:ObjectCategoryDefinition_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "object", + "category", + "definition", + "v1", + "update", + ":ObjectCategoryDefinition_Id" + ], + "variable": [ + { + "key": "ObjectCategoryDefinition_Id", + "value": "sit", + "description": "(Required) Please append a valid Object Category Definition Id To the Request URL" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.object.category.definition.update\",\n \"ver\": \"3.0\",\n \"ts\": \"2020-12-12T00:31:16ZZ\",\n \"params\": {\n \"resmsgid\": \"c29ffd8b-d105-4eeb-a875-deaeae2bf5b8\",\n \"msgid\": null,\n \"err\": \"NOT_FOUND\",\n \"status\": \"failed\",\n \"errmsg\": \"Error! Node(s) doesn't Exists. | [Invalid Node Id.]: do_21317015414162227213604\"\n },\n \"responseCode\": \"RESOURCE_NOT_FOUND\",\n \"result\": {\n \"messages\": null\n }\n}" + }, + { + "name": "INTERNAL SERVER ERROR ! Looks like something went wrong! These errors are tracked automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.", + "originalRequest": { + "method": "PATCH", + "header": [ + { + "description": "Added as a part of security scheme: apikey", + "key": "Authorization", + "value": "" + }, + { + "description": "(Required) The Content Type entity is the media type of the resource.Possible media types can be:\n - Application/json", + "key": "Content-Type", + "value": "sit" + }, + { + "description": "(Required) All content APIs require authorization for use. Specify the authorization key received from the administrator when placing the request for use of the API.", + "key": "Authorization", + "value": "sit" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"request\": {\n \"content\": {\n \"description\": \"Updated description\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/object/category/definition/v1/update/:ObjectCategoryDefinition_Id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "object", + "category", + "definition", + "v1", + "update", + ":ObjectCategoryDefinition_Id" + ], + "variable": [ + { + "key": "ObjectCategoryDefinition_Id", + "value": "sit", + "description": "(Required) Please append a valid Object Category Definition Id To the Request URL" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"api.object.category.create\",\n \"ver\": \"1.0\",\n \"ts\": \"2020-12-10T08:51:51.647Z\",\n \"params\": {\n \"resmsgid\": \"f234a6f0-3ac4-11eb-b0a2-8d5c9f561887\",\n \"msgid\": null,\n \"status\": \"failed\",\n \"err\": null,\n \"errmsg\": null\n },\n \"responseCode\": \"SERVER_ERROR\",\n \"result\": {}\n}" + } + ] + } + ] + } + ] + } + ], + "variable": [ + { + "key": "baseUrl", + "value": "https://staging.sunbirded.org/api", + "type": "string" + } + ] +} \ No newline at end of file From 22dec38f7d91a374f672c7e747a4869053e4ee95 Mon Sep 17 00:00:00 2001 From: Kartheek Palla Date: Mon, 30 Jan 2023 11:45:48 +0530 Subject: [PATCH 187/490] Issue #KN-754 fix: Sunbird License API collection name updated --- .../Collections/License API(s).postman_collection.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content-api/api-tests/Collections/License API(s).postman_collection.json b/content-api/api-tests/Collections/License API(s).postman_collection.json index 8a013c354..50f8db7ad 100644 --- a/content-api/api-tests/Collections/License API(s).postman_collection.json +++ b/content-api/api-tests/Collections/License API(s).postman_collection.json @@ -1,7 +1,7 @@ { "info": { "_postman_id": "fb7f316f-8f4d-4ebf-a358-e1cc9ba3e8ed", - "name": "License API(s)", + "name": "Sunbird License API(s)", "description": "The License API allows you to create licenses which can be tagged to content based on your requirements over the sunbird platform. Each endpoint serves a different purpose, so based on your requirement you can do the following: \n- The operations include CRUD (Create, Update, Read and Delete) operations\n- The URL for Sunbird License API(s) is `/license/v3`\n- The backend URL for the same is `/license/v3`. \n\n\nContact Support:\n Email: info@sunbird.org", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" }, @@ -1014,4 +1014,4 @@ "type": "string" } ] -} \ No newline at end of file +} From 6c9a27a9c1bd440e782264945e6a2837de30e3a5 Mon Sep 17 00:00:00 2001 From: Kartheek Palla Date: Mon, 6 Feb 2023 15:17:38 +0530 Subject: [PATCH 188/490] Issue KN-767 feat (doc): content service local setup using kubernetes --- kubernetes/content/Chart.yaml | 5 + .../content/content-service_application.conf | 657 ++++++++++++++++++ .../content/content-service_logback.xml | 59 ++ kubernetes/content/templates/deployment.yaml | 74 ++ kubernetes/content/values.yaml | 22 + .../taxonomy-service_application.conf | 2 +- kubernetes/taxonomy/values.yaml | 2 +- 7 files changed, 819 insertions(+), 2 deletions(-) create mode 100644 kubernetes/content/Chart.yaml create mode 100644 kubernetes/content/content-service_application.conf create mode 100644 kubernetes/content/content-service_logback.xml create mode 100644 kubernetes/content/templates/deployment.yaml create mode 100644 kubernetes/content/values.yaml diff --git a/kubernetes/content/Chart.yaml b/kubernetes/content/Chart.yaml new file mode 100644 index 000000000..dbf9d485c --- /dev/null +++ b/kubernetes/content/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: content +version: 0.1.0 diff --git a/kubernetes/content/content-service_application.conf b/kubernetes/content/content-service_application.conf new file mode 100644 index 000000000..34f18a850 --- /dev/null +++ b/kubernetes/content/content-service_application.conf @@ -0,0 +1,657 @@ +# This is the main configuration file for the application. +# https://www.playframework.com/documentation/latest/ConfigFile +# ~~~~~ +# Play uses HOCON as its configuration file format. HOCON has a number +# of advantages over other config formats, but there are two things that +# can be used when modifying settings. +# +# You can include other configuration files in this main application.conf file: +#include "extra-config.conf" +# +# You can declare variables and substitute for them: +#mykey = ${some.value} +# +# And if an environment variable exists when there is no other substitution, then +# HOCON will fall back to substituting environment variable: +#mykey = ${JAVA_HOME} + +## Akka +# https://www.playframework.com/documentation/latest/ScalaAkka#Configuration +# https://www.playframework.com/documentation/latest/JavaAkka#Configuration +# ~~~~~ +# Play uses Akka internally and exposes Akka Streams and actors in Websockets and +# other streaming HTTP responses. +akka { + # "akka.log-config-on-start" is extraordinarly useful because it log the complete + # configuration at INFO level, including defaults and overrides, so it s worth + # putting at the very top. + # + # Put the following in your conf/logback.xml file: + # + # + # + # And then uncomment this line to debug the configuration. + # + #log-config-on-start = true + default-dispatcher { + # This will be used if you have set "executor = "fork-join-executor"" + fork-join-executor { + # Min number of threads to cap factor-based parallelism number to + parallelism-min = 8 + + # The parallelism factor is used to determine thread pool size using the + # following formula: ceil(available processors * factor). Resulting size + # is then bounded by the parallelism-min and parallelism-max values. + parallelism-factor = 32.0 + + # Max number of threads to cap factor-based parallelism number to + parallelism-max = 64 + + # Setting to "FIFO" to use queue like peeking mode which "poll" or "LIFO" to use stack + # like peeking mode which "pop". + task-peeking-mode = "FIFO" + } + } + actors-dispatcher { + type = "Dispatcher" + executor = "fork-join-executor" + fork-join-executor { + parallelism-min = 8 + parallelism-factor = 32.0 + parallelism-max = 64 + } + # Throughput for default Dispatcher, set to 1 for as fair as possible + throughput = 1 + } + actor { + deployment { + /contentActor + { + router = smallest-mailbox-pool + nr-of-instances = 10 + dispatcher = actors-dispatcher + } + /channelActor + { + router = smallest-mailbox-pool + nr-of-instances = 10 + dispatcher = actors-dispatcher + } + /collectionActor + { + router = smallest-mailbox-pool + nr-of-instances = 10 + dispatcher = actors-dispatcher + } + /healthActor + { + router = smallest-mailbox-pool + nr-of-instances = 5 + dispatcher = actors-dispatcher + } + /licenseActor + { + router = smallest-mailbox-pool + nr-of-instances = 2 + dispatcher = actors-dispatcher + } + } + } +} + +## Secret key +# http://www.playframework.com/documentation/latest/ApplicationSecret +# ~~~~~ +# The secret key is used to sign Play's session cookie. +# This must be changed for production, but we don't recommend you change it in this file. +play.http.secret.key="jd5ECm/o0BXwQCe8PfZY1NoUkB9HN41QjA80p22MKyRIcP5RW4qHw8sZztCzv87M" + +## Modules +# https://www.playframework.com/documentation/latest/Modules +# ~~~~~ +# Control which modules are loaded when Play starts. Note that modules are +# the replacement for "GlobalSettings", which are deprecated in 2.5.x. +# Please see https://www.playframework.com/documentation/latest/GlobalSettings +# for more information. +# +# You can also extend Play functionality by using one of the publically available +# Play modules: https://playframework.com/documentation/latest/ModuleDirectory +play.modules { + # By default, Play will load any class called Module that is defined + # in the root package (the "app" directory), or you can define them + # explicitly below. + # If there are any built-in modules that you want to enable, you can list them here. + enabled += modules.ContentModule + + # If there are any built-in modules that you want to disable, you can list them here. + #disabled += "" +} + +## IDE +# https://www.playframework.com/documentation/latest/IDE +# ~~~~~ +# Depending on your IDE, you can add a hyperlink for errors that will jump you +# directly to the code location in the IDE in dev mode. The following line makes +# use of the IntelliJ IDEA REST interface: +#play.editor="http://localhost:63342/api/file/?file=%s&line=%s" + +## Internationalisation +# https://www.playframework.com/documentation/latest/JavaI18N +# https://www.playframework.com/documentation/latest/ScalaI18N +# ~~~~~ +# Play comes with its own i18n settings, which allow the user's preferred language +# to map through to internal messages, or allow the language to be stored in a cookie. +play.i18n { + # The application languages + langs = [ "en" ] + + # Whether the language cookie should be secure or not + #langCookieSecure = true + + # Whether the HTTP only attribute of the cookie should be set to true + #langCookieHttpOnly = true +} + +## Play HTTP settings +# ~~~~~ +play.http { + ## Router + # https://www.playframework.com/documentation/latest/JavaRouting + # https://www.playframework.com/documentation/latest/ScalaRouting + # ~~~~~ + # Define the Router object to use for this application. + # This router will be looked up first when the application is starting up, + # so make sure this is the entry point. + # Furthermore, it's assumed your route file is named properly. + # So for an application router like `my.application.Router`, + # you may need to define a router file `conf/my.application.routes`. + # Default to Routes in the root package (aka "apps" folder) (and conf/routes) + #router = my.application.Router + + ## Action Creator + # https://www.playframework.com/documentation/latest/JavaActionCreator + # ~~~~~ + #actionCreator = null + + ## ErrorHandler + # https://www.playframework.com/documentation/latest/JavaRouting + # https://www.playframework.com/documentation/latest/ScalaRouting + # ~~~~~ + # If null, will attempt to load a class called ErrorHandler in the root package, + #errorHandler = null + + ## Session & Flash + # https://www.playframework.com/documentation/latest/JavaSessionFlash + # https://www.playframework.com/documentation/latest/ScalaSessionFlash + # ~~~~~ + session { + # Sets the cookie to be sent only over HTTPS. + #secure = true + + # Sets the cookie to be accessed only by the server. + #httpOnly = true + + # Sets the max-age field of the cookie to 5 minutes. + # NOTE: this only sets when the browser will discard the cookie. Play will consider any + # cookie value with a valid signature to be a valid session forever. To implement a server side session timeout, + # you need to put a timestamp in the session and check it at regular intervals to possibly expire it. + #maxAge = 300 + + # Sets the domain on the session cookie. + #domain = "example.com" + } + + flash { + # Sets the cookie to be sent only over HTTPS. + #secure = true + + # Sets the cookie to be accessed only by the server. + #httpOnly = true + } +} + +play.server.http.idleTimeout = 60s +play.http.parser.maxDiskBuffer = 100MB +parsers.anyContent.maxLength = 100MB + +## Netty Provider +# https://www.playframework.com/documentation/latest/SettingsNetty +# ~~~~~ +play.server.netty { + # Whether the Netty wire should be logged + log.wire = true + + # If you run Play on Linux, you can use Netty's native socket transport + # for higher performance with less garbage. + transport = "native" +} + +## WS (HTTP Client) +# https://www.playframework.com/documentation/latest/ScalaWS#Configuring-WS +# ~~~~~ +# The HTTP client primarily used for REST APIs. The default client can be +# configured directly, but you can also create different client instances +# with customized settings. You must enable this by adding to build.sbt: +# +# libraryDependencies += ws // or javaWs if using java +# +play.ws { + # Sets HTTP requests not to follow 302 requests + #followRedirects = false + + # Sets the maximum number of open HTTP connections for the client. + #ahc.maxConnectionsTotal = 50 + + ## WS SSL + # https://www.playframework.com/documentation/latest/WsSSL + # ~~~~~ + ssl { + # Configuring HTTPS with Play WS does not require programming. You can + # set up both trustManager and keyManager for mutual authentication, and + # turn on JSSE debugging in development with a reload. + #debug.handshake = true + #trustManager = { + # stores = [ + # { type = "JKS", path = "exampletrust.jks" } + # ] + #} + } +} + +## Cache +# https://www.playframework.com/documentation/latest/JavaCache +# https://www.playframework.com/documentation/latest/ScalaCache +# ~~~~~ +# Play comes with an integrated cache API that can reduce the operational +# overhead of repeated requests. You must enable this by adding to build.sbt: +# +# libraryDependencies += cache +# +play.cache { +# If you want to bind several caches, you can bind the individually +#bindCaches = ["db-cache", "user-cache", "session-cache"] +} + +## Filter Configuration +# https://www.playframework.com/documentation/latest/Filters +# ~~~~~ +# There are a number of built-in filters that can be enabled and configured +# to give Play greater security. +# +play.filters { + + # Enabled filters are run automatically against Play. + # CSRFFilter, AllowedHostFilters, and SecurityHeadersFilters are enabled by default. + enabled = [filters.AccessLogFilter] + + # Disabled filters remove elements from the enabled list. + # disabled += filters.CSRFFilter + + + ## CORS filter configuration + # https://www.playframework.com/documentation/latest/CorsFilter + # ~~~~~ + # CORS is a protocol that allows web applications to make requests from the browser + # across different domains. + # NOTE: You MUST apply the CORS configuration before the CSRF filter, as CSRF has + # dependencies on CORS settings. + cors { + # Filter paths by a whitelist of path prefixes + #pathPrefixes = ["/some/path", ...] + + # The allowed origins. If null, all origins are allowed. + #allowedOrigins = ["http://www.example.com"] + + # The allowed HTTP methods. If null, all methods are allowed + #allowedHttpMethods = ["GET", "POST"] + } + + ## Security headers filter configuration + # https://www.playframework.com/documentation/latest/SecurityHeaders + # ~~~~~ + # Defines security headers that prevent XSS attacks. + # If enabled, then all options are set to the below configuration by default: + headers { + # The X-Frame-Options header. If null, the header is not set. + #frameOptions = "DENY" + + # The X-XSS-Protection header. If null, the header is not set. + #xssProtection = "1; mode=block" + + # The X-Content-Type-Options header. If null, the header is not set. + #contentTypeOptions = "nosniff" + + # The X-Permitted-Cross-Domain-Policies header. If null, the header is not set. + #permittedCrossDomainPolicies = "master-only" + + # The Content-Security-Policy header. If null, the header is not set. + #contentSecurityPolicy = "default-src 'self'" + } + + ## Allowed hosts filter configuration + # https://www.playframework.com/documentation/latest/AllowedHostsFilter + # ~~~~~ + # Play provides a filter that lets you configure which hosts can access your application. + # This is useful to prevent cache poisoning attacks. + hosts { + # Allow requests to example.com, its subdomains, and localhost:9000. + #allowed = [".example.com", "localhost:9000"] + } +} + +play.http.parser.maxMemoryBuffer = 50MB +akka.http.parsing.max-content-length = 50MB +schema.base_path="https://sunbirddevbbpublic.blob.core.windows.net/sunbird-content-staging-knowlg/schemas/local" + +# Cassandra Configuration +cassandra { + lp { + connection: "cassandra-db.knowlg-db.svc.cluster.local:9042" + } + lpa { + connection: "cassandra-db.knowlg-db.svc.cluster.local:9042" + } +} + +# Consistency Level for Multi Node Cassandra cluster +cassandra.lp.consistency.level=QUORUM + +collection { + keyspace: "dev_hierarchy_store" + cache.enable: true + image.migration.enabled: true +} + +content { + keyspace: "dev_content_store" + cache { + ttl: 86400 + enable: true + } + hierarchy { + removed_props_for_leafNodes: ["collections", "children", "usedByContent", "item_sets", "methods", "libraries", "editorState"] + } + # Added for supporting backward compatibility - remove in release-2.7.0 + tagging { + backward_enable: true + property: "subject,medium" + } + h5p { + library { + path: "https://sunbirdpublic.blob.core.windows.net/installation/h5p-standalone-1.3.4.zip" + } + } + copy { + invalid_statusList: ["Flagged","FlaggedDraft","FraggedReview","Retired", "Processing"] + origin_data: ["name", "author", "license", "organisation"] + props_to_remove: ["downloadUrl", "artifactUrl", "variants", "createdOn", "collections", "children", "lastUpdatedOn", "SYS_INTERNAL_LAST_UPDATED_ON", "versionKey", "s3Key", "status", "pkgVersion", "toc_url", "mimeTypesCount", "contentTypesCount", "leafNodesCount", "childNodes", "prevState", "lastPublishedOn", "flagReasons", "compatibilityLevel", "size", "publishChecklist", "publishComment", "LastPublishedBy", "rejectReasons", "rejectComment", "gradeLevel", "subject", "medium", "board", "topic", "purpose", "subtopic", "contentCredits", "owner", "collaborators", "creators", "contributors", "badgeAssertions", "dialcodes", "concepts", "keywords", "reservedDialcodes", "dialcodeRequired", "leafNodes", "sYS_INTERNAL_LAST_UPDATED_ON", "prevStatus", "lastPublishedBy", "streamingUrl", "boardIds", "gradeLevelIds", "subjectIds", "mediumIds", "topicsIds", "targetFWIds", "targetBoardIds", "targetGradeLevelIds", "targetSubjectIds", "targetMediumIds", "targetTopicIds", "se_boards", "se_subjects", "se_mediums", "se_gradeLevels", "se_topics", "se_FWIds", "se_boardIds", "se_subjectIds", "se_mediumIds", "se_gradeLevelIds", "se_topicIds"] + } + # Need to depreacte + media { + base.url: "https://dev.knowlg.sunbird.org" + } +} + +# Redis Configuration +redis { + host: "redis-db.knowlg-db.svc.cluster.local" + port: 6379 + maxConnections: 128 +} + + +#--Maximum Content Package File Size Limit in Bytes (50 MB) +MAX_CONTENT_PACKAGE_FILE_SIZE_LIMIT=157286400 + +#--Maximum Asset File Size Limit in Bytes (50 MB) +MAX_ASSET_FILE_SIZE_LIMIT=157286400 + +#--No of Retry While File Download Fails +RETRY_ASSET_DOWNLOAD_COUNT=1 + +#Current environment - need this property post cloud-store implementation +cloud_storage { + env: staging + content.folder: "content" + asset.folder: "assets" + artefact.folder: "artifact" + bundle.folder: "bundle" + media.folder: "media" + ecar.folder: "ecar_files" + upload.url.ttl: "54000" +} + +# Configuration +akka.request_timeout: 30 +environment.id: 20000000 +graph { + dir: "/data/graphDB" + ids: ["domain"] + passport.key.base: "jd5ECm/o0BXwQCe8PfZY1NoUkB9HN41QjA80p22MKyRIcP5RW4qHw8sZztCzv87M" +} +route { + domain: "bolt://neo4j-db.knowlg-db.svc.cluster.local:7687" + all: "bolt://neo4j-db.knowlg-db.svc.cluster.local:8687" + bolt { + write { + domain: "bolt://neo4j-db.knowlg-db.svc.cluster.local:7687" + all: "bolt://neo4j-db.knowlg-db.svc.cluster.local:8687" + }, + read { + domain: "bolt://neo4j-db.knowlg-db.svc.cluster.local:7687" + all: "bolt://neo4j-db.knowlg-db.svc.cluster.local:8687" + } + } +} + +shard.id: 1 +platform { + auth.check.enabled: false + cache.ttl: 3600000 + language.codes: ["as","bn","en","gu","hi","hoc","jun","ka","mai","mr","unx","or","san","sat","ta","te","urd", "pj"] +} + +#Top N Config for Search Telemetry +telemetry_env: staging + +installation.id: ekstep + +channel { + default: "in.ekstep" + fetch.suggested_frameworks: true +} + +languageCode { + assamese : "as" + bengali : "bn" + english : "en" + gujarati : "gu" + hindi : "hi" + kannada : "ka" + marathi : "mr" + odia : "or" + tamil : "ta" + telugu : "te" +} +# Need to depreacte +composite { + search { + url : "http://search-service.knowlg.svc.cluster.local:9000/v3/search" + } +} + +cloud_storage_type: "azure" +cloud_storage_key: "" +cloud_storage_secret: "" +cloud_storage_endpoint: "" +cloud_storage_container: "" + +# Google Drive APIKEY +learning_content_drive_apiKey = "" + +#Youtube Standard Licence Validation +learning.content.youtube.application.name="" +learning_content_youtube_apikey="" +youtube.license.regex.pattern=["\\?vi?=([^&]*)", "watch\\?.*v=([^&]*)", "(?:embed|vi?)/([^/?]*)","^([A-Za-z0-9\\-\\_]*)"] +learning.valid_license=["creativeCommon"] + +kafka { + urls : "kafka.knowlg-db.svc.cluster.local:9092" + topic.send.enable : true + topics.instruction : "" + publish.request.topic : "" +} + +# Need to depreacte +# DIAL Link Config +dial_service { + api { + base_url : "http://dial-service.knowlg.svc.cluster.local:9000" + auth_key : "" + search : "/dialcode/v3/search" + generate : "/dialcode/v3/generate" + } +} +# Need to depreacte +reserve_dialcode { + mimeType : ["application/vnd.ekstep.content-collection"] + max_count : 250 +} + +content.link_dialcode.validation=true +content.link_dialcode.max_limit=10 + +# Content Import API Config +import { + request_size_limit : 1000 + output_topic_name : "sunbirdstaging.knowlg.auto.creation.job.request" + required_props : ["name","code","mimeType","primaryCategory","artifactUrl","framework"] + remove_props : ["downloadUrl","variants","previewUrl","streamingUrl","itemSets","level1Name","level1Concept","level2Name","level2Concept","level3Name","level3Concept","me_totalPlaySessionCount","me_totalTimeSpentInSec","me_totalSessionsCount","me_totalTimespent","me_totalInteractions","me_creationSessions","me_creationTimespent","me_averageInteractionsPerMin","me_averageSessionsPerDevice","me_totalDevices","me_averageTimespentPerSession","me_averageRating","me_totalDownloads","me_totalSideloads","me_totalRatings","me_totalComments","me_totalDialcode","me_totalDialcodeLinkedToContent","me_totalDialcodeAttached","me_hierarchyLevel","origin","originData","contentPolicyCheck","questions"] +} + +# Need to depreacte +contentTypeToPrimaryCategory { + ClassroomTeachingVideo: "Explanation Content" + ConceptMap: "Learning Resource" + Course: "Course" + CuriosityQuestionSet: "Practice Question Set" + eTextBook: "eTextbook" + ExperientialResource: "Learning Resource" + ExplanationResource: "Explanation Content" + ExplanationVideo: "Explanation Content" + FocusSpot: "Teacher Resource" + LearningOutcomeDefinition: "Teacher Resource" + MarkingSchemeRubric: "Teacher Resource" + PedagogyFlow: "Teacher Resource" + PracticeQuestionSet: "Practice Question Set" + PracticeResource: "Practice Question Set" + SelfAssess: "Course Assessment" + TeachingMethod: "Teacher Resource" + TextBook: "Digital Textbook" + Collection: "Content Playlist" + ExplanationReadingMaterial: "Learning Resource" + LearningActivity: "Learning Resource" + LessonPlan: "Content Playlist" + LessonPlanResource: "Teacher Resource" + PreviousBoardExamPapers: "Learning Resource" + TVLesson: "Explanation Content" + OnboardingResource: "Learning Resource" + ReadingMaterial: "Learning Resource" + Template: "Template" + Asset: "Asset" + Plugin: "Plugin" + LessonPlanUnit: "Lesson Plan Unit" + CourseUnit: "Course Unit" + TextBookUnit: "Textbook Unit" + Asset: "Certificate Template" +} +# Need to depreacte +resourceTypeToPrimaryCategory { + Learn: "Learning Resource" + Read: "Learning Resource" + Practice: "Learning Resource" + Teach: "Teacher Resource" + Test: "Learning Resource" + Experiment: "Learning Resource" + LessonPlan: "Teacher Resource" +} +# Need to depreacte +mimeTypeToPrimaryCategory { + "application/vnd.ekstep.h5p-archive": ["Learning Resource"] + "application/vnd.ekstep.html-archive": ["Learning Resource"] + "application/vnd.android.package-archive": ["Learning Resource"] + "video/webm": ["Explanation Content"] + "video/x-youtube": ["Explanation Content"] + "video/mp4": ["Explanation Content"] + "application/pdf": ["Learning Resource", "Teacher Resource"] + "application/epub": ["Learning Resource", "Teacher Resource"] + "application/vnd.ekstep.ecml-archive": ["Learning Resource", "Teacher Resource"] + "text/x-url": ["Learnin Resource", "Teacher Resource"] +} + +objectcategorydefinition.keyspace="dev_category_store" + +# Need to depreacte +#Default objectCategory mapping for channel +channel { + content{ + primarycategories=["Course Assessment", "eTextbook", "Explanation Content", "Learning Resource", "Practice Question Set", "Teacher Resource", "Exam Question"] + additionalcategories= ["Classroom Teaching Video", "Concept Map", "Curiosity Question Set", "Experiential Resource", "Explanation Video", "Focus Spot", "Learning Outcome Definition", "Lesson Plan", "Marking Scheme Rubric", "Pedagogy Flow", "Previous Board Exam Papers", "TV Lesson", "Textbook"] + } + collection { + primarycategories=["Content Playlist", "Course", "Digital Textbook", "Question paper"] + additionalcategories=["Textbook", "Lesson Plan"] + } + asset { + primarycategories=["Asset", "CertAsset", "Certificate Template"] + additionalcategories=[] + } +} +master.category.validation.enabled="Yes" + +# Need to depreacte +#Collection CSV +sunbird_dialcode_search_api="http://dial-service.knowlg.svc.cluster.local:9000/dialcode/v3/list" +framework_read_api_url="http://localhost:8080/learning-service/framework/v3/read" +sunbird_link_dial_code_api="http://content-service.knowlg.svc.cluster.local:9000/collection/v4/dialcode/link" + +# Need to depreacte +collection { + csv { + maxRows = 6500 + allowedContentTypes = ["TextBook","Collection","Course"] + maxFirstLevelUnits=30 + ttl = 86400 + maxUnitFieldLength=120 + maxDescFieldLength=1500 + contentTypeToUnitType = {"TextBook": "TextBookUnit", "Course": "CourseUnit", "Collection":"Collection"} + headers { + folderIdentifier = ["Folder Identifier"] + hierarchy = ["Level 1 Folder","Level 2 Folder","Level 3 Folder","Level 4 Folder"] + QR = ["QR Code Required?","QR Code"] + topics = ["Mapped Topics"] + collectionName = ["Collection Name"] + linkedContents = ["Linked Content 1","Linked Content 2","Linked Content 3","Linked Content 4","Linked Content 5","Linked Content 6","Linked Content 7","Linked Content 8","Linked Content 9","Linked Content 10","Linked Content 11","Linked Content 12","Linked Content 13","Linked Content 14","Linked Content 15","Linked Content 16","Linked Content 17","Linked Content 18","Linked Content 19","Linked Content 20","Linked Content 21","Linked Content 22","Linked Content 23","Linked Content 24","Linked Content 25","Linked Content 26","Linked Content 27","Linked Content 28","Linked Content 29","Linked Content 30"] + output = ["Collection Name","Folder Identifier","Level 1 Folder","Level 2 Folder","Level 3 Folder","Level 4 Folder","Description","Mapped Topics","Keywords","QR Code Required?","QR Code","Linked Content 1","Linked Content 2","Linked Content 3","Linked Content 4","Linked Content 5","Linked Content 6","Linked Content 7","Linked Content 8","Linked Content 9","Linked Content 10","Linked Content 11","Linked Content 12","Linked Content 13","Linked Content 14","Linked Content 15","Linked Content 16","Linked Content 17","Linked Content 18","Linked Content 19","Linked Content 20","Linked Content 21","Linked Content 22","Linked Content 23","Linked Content 24","Linked Content 25","Linked Content 26","Linked Content 27","Linked Content 28","Linked Content 29","Linked Content 30"] + sequence { + create = {"Level 1 Folder":0,"Level 2 Folder":1,"Level 3 Folder":2,"Level 4 Folder":3,"Description":4} + update = {"Collection Name":0,"Folder Identifier":1,"Level 1 Folder":2,"Level 2 Folder":3,"Level 3 Folder":4,"Level 4 Folder":5,"Description":6,"Mapped Topics":7,"Keywords":8,"QR Code Required?":9,"QR Code":10,"Linked Content 1":11,"Linked Content 2":12,"Linked Content 3":13,"Linked Content 4":14,"Linked Content 5":15,"Linked Content 6":16,"Linked Content 7":17,"Linked Content 8":18,"Linked Content 9":19,"Linked Content 10":20,"Linked Content 11":21,"Linked Content 12":22,"Linked Content 13":23,"Linked Content 14":24,"Linked Content 15":25,"Linked Content 16":26,"Linked Content 17":27,"Linked Content 18":28,"Linked Content 19":29,"Linked Content 20":30,"Linked Content 21":31,"Linked Content 22":32,"Linked Content 23":33,"Linked Content 24":34,"Linked Content 25":35,"Linked Content 26":36,"Linked Content 27":37,"Linked Content 28":38,"Linked Content 29":39,"Linked Content 30":40} + linkedContents = {"Linked Content 1":0,"Linked Content 2":1,"Linked Content 3":2,"Linked Content 4":3,"Linked Content 5":4,"Linked Content 6":5,"Linked Content 7":6,"Linked Content 8":7,"Linked Content 9":8,"Linked Content 10":9,"Linked Content 11":10,"Linked Content 12":11,"Linked Content 13":12,"Linked Content 14":13,"Linked Content 15":14,"Linked Content 16":15,"Linked Content 17":16,"Linked Content 18":17,"Linked Content 19":18,"Linked Content 20":19,"Linked Content 21":20,"Linked Content 22":21,"Linked Content 23":22,"Linked Content 24":23,"Linked Content 25":24,"Linked Content 26":25,"Linked Content 27":26,"Linked Content 28":27,"Linked Content 29":28,"Linked Content 30":29} + } + } + mandatory { + create = ["Level 1 Folder"] + update = ["Collection Name","Folder Identifier"] + } + } +} + +plugin.media.base.url="https://dev.knowlg.sunbird.org" + +cloudstorage { + metadata.replace_absolute_path=true + relative_path_prefix=CONTENT_STORAGE_BASE_PATH + metadata.list=["appIcon", "artifactUrl", "posterImage", "previewUrl", "thumbnail", "assetsMap", "certTemplate", "itemSetPreviewUrl", "grayScaleAppIcon", "sourceURL", "variants", "downloadUrl", "streamingUrl", "toc_url", "data", "question", "solutions", "editorState", "media", "pdfUrl", "transcripts"] + read_base_path="https://sunbirddevbbpublic.blob.core.windows.net" + write_base_path=["https://sunbirdstagingpublic.blob.core.windows.net", "https://sunbirddevbbpublic.blob.core.windows.net"] +} \ No newline at end of file diff --git a/kubernetes/content/content-service_logback.xml b/kubernetes/content/content-service_logback.xml new file mode 100644 index 000000000..aa39401d6 --- /dev/null +++ b/kubernetes/content/content-service_logback.xml @@ -0,0 +1,59 @@ + + + + + + + + + %d %msg%n + + + + + + + + + + + + + + + + + + + + %msg + + + sunbirdstaging.telemetry.raw + + + + + + + + + bootstrap.servers=kafka.knowlg-db.svc.cluster.local:9092 + + acks=0 + + linger.ms=15000 + + max.block.ms=0 + + client.id=${HOSTNAME}-${CONTEXT_NAME}-logback-relaxed + + + + + + + + + + diff --git a/kubernetes/content/templates/deployment.yaml b/kubernetes/content/templates/deployment.yaml new file mode 100644 index 000000000..947765d55 --- /dev/null +++ b/kubernetes/content/templates/deployment.yaml @@ -0,0 +1,74 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Chart.Name }} + namespace: {{ .Values.namespace }} + annotations: + reloader.stakater.com/auto: "true" +spec: + replicas: {{ .Values.replicaCount }} + strategy: + rollingUpdate: + maxSurge: {{ .Values.strategy.maxsurge }} + maxUnavailable: {{ .Values.strategy.maxunavailable }} + selector: + matchLabels: + app: {{ .Chart.Name }} + template: + metadata: + labels: + app: {{ .Chart.Name }} + spec: + volumes: + - name: {{ .Chart.Name }}-config + configMap: + name: {{ .Chart.Name }}-config + - name: {{ .Chart.Name }}-xml-config + configMap: + name: {{ .Chart.Name }}-xml-config + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.repository }}:{{ .Values.image_tag }}" + imagePullPolicy: Never + env: + - name: JAVA_OPTIONS + value: {{ .Values.env.javaoptions | quote }} + - name: _JAVA_OPTIONS + value: -Dlog4j2.formatMsgNoLookups=true + envFrom: + - configMapRef: + name: {{ .Chart.Name }}-config + resources: +{{ toYaml .Values.resources | indent 10 }} + ports: + - containerPort: {{ .Values.network.port }} + {{- if .Values.healthcheck }} + livenessProbe: +{{ toYaml .Values.livenessProbe | indent 10 }} + readinessProbe: +{{ toYaml .Values.readinessProbe | indent 10 }} + {{- end }} + volumeMounts: + - name: {{ .Chart.Name }}-config + mountPath: /home/sunbird/content-service-1.0-SNAPSHOT/config/application.conf + subPath: content-service_application.conf + - name: {{ .Chart.Name }}-xml-config + mountPath: /home/sunbird/content-service-1.0-SNAPSHOT/config/logback.xml + subPath: content-service_logback.xml + +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ .Chart.Name }}-service + namespace: {{ .Values.namespace }} + labels: + app: {{ .Chart.Name }} +spec: + ports: + - name: http-{{ .Chart.Name }} + protocol: TCP + port: {{ .Values.network.targetport }} + selector: + app: {{ .Chart.Name }} diff --git a/kubernetes/content/values.yaml b/kubernetes/content/values.yaml new file mode 100644 index 000000000..cce4738a7 --- /dev/null +++ b/kubernetes/content/values.yaml @@ -0,0 +1,22 @@ +namespace: knowlg-api + +env: + javaoptions: -Xmx600m + +replicaCount: 1 +repository: content-service +image_tag: R5.2.0 +resources: + requests: + cpu: 100m + memory: 100Mi + limits: + cpu: 1 + memory: 1024Mi +network: + port: 9000 + targetport: 9000 +strategy: + type: RollingUpdate + maxsurge: 25% + maxunavailable: 25% \ No newline at end of file diff --git a/kubernetes/taxonomy/taxonomy-service_application.conf b/kubernetes/taxonomy/taxonomy-service_application.conf index 149f3bf71..b48fa7441 100644 --- a/kubernetes/taxonomy/taxonomy-service_application.conf +++ b/kubernetes/taxonomy/taxonomy-service_application.conf @@ -393,7 +393,7 @@ languageCode { tamil : "ta" telugu : "te" } -objectcategorydefinition.keyspace="category_store" +objectcategorydefinition.keyspace="dev_category_store" # Framework master category validation Supported values are Yes/No master.category.validation.enabled="Yes" diff --git a/kubernetes/taxonomy/values.yaml b/kubernetes/taxonomy/values.yaml index 1c1fe162c..76033d47e 100644 --- a/kubernetes/taxonomy/values.yaml +++ b/kubernetes/taxonomy/values.yaml @@ -7,7 +7,7 @@ env: replicaCount: 1 repository: taxonomy-service -image_tag: R5.0.0 +image_tag: R5.2.0 resources: requests: cpu: 100m From c9e94c94a20cc84fec6c1920c2ae56d0bff7e133 Mon Sep 17 00:00:00 2001 From: Kartheek Palla Date: Mon, 6 Feb 2023 15:24:32 +0530 Subject: [PATCH 189/490] Issue KN-767 feat (doc): content service local setup using kubernetes --- KNOWLG-SETUP.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/KNOWLG-SETUP.md b/KNOWLG-SETUP.md index 1e93c580d..5c7c5f5d7 100644 --- a/KNOWLG-SETUP.md +++ b/KNOWLG-SETUP.md @@ -119,6 +119,33 @@ helm install taxonomy taxonomy -n knowlg-api ``` Use Port Forwarding to access the application in the cluster from local. +```shell +kubectl port-forward 9000:9000 -n knowlg-api +curl 'localhost:9000/health' +``` + +#### ConfigMap for Content-Service +Use the below commands to load the configmap of content-Service. +1. `content-config` - this has the application configuration. Please update the variables with respect to your context and load. +2. `content-xml-config` - this has the logback configuration to handle the logs. + +We have to update the below configurations in `content/templates/content-service_application` specific to your context. + +```shell +cd kubernetes +kubectl create configmap content-xml-config --from-file=content/content-service_logback.xml -n knowlg-api -o=yaml +kubectl create configmap content-config --from-file=content/content-service_application.conf -n knowlg-api -o=yaml +``` + +### Run Content-Service +Use the `taxonomy` helm chart to run the Content-Service in local kubernetes cluster. + +```shell +cd kubernetes +helm install content content -n knowlg-api +``` +Use Port Forwarding to access the application in the cluster from local. + ```shell kubectl port-forward 9000:9000 -n knowlg-api curl 'localhost:9000/health' From 3c9880b8e1697e40196d55409b865f4735f7b708 Mon Sep 17 00:00:00 2001 From: Kartheek Palla Date: Mon, 6 Feb 2023 15:25:44 +0530 Subject: [PATCH 190/490] Issue KN-767 feat (doc): content service local setup using kubernetes --- KNOWLG-SETUP.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/KNOWLG-SETUP.md b/KNOWLG-SETUP.md index 5c7c5f5d7..1ca3ee9c1 100644 --- a/KNOWLG-SETUP.md +++ b/KNOWLG-SETUP.md @@ -124,7 +124,7 @@ kubectl port-forward 9000:9000 -n knowlg-api curl 'localhost:9000/health' ``` -#### ConfigMap for Content-Service +### Define ConfigMap for Content-Service Use the below commands to load the configmap of content-Service. 1. `content-config` - this has the application configuration. Please update the variables with respect to your context and load. 2. `content-xml-config` - this has the logback configuration to handle the logs. From 4ba654cc603a4669f1f1b32eea758012ef9fed86 Mon Sep 17 00:00:00 2001 From: Kartheek Palla Date: Mon, 6 Feb 2023 16:05:00 +0530 Subject: [PATCH 191/490] Issue KN-767 fix: content service config map updated --- .../content/content-service_logback.xml | 31 +------------------ 1 file changed, 1 insertion(+), 30 deletions(-) diff --git a/kubernetes/content/content-service_logback.xml b/kubernetes/content/content-service_logback.xml index aa39401d6..3b3a26ecc 100644 --- a/kubernetes/content/content-service_logback.xml +++ b/kubernetes/content/content-service_logback.xml @@ -23,37 +23,8 @@ - - - - %msg - - - sunbirdstaging.telemetry.raw - - - - - - - - - bootstrap.servers=kafka.knowlg-db.svc.cluster.local:9092 - - acks=0 - - linger.ms=15000 - - max.block.ms=0 - - client.id=${HOSTNAME}-${CONTEXT_NAME}-logback-relaxed - - - - - - + From 9a0f8537034c8b7db9472858c0b2c4e54b096788 Mon Sep 17 00:00:00 2001 From: Surendrasingh Sucharia Date: Tue, 14 Feb 2023 15:28:47 +0530 Subject: [PATCH 192/490] Corrected the typo in line 20 Refer to https://github.com/orgs/Sunbird-inQuiry/discussions/62 --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9241fb1d1..e69d267a5 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ This readme file contains the instruction to set up and run the content-service 1. Go to Root folder (knowledge-platform) 2. Run "local-setup.sh" file ``` shell -sh ./local-setup.md +sh ./local-setup.sh ``` This will install all the requied dcoker images & local folders for DB mounting. @@ -203,4 +203,4 @@ mvn play2:run 3. Using the below command we can verify whether the databases(neo4j,redis & cassandra) connection is established or not. If all connections are good, health is shown as 'true' otherwise it will be 'false'. ```shell curl http://localhost:9000/health -``` \ No newline at end of file +``` From 59e42ecb04d66c53f170dff329ccc3d5676f12cb Mon Sep 17 00:00:00 2001 From: aimansharief <110388201+aimansharief@users.noreply.github.com> Date: Mon, 20 Mar 2023 10:52:32 +0530 Subject: [PATCH 193/490] feat : Updated scala version to 2.12 --- .github/pull_request_template.md | 2 +- .github/pull_request_template.md.yaml | 2 +- README.md | 2 +- assessment-api/assessment-actors/pom.xml | 2 +- .../src/main/scala/org/sunbird/actors/ItemSetActor.scala | 2 +- .../main/scala/org/sunbird/managers/AssessmentManager.scala | 2 +- .../src/main/scala/org/sunbird/utils/RequestUtil.scala | 2 +- .../test/scala/org/sunbird/actors/QuestionActorTest.scala | 2 +- .../scala/org/sunbird/actors/QuestionSetActorTest.scala | 2 +- assessment-api/pom.xml | 2 +- assessment-api/qs-hierarchy-manager/pom.xml | 2 +- .../main/scala/org/sunbird/managers/HierarchyManager.scala | 2 +- .../scala/org/sunbird/managers/UpdateHierarchyManager.scala | 3 +-- .../scala/org/sunbird/managers/HierarchyManagerTest.scala | 2 +- content-api/collection-csv-actors/pom.xml | 2 +- .../collectioncsv/manager/CollectionCSVManager.scala | 2 +- .../collectioncsv/validator/CollectionCSVValidator.scala | 2 +- content-api/content-actors/pom.xml | 2 +- .../scala/org/sunbird/channel/managers/ChannelManager.scala | 2 +- .../scala/org/sunbird/content/util/DiscardManager.scala | 2 +- .../main/scala/org/sunbird/content/util/FlagManager.scala | 2 +- .../main/scala/org/sunbird/content/util/RetireManager.scala | 3 ++- .../scala/org/sunbird/content/actors/TestChannelActor.scala | 2 +- .../scala/org/sunbird/content/actors/TestContentActor.scala | 3 ++- .../scala/org/sunbird/content/actors/TestEventActor.scala | 2 +- content-api/content-service/pom.xml | 2 +- content-api/hierarchy-manager/pom.xml | 2 +- .../main/scala/org/sunbird/managers/HierarchyManager.scala | 3 ++- .../scala/org/sunbird/managers/UpdateHierarchyManager.scala | 3 ++- .../src/test/scala/org/sunbird/managers/TestHierarchy.scala | 2 +- content-api/pom.xml | 2 +- docker-compose.yml | 2 +- .../{graph-core_2.11 => graph-core_2.12}/pom.xml | 2 +- .../src/main/scala/org/sunbird/graph/GraphService.scala | 0 .../scala/org/sunbird/graph/OntologyEngineContext.scala | 0 .../scala/org/sunbird/graph/exception/GraphErrorCodes.scala | 0 .../org/sunbird/graph/external/ExternalPropsManager.scala | 0 .../org/sunbird/graph/external/store/ExternalStore.scala | 5 +++-- .../sunbird/graph/external/store/ExternalStoreFactory.scala | 0 .../src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala | 0 .../main/scala/org/sunbird/graph/util/ScalaJsonUtil.scala | 0 .../scala/org/sunbird/graph/validator/NodeValidator.scala | 2 +- .../src/test/resources/application.conf | 0 .../src/test/resources/cassandra-unit.yaml | 0 .../src/test/resources/logback.xml | 0 .../src/test/scala/org/sunbird/graph/BaseSpec.scala | 0 .../sunbird/graph/external/ExternalPropsManagerTest.scala | 0 .../test/scala/org/sunbird/graph/util/CSPMetaUtilTest.scala | 0 .../scala/org/sunbird/graph/util/ScalaJsonUtilTest.scala | 0 .../{graph-engine_2.11 => graph-engine_2.12}/pom.xml | 4 ++-- .../main/scala/org/sunbird/graph/engine/CaseClasses.scala | 0 .../org/sunbird/graph/external/store/ExternalStore.scala | 5 +++-- .../scala/org/sunbird/graph/health/HealthCheckManager.scala | 0 .../src/main/scala/org/sunbird/graph/nodes/DataNode.scala | 2 +- .../org/sunbird/graph/relations/AbstractRelation.scala | 0 .../org/sunbird/graph/relations/AssociationRelation.scala | 0 .../main/scala/org/sunbird/graph/relations/IRelation.scala | 0 .../scala/org/sunbird/graph/relations/RelationHandler.scala | 0 .../graph/relations/SequenceMembershipRelation.scala | 0 .../sunbird/graph/schema/CategoryDefinitionValidator.scala | 0 .../scala/org/sunbird/graph/schema/CoreDomainObject.scala | 0 .../main/scala/org/sunbird/graph/schema/DefinitionDTO.scala | 0 .../scala/org/sunbird/graph/schema/DefinitionFactory.scala | 0 .../scala/org/sunbird/graph/schema/DefinitionNode.scala | 2 +- .../sunbird/graph/schema/FrameworkMasterCategoryMap.scala | 0 .../main/scala/org/sunbird/graph/schema/IDefinition.scala | 0 .../org/sunbird/graph/schema/ObjectCategoryDefinition.scala | 0 .../sunbird/graph/schema/ObjectCategoryDefinitionMap.scala | 0 .../sunbird/graph/schema/validator/BaseDefinitionNode.scala | 0 .../sunbird/graph/schema/validator/FrameworkValidator.scala | 2 +- .../graph/schema/validator/PropAsEdgeValidator.scala | 2 +- .../sunbird/graph/schema/validator/RelationValidator.scala | 0 .../sunbird/graph/schema/validator/SchemaValidator.scala | 0 .../graph/schema/validator/VersionKeyValidator.scala | 0 .../org/sunbird/graph/schema/validator/VersioningNode.scala | 2 +- .../src/main/scala/org/sunbird/graph/utils/NodeUtil.scala | 1 - .../main/scala/org/sunbird/graph/utils/ScalaJsonUtils.scala | 0 .../src/test/resources/application.conf | 0 .../src/test/resources/cassandra-unit.yaml | 0 .../src/test/resources/logback.xml | 0 .../src/test/scala/org/sunbird/graph/BaseSpec.scala | 0 .../org/sunbird/graph/health/TestHealthCheckManager.scala | 0 .../test/scala/org/sunbird/graph/nodes/TestDataNode.scala | 0 .../graph/schema/TestObjectCategoryDefinitionMap.scala | 0 .../graph/schema/validator/TestSchemaValidator.scala | 0 .../test/scala/org/sunbird/graph/utils/NodeUtilTest.scala | 0 .../scala/org/sunbird/graph/utils/ScalaJsonUtilsTest.scala | 0 ontology-engine/pom.xml | 4 ++-- platform-modules/import-manager/pom.xml | 2 +- platform-modules/mimetype-manager/pom.xml | 6 +++--- .../mimetype/mgr/impl/CollectionMimeTypeMgrImpl.scala | 2 +- .../org/sunbird/mimetype/mgr/impl/EcmlMimeTypeMgrImpl.scala | 2 +- pom.xml | 4 ++-- search-api/pom.xml | 2 +- search-api/search-core/pom.xml | 2 +- .../app/controllers/SearchBaseController.scala | 2 +- taxonomy-api/pom.xml | 2 +- taxonomy-api/taxonomy-actors/pom.xml | 2 +- .../taxonomy-service/app/controllers/BaseController.scala | 2 +- vmsetup.sh | 2 +- 100 files changed, 68 insertions(+), 64 deletions(-) rename ontology-engine/{graph-core_2.11 => graph-core_2.12}/pom.xml (99%) rename ontology-engine/{graph-core_2.11 => graph-core_2.12}/src/main/scala/org/sunbird/graph/GraphService.scala (100%) rename ontology-engine/{graph-core_2.11 => graph-core_2.12}/src/main/scala/org/sunbird/graph/OntologyEngineContext.scala (100%) rename ontology-engine/{graph-core_2.11 => graph-core_2.12}/src/main/scala/org/sunbird/graph/exception/GraphErrorCodes.scala (100%) rename ontology-engine/{graph-core_2.11 => graph-core_2.12}/src/main/scala/org/sunbird/graph/external/ExternalPropsManager.scala (100%) rename ontology-engine/{graph-core_2.11 => graph-core_2.12}/src/main/scala/org/sunbird/graph/external/store/ExternalStore.scala (98%) rename ontology-engine/{graph-core_2.11 => graph-core_2.12}/src/main/scala/org/sunbird/graph/external/store/ExternalStoreFactory.scala (100%) rename ontology-engine/{graph-core_2.11 => graph-core_2.12}/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala (100%) rename ontology-engine/{graph-core_2.11 => graph-core_2.12}/src/main/scala/org/sunbird/graph/util/ScalaJsonUtil.scala (100%) rename ontology-engine/{graph-core_2.11 => graph-core_2.12}/src/main/scala/org/sunbird/graph/validator/NodeValidator.scala (97%) rename ontology-engine/{graph-core_2.11 => graph-core_2.12}/src/test/resources/application.conf (100%) rename ontology-engine/{graph-core_2.11 => graph-core_2.12}/src/test/resources/cassandra-unit.yaml (100%) rename ontology-engine/{graph-core_2.11 => graph-core_2.12}/src/test/resources/logback.xml (100%) rename ontology-engine/{graph-core_2.11 => graph-core_2.12}/src/test/scala/org/sunbird/graph/BaseSpec.scala (100%) rename ontology-engine/{graph-core_2.11 => graph-core_2.12}/src/test/scala/org/sunbird/graph/external/ExternalPropsManagerTest.scala (100%) rename ontology-engine/{graph-core_2.11 => graph-core_2.12}/src/test/scala/org/sunbird/graph/util/CSPMetaUtilTest.scala (100%) rename ontology-engine/{graph-core_2.11 => graph-core_2.12}/src/test/scala/org/sunbird/graph/util/ScalaJsonUtilTest.scala (100%) rename ontology-engine/{graph-engine_2.11 => graph-engine_2.12}/pom.xml (97%) rename ontology-engine/{graph-engine_2.11 => graph-engine_2.12}/src/main/scala/org/sunbird/graph/engine/CaseClasses.scala (100%) rename ontology-engine/{graph-engine_2.11 => graph-engine_2.12}/src/main/scala/org/sunbird/graph/external/store/ExternalStore.scala (98%) rename ontology-engine/{graph-engine_2.11 => graph-engine_2.12}/src/main/scala/org/sunbird/graph/health/HealthCheckManager.scala (100%) rename ontology-engine/{graph-engine_2.11 => graph-engine_2.12}/src/main/scala/org/sunbird/graph/nodes/DataNode.scala (99%) rename ontology-engine/{graph-engine_2.11 => graph-engine_2.12}/src/main/scala/org/sunbird/graph/relations/AbstractRelation.scala (100%) rename ontology-engine/{graph-engine_2.11 => graph-engine_2.12}/src/main/scala/org/sunbird/graph/relations/AssociationRelation.scala (100%) rename ontology-engine/{graph-engine_2.11 => graph-engine_2.12}/src/main/scala/org/sunbird/graph/relations/IRelation.scala (100%) rename ontology-engine/{graph-engine_2.11 => graph-engine_2.12}/src/main/scala/org/sunbird/graph/relations/RelationHandler.scala (100%) rename ontology-engine/{graph-engine_2.11 => graph-engine_2.12}/src/main/scala/org/sunbird/graph/relations/SequenceMembershipRelation.scala (100%) rename ontology-engine/{graph-engine_2.11 => graph-engine_2.12}/src/main/scala/org/sunbird/graph/schema/CategoryDefinitionValidator.scala (100%) rename ontology-engine/{graph-engine_2.11 => graph-engine_2.12}/src/main/scala/org/sunbird/graph/schema/CoreDomainObject.scala (100%) rename ontology-engine/{graph-engine_2.11 => graph-engine_2.12}/src/main/scala/org/sunbird/graph/schema/DefinitionDTO.scala (100%) rename ontology-engine/{graph-engine_2.11 => graph-engine_2.12}/src/main/scala/org/sunbird/graph/schema/DefinitionFactory.scala (100%) rename ontology-engine/{graph-engine_2.11 => graph-engine_2.12}/src/main/scala/org/sunbird/graph/schema/DefinitionNode.scala (99%) rename ontology-engine/{graph-engine_2.11 => graph-engine_2.12}/src/main/scala/org/sunbird/graph/schema/FrameworkMasterCategoryMap.scala (100%) rename ontology-engine/{graph-engine_2.11 => graph-engine_2.12}/src/main/scala/org/sunbird/graph/schema/IDefinition.scala (100%) rename ontology-engine/{graph-engine_2.11 => graph-engine_2.12}/src/main/scala/org/sunbird/graph/schema/ObjectCategoryDefinition.scala (100%) rename ontology-engine/{graph-engine_2.11 => graph-engine_2.12}/src/main/scala/org/sunbird/graph/schema/ObjectCategoryDefinitionMap.scala (100%) rename ontology-engine/{graph-engine_2.11 => graph-engine_2.12}/src/main/scala/org/sunbird/graph/schema/validator/BaseDefinitionNode.scala (100%) rename ontology-engine/{graph-engine_2.11 => graph-engine_2.12}/src/main/scala/org/sunbird/graph/schema/validator/FrameworkValidator.scala (99%) rename ontology-engine/{graph-engine_2.11 => graph-engine_2.12}/src/main/scala/org/sunbird/graph/schema/validator/PropAsEdgeValidator.scala (97%) rename ontology-engine/{graph-engine_2.11 => graph-engine_2.12}/src/main/scala/org/sunbird/graph/schema/validator/RelationValidator.scala (100%) rename ontology-engine/{graph-engine_2.11 => graph-engine_2.12}/src/main/scala/org/sunbird/graph/schema/validator/SchemaValidator.scala (100%) rename ontology-engine/{graph-engine_2.11 => graph-engine_2.12}/src/main/scala/org/sunbird/graph/schema/validator/VersionKeyValidator.scala (100%) rename ontology-engine/{graph-engine_2.11 => graph-engine_2.12}/src/main/scala/org/sunbird/graph/schema/validator/VersioningNode.scala (99%) rename ontology-engine/{graph-engine_2.11 => graph-engine_2.12}/src/main/scala/org/sunbird/graph/utils/NodeUtil.scala (99%) rename ontology-engine/{graph-engine_2.11 => graph-engine_2.12}/src/main/scala/org/sunbird/graph/utils/ScalaJsonUtils.scala (100%) rename ontology-engine/{graph-engine_2.11 => graph-engine_2.12}/src/test/resources/application.conf (100%) rename ontology-engine/{graph-engine_2.11 => graph-engine_2.12}/src/test/resources/cassandra-unit.yaml (100%) rename ontology-engine/{graph-engine_2.11 => graph-engine_2.12}/src/test/resources/logback.xml (100%) rename ontology-engine/{graph-engine_2.11 => graph-engine_2.12}/src/test/scala/org/sunbird/graph/BaseSpec.scala (100%) rename ontology-engine/{graph-engine_2.11 => graph-engine_2.12}/src/test/scala/org/sunbird/graph/health/TestHealthCheckManager.scala (100%) rename ontology-engine/{graph-engine_2.11 => graph-engine_2.12}/src/test/scala/org/sunbird/graph/nodes/TestDataNode.scala (100%) rename ontology-engine/{graph-engine_2.11 => graph-engine_2.12}/src/test/scala/org/sunbird/graph/schema/TestObjectCategoryDefinitionMap.scala (100%) rename ontology-engine/{graph-engine_2.11 => graph-engine_2.12}/src/test/scala/org/sunbird/graph/schema/validator/TestSchemaValidator.scala (100%) rename ontology-engine/{graph-engine_2.11 => graph-engine_2.12}/src/test/scala/org/sunbird/graph/utils/NodeUtilTest.scala (100%) rename ontology-engine/{graph-engine_2.11 => graph-engine_2.12}/src/test/scala/org/sunbird/graph/utils/ScalaJsonUtilsTest.scala (100%) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 920b32a49..f7e45f070 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -17,7 +17,7 @@ Please describe the tests that you ran to verify your changes in the below check - [ ] Ran Test B **Test Configuration**: -* Software versions: Java 11, scala-2.11, play-2.7.2 +* Software versions: Java 11, scala-2.12, play-2.7.2 * Hardware versions: 2 CPU/ 4GB RAM ### Checklist: diff --git a/.github/pull_request_template.md.yaml b/.github/pull_request_template.md.yaml index f1826648e..64750cf63 100644 --- a/.github/pull_request_template.md.yaml +++ b/.github/pull_request_template.md.yaml @@ -17,7 +17,7 @@ Please describe the tests that you ran to verify your changes in the below check - [ ] Ran Test B **Test Configuration**: -* Software versions: Java 11, scala-2.11, play-2.7.2 +* Software versions: Java 11, scala-2.12, play-2.7.2 * Hardware versions: ### Checklist: diff --git a/README.md b/README.md index e69d267a5..1e4e3d577 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,7 @@ services: - KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://127.0.0.1:2181 kafka: - image: 'wurstmeister/kafka:2.11-1.0.1' + image: 'wurstmeister/kafka:2.12-1.0.1' container_name: kafka ports: - "9092:9092" diff --git a/assessment-api/assessment-actors/pom.xml b/assessment-api/assessment-actors/pom.xml index d56dd0a67..5e5418207 100644 --- a/assessment-api/assessment-actors/pom.xml +++ b/assessment-api/assessment-actors/pom.xml @@ -28,7 +28,7 @@ org.sunbird - graph-engine_2.11 + graph-engine_2.12 1.0-SNAPSHOT jar diff --git a/assessment-api/assessment-actors/src/main/scala/org/sunbird/actors/ItemSetActor.scala b/assessment-api/assessment-actors/src/main/scala/org/sunbird/actors/ItemSetActor.scala index 524a0604c..ba6d39993 100644 --- a/assessment-api/assessment-actors/src/main/scala/org/sunbird/actors/ItemSetActor.scala +++ b/assessment-api/assessment-actors/src/main/scala/org/sunbird/actors/ItemSetActor.scala @@ -13,7 +13,7 @@ import org.sunbird.graph.nodes.DataNode import org.sunbird.graph.utils.NodeUtil import org.sunbird.parseq.Task -import scala.collection.JavaConversions._ +import scala.collection.convert.ImplicitConversions._ import scala.collection.JavaConverters.seqAsJavaListConverter import scala.concurrent.{ExecutionContext, Future} diff --git a/assessment-api/assessment-actors/src/main/scala/org/sunbird/managers/AssessmentManager.scala b/assessment-api/assessment-actors/src/main/scala/org/sunbird/managers/AssessmentManager.scala index 407d2d534..e81fe6c9d 100644 --- a/assessment-api/assessment-actors/src/main/scala/org/sunbird/managers/AssessmentManager.scala +++ b/assessment-api/assessment-actors/src/main/scala/org/sunbird/managers/AssessmentManager.scala @@ -15,7 +15,7 @@ import org.sunbird.telemetry.util.LogTelemetryEventUtil import org.sunbird.utils.RequestUtil import scala.concurrent.{ExecutionContext, Future} -import scala.collection.JavaConversions._ +import scala.collection.convert.ImplicitConversions._ import scala.collection.JavaConverters import scala.collection.JavaConverters._ diff --git a/assessment-api/assessment-actors/src/main/scala/org/sunbird/utils/RequestUtil.scala b/assessment-api/assessment-actors/src/main/scala/org/sunbird/utils/RequestUtil.scala index 4246a74c8..c96e218b0 100644 --- a/assessment-api/assessment-actors/src/main/scala/org/sunbird/utils/RequestUtil.scala +++ b/assessment-api/assessment-actors/src/main/scala/org/sunbird/utils/RequestUtil.scala @@ -7,7 +7,7 @@ import org.sunbird.graph.OntologyEngineContext import org.sunbird.graph.schema.DefinitionNode import scala.concurrent.ExecutionContext -import scala.collection.JavaConversions._ +import scala.collection.convert.ImplicitConversions._ object RequestUtil { diff --git a/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/QuestionActorTest.scala b/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/QuestionActorTest.scala index 349bc916a..a6ded4683 100644 --- a/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/QuestionActorTest.scala +++ b/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/QuestionActorTest.scala @@ -11,7 +11,7 @@ import org.sunbird.graph.utils.ScalaJsonUtils import org.sunbird.graph.{GraphService, OntologyEngineContext} import org.sunbird.kafka.client.KafkaClient -import scala.collection.JavaConversions._ +import scala.collection.convert.ImplicitConversions._ import scala.collection.JavaConverters._ import scala.concurrent.Future import scala.concurrent.ExecutionContext.Implicits.global diff --git a/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/QuestionSetActorTest.scala b/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/QuestionSetActorTest.scala index 25591cd35..dbd30c7e5 100644 --- a/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/QuestionSetActorTest.scala +++ b/assessment-api/assessment-actors/src/test/scala/org/sunbird/actors/QuestionSetActorTest.scala @@ -15,7 +15,7 @@ import org.sunbird.managers.CopyManager import org.sunbird.utils.{AssessmentConstants, BranchingUtil, JavaJsonUtils} import java.util -import scala.collection.JavaConversions._ +import scala.collection.convert.ImplicitConversions._ import scala.collection.JavaConverters._ import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future diff --git a/assessment-api/pom.xml b/assessment-api/pom.xml index b1bb913ed..7c55ee4d4 100644 --- a/assessment-api/pom.xml +++ b/assessment-api/pom.xml @@ -20,7 +20,7 @@ UTF-8 UTF-8 - 2.11 + 2.12 diff --git a/assessment-api/qs-hierarchy-manager/pom.xml b/assessment-api/qs-hierarchy-manager/pom.xml index e4de07f22..8431814a0 100644 --- a/assessment-api/qs-hierarchy-manager/pom.xml +++ b/assessment-api/qs-hierarchy-manager/pom.xml @@ -14,7 +14,7 @@ org.sunbird - graph-engine_2.11 + graph-engine_2.12 1.0-SNAPSHOT jar diff --git a/assessment-api/qs-hierarchy-manager/src/main/scala/org/sunbird/managers/HierarchyManager.scala b/assessment-api/qs-hierarchy-manager/src/main/scala/org/sunbird/managers/HierarchyManager.scala index 125489648..906d838a7 100644 --- a/assessment-api/qs-hierarchy-manager/src/main/scala/org/sunbird/managers/HierarchyManager.scala +++ b/assessment-api/qs-hierarchy-manager/src/main/scala/org/sunbird/managers/HierarchyManager.scala @@ -13,7 +13,7 @@ import org.sunbird.graph.dac.model.Node import org.sunbird.graph.nodes.DataNode import org.sunbird.graph.utils.{NodeUtil, ScalaJsonUtils} -import scala.collection.JavaConversions._ +import scala.collection.convert.ImplicitConversions._ import scala.collection.JavaConverters._ import scala.collection.JavaConverters import scala.concurrent.{ExecutionContext, Future} diff --git a/assessment-api/qs-hierarchy-manager/src/main/scala/org/sunbird/managers/UpdateHierarchyManager.scala b/assessment-api/qs-hierarchy-manager/src/main/scala/org/sunbird/managers/UpdateHierarchyManager.scala index 59cdd1810..6ce0dc829 100644 --- a/assessment-api/qs-hierarchy-manager/src/main/scala/org/sunbird/managers/UpdateHierarchyManager.scala +++ b/assessment-api/qs-hierarchy-manager/src/main/scala/org/sunbird/managers/UpdateHierarchyManager.scala @@ -2,7 +2,6 @@ package org.sunbird.managers import java.util import java.util.concurrent.CompletionException - import org.apache.commons.collections4.{CollectionUtils, MapUtils} import org.apache.commons.lang3.StringUtils import org.sunbird.common.dto.{Request, Response, ResponseHandler} @@ -18,8 +17,8 @@ import org.sunbird.graph.utils.{NodeUtil, ScalaJsonUtils} import org.sunbird.telemetry.logger.TelemetryManager import org.sunbird.utils.{HierarchyConstants, HierarchyErrorCodes} -import scala.collection.JavaConversions._ import scala.collection.JavaConverters._ +import scala.collection.convert.ImplicitConversions._ import scala.collection.mutable import scala.concurrent.{ExecutionContext, Future} diff --git a/assessment-api/qs-hierarchy-manager/src/test/scala/org/sunbird/managers/HierarchyManagerTest.scala b/assessment-api/qs-hierarchy-manager/src/test/scala/org/sunbird/managers/HierarchyManagerTest.scala index baaaeadb8..b1b6dbef6 100644 --- a/assessment-api/qs-hierarchy-manager/src/test/scala/org/sunbird/managers/HierarchyManagerTest.scala +++ b/assessment-api/qs-hierarchy-manager/src/test/scala/org/sunbird/managers/HierarchyManagerTest.scala @@ -8,7 +8,7 @@ import org.sunbird.common.exception.ClientException import org.sunbird.graph.OntologyEngineContext import org.sunbird.utils.HierarchyConstants -import scala.collection.JavaConversions._ + import scala.collection.JavaConverters._ class HierarchyManagerTest extends BaseSpec { diff --git a/content-api/collection-csv-actors/pom.xml b/content-api/collection-csv-actors/pom.xml index 45e1060f9..e225b3f7a 100644 --- a/content-api/collection-csv-actors/pom.xml +++ b/content-api/collection-csv-actors/pom.xml @@ -28,7 +28,7 @@ org.sunbird - graph-engine_2.11 + graph-engine_2.12 1.0-SNAPSHOT jar diff --git a/content-api/collection-csv-actors/src/main/scala/org.sunbird/collectioncsv/manager/CollectionCSVManager.scala b/content-api/collection-csv-actors/src/main/scala/org.sunbird/collectioncsv/manager/CollectionCSVManager.scala index f97a9c56f..3fe0c06e9 100644 --- a/content-api/collection-csv-actors/src/main/scala/org.sunbird/collectioncsv/manager/CollectionCSVManager.scala +++ b/content-api/collection-csv-actors/src/main/scala/org.sunbird/collectioncsv/manager/CollectionCSVManager.scala @@ -23,7 +23,7 @@ import java.nio.charset.StandardCharsets import java.util import java.util.logging.Logger import scala.collection.immutable.{ListMap, Map} -import scala.collection.JavaConversions._ +import scala.collection.convert.ImplicitConversions._ import scala.collection.JavaConverters.{asJavaIterableConverter, mapAsScalaMapConverter} import scala.collection.mutable import scala.collection.mutable.ListBuffer diff --git a/content-api/collection-csv-actors/src/main/scala/org.sunbird/collectioncsv/validator/CollectionCSVValidator.scala b/content-api/collection-csv-actors/src/main/scala/org.sunbird/collectioncsv/validator/CollectionCSVValidator.scala index e9678f7e5..da0bee6ed 100644 --- a/content-api/collection-csv-actors/src/main/scala/org.sunbird/collectioncsv/validator/CollectionCSVValidator.scala +++ b/content-api/collection-csv-actors/src/main/scala/org.sunbird/collectioncsv/validator/CollectionCSVValidator.scala @@ -16,7 +16,7 @@ import java.io.{File, FileInputStream, IOException, InputStreamReader} import java.nio.charset.StandardCharsets import java.text.MessageFormat import java.util -import scala.collection.JavaConversions._ +import scala.collection.convert.ImplicitConversions._ import scala.collection.JavaConverters.{asScalaBufferConverter, mapAsJavaMapConverter, mapAsScalaMapConverter} import scala.collection.immutable.{HashMap, ListMap, Map} import scala.concurrent.ExecutionContext diff --git a/content-api/content-actors/pom.xml b/content-api/content-actors/pom.xml index a429dc8f9..83ff1fd23 100644 --- a/content-api/content-actors/pom.xml +++ b/content-api/content-actors/pom.xml @@ -28,7 +28,7 @@ org.sunbird - graph-engine_2.11 + graph-engine_2.12 1.0-SNAPSHOT jar diff --git a/content-api/content-actors/src/main/scala/org/sunbird/channel/managers/ChannelManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/channel/managers/ChannelManager.scala index ae9d69050..c0cabc2fe 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/channel/managers/ChannelManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/channel/managers/ChannelManager.scala @@ -14,7 +14,7 @@ import org.apache.commons.lang3.StringUtils import org.sunbird.common.JsonUtils import scala.collection.JavaConverters._ -import scala.collection.JavaConversions._ +import scala.collection.convert.ImplicitConversions._ import scala.collection.mutable.ListBuffer object ChannelManager { diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/util/DiscardManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/util/DiscardManager.scala index 6a2aa5ed6..92d762e57 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/util/DiscardManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/util/DiscardManager.scala @@ -16,7 +16,7 @@ import org.sunbird.managers.UpdateHierarchyManager.{fetchHierarchy, shouldImageB import org.sunbird.telemetry.logger.TelemetryManager import org.sunbird.utils.{HierarchyConstants, HierarchyErrorCodes} -import scala.collection.JavaConversions._ + import scala.concurrent.{ExecutionContext, Future} object DiscardManager { diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/util/FlagManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/util/FlagManager.scala index c42d4e48d..3de36faaf 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/util/FlagManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/util/FlagManager.scala @@ -16,7 +16,7 @@ import org.sunbird.graph.utils.NodeUtil import org.sunbird.telemetry.logger.TelemetryManager import org.sunbird.utils.HierarchyConstants import scala.concurrent.{ExecutionContext, Future} -import scala.collection.JavaConversions._ +import scala.collection.convert.ImplicitConversions._ import scala.collection.JavaConverters._ object FlagManager { diff --git a/content-api/content-actors/src/main/scala/org/sunbird/content/util/RetireManager.scala b/content-api/content-actors/src/main/scala/org/sunbird/content/util/RetireManager.scala index cbd8e4eba..58ed4a138 100644 --- a/content-api/content-actors/src/main/scala/org/sunbird/content/util/RetireManager.scala +++ b/content-api/content-actors/src/main/scala/org/sunbird/content/util/RetireManager.scala @@ -19,7 +19,8 @@ import org.sunbird.parseq.Task import org.sunbird.telemetry.logger.TelemetryManager import org.sunbird.utils.HierarchyConstants -import scala.collection.JavaConversions._ +import scala.collection.JavaConverters._ +import scala.collection.convert.ImplicitConversions._ import scala.collection.mutable.ListBuffer import scala.concurrent.{ExecutionContext, Future} diff --git a/content-api/content-actors/src/test/scala/org/sunbird/content/actors/TestChannelActor.scala b/content-api/content-actors/src/test/scala/org/sunbird/content/actors/TestChannelActor.scala index 446428ea7..4d3fb1475 100644 --- a/content-api/content-actors/src/test/scala/org/sunbird/content/actors/TestChannelActor.scala +++ b/content-api/content-actors/src/test/scala/org/sunbird/content/actors/TestChannelActor.scala @@ -9,7 +9,7 @@ import org.sunbird.common.dto.Request import org.sunbird.graph.dac.model.{Node, SearchCriteria} import org.sunbird.graph.{GraphService, OntologyEngineContext} -import scala.collection.JavaConversions._ +import scala.collection.JavaConverters._ import scala.concurrent.Future import scala.concurrent.ExecutionContext.Implicits.global diff --git a/content-api/content-actors/src/test/scala/org/sunbird/content/actors/TestContentActor.scala b/content-api/content-actors/src/test/scala/org/sunbird/content/actors/TestContentActor.scala index fb9f22de2..353786f6b 100644 --- a/content-api/content-actors/src/test/scala/org/sunbird/content/actors/TestContentActor.scala +++ b/content-api/content-actors/src/test/scala/org/sunbird/content/actors/TestContentActor.scala @@ -14,7 +14,8 @@ import org.sunbird.kafka.client.KafkaClient import java.io.File import java.util -import scala.collection.JavaConversions._ +import scala.collection.JavaConverters._ +import scala.collection.convert.ImplicitConversions._ import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future diff --git a/content-api/content-actors/src/test/scala/org/sunbird/content/actors/TestEventActor.scala b/content-api/content-actors/src/test/scala/org/sunbird/content/actors/TestEventActor.scala index f701e5d22..048aa6f46 100644 --- a/content-api/content-actors/src/test/scala/org/sunbird/content/actors/TestEventActor.scala +++ b/content-api/content-actors/src/test/scala/org/sunbird/content/actors/TestEventActor.scala @@ -9,7 +9,7 @@ import org.sunbird.graph.dac.model.{Node, SearchCriteria} import org.sunbird.graph.{GraphService, OntologyEngineContext} import java.util -import scala.collection.JavaConversions._ +import scala.collection.JavaConverters._ import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future diff --git a/content-api/content-service/pom.xml b/content-api/content-service/pom.xml index 77e6eabf5..802c39f3a 100755 --- a/content-api/content-service/pom.xml +++ b/content-api/content-service/pom.xml @@ -32,7 +32,7 @@ 2.7.2 1.0.0-rc5 1.0.0 - 2.11 + 2.12 diff --git a/content-api/hierarchy-manager/pom.xml b/content-api/hierarchy-manager/pom.xml index 828dac9fc..60ef906b7 100644 --- a/content-api/hierarchy-manager/pom.xml +++ b/content-api/hierarchy-manager/pom.xml @@ -14,7 +14,7 @@ org.sunbird - graph-engine_2.11 + graph-engine_2.12 1.0-SNAPSHOT jar diff --git a/content-api/hierarchy-manager/src/main/scala/org/sunbird/managers/HierarchyManager.scala b/content-api/hierarchy-manager/src/main/scala/org/sunbird/managers/HierarchyManager.scala index 1640d3680..74fe19e72 100644 --- a/content-api/hierarchy-manager/src/main/scala/org/sunbird/managers/HierarchyManager.scala +++ b/content-api/hierarchy-manager/src/main/scala/org/sunbird/managers/HierarchyManager.scala @@ -12,8 +12,9 @@ import org.sunbird.graph.dac.model.Node import org.sunbird.graph.nodes.DataNode import org.sunbird.graph.utils.{NodeUtil, ScalaJsonUtils} -import scala.collection.JavaConversions._ +import scala.collection.convert.ImplicitConversions._ import scala.collection.JavaConverters +import scala.collection.JavaConverters._ import scala.collection.JavaConverters.{asJavaIterableConverter, mapAsScalaMapConverter} import scala.concurrent.{ExecutionContext, Future} import com.mashape.unirest.http.HttpResponse diff --git a/content-api/hierarchy-manager/src/main/scala/org/sunbird/managers/UpdateHierarchyManager.scala b/content-api/hierarchy-manager/src/main/scala/org/sunbird/managers/UpdateHierarchyManager.scala index dba9791fd..7ec8c99d8 100644 --- a/content-api/hierarchy-manager/src/main/scala/org/sunbird/managers/UpdateHierarchyManager.scala +++ b/content-api/hierarchy-manager/src/main/scala/org/sunbird/managers/UpdateHierarchyManager.scala @@ -17,7 +17,8 @@ import org.sunbird.schema.{ISchemaValidator, SchemaValidatorFactory} import org.sunbird.telemetry.logger.TelemetryManager import org.sunbird.utils.{HierarchyBackwardCompatibilityUtil, HierarchyConstants, HierarchyErrorCodes} -import scala.collection.JavaConversions._ +import scala.collection.JavaConverters._ +import scala.collection.convert.ImplicitConversions._ import scala.collection.mutable import scala.concurrent.{ExecutionContext, Future} diff --git a/content-api/hierarchy-manager/src/test/scala/org/sunbird/managers/TestHierarchy.scala b/content-api/hierarchy-manager/src/test/scala/org/sunbird/managers/TestHierarchy.scala index ae34a5181..5823c557e 100644 --- a/content-api/hierarchy-manager/src/test/scala/org/sunbird/managers/TestHierarchy.scala +++ b/content-api/hierarchy-manager/src/test/scala/org/sunbird/managers/TestHierarchy.scala @@ -8,7 +8,7 @@ import org.sunbird.common.JsonUtils import org.sunbird.common.dto.Request import org.sunbird.common.exception.ClientException import org.sunbird.graph.OntologyEngineContext -import scala.collection.JavaConversions._ +import scala.collection.JavaConverters._ class TestHierarchy extends BaseSpec { diff --git a/content-api/pom.xml b/content-api/pom.xml index 4340d17fb..052a4d0fe 100755 --- a/content-api/pom.xml +++ b/content-api/pom.xml @@ -14,7 +14,7 @@ UTF-8 UTF-8 - 2.11.12 + 2.12.11 content-service diff --git a/docker-compose.yml b/docker-compose.yml index 27479d98f..b655a4691 100755 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -47,7 +47,7 @@ services: - KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://127.0.0.1:2181 kafka: - image: 'wurstmeister/kafka:2.11-1.0.1' + image: 'wurstmeister/kafka:2.12-1.0.1' container_name: kafka ports: - "9092:9092" diff --git a/ontology-engine/graph-core_2.11/pom.xml b/ontology-engine/graph-core_2.12/pom.xml similarity index 99% rename from ontology-engine/graph-core_2.11/pom.xml rename to ontology-engine/graph-core_2.12/pom.xml index 9a0f5f9b1..bb89cb77e 100644 --- a/ontology-engine/graph-core_2.11/pom.xml +++ b/ontology-engine/graph-core_2.12/pom.xml @@ -7,7 +7,7 @@ 1.0-SNAPSHOT org.sunbird - graph-core_2.11 + graph-core_2.12 1.0-SNAPSHOT diff --git a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/GraphService.scala b/ontology-engine/graph-core_2.12/src/main/scala/org/sunbird/graph/GraphService.scala similarity index 100% rename from ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/GraphService.scala rename to ontology-engine/graph-core_2.12/src/main/scala/org/sunbird/graph/GraphService.scala diff --git a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/OntologyEngineContext.scala b/ontology-engine/graph-core_2.12/src/main/scala/org/sunbird/graph/OntologyEngineContext.scala similarity index 100% rename from ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/OntologyEngineContext.scala rename to ontology-engine/graph-core_2.12/src/main/scala/org/sunbird/graph/OntologyEngineContext.scala diff --git a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/exception/GraphErrorCodes.scala b/ontology-engine/graph-core_2.12/src/main/scala/org/sunbird/graph/exception/GraphErrorCodes.scala similarity index 100% rename from ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/exception/GraphErrorCodes.scala rename to ontology-engine/graph-core_2.12/src/main/scala/org/sunbird/graph/exception/GraphErrorCodes.scala diff --git a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/external/ExternalPropsManager.scala b/ontology-engine/graph-core_2.12/src/main/scala/org/sunbird/graph/external/ExternalPropsManager.scala similarity index 100% rename from ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/external/ExternalPropsManager.scala rename to ontology-engine/graph-core_2.12/src/main/scala/org/sunbird/graph/external/ExternalPropsManager.scala diff --git a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/external/store/ExternalStore.scala b/ontology-engine/graph-core_2.12/src/main/scala/org/sunbird/graph/external/store/ExternalStore.scala similarity index 98% rename from ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/external/store/ExternalStore.scala rename to ontology-engine/graph-core_2.12/src/main/scala/org/sunbird/graph/external/store/ExternalStore.scala index c9cb9f11e..5cd821c16 100644 --- a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/external/store/ExternalStore.scala +++ b/ontology-engine/graph-core_2.12/src/main/scala/org/sunbird/graph/external/store/ExternalStore.scala @@ -105,7 +105,8 @@ class ExternalStore(keySpace: String , table: String , primaryKey: java.util.Lis }) } val selectQuery = select.from(keySpace, table) - import scala.collection.JavaConversions._ + import scala.collection.JavaConverters._ + import scala.collection.convert.ImplicitConversions._ val clause: Clause = QueryBuilder.in(primaryKey.get(0), seqAsJavaList(identifiers)) selectQuery.where.and(clause) try { @@ -135,7 +136,7 @@ class ExternalStore(keySpace: String , table: String , primaryKey: java.util.Lis def delete(identifiers: List[String])(implicit ec: ExecutionContext): Future[Response] = { val delete = QueryBuilder.delete() - import scala.collection.JavaConversions._ + import scala.collection.JavaConverters._ val deleteQuery = delete.from(keySpace, table).where(QueryBuilder.in(primaryKey.get(0), seqAsJavaList(identifiers))) try { val session: Session = CassandraConnector.getSession diff --git a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/external/store/ExternalStoreFactory.scala b/ontology-engine/graph-core_2.12/src/main/scala/org/sunbird/graph/external/store/ExternalStoreFactory.scala similarity index 100% rename from ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/external/store/ExternalStoreFactory.scala rename to ontology-engine/graph-core_2.12/src/main/scala/org/sunbird/graph/external/store/ExternalStoreFactory.scala diff --git a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala b/ontology-engine/graph-core_2.12/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala similarity index 100% rename from ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala rename to ontology-engine/graph-core_2.12/src/main/scala/org/sunbird/graph/util/CSPMetaUtil.scala diff --git a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/ScalaJsonUtil.scala b/ontology-engine/graph-core_2.12/src/main/scala/org/sunbird/graph/util/ScalaJsonUtil.scala similarity index 100% rename from ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/util/ScalaJsonUtil.scala rename to ontology-engine/graph-core_2.12/src/main/scala/org/sunbird/graph/util/ScalaJsonUtil.scala diff --git a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/validator/NodeValidator.scala b/ontology-engine/graph-core_2.12/src/main/scala/org/sunbird/graph/validator/NodeValidator.scala similarity index 97% rename from ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/validator/NodeValidator.scala rename to ontology-engine/graph-core_2.12/src/main/scala/org/sunbird/graph/validator/NodeValidator.scala index 5e102a05f..3d533f7dd 100644 --- a/ontology-engine/graph-core_2.11/src/main/scala/org/sunbird/graph/validator/NodeValidator.scala +++ b/ontology-engine/graph-core_2.12/src/main/scala/org/sunbird/graph/validator/NodeValidator.scala @@ -10,7 +10,7 @@ import org.sunbird.graph.dac.model.{Filter, MetadataCriterion, Node, SearchCondi import org.sunbird.graph.exception.GraphErrorCodes import org.sunbird.graph.service.operation.SearchAsyncOperations -import scala.collection.JavaConversions._ +import scala.collection.convert.ImplicitConversions._ import scala.collection.JavaConverters._ import scala.concurrent.{ExecutionContext, Future} diff --git a/ontology-engine/graph-core_2.11/src/test/resources/application.conf b/ontology-engine/graph-core_2.12/src/test/resources/application.conf similarity index 100% rename from ontology-engine/graph-core_2.11/src/test/resources/application.conf rename to ontology-engine/graph-core_2.12/src/test/resources/application.conf diff --git a/ontology-engine/graph-core_2.11/src/test/resources/cassandra-unit.yaml b/ontology-engine/graph-core_2.12/src/test/resources/cassandra-unit.yaml similarity index 100% rename from ontology-engine/graph-core_2.11/src/test/resources/cassandra-unit.yaml rename to ontology-engine/graph-core_2.12/src/test/resources/cassandra-unit.yaml diff --git a/ontology-engine/graph-core_2.11/src/test/resources/logback.xml b/ontology-engine/graph-core_2.12/src/test/resources/logback.xml similarity index 100% rename from ontology-engine/graph-core_2.11/src/test/resources/logback.xml rename to ontology-engine/graph-core_2.12/src/test/resources/logback.xml diff --git a/ontology-engine/graph-core_2.11/src/test/scala/org/sunbird/graph/BaseSpec.scala b/ontology-engine/graph-core_2.12/src/test/scala/org/sunbird/graph/BaseSpec.scala similarity index 100% rename from ontology-engine/graph-core_2.11/src/test/scala/org/sunbird/graph/BaseSpec.scala rename to ontology-engine/graph-core_2.12/src/test/scala/org/sunbird/graph/BaseSpec.scala diff --git a/ontology-engine/graph-core_2.11/src/test/scala/org/sunbird/graph/external/ExternalPropsManagerTest.scala b/ontology-engine/graph-core_2.12/src/test/scala/org/sunbird/graph/external/ExternalPropsManagerTest.scala similarity index 100% rename from ontology-engine/graph-core_2.11/src/test/scala/org/sunbird/graph/external/ExternalPropsManagerTest.scala rename to ontology-engine/graph-core_2.12/src/test/scala/org/sunbird/graph/external/ExternalPropsManagerTest.scala diff --git a/ontology-engine/graph-core_2.11/src/test/scala/org/sunbird/graph/util/CSPMetaUtilTest.scala b/ontology-engine/graph-core_2.12/src/test/scala/org/sunbird/graph/util/CSPMetaUtilTest.scala similarity index 100% rename from ontology-engine/graph-core_2.11/src/test/scala/org/sunbird/graph/util/CSPMetaUtilTest.scala rename to ontology-engine/graph-core_2.12/src/test/scala/org/sunbird/graph/util/CSPMetaUtilTest.scala diff --git a/ontology-engine/graph-core_2.11/src/test/scala/org/sunbird/graph/util/ScalaJsonUtilTest.scala b/ontology-engine/graph-core_2.12/src/test/scala/org/sunbird/graph/util/ScalaJsonUtilTest.scala similarity index 100% rename from ontology-engine/graph-core_2.11/src/test/scala/org/sunbird/graph/util/ScalaJsonUtilTest.scala rename to ontology-engine/graph-core_2.12/src/test/scala/org/sunbird/graph/util/ScalaJsonUtilTest.scala diff --git a/ontology-engine/graph-engine_2.11/pom.xml b/ontology-engine/graph-engine_2.12/pom.xml similarity index 97% rename from ontology-engine/graph-engine_2.11/pom.xml rename to ontology-engine/graph-engine_2.12/pom.xml index a85149bf0..a12d04d19 100644 --- a/ontology-engine/graph-engine_2.11/pom.xml +++ b/ontology-engine/graph-engine_2.12/pom.xml @@ -9,12 +9,12 @@ 4.0.0 - graph-engine_2.11 + graph-engine_2.12 org.sunbird - graph-core_2.11 + graph-core_2.12 1.0-SNAPSHOT diff --git a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/engine/CaseClasses.scala b/ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/engine/CaseClasses.scala similarity index 100% rename from ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/engine/CaseClasses.scala rename to ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/engine/CaseClasses.scala diff --git a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/external/store/ExternalStore.scala b/ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/external/store/ExternalStore.scala similarity index 98% rename from ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/external/store/ExternalStore.scala rename to ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/external/store/ExternalStore.scala index 5baf6639a..491a21ec5 100644 --- a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/external/store/ExternalStore.scala +++ b/ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/external/store/ExternalStore.scala @@ -95,7 +95,7 @@ class ExternalStore(keySpace: String , table: String , primaryKey: java.util.Lis def delete(identifiers: List[String])(implicit ec: ExecutionContext): Future[Response] = { val delete = QueryBuilder.delete() - import scala.collection.JavaConversions._ + import scala.collection.JavaConverters._ val deleteQuery = delete.from(keySpace, table).where(QueryBuilder.in(primaryKey.get(0), seqAsJavaList(identifiers))) try { val session: Session = CassandraConnector.getSession @@ -123,7 +123,8 @@ class ExternalStore(keySpace: String , table: String , primaryKey: java.util.Lis }) } val selectQuery = select.from(keySpace, table) - import scala.collection.JavaConversions._ + import scala.collection.JavaConverters._ + import scala.collection.convert.ImplicitConversions._ val clause: Clause = QueryBuilder.in(primaryKey.get(0), seqAsJavaList(identifiers)) selectQuery.where.and(clause) try { diff --git a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/health/HealthCheckManager.scala b/ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/health/HealthCheckManager.scala similarity index 100% rename from ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/health/HealthCheckManager.scala rename to ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/health/HealthCheckManager.scala diff --git a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/nodes/DataNode.scala b/ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/nodes/DataNode.scala similarity index 99% rename from ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/nodes/DataNode.scala rename to ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/nodes/DataNode.scala index 1e7b83178..1b7dc969a 100644 --- a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/nodes/DataNode.scala +++ b/ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/nodes/DataNode.scala @@ -14,7 +14,7 @@ import org.sunbird.graph.dac.model.{Filter, MetadataCriterion, Node, Relation, S import org.sunbird.graph.schema.{DefinitionDTO, DefinitionFactory, DefinitionNode} import org.sunbird.parseq.Task -import scala.collection.JavaConversions._ +import scala.collection.convert.ImplicitConversions._ import scala.collection.JavaConverters._ import scala.concurrent.{ExecutionContext, Future} diff --git a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/relations/AbstractRelation.scala b/ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/relations/AbstractRelation.scala similarity index 100% rename from ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/relations/AbstractRelation.scala rename to ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/relations/AbstractRelation.scala diff --git a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/relations/AssociationRelation.scala b/ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/relations/AssociationRelation.scala similarity index 100% rename from ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/relations/AssociationRelation.scala rename to ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/relations/AssociationRelation.scala diff --git a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/relations/IRelation.scala b/ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/relations/IRelation.scala similarity index 100% rename from ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/relations/IRelation.scala rename to ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/relations/IRelation.scala diff --git a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/relations/RelationHandler.scala b/ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/relations/RelationHandler.scala similarity index 100% rename from ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/relations/RelationHandler.scala rename to ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/relations/RelationHandler.scala diff --git a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/relations/SequenceMembershipRelation.scala b/ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/relations/SequenceMembershipRelation.scala similarity index 100% rename from ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/relations/SequenceMembershipRelation.scala rename to ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/relations/SequenceMembershipRelation.scala diff --git a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/CategoryDefinitionValidator.scala b/ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/schema/CategoryDefinitionValidator.scala similarity index 100% rename from ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/CategoryDefinitionValidator.scala rename to ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/schema/CategoryDefinitionValidator.scala diff --git a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/CoreDomainObject.scala b/ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/schema/CoreDomainObject.scala similarity index 100% rename from ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/CoreDomainObject.scala rename to ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/schema/CoreDomainObject.scala diff --git a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/DefinitionDTO.scala b/ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/schema/DefinitionDTO.scala similarity index 100% rename from ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/DefinitionDTO.scala rename to ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/schema/DefinitionDTO.scala diff --git a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/DefinitionFactory.scala b/ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/schema/DefinitionFactory.scala similarity index 100% rename from ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/DefinitionFactory.scala rename to ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/schema/DefinitionFactory.scala diff --git a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/DefinitionNode.scala b/ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/schema/DefinitionNode.scala similarity index 99% rename from ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/DefinitionNode.scala rename to ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/schema/DefinitionNode.scala index f09c086ee..8fadf9b19 100644 --- a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/DefinitionNode.scala +++ b/ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/schema/DefinitionNode.scala @@ -11,7 +11,7 @@ import org.sunbird.common.dto.Request import org.sunbird.graph.OntologyEngineContext import org.sunbird.graph.dac.model.{Node, Relation} -import scala.collection.JavaConversions._ +import scala.collection.convert.ImplicitConversions._ import scala.concurrent.{ExecutionContext, Future} object DefinitionNode { diff --git a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/FrameworkMasterCategoryMap.scala b/ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/schema/FrameworkMasterCategoryMap.scala similarity index 100% rename from ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/FrameworkMasterCategoryMap.scala rename to ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/schema/FrameworkMasterCategoryMap.scala diff --git a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/IDefinition.scala b/ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/schema/IDefinition.scala similarity index 100% rename from ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/IDefinition.scala rename to ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/schema/IDefinition.scala diff --git a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/ObjectCategoryDefinition.scala b/ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/schema/ObjectCategoryDefinition.scala similarity index 100% rename from ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/ObjectCategoryDefinition.scala rename to ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/schema/ObjectCategoryDefinition.scala diff --git a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/ObjectCategoryDefinitionMap.scala b/ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/schema/ObjectCategoryDefinitionMap.scala similarity index 100% rename from ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/ObjectCategoryDefinitionMap.scala rename to ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/schema/ObjectCategoryDefinitionMap.scala diff --git a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/validator/BaseDefinitionNode.scala b/ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/schema/validator/BaseDefinitionNode.scala similarity index 100% rename from ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/validator/BaseDefinitionNode.scala rename to ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/schema/validator/BaseDefinitionNode.scala diff --git a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/validator/FrameworkValidator.scala b/ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/schema/validator/FrameworkValidator.scala similarity index 99% rename from ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/validator/FrameworkValidator.scala rename to ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/schema/validator/FrameworkValidator.scala index 4e74cab31..86c209efd 100644 --- a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/validator/FrameworkValidator.scala +++ b/ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/schema/validator/FrameworkValidator.scala @@ -13,7 +13,7 @@ import org.sunbird.graph.common.enums.SystemProperties import org.sunbird.graph.dac.model._ import org.sunbird.graph.schema.{FrameworkMasterCategoryMap, IDefinition} -import scala.collection.JavaConversions._ +import scala.collection.convert.ImplicitConversions._ import scala.collection.JavaConverters._ import scala.collection.Map import scala.concurrent.{ExecutionContext, Future} diff --git a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/validator/PropAsEdgeValidator.scala b/ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/schema/validator/PropAsEdgeValidator.scala similarity index 97% rename from ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/validator/PropAsEdgeValidator.scala rename to ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/schema/validator/PropAsEdgeValidator.scala index a0ef9353d..fed42747a 100644 --- a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/validator/PropAsEdgeValidator.scala +++ b/ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/schema/validator/PropAsEdgeValidator.scala @@ -7,7 +7,7 @@ import org.sunbird.graph.OntologyEngineContext import org.sunbird.graph.dac.model.Node import org.sunbird.graph.schema.IDefinition -import scala.collection.JavaConversions._ +import scala.collection.convert.ImplicitConversions._ import scala.concurrent.{ExecutionContext, Future} trait PropAsEdgeValidator extends IDefinition { diff --git a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/validator/RelationValidator.scala b/ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/schema/validator/RelationValidator.scala similarity index 100% rename from ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/validator/RelationValidator.scala rename to ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/schema/validator/RelationValidator.scala diff --git a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/validator/SchemaValidator.scala b/ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/schema/validator/SchemaValidator.scala similarity index 100% rename from ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/validator/SchemaValidator.scala rename to ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/schema/validator/SchemaValidator.scala diff --git a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/validator/VersionKeyValidator.scala b/ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/schema/validator/VersionKeyValidator.scala similarity index 100% rename from ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/validator/VersionKeyValidator.scala rename to ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/schema/validator/VersionKeyValidator.scala diff --git a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/validator/VersioningNode.scala b/ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/schema/validator/VersioningNode.scala similarity index 99% rename from ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/validator/VersioningNode.scala rename to ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/schema/validator/VersioningNode.scala index 36190dca2..452b75fda 100644 --- a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/schema/validator/VersioningNode.scala +++ b/ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/schema/validator/VersioningNode.scala @@ -17,7 +17,7 @@ import org.sunbird.graph.service.operation.{NodeAsyncOperations, SearchAsyncOper import org.sunbird.graph.utils.{NodeUtil, ScalaJsonUtils} import org.sunbird.telemetry.logger.TelemetryManager -import scala.collection.JavaConversions._ +import scala.collection.convert.ImplicitConversions._ import scala.concurrent.{ExecutionContext, Future} trait VersioningNode extends IDefinition { diff --git a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/utils/NodeUtil.scala b/ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/utils/NodeUtil.scala similarity index 99% rename from ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/utils/NodeUtil.scala rename to ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/utils/NodeUtil.scala index 79c672cc9..2719a6dcd 100644 --- a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/utils/NodeUtil.scala +++ b/ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/utils/NodeUtil.scala @@ -14,7 +14,6 @@ import org.sunbird.graph.schema.{DefinitionNode, ObjectCategoryDefinition, Objec import scala.collection.JavaConverters import scala.collection.JavaConverters._ -import scala.collection.JavaConversions._ import scala.concurrent.ExecutionContext object NodeUtil { diff --git a/ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/utils/ScalaJsonUtils.scala b/ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/utils/ScalaJsonUtils.scala similarity index 100% rename from ontology-engine/graph-engine_2.11/src/main/scala/org/sunbird/graph/utils/ScalaJsonUtils.scala rename to ontology-engine/graph-engine_2.12/src/main/scala/org/sunbird/graph/utils/ScalaJsonUtils.scala diff --git a/ontology-engine/graph-engine_2.11/src/test/resources/application.conf b/ontology-engine/graph-engine_2.12/src/test/resources/application.conf similarity index 100% rename from ontology-engine/graph-engine_2.11/src/test/resources/application.conf rename to ontology-engine/graph-engine_2.12/src/test/resources/application.conf diff --git a/ontology-engine/graph-engine_2.11/src/test/resources/cassandra-unit.yaml b/ontology-engine/graph-engine_2.12/src/test/resources/cassandra-unit.yaml similarity index 100% rename from ontology-engine/graph-engine_2.11/src/test/resources/cassandra-unit.yaml rename to ontology-engine/graph-engine_2.12/src/test/resources/cassandra-unit.yaml diff --git a/ontology-engine/graph-engine_2.11/src/test/resources/logback.xml b/ontology-engine/graph-engine_2.12/src/test/resources/logback.xml similarity index 100% rename from ontology-engine/graph-engine_2.11/src/test/resources/logback.xml rename to ontology-engine/graph-engine_2.12/src/test/resources/logback.xml diff --git a/ontology-engine/graph-engine_2.11/src/test/scala/org/sunbird/graph/BaseSpec.scala b/ontology-engine/graph-engine_2.12/src/test/scala/org/sunbird/graph/BaseSpec.scala similarity index 100% rename from ontology-engine/graph-engine_2.11/src/test/scala/org/sunbird/graph/BaseSpec.scala rename to ontology-engine/graph-engine_2.12/src/test/scala/org/sunbird/graph/BaseSpec.scala diff --git a/ontology-engine/graph-engine_2.11/src/test/scala/org/sunbird/graph/health/TestHealthCheckManager.scala b/ontology-engine/graph-engine_2.12/src/test/scala/org/sunbird/graph/health/TestHealthCheckManager.scala similarity index 100% rename from ontology-engine/graph-engine_2.11/src/test/scala/org/sunbird/graph/health/TestHealthCheckManager.scala rename to ontology-engine/graph-engine_2.12/src/test/scala/org/sunbird/graph/health/TestHealthCheckManager.scala diff --git a/ontology-engine/graph-engine_2.11/src/test/scala/org/sunbird/graph/nodes/TestDataNode.scala b/ontology-engine/graph-engine_2.12/src/test/scala/org/sunbird/graph/nodes/TestDataNode.scala similarity index 100% rename from ontology-engine/graph-engine_2.11/src/test/scala/org/sunbird/graph/nodes/TestDataNode.scala rename to ontology-engine/graph-engine_2.12/src/test/scala/org/sunbird/graph/nodes/TestDataNode.scala diff --git a/ontology-engine/graph-engine_2.11/src/test/scala/org/sunbird/graph/schema/TestObjectCategoryDefinitionMap.scala b/ontology-engine/graph-engine_2.12/src/test/scala/org/sunbird/graph/schema/TestObjectCategoryDefinitionMap.scala similarity index 100% rename from ontology-engine/graph-engine_2.11/src/test/scala/org/sunbird/graph/schema/TestObjectCategoryDefinitionMap.scala rename to ontology-engine/graph-engine_2.12/src/test/scala/org/sunbird/graph/schema/TestObjectCategoryDefinitionMap.scala diff --git a/ontology-engine/graph-engine_2.11/src/test/scala/org/sunbird/graph/schema/validator/TestSchemaValidator.scala b/ontology-engine/graph-engine_2.12/src/test/scala/org/sunbird/graph/schema/validator/TestSchemaValidator.scala similarity index 100% rename from ontology-engine/graph-engine_2.11/src/test/scala/org/sunbird/graph/schema/validator/TestSchemaValidator.scala rename to ontology-engine/graph-engine_2.12/src/test/scala/org/sunbird/graph/schema/validator/TestSchemaValidator.scala diff --git a/ontology-engine/graph-engine_2.11/src/test/scala/org/sunbird/graph/utils/NodeUtilTest.scala b/ontology-engine/graph-engine_2.12/src/test/scala/org/sunbird/graph/utils/NodeUtilTest.scala similarity index 100% rename from ontology-engine/graph-engine_2.11/src/test/scala/org/sunbird/graph/utils/NodeUtilTest.scala rename to ontology-engine/graph-engine_2.12/src/test/scala/org/sunbird/graph/utils/NodeUtilTest.scala diff --git a/ontology-engine/graph-engine_2.11/src/test/scala/org/sunbird/graph/utils/ScalaJsonUtilsTest.scala b/ontology-engine/graph-engine_2.12/src/test/scala/org/sunbird/graph/utils/ScalaJsonUtilsTest.scala similarity index 100% rename from ontology-engine/graph-engine_2.11/src/test/scala/org/sunbird/graph/utils/ScalaJsonUtilsTest.scala rename to ontology-engine/graph-engine_2.12/src/test/scala/org/sunbird/graph/utils/ScalaJsonUtilsTest.scala diff --git a/ontology-engine/pom.xml b/ontology-engine/pom.xml index b44cb78ef..3dc8826f6 100644 --- a/ontology-engine/pom.xml +++ b/ontology-engine/pom.xml @@ -13,8 +13,8 @@ graph-common graph-dac-api - graph-core_2.11 - graph-engine_2.11 + graph-core_2.12 + graph-engine_2.12 parseq diff --git a/platform-modules/import-manager/pom.xml b/platform-modules/import-manager/pom.xml index 7914fcf5c..79c113220 100644 --- a/platform-modules/import-manager/pom.xml +++ b/platform-modules/import-manager/pom.xml @@ -24,7 +24,7 @@ org.sunbird - graph-engine_2.11 + graph-engine_2.12 1.0-SNAPSHOT jar diff --git a/platform-modules/mimetype-manager/pom.xml b/platform-modules/mimetype-manager/pom.xml index ea978cbd5..1cd5cd3fb 100644 --- a/platform-modules/mimetype-manager/pom.xml +++ b/platform-modules/mimetype-manager/pom.xml @@ -10,7 +10,7 @@ 4.0.0 mimetype-manager - 2.11 + 2.12 2.7.2 @@ -23,13 +23,13 @@ org.sunbird - graph-engine_2.11 + graph-engine_2.12 1.0-SNAPSHOT jar org.sunbird - cloud-store-sdk + cloud-store-sdk_2.12 1.4.3 diff --git a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/CollectionMimeTypeMgrImpl.scala b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/CollectionMimeTypeMgrImpl.scala index 0f833977d..b1c7bd8bc 100644 --- a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/CollectionMimeTypeMgrImpl.scala +++ b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/CollectionMimeTypeMgrImpl.scala @@ -15,7 +15,7 @@ import org.sunbird.graph.utils.ScalaJsonUtils import org.sunbird.mimetype.mgr.{BaseMimeTypeManager, MimeTypeManager} import scala.collection.JavaConverters._ -import scala.collection.JavaConversions._ +import scala.collection.convert.ImplicitConversions._ import scala.concurrent.{ExecutionContext, Future} class CollectionMimeTypeMgrImpl(implicit ss: StorageService) extends BaseMimeTypeManager with MimeTypeManager { diff --git a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/EcmlMimeTypeMgrImpl.scala b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/EcmlMimeTypeMgrImpl.scala index 773ca2842..e1c3c77f0 100644 --- a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/EcmlMimeTypeMgrImpl.scala +++ b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/mimetype/mgr/impl/EcmlMimeTypeMgrImpl.scala @@ -18,7 +18,7 @@ import org.sunbird.telemetry.logger.TelemetryManager import org.xml.sax.{InputSource, SAXException} import scala.collection.JavaConverters._ -import scala.collection.JavaConversions._ +import scala.collection.convert.ImplicitConversions._ import scala.concurrent.{ExecutionContext, Future} class EcmlMimeTypeMgrImpl(implicit ss: StorageService) extends BaseMimeTypeManager with MimeTypeManager { diff --git a/pom.xml b/pom.xml index 7810ee416..0ff46d962 100644 --- a/pom.xml +++ b/pom.xml @@ -12,8 +12,8 @@ UTF-8 1.4.1 - 2.11 - 2.11.12 + 2.12 + 2.12.11 3.0.8 2.9.8 diff --git a/search-api/pom.xml b/search-api/pom.xml index 0ffe3c713..95915182b 100644 --- a/search-api/pom.xml +++ b/search-api/pom.xml @@ -11,7 +11,7 @@ UTF-8 UTF-8 - 2.11 + 2.12 search-api diff --git a/search-api/search-core/pom.xml b/search-api/search-core/pom.xml index fbdf374ff..38b8e3b9d 100644 --- a/search-api/search-core/pom.xml +++ b/search-api/search-core/pom.xml @@ -14,7 +14,7 @@ UTF-8 2.5.22 - 2.11 + 2.12 diff --git a/search-api/search-service/app/controllers/SearchBaseController.scala b/search-api/search-service/app/controllers/SearchBaseController.scala index 9e1735b35..faecaf067 100644 --- a/search-api/search-service/app/controllers/SearchBaseController.scala +++ b/search-api/search-service/app/controllers/SearchBaseController.scala @@ -11,7 +11,7 @@ import play.api.mvc._ import java.util import java.util.UUID -import scala.collection.JavaConversions._ +import scala.collection.JavaConverters._ import scala.concurrent.{ExecutionContext, Future} abstract class SearchBaseController(protected val cc: ControllerComponents)(implicit exec: ExecutionContext) extends AbstractController(cc) { diff --git a/taxonomy-api/pom.xml b/taxonomy-api/pom.xml index f0c74ceaf..021ae730a 100644 --- a/taxonomy-api/pom.xml +++ b/taxonomy-api/pom.xml @@ -19,7 +19,7 @@ UTF-8 UTF-8 - 2.11 + 2.12 diff --git a/taxonomy-api/taxonomy-actors/pom.xml b/taxonomy-api/taxonomy-actors/pom.xml index e4fb38295..f37c10c7f 100644 --- a/taxonomy-api/taxonomy-actors/pom.xml +++ b/taxonomy-api/taxonomy-actors/pom.xml @@ -23,7 +23,7 @@ org.sunbird - graph-engine_2.11 + graph-engine_2.12 1.0-SNAPSHOT jar diff --git a/taxonomy-api/taxonomy-service/app/controllers/BaseController.scala b/taxonomy-api/taxonomy-service/app/controllers/BaseController.scala index 47670af40..07271a486 100644 --- a/taxonomy-api/taxonomy-service/app/controllers/BaseController.scala +++ b/taxonomy-api/taxonomy-service/app/controllers/BaseController.scala @@ -10,7 +10,7 @@ import org.sunbird.common.exception.ResponseCode import play.api.mvc._ import utils.JavaJsonUtils -import scala.collection.JavaConversions._ + import scala.collection.JavaConverters._ import scala.collection.mutable import scala.concurrent.{ExecutionContext, Future} diff --git a/vmsetup.sh b/vmsetup.sh index 83b01344c..acbdc056d 100644 --- a/vmsetup.sh +++ b/vmsetup.sh @@ -26,4 +26,4 @@ find ./ -type f -name "*.java" -print0 | xargs -0 sed -i -e 's/\/data\//~\//g' mvn scoverage:report JAVA_REPORT_PATHS=`find /home/circleci/project -iname jacoco.xml | awk 'BEGIN { RS = "" ; FS = "\n"; OFS = ","}{$1=$1; print $0}'` -mvn verify sonar:sonar -Dsonar.projectKey=project-sunbird_knowledge-platform -Dsonar.organization=project-sunbird -Dsonar.host.url=https://sonarcloud.io -Dsonar.coverage.exclusions=**/CustomProblemHandler.java -Dsonar.scala.coverage.reportPaths=/home/circleci/project/content-api/hierarchy-manager/target/scoverage.xml,/home/circleci/project/content-api/content-service/target/scoverage.xml,/home/circleci/project/target/scoverage.xml,/home/circleci/project/ontology-engine/graph-engine_2.11/target/scoverage.xml,/home/circleci/project/ontology-engine/parseq/target/scoverage.xml -Dsonar.coverage.jacoco.xmlReportPaths=${JAVA_REPORT_PATHS} +mvn verify sonar:sonar -Dsonar.projectKey=project-sunbird_knowledge-platform -Dsonar.organization=project-sunbird -Dsonar.host.url=https://sonarcloud.io -Dsonar.coverage.exclusions=**/CustomProblemHandler.java -Dsonar.scala.coverage.reportPaths=/home/circleci/project/content-api/hierarchy-manager/target/scoverage.xml,/home/circleci/project/content-api/content-service/target/scoverage.xml,/home/circleci/project/target/scoverage.xml,/home/circleci/project/ontology-engine/graph-engine_2.12/target/scoverage.xml,/home/circleci/project/ontology-engine/parseq/target/scoverage.xml -Dsonar.coverage.jacoco.xmlReportPaths=${JAVA_REPORT_PATHS} From bb93d6d612f4eef598d4ae9be527e65604cf4d9e Mon Sep 17 00:00:00 2001 From: aimansharief <110388201+aimansharief@users.noreply.github.com> Date: Fri, 24 Mar 2023 09:47:35 +0530 Subject: [PATCH 194/490] Issue KN-767 feat: Added Terraform scripts and Helmcharts for one click installation --- knowlg-automation/LICENSE | 21 + knowlg-automation/README.md | 29 + .../helm_charts/cassandra/Chart.yaml | 5 + .../cassandra/templates/deployment.yaml | 33 + .../helm_charts/cassandra/values.yaml | 4 + .../helm_charts/druid-cluster/.helmignore | 22 + .../helm_charts/druid-cluster/Chart.yaml | 19 + .../druid-cluster/charts/zookeeper-10.0.0.tgz | Bin 0 -> 43731 bytes .../helm_charts/druid-cluster/druid-crd.yaml | 4487 +++++++++++++++++ .../druid-cluster/templates/_helpers.tpl | 51 + .../templates/druid_statefulset.yaml | 476 ++ .../templates/historical_hpa.yaml | 29 + .../helm_charts/druid-cluster/values.yaml | 345 ++ .../helm_charts/druid-operator/Chart.yaml | 23 + .../druid-operator/templates/NOTES.txt | 1 + .../druid-operator/templates/_helpers.tpl | 62 + .../crds/druid.apache.org_druids.yaml | 4467 ++++++++++++++++ .../druid-operator/templates/deployment.yaml | 59 + .../druid-operator/templates/role.yaml | 95 + .../templates/role_binding.yaml | 23 + .../templates/serviceaccount.yaml | 13 + .../helm_charts/druid-operator/values.yaml | 63 + .../helm_charts/elasticsearch/.helmignore | 21 + .../helm_charts/elasticsearch/Chart.lock | 9 + .../helm_charts/elasticsearch/Chart.yaml | 28 + .../helm_charts/elasticsearch/README.md | 969 ++++ .../elasticsearch/charts/common-2.2.4.tgz | Bin 0 -> 12364 bytes .../elasticsearch/charts/kibana-10.2.16.tgz | Bin 0 -> 42078 bytes .../elasticsearch/templates/NOTES.txt | 121 + .../elasticsearch/templates/_helpers.tpl | 585 +++ .../elasticsearch/templates/configmap.yaml | 23 + .../templates/coordinating/hpa.yaml | 47 + .../coordinating/serviceaccount.yaml | 22 + .../templates/coordinating/statefulset.yaml | 313 ++ .../templates/coordinating/svc-headless.yaml | 28 + .../elasticsearch/templates/data/hpa.yaml | 47 + .../templates/data/serviceaccount.yaml | 22 + .../templates/data/statefulset.yaml | 373 ++ .../templates/data/svc-headless.yaml | 28 + .../elasticsearch/templates/extra-list.yaml | 4 + .../elasticsearch/templates/ingest/hpa.yaml | 47 + .../templates/ingest/ingress.yaml | 65 + .../templates/ingest/service.yaml | 60 + .../templates/ingest/serviceaccount.yaml | 22 + .../templates/ingest/statefulset.yaml | 313 ++ .../templates/ingest/svc-headless.yaml | 28 + .../templates/ingress-tls-secrets.yaml | 94 + .../elasticsearch/templates/ingress.yaml | 65 + .../templates/initialization-configmap.yaml | 16 + .../elasticsearch/templates/master/hpa.yaml | 47 + .../templates/master/serviceaccount.yaml | 22 + .../templates/master/statefulset.yaml | 373 ++ .../templates/master/svc-headless.yaml | 28 + .../templates/metrics/deployment.yaml | 182 + .../templates/metrics/prometheusrule.yaml | 22 + .../templates/metrics/service.yaml | 24 + .../templates/metrics/servicemonitor.yaml | 46 + .../elasticsearch/templates/secrets.yaml | 43 + .../elasticsearch/templates/service.yaml | 66 + .../elasticsearch/templates/tls-secret.yaml | 117 + .../helm_charts/elasticsearch/values.yaml | 2145 ++++++++ .../helm_charts/flink/.helmignore | 22 + .../helm_charts/flink/Chart.yaml | 21 + .../helm_charts/flink/templates/_helpers.tpl | 67 + .../flink/templates/flink_job_configmap.yaml | 14 + .../flink/templates/flink_job_deployment.yaml | 202 + .../helm_charts/flink/values.yaml | 183 + .../helm_charts/kafka/.helmignore | 21 + .../helm_charts/kafka/Chart.lock | 9 + .../helm_charts/kafka/Chart.yaml | 33 + knowlg-automation/helm_charts/kafka/README.md | 1050 ++++ .../kafka/charts/common/.helmignore | 22 + .../kafka/charts/common/Chart.yaml | 23 + .../helm_charts/kafka/charts/common/README.md | 351 ++ .../charts/common/templates/_affinities.tpl | 106 + .../charts/common/templates/_capabilities.tpl | 154 + .../kafka/charts/common/templates/_errors.tpl | 23 + .../kafka/charts/common/templates/_images.tpl | 76 + .../charts/common/templates/_ingress.tpl | 68 + .../kafka/charts/common/templates/_labels.tpl | 18 + .../kafka/charts/common/templates/_names.tpl | 66 + .../charts/common/templates/_secrets.tpl | 165 + .../charts/common/templates/_storage.tpl | 23 + .../charts/common/templates/_tplvalues.tpl | 13 + .../kafka/charts/common/templates/_utils.tpl | 62 + .../charts/common/templates/_warnings.tpl | 14 + .../templates/validations/_cassandra.tpl | 72 + .../common/templates/validations/_mariadb.tpl | 103 + .../common/templates/validations/_mongodb.tpl | 108 + .../common/templates/validations/_mysql.tpl | 103 + .../templates/validations/_postgresql.tpl | 129 + .../common/templates/validations/_redis.tpl | 76 + .../templates/validations/_validations.tpl | 46 + .../kafka/charts/common/values.yaml | 5 + .../kafka/charts/zookeeper/.helmignore | 21 + .../kafka/charts/zookeeper/Chart.lock | 6 + .../kafka/charts/zookeeper/Chart.yaml | 24 + .../kafka/charts/zookeeper/README.md | 532 ++ .../zookeeper/charts/common/.helmignore | 22 + .../charts/zookeeper/charts/common/Chart.yaml | 23 + .../charts/zookeeper/charts/common/README.md | 351 ++ .../charts/common/templates/_affinities.tpl | 106 + .../charts/common/templates/_capabilities.tpl | 154 + .../charts/common/templates/_errors.tpl | 23 + .../charts/common/templates/_images.tpl | 76 + .../charts/common/templates/_ingress.tpl | 68 + .../charts/common/templates/_labels.tpl | 18 + .../charts/common/templates/_names.tpl | 70 + .../charts/common/templates/_secrets.tpl | 165 + .../charts/common/templates/_storage.tpl | 23 + .../charts/common/templates/_tplvalues.tpl | 13 + .../charts/common/templates/_utils.tpl | 62 + .../charts/common/templates/_warnings.tpl | 14 + .../templates/validations/_cassandra.tpl | 72 + .../common/templates/validations/_mariadb.tpl | 103 + .../common/templates/validations/_mongodb.tpl | 108 + .../common/templates/validations/_mysql.tpl | 103 + .../templates/validations/_postgresql.tpl | 129 + .../common/templates/validations/_redis.tpl | 76 + .../templates/validations/_validations.tpl | 46 + .../zookeeper/charts/common/values.yaml | 5 + .../charts/zookeeper/templates/NOTES.txt | 76 + .../charts/zookeeper/templates/_helpers.tpl | 361 ++ .../charts/zookeeper/templates/configmap.yaml | 17 + .../zookeeper/templates/extra-list.yaml | 4 + .../zookeeper/templates/metrics-svc.yaml | 29 + .../zookeeper/templates/networkpolicy.yaml | 41 + .../kafka/charts/zookeeper/templates/pdb.yaml | 26 + .../zookeeper/templates/prometheusrule.yaml | 27 + .../templates/scripts-configmap.yaml | 102 + .../charts/zookeeper/templates/secrets.yaml | 77 + .../zookeeper/templates/serviceaccount.yaml | 21 + .../zookeeper/templates/servicemonitor.yaml | 53 + .../zookeeper/templates/statefulset.yaml | 532 ++ .../zookeeper/templates/svc-headless.yaml | 42 + .../kafka/charts/zookeeper/templates/svc.yaml | 71 + .../zookeeper/templates/tls-secrets.yaml | 55 + .../kafka/charts/zookeeper/values.yaml | 877 ++++ .../helm_charts/kafka/templates/NOTES.txt | 310 ++ .../helm_charts/kafka/templates/_helpers.tpl | 509 ++ .../kafka/templates/configmap.yaml | 17 + .../kafka/templates/extra-list.yaml | 4 + .../kafka/templates/jaas-secret.yaml | 40 + .../kafka/templates/jmx-configmap.yaml | 64 + .../kafka/templates/jmx-metrics-svc.yaml | 34 + .../templates/kafka-metrics-deployment.yaml | 171 + .../kafka-metrics-serviceaccount.yaml | 16 + .../kafka/templates/kafka-metrics-svc.yaml | 34 + .../templates/kafka-provisioning-secret.yaml | 19 + .../kafka-provisioning-serviceaccount.yaml | 15 + .../kafka/templates/kafka-provisioning.yaml | 260 + .../kafka/templates/log4j-configmap.yaml | 17 + .../kafka/templates/networkpolicy-egress.yaml | 22 + .../templates/networkpolicy-ingress.yaml | 53 + .../kafka/templates/poddisruptionbudget.yaml | 26 + .../kafka/templates/prometheusrule.yaml | 20 + .../helm_charts/kafka/templates/role.yaml | 24 + .../kafka/templates/rolebinding.yaml | 23 + .../kafka/templates/scripts-configmap.yaml | 202 + .../kafka/templates/serviceaccount.yaml | 20 + .../templates/servicemonitor-jmx-metrics.yaml | 53 + .../templates/servicemonitor-metrics.yaml | 53 + .../kafka/templates/statefulset.yaml | 610 +++ .../kafka/templates/svc-external-access.yaml | 63 + .../kafka/templates/svc-headless.yaml | 37 + .../helm_charts/kafka/templates/svc.yaml | 63 + .../kafka/templates/tls-secrets.yaml | 30 + .../helm_charts/kafka/values.yaml | 1781 +++++++ .../helm_charts/logstash/.helmignore | 22 + .../helm_charts/logstash/Chart.lock | 6 + .../helm_charts/logstash/Chart.yaml | 26 + .../helm_charts/logstash/README.md | 340 ++ .../logstash/charts/common-2.2.4.tgz | Bin 0 -> 12364 bytes .../helm_charts/logstash/templates/NOTES.txt | 63 + .../logstash/templates/_helpers.tpl | 67 + .../logstash/templates/configuration-cm.yaml | 39 + .../logstash/templates/extra-list.yaml | 4 + .../logstash/templates/headless-svc.yaml | 22 + .../logstash/templates/ingress.yaml | 62 + .../helm_charts/logstash/templates/pdb.yaml | 23 + .../logstash/templates/serviceaccount.yaml | 21 + .../helm_charts/logstash/templates/sts.yaml | 219 + .../helm_charts/logstash/templates/svc.yaml | 39 + .../logstash/templates/tls-secret.yaml | 44 + .../helm_charts/logstash/values.yaml | 662 +++ .../helm_charts/neo4j/Chart.yaml | 5 + .../neo4j/templates/deployment.yaml | 51 + .../helm_charts/neo4j/values.yaml | 18 + .../helm_charts/redis/Chart.yaml | 5 + .../redis/templates/deployment.yaml | 34 + .../helm_charts/redis/values.yaml | 5 + .../helm_charts/superset-helm/Chart.lock | 9 + .../helm_charts/superset-helm/Chart.yaml | 37 + .../helm_charts/superset-helm/LICENSE | 21 + .../helm_charts/superset-helm/README.md | 2 + .../superset-helm/charts/redis-12.3.3.tgz | Bin 0 -> 66002 bytes .../superset-helm/templates/_helpers.tpl | 105 + .../templates/configmap-superset.yaml | 32 + .../superset-helm/templates/deployment.yaml | 128 + .../superset-helm/templates/ingress.yaml | 56 + .../superset-helm/templates/init-job.yaml | 80 + .../superset-helm/templates/secret-env.yaml | 39 + .../templates/secret-superset-config.yaml | 40 + .../superset-helm/templates/service.yaml | 42 + .../helm_charts/superset-helm/values.yaml | 312 ++ .../helm_charts/taxonomy/Chart.yaml | 5 + .../taxonomy-service_application.conf | 399 ++ .../taxonomy/taxonomy-service_logback.xml | 31 + .../taxonomy/templates/deployment.yaml | 74 + .../helm_charts/taxonomy/values.yaml | 28 + knowlg-automation/terraform/aks.yaml | 8 + knowlg-automation/terraform/aws/data.tf | 1 + .../terraform/aws/druid_cluster_vars.tf | 1 + .../terraform/aws/druid_operator.tf | 1 + .../terraform/aws/druid_operator_vars.tf | 1 + knowlg-automation/terraform/aws/druid_raw.tf | 1 + knowlg-automation/terraform/aws/eks_addons.tf | 7 + knowlg-automation/terraform/aws/eks_master.tf | 20 + .../aws/eks_master_policy_attachment.tf | 4 + .../terraform/aws/eks_master_role.tf | 23 + knowlg-automation/terraform/aws/eks_nodes.tf | 29 + .../aws/eks_nodes_policy_attachment.tf | 19 + .../terraform/aws/eks_nodes_role.tf | 19 + knowlg-automation/terraform/aws/flink.tf | 1 + knowlg-automation/terraform/aws/flink_vars.tf | 1 + .../terraform/aws/iam_s3_user.tf | 9 + .../terraform/aws/internet_gateway.tf | 10 + knowlg-automation/terraform/aws/kafka.tf | 1 + knowlg-automation/terraform/aws/kafka_vars.tf | 1 + knowlg-automation/terraform/aws/local.tf | 45 + knowlg-automation/terraform/aws/local_file.tf | 4 + knowlg-automation/terraform/aws/monitoring.tf | 1 + .../terraform/aws/monitoring_vars.tf | 1 + knowlg-automation/terraform/aws/output.tf | 15 + knowlg-automation/terraform/aws/postgresql.tf | 1 + .../terraform/aws/postgresql_vars.tf | 1 + knowlg-automation/terraform/aws/provider.tf | 15 + .../terraform/aws/route_table.tf | 15 + .../terraform/aws/route_table_association.tf | 5 + knowlg-automation/terraform/aws/s3_bucket.tf | 10 + .../terraform/aws/s3_bucket_object.tf | 7 + .../terraform/aws/s3_bucket_policy.tf | 20 + knowlg-automation/terraform/aws/subnets.tf | 14 + knowlg-automation/terraform/aws/superset.tf | 1 + .../terraform/aws/superset_vars.tf | 1 + .../terraform/aws/terraform_backend.conf | 2 + .../terraform/aws/terraform_backend.tf | 5 + knowlg-automation/terraform/aws/variables.tf | 155 + knowlg-automation/terraform/aws/vpc.tf | 10 + .../terraform/azure/druid-raw.tf | 60 + .../terraform/azure/flink-provision.tf | 23 + .../terraform/azure/kafka-client.yaml | 13 + .../terraform/azure/kafka-provision.tf | 63 + .../terraform/azure/obsrv-aks-cluster.tf | 43 + .../terraform/azure/obsrv-infra.tf | 39 + .../terraform/azure/obsrv-storage.tf | 35 + .../terraform/azure/postgres_provision.tf | 12 + .../azure/postgresql/custom-values.yaml | 24 + .../terraform/azure/superset-provision.tf | 32 + knowlg-automation/terraform/azure/vars.tf | 181 + knowlg-automation/terraform/create | 16 + knowlg-automation/terraform/destroy | 5 + .../terraform/helm/druid_cluster_vars.tf | 59 + .../terraform/helm/druid_operator.tf | 8 + .../terraform/helm/druid_operator_vars.tf | 35 + knowlg-automation/terraform/helm/druid_raw.tf | 25 + knowlg-automation/terraform/helm/flink.tf | 18 + .../terraform/helm/flink_vars.tf | 47 + knowlg-automation/terraform/helm/kafka.tf | 19 + .../terraform/helm/kafka_vars.tf | 77 + .../terraform/helm/monitoring.tf | 10 + .../terraform/helm/monitoring_vars.tf | 53 + knowlg-automation/terraform/helm/postgres.tf | 14 + .../terraform/helm/postgresql_vars.tf | 53 + knowlg-automation/terraform/helm/superset.tf | 10 + .../terraform/helm/superset_vars.tf | 41 + .../terraform/local/cassandra-provision.tf | 11 + .../local/elasticsearch-provision.tf | 11 + .../terraform/local/kafka-provision.tf | 67 + .../terraform/local/logstash-provision.tf | 11 + knowlg-automation/terraform/local/main.tf | 55 + .../terraform/local/neo4j-provision.tf | 10 + .../local/postgresql/custom-values.yaml | 24 + .../terraform/local/redis-provision.tf | 10 + .../terraform/local/taxonomy-provision.tf | 11 + knowlg-automation/terraform/local/vars.tf | 176 + knowlg-automation/terraform/local/versions.tf | 21 + .../druid_cluster.yaml.tfpl | 10 + .../terraform_helm_templates/flink.yaml.tfpl | 3 + .../postgres.yaml.tfpl | 23 + 290 files changed, 34814 insertions(+) create mode 100644 knowlg-automation/LICENSE create mode 100644 knowlg-automation/README.md create mode 100644 knowlg-automation/helm_charts/cassandra/Chart.yaml create mode 100644 knowlg-automation/helm_charts/cassandra/templates/deployment.yaml create mode 100644 knowlg-automation/helm_charts/cassandra/values.yaml create mode 100644 knowlg-automation/helm_charts/druid-cluster/.helmignore create mode 100644 knowlg-automation/helm_charts/druid-cluster/Chart.yaml create mode 100644 knowlg-automation/helm_charts/druid-cluster/charts/zookeeper-10.0.0.tgz create mode 100644 knowlg-automation/helm_charts/druid-cluster/druid-crd.yaml create mode 100644 knowlg-automation/helm_charts/druid-cluster/templates/_helpers.tpl create mode 100644 knowlg-automation/helm_charts/druid-cluster/templates/druid_statefulset.yaml create mode 100644 knowlg-automation/helm_charts/druid-cluster/templates/historical_hpa.yaml create mode 100644 knowlg-automation/helm_charts/druid-cluster/values.yaml create mode 100644 knowlg-automation/helm_charts/druid-operator/Chart.yaml create mode 100644 knowlg-automation/helm_charts/druid-operator/templates/NOTES.txt create mode 100644 knowlg-automation/helm_charts/druid-operator/templates/_helpers.tpl create mode 100644 knowlg-automation/helm_charts/druid-operator/templates/crds/druid.apache.org_druids.yaml create mode 100644 knowlg-automation/helm_charts/druid-operator/templates/deployment.yaml create mode 100644 knowlg-automation/helm_charts/druid-operator/templates/role.yaml create mode 100644 knowlg-automation/helm_charts/druid-operator/templates/role_binding.yaml create mode 100644 knowlg-automation/helm_charts/druid-operator/templates/serviceaccount.yaml create mode 100644 knowlg-automation/helm_charts/druid-operator/values.yaml create mode 100644 knowlg-automation/helm_charts/elasticsearch/.helmignore create mode 100644 knowlg-automation/helm_charts/elasticsearch/Chart.lock create mode 100644 knowlg-automation/helm_charts/elasticsearch/Chart.yaml create mode 100644 knowlg-automation/helm_charts/elasticsearch/README.md create mode 100644 knowlg-automation/helm_charts/elasticsearch/charts/common-2.2.4.tgz create mode 100644 knowlg-automation/helm_charts/elasticsearch/charts/kibana-10.2.16.tgz create mode 100644 knowlg-automation/helm_charts/elasticsearch/templates/NOTES.txt create mode 100644 knowlg-automation/helm_charts/elasticsearch/templates/_helpers.tpl create mode 100644 knowlg-automation/helm_charts/elasticsearch/templates/configmap.yaml create mode 100644 knowlg-automation/helm_charts/elasticsearch/templates/coordinating/hpa.yaml create mode 100644 knowlg-automation/helm_charts/elasticsearch/templates/coordinating/serviceaccount.yaml create mode 100644 knowlg-automation/helm_charts/elasticsearch/templates/coordinating/statefulset.yaml create mode 100644 knowlg-automation/helm_charts/elasticsearch/templates/coordinating/svc-headless.yaml create mode 100644 knowlg-automation/helm_charts/elasticsearch/templates/data/hpa.yaml create mode 100644 knowlg-automation/helm_charts/elasticsearch/templates/data/serviceaccount.yaml create mode 100644 knowlg-automation/helm_charts/elasticsearch/templates/data/statefulset.yaml create mode 100644 knowlg-automation/helm_charts/elasticsearch/templates/data/svc-headless.yaml create mode 100644 knowlg-automation/helm_charts/elasticsearch/templates/extra-list.yaml create mode 100644 knowlg-automation/helm_charts/elasticsearch/templates/ingest/hpa.yaml create mode 100644 knowlg-automation/helm_charts/elasticsearch/templates/ingest/ingress.yaml create mode 100644 knowlg-automation/helm_charts/elasticsearch/templates/ingest/service.yaml create mode 100644 knowlg-automation/helm_charts/elasticsearch/templates/ingest/serviceaccount.yaml create mode 100644 knowlg-automation/helm_charts/elasticsearch/templates/ingest/statefulset.yaml create mode 100644 knowlg-automation/helm_charts/elasticsearch/templates/ingest/svc-headless.yaml create mode 100644 knowlg-automation/helm_charts/elasticsearch/templates/ingress-tls-secrets.yaml create mode 100644 knowlg-automation/helm_charts/elasticsearch/templates/ingress.yaml create mode 100644 knowlg-automation/helm_charts/elasticsearch/templates/initialization-configmap.yaml create mode 100644 knowlg-automation/helm_charts/elasticsearch/templates/master/hpa.yaml create mode 100644 knowlg-automation/helm_charts/elasticsearch/templates/master/serviceaccount.yaml create mode 100644 knowlg-automation/helm_charts/elasticsearch/templates/master/statefulset.yaml create mode 100644 knowlg-automation/helm_charts/elasticsearch/templates/master/svc-headless.yaml create mode 100644 knowlg-automation/helm_charts/elasticsearch/templates/metrics/deployment.yaml create mode 100644 knowlg-automation/helm_charts/elasticsearch/templates/metrics/prometheusrule.yaml create mode 100644 knowlg-automation/helm_charts/elasticsearch/templates/metrics/service.yaml create mode 100644 knowlg-automation/helm_charts/elasticsearch/templates/metrics/servicemonitor.yaml create mode 100644 knowlg-automation/helm_charts/elasticsearch/templates/secrets.yaml create mode 100644 knowlg-automation/helm_charts/elasticsearch/templates/service.yaml create mode 100644 knowlg-automation/helm_charts/elasticsearch/templates/tls-secret.yaml create mode 100644 knowlg-automation/helm_charts/elasticsearch/values.yaml create mode 100644 knowlg-automation/helm_charts/flink/.helmignore create mode 100644 knowlg-automation/helm_charts/flink/Chart.yaml create mode 100644 knowlg-automation/helm_charts/flink/templates/_helpers.tpl create mode 100644 knowlg-automation/helm_charts/flink/templates/flink_job_configmap.yaml create mode 100644 knowlg-automation/helm_charts/flink/templates/flink_job_deployment.yaml create mode 100644 knowlg-automation/helm_charts/flink/values.yaml create mode 100644 knowlg-automation/helm_charts/kafka/.helmignore create mode 100644 knowlg-automation/helm_charts/kafka/Chart.lock create mode 100644 knowlg-automation/helm_charts/kafka/Chart.yaml create mode 100644 knowlg-automation/helm_charts/kafka/README.md create mode 100644 knowlg-automation/helm_charts/kafka/charts/common/.helmignore create mode 100644 knowlg-automation/helm_charts/kafka/charts/common/Chart.yaml create mode 100644 knowlg-automation/helm_charts/kafka/charts/common/README.md create mode 100644 knowlg-automation/helm_charts/kafka/charts/common/templates/_affinities.tpl create mode 100644 knowlg-automation/helm_charts/kafka/charts/common/templates/_capabilities.tpl create mode 100644 knowlg-automation/helm_charts/kafka/charts/common/templates/_errors.tpl create mode 100644 knowlg-automation/helm_charts/kafka/charts/common/templates/_images.tpl create mode 100644 knowlg-automation/helm_charts/kafka/charts/common/templates/_ingress.tpl create mode 100644 knowlg-automation/helm_charts/kafka/charts/common/templates/_labels.tpl create mode 100644 knowlg-automation/helm_charts/kafka/charts/common/templates/_names.tpl create mode 100644 knowlg-automation/helm_charts/kafka/charts/common/templates/_secrets.tpl create mode 100644 knowlg-automation/helm_charts/kafka/charts/common/templates/_storage.tpl create mode 100644 knowlg-automation/helm_charts/kafka/charts/common/templates/_tplvalues.tpl create mode 100644 knowlg-automation/helm_charts/kafka/charts/common/templates/_utils.tpl create mode 100644 knowlg-automation/helm_charts/kafka/charts/common/templates/_warnings.tpl create mode 100644 knowlg-automation/helm_charts/kafka/charts/common/templates/validations/_cassandra.tpl create mode 100644 knowlg-automation/helm_charts/kafka/charts/common/templates/validations/_mariadb.tpl create mode 100644 knowlg-automation/helm_charts/kafka/charts/common/templates/validations/_mongodb.tpl create mode 100644 knowlg-automation/helm_charts/kafka/charts/common/templates/validations/_mysql.tpl create mode 100644 knowlg-automation/helm_charts/kafka/charts/common/templates/validations/_postgresql.tpl create mode 100644 knowlg-automation/helm_charts/kafka/charts/common/templates/validations/_redis.tpl create mode 100644 knowlg-automation/helm_charts/kafka/charts/common/templates/validations/_validations.tpl create mode 100644 knowlg-automation/helm_charts/kafka/charts/common/values.yaml create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/.helmignore create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/Chart.lock create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/Chart.yaml create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/README.md create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/.helmignore create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/Chart.yaml create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/README.md create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_affinities.tpl create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_capabilities.tpl create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_errors.tpl create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_images.tpl create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_ingress.tpl create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_labels.tpl create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_names.tpl create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_secrets.tpl create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_storage.tpl create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_tplvalues.tpl create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_utils.tpl create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_warnings.tpl create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/validations/_cassandra.tpl create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/validations/_mariadb.tpl create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/validations/_mongodb.tpl create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/validations/_mysql.tpl create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/validations/_postgresql.tpl create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/validations/_redis.tpl create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/validations/_validations.tpl create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/values.yaml create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/NOTES.txt create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/_helpers.tpl create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/configmap.yaml create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/extra-list.yaml create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/metrics-svc.yaml create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/networkpolicy.yaml create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/pdb.yaml create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/prometheusrule.yaml create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/scripts-configmap.yaml create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/secrets.yaml create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/serviceaccount.yaml create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/servicemonitor.yaml create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/statefulset.yaml create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/svc-headless.yaml create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/svc.yaml create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/tls-secrets.yaml create mode 100644 knowlg-automation/helm_charts/kafka/charts/zookeeper/values.yaml create mode 100644 knowlg-automation/helm_charts/kafka/templates/NOTES.txt create mode 100644 knowlg-automation/helm_charts/kafka/templates/_helpers.tpl create mode 100644 knowlg-automation/helm_charts/kafka/templates/configmap.yaml create mode 100644 knowlg-automation/helm_charts/kafka/templates/extra-list.yaml create mode 100644 knowlg-automation/helm_charts/kafka/templates/jaas-secret.yaml create mode 100644 knowlg-automation/helm_charts/kafka/templates/jmx-configmap.yaml create mode 100644 knowlg-automation/helm_charts/kafka/templates/jmx-metrics-svc.yaml create mode 100644 knowlg-automation/helm_charts/kafka/templates/kafka-metrics-deployment.yaml create mode 100644 knowlg-automation/helm_charts/kafka/templates/kafka-metrics-serviceaccount.yaml create mode 100644 knowlg-automation/helm_charts/kafka/templates/kafka-metrics-svc.yaml create mode 100644 knowlg-automation/helm_charts/kafka/templates/kafka-provisioning-secret.yaml create mode 100644 knowlg-automation/helm_charts/kafka/templates/kafka-provisioning-serviceaccount.yaml create mode 100644 knowlg-automation/helm_charts/kafka/templates/kafka-provisioning.yaml create mode 100644 knowlg-automation/helm_charts/kafka/templates/log4j-configmap.yaml create mode 100644 knowlg-automation/helm_charts/kafka/templates/networkpolicy-egress.yaml create mode 100644 knowlg-automation/helm_charts/kafka/templates/networkpolicy-ingress.yaml create mode 100644 knowlg-automation/helm_charts/kafka/templates/poddisruptionbudget.yaml create mode 100644 knowlg-automation/helm_charts/kafka/templates/prometheusrule.yaml create mode 100644 knowlg-automation/helm_charts/kafka/templates/role.yaml create mode 100644 knowlg-automation/helm_charts/kafka/templates/rolebinding.yaml create mode 100644 knowlg-automation/helm_charts/kafka/templates/scripts-configmap.yaml create mode 100644 knowlg-automation/helm_charts/kafka/templates/serviceaccount.yaml create mode 100644 knowlg-automation/helm_charts/kafka/templates/servicemonitor-jmx-metrics.yaml create mode 100644 knowlg-automation/helm_charts/kafka/templates/servicemonitor-metrics.yaml create mode 100644 knowlg-automation/helm_charts/kafka/templates/statefulset.yaml create mode 100644 knowlg-automation/helm_charts/kafka/templates/svc-external-access.yaml create mode 100644 knowlg-automation/helm_charts/kafka/templates/svc-headless.yaml create mode 100644 knowlg-automation/helm_charts/kafka/templates/svc.yaml create mode 100644 knowlg-automation/helm_charts/kafka/templates/tls-secrets.yaml create mode 100644 knowlg-automation/helm_charts/kafka/values.yaml create mode 100644 knowlg-automation/helm_charts/logstash/.helmignore create mode 100644 knowlg-automation/helm_charts/logstash/Chart.lock create mode 100644 knowlg-automation/helm_charts/logstash/Chart.yaml create mode 100644 knowlg-automation/helm_charts/logstash/README.md create mode 100644 knowlg-automation/helm_charts/logstash/charts/common-2.2.4.tgz create mode 100644 knowlg-automation/helm_charts/logstash/templates/NOTES.txt create mode 100644 knowlg-automation/helm_charts/logstash/templates/_helpers.tpl create mode 100644 knowlg-automation/helm_charts/logstash/templates/configuration-cm.yaml create mode 100644 knowlg-automation/helm_charts/logstash/templates/extra-list.yaml create mode 100644 knowlg-automation/helm_charts/logstash/templates/headless-svc.yaml create mode 100644 knowlg-automation/helm_charts/logstash/templates/ingress.yaml create mode 100644 knowlg-automation/helm_charts/logstash/templates/pdb.yaml create mode 100644 knowlg-automation/helm_charts/logstash/templates/serviceaccount.yaml create mode 100644 knowlg-automation/helm_charts/logstash/templates/sts.yaml create mode 100644 knowlg-automation/helm_charts/logstash/templates/svc.yaml create mode 100644 knowlg-automation/helm_charts/logstash/templates/tls-secret.yaml create mode 100644 knowlg-automation/helm_charts/logstash/values.yaml create mode 100644 knowlg-automation/helm_charts/neo4j/Chart.yaml create mode 100644 knowlg-automation/helm_charts/neo4j/templates/deployment.yaml create mode 100644 knowlg-automation/helm_charts/neo4j/values.yaml create mode 100644 knowlg-automation/helm_charts/redis/Chart.yaml create mode 100644 knowlg-automation/helm_charts/redis/templates/deployment.yaml create mode 100644 knowlg-automation/helm_charts/redis/values.yaml create mode 100644 knowlg-automation/helm_charts/superset-helm/Chart.lock create mode 100644 knowlg-automation/helm_charts/superset-helm/Chart.yaml create mode 100644 knowlg-automation/helm_charts/superset-helm/LICENSE create mode 100644 knowlg-automation/helm_charts/superset-helm/README.md create mode 100644 knowlg-automation/helm_charts/superset-helm/charts/redis-12.3.3.tgz create mode 100644 knowlg-automation/helm_charts/superset-helm/templates/_helpers.tpl create mode 100644 knowlg-automation/helm_charts/superset-helm/templates/configmap-superset.yaml create mode 100644 knowlg-automation/helm_charts/superset-helm/templates/deployment.yaml create mode 100644 knowlg-automation/helm_charts/superset-helm/templates/ingress.yaml create mode 100644 knowlg-automation/helm_charts/superset-helm/templates/init-job.yaml create mode 100644 knowlg-automation/helm_charts/superset-helm/templates/secret-env.yaml create mode 100644 knowlg-automation/helm_charts/superset-helm/templates/secret-superset-config.yaml create mode 100644 knowlg-automation/helm_charts/superset-helm/templates/service.yaml create mode 100644 knowlg-automation/helm_charts/superset-helm/values.yaml create mode 100644 knowlg-automation/helm_charts/taxonomy/Chart.yaml create mode 100644 knowlg-automation/helm_charts/taxonomy/taxonomy-service_application.conf create mode 100644 knowlg-automation/helm_charts/taxonomy/taxonomy-service_logback.xml create mode 100644 knowlg-automation/helm_charts/taxonomy/templates/deployment.yaml create mode 100644 knowlg-automation/helm_charts/taxonomy/values.yaml create mode 100644 knowlg-automation/terraform/aks.yaml create mode 100644 knowlg-automation/terraform/aws/data.tf create mode 120000 knowlg-automation/terraform/aws/druid_cluster_vars.tf create mode 120000 knowlg-automation/terraform/aws/druid_operator.tf create mode 120000 knowlg-automation/terraform/aws/druid_operator_vars.tf create mode 120000 knowlg-automation/terraform/aws/druid_raw.tf create mode 100644 knowlg-automation/terraform/aws/eks_addons.tf create mode 100644 knowlg-automation/terraform/aws/eks_master.tf create mode 100644 knowlg-automation/terraform/aws/eks_master_policy_attachment.tf create mode 100644 knowlg-automation/terraform/aws/eks_master_role.tf create mode 100644 knowlg-automation/terraform/aws/eks_nodes.tf create mode 100644 knowlg-automation/terraform/aws/eks_nodes_policy_attachment.tf create mode 100644 knowlg-automation/terraform/aws/eks_nodes_role.tf create mode 120000 knowlg-automation/terraform/aws/flink.tf create mode 120000 knowlg-automation/terraform/aws/flink_vars.tf create mode 100644 knowlg-automation/terraform/aws/iam_s3_user.tf create mode 100644 knowlg-automation/terraform/aws/internet_gateway.tf create mode 120000 knowlg-automation/terraform/aws/kafka.tf create mode 120000 knowlg-automation/terraform/aws/kafka_vars.tf create mode 100644 knowlg-automation/terraform/aws/local.tf create mode 100644 knowlg-automation/terraform/aws/local_file.tf create mode 120000 knowlg-automation/terraform/aws/monitoring.tf create mode 120000 knowlg-automation/terraform/aws/monitoring_vars.tf create mode 100644 knowlg-automation/terraform/aws/output.tf create mode 120000 knowlg-automation/terraform/aws/postgresql.tf create mode 120000 knowlg-automation/terraform/aws/postgresql_vars.tf create mode 100644 knowlg-automation/terraform/aws/provider.tf create mode 100644 knowlg-automation/terraform/aws/route_table.tf create mode 100644 knowlg-automation/terraform/aws/route_table_association.tf create mode 100644 knowlg-automation/terraform/aws/s3_bucket.tf create mode 100644 knowlg-automation/terraform/aws/s3_bucket_object.tf create mode 100644 knowlg-automation/terraform/aws/s3_bucket_policy.tf create mode 100644 knowlg-automation/terraform/aws/subnets.tf create mode 120000 knowlg-automation/terraform/aws/superset.tf create mode 120000 knowlg-automation/terraform/aws/superset_vars.tf create mode 100644 knowlg-automation/terraform/aws/terraform_backend.conf create mode 100644 knowlg-automation/terraform/aws/terraform_backend.tf create mode 100644 knowlg-automation/terraform/aws/variables.tf create mode 100644 knowlg-automation/terraform/aws/vpc.tf create mode 100644 knowlg-automation/terraform/azure/druid-raw.tf create mode 100644 knowlg-automation/terraform/azure/flink-provision.tf create mode 100644 knowlg-automation/terraform/azure/kafka-client.yaml create mode 100644 knowlg-automation/terraform/azure/kafka-provision.tf create mode 100644 knowlg-automation/terraform/azure/obsrv-aks-cluster.tf create mode 100644 knowlg-automation/terraform/azure/obsrv-infra.tf create mode 100644 knowlg-automation/terraform/azure/obsrv-storage.tf create mode 100644 knowlg-automation/terraform/azure/postgres_provision.tf create mode 100644 knowlg-automation/terraform/azure/postgresql/custom-values.yaml create mode 100644 knowlg-automation/terraform/azure/superset-provision.tf create mode 100644 knowlg-automation/terraform/azure/vars.tf create mode 100644 knowlg-automation/terraform/create create mode 100644 knowlg-automation/terraform/destroy create mode 100644 knowlg-automation/terraform/helm/druid_cluster_vars.tf create mode 100644 knowlg-automation/terraform/helm/druid_operator.tf create mode 100644 knowlg-automation/terraform/helm/druid_operator_vars.tf create mode 100644 knowlg-automation/terraform/helm/druid_raw.tf create mode 100644 knowlg-automation/terraform/helm/flink.tf create mode 100644 knowlg-automation/terraform/helm/flink_vars.tf create mode 100644 knowlg-automation/terraform/helm/kafka.tf create mode 100644 knowlg-automation/terraform/helm/kafka_vars.tf create mode 100644 knowlg-automation/terraform/helm/monitoring.tf create mode 100644 knowlg-automation/terraform/helm/monitoring_vars.tf create mode 100644 knowlg-automation/terraform/helm/postgres.tf create mode 100644 knowlg-automation/terraform/helm/postgresql_vars.tf create mode 100644 knowlg-automation/terraform/helm/superset.tf create mode 100644 knowlg-automation/terraform/helm/superset_vars.tf create mode 100644 knowlg-automation/terraform/local/cassandra-provision.tf create mode 100644 knowlg-automation/terraform/local/elasticsearch-provision.tf create mode 100644 knowlg-automation/terraform/local/kafka-provision.tf create mode 100644 knowlg-automation/terraform/local/logstash-provision.tf create mode 100644 knowlg-automation/terraform/local/main.tf create mode 100644 knowlg-automation/terraform/local/neo4j-provision.tf create mode 100644 knowlg-automation/terraform/local/postgresql/custom-values.yaml create mode 100644 knowlg-automation/terraform/local/redis-provision.tf create mode 100644 knowlg-automation/terraform/local/taxonomy-provision.tf create mode 100644 knowlg-automation/terraform/local/vars.tf create mode 100644 knowlg-automation/terraform/local/versions.tf create mode 100644 knowlg-automation/terraform/terraform_helm_templates/druid_cluster.yaml.tfpl create mode 100644 knowlg-automation/terraform/terraform_helm_templates/flink.yaml.tfpl create mode 100644 knowlg-automation/terraform/terraform_helm_templates/postgres.yaml.tfpl diff --git a/knowlg-automation/LICENSE b/knowlg-automation/LICENSE new file mode 100644 index 000000000..5dcd61e28 --- /dev/null +++ b/knowlg-automation/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Sunbird Obsrv + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/knowlg-automation/README.md b/knowlg-automation/README.md new file mode 100644 index 000000000..a7608b22c --- /dev/null +++ b/knowlg-automation/README.md @@ -0,0 +1,29 @@ +# sunbird-infra-provision + +Infra provision on Azure + +Pre-requisites: +Install azure cli(az) tool on local machine and login to azure account with "az login" command. +Create a azure service account to be used for the infra provision. +Copy the ingestion spec to any folder and share the path in variables +Replace the default values in infra-provision/azure/vars.tf . + +Steps: +Run create shell script and provide option input as "azure" . + + +Destroy infra on Azure +Steps: +Run destroy shell script with option as "azure" and append --auto-approve to continue without confirmation. + + +Infra provision on local + +Pre-requisites: +For local provision, kind provider is used to provision the cluster. +Steps: +Run shell script and provide option as "local" + +Destroy infra on local +Steps: +Run destroy shell script and provide option as "local" append --auto-approve to continue without confirmation. diff --git a/knowlg-automation/helm_charts/cassandra/Chart.yaml b/knowlg-automation/helm_charts/cassandra/Chart.yaml new file mode 100644 index 000000000..f0250559a --- /dev/null +++ b/knowlg-automation/helm_charts/cassandra/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: cassandra +version: 0.1.0 \ No newline at end of file diff --git a/knowlg-automation/helm_charts/cassandra/templates/deployment.yaml b/knowlg-automation/helm_charts/cassandra/templates/deployment.yaml new file mode 100644 index 000000000..4d00c736b --- /dev/null +++ b/knowlg-automation/helm_charts/cassandra/templates/deployment.yaml @@ -0,0 +1,33 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Chart.Name }} + namespace: {{ .Values.namespace }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ .Chart.Name }} + template: + metadata: + labels: + app: {{ .Chart.Name }} + spec: + containers: + - name: {{ .Chart.Name }} + image: cassandra:3.11.11 + ports: + - containerPort: {{ .Values.network.port }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ .Chart.Name }}-db + namespace: {{ .Values.namespace }} + labels: + app: {{ .Chart.Name }} +spec: + ports: + - port: {{ .Values.network.port }} + selector: + app: {{ .Chart.Name }} \ No newline at end of file diff --git a/knowlg-automation/helm_charts/cassandra/values.yaml b/knowlg-automation/helm_charts/cassandra/values.yaml new file mode 100644 index 000000000..ef6b4aad2 --- /dev/null +++ b/knowlg-automation/helm_charts/cassandra/values.yaml @@ -0,0 +1,4 @@ +namespace: knowlg-db +replicaCount: 1 +network: + port: 9042 \ No newline at end of file diff --git a/knowlg-automation/helm_charts/druid-cluster/.helmignore b/knowlg-automation/helm_charts/druid-cluster/.helmignore new file mode 100644 index 000000000..fbe01f88f --- /dev/null +++ b/knowlg-automation/helm_charts/druid-cluster/.helmignore @@ -0,0 +1,22 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ \ No newline at end of file diff --git a/knowlg-automation/helm_charts/druid-cluster/Chart.yaml b/knowlg-automation/helm_charts/druid-cluster/Chart.yaml new file mode 100644 index 000000000..36bb4b50b --- /dev/null +++ b/knowlg-automation/helm_charts/druid-cluster/Chart.yaml @@ -0,0 +1,19 @@ +name: druid-cluster +version: 0.1.0 +appVersion: 0.21.1 +description: Helm chart to deploy a druid cluster on Kubernetes +keywords: +- druid +- zookeeper +- postgres +maintainers: +- name: druid-operator-maintainers +sources: +- https://github.com/druid-io/druid-operator +engine: gotpl +home: https://github.com/druid-io/druid-operator +dependencies: +- name: zookeeper + version: 10.0.0 + repository: https://charts.bitnami.com/bitnami + condition: zookeeper.enabled diff --git a/knowlg-automation/helm_charts/druid-cluster/charts/zookeeper-10.0.0.tgz b/knowlg-automation/helm_charts/druid-cluster/charts/zookeeper-10.0.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..d9d1685899284f451a1f9332ca6b775cc7e5a4d5 GIT binary patch literal 43731 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 T_x%3>00960Bk5W907wJ?Xvi)O literal 0 HcmV?d00001 diff --git a/knowlg-automation/helm_charts/druid-cluster/druid-crd.yaml b/knowlg-automation/helm_charts/druid-cluster/druid-crd.yaml new file mode 100644 index 000000000..a05f02026 --- /dev/null +++ b/knowlg-automation/helm_charts/druid-cluster/druid-crd.yaml @@ -0,0 +1,4487 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.6.0-beta.0 + meta.helm.sh/release-name: druid-operator + meta.helm.sh/release-namespace: druid-raw + creationTimestamp: "2022-06-21T10:47:15Z" + generation: 1 + labels: + app.kubernetes.io/managed-by: Helm + name: druids.druid.apache.org + resourceVersion: "934" + uid: c464c8d8-e136-46c1-8458-d4968d18350c +spec: + conversion: + strategy: None + group: druid.apache.org + names: + kind: Druid + listKind: DruidList + plural: druids + singular: druid + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + affinity: + properties: + nodeAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + preference: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + properties: + nodeSelectorTerms: + items: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + podAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + common.runtime.properties: + type: string + commonConfigMountPath: + type: string + containerSecurityContext: + properties: + allowPrivilegeEscalation: + type: boolean + capabilities: + properties: + add: + items: + type: string + type: array + drop: + items: + type: string + type: array + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + runAsUserName: + type: string + type: object + type: object + deepStorage: + properties: + spec: + format: byte + type: string + type: + type: string + required: + - spec + - type + type: object + deleteOrphanPvc: + type: boolean + disablePVCDeletionFinalizer: + type: boolean + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + type: object + required: + - name + type: object + type: array + envFrom: + items: + properties: + configMapRef: + properties: + name: + type: string + optional: + type: boolean + type: object + prefix: + type: string + secretRef: + properties: + name: + type: string + optional: + type: boolean + type: object + type: object + type: array + forceDeleteStsPodOnError: + type: boolean + ignored: + type: boolean + image: + type: string + imagePullPolicy: + type: string + imagePullSecrets: + items: + properties: + name: + type: string + type: object + type: array + jvm.options: + type: string + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + timeoutSeconds: + format: int32 + type: integer + type: object + log4j.config: + type: string + metadataStore: + properties: + spec: + format: byte + type: string + type: + type: string + required: + - spec + - type + type: object + nodeSelector: + additionalProperties: + type: string + type: object + nodes: + additionalProperties: + properties: + affinity: + properties: + nodeAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + preference: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + properties: + nodeSelectorTerms: + items: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + podAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + containerSecurityContext: + properties: + allowPrivilegeEscalation: + type: boolean + capabilities: + properties: + add: + items: + type: string + type: array + drop: + items: + type: string + type: array + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + runAsUserName: + type: string + type: object + type: object + druid.port: + format: int32 + type: integer + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + type: object + required: + - name + type: object + type: array + envFrom: + items: + properties: + configMapRef: + properties: + name: + type: string + optional: + type: boolean + type: object + prefix: + type: string + secretRef: + properties: + name: + type: string + optional: + type: boolean + type: object + type: object + type: array + extra.jvm.options: + type: string + hpAutoscaler: + properties: + behavior: + properties: + scaleDown: + properties: + policies: + items: + properties: + periodSeconds: + format: int32 + type: integer + type: + type: string + value: + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + selectPolicy: + type: string + stabilizationWindowSeconds: + format: int32 + type: integer + type: object + scaleUp: + properties: + policies: + items: + properties: + periodSeconds: + format: int32 + type: integer + type: + type: string + value: + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + selectPolicy: + type: string + stabilizationWindowSeconds: + format: int32 + type: integer + type: object + type: object + maxReplicas: + format: int32 + type: integer + metrics: + items: + properties: + containerResource: + properties: + container: + type: string + name: + type: string + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: + type: string + value: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + required: + - type + type: object + required: + - container + - name + - target + type: object + external: + properties: + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: + type: string + value: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + required: + - type + type: object + required: + - metric + - target + type: object + object: + properties: + describedObject: + properties: + apiVersion: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: + type: string + value: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + required: + - type + type: object + required: + - describedObject + - metric + - target + type: object + pods: + properties: + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: + type: string + value: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + required: + - type + type: object + required: + - metric + - target + type: object + resource: + properties: + name: + type: string + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: + type: string + value: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + required: + - type + type: object + required: + - name + - target + type: object + type: + type: string + required: + - type + type: object + type: array + minReplicas: + format: int32 + type: integer + scaleTargetRef: + properties: + apiVersion: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + required: + - maxReplicas + - scaleTargetRef + type: object + image: + type: string + imagePullPolicy: + type: string + imagePullSecrets: + items: + properties: + name: + type: string + type: object + type: array + ingress: + properties: + defaultBackend: + properties: + resource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + service: + properties: + name: + type: string + port: + properties: + name: + type: string + number: + format: int32 + type: integer + type: object + required: + - name + type: object + type: object + ingressClassName: + type: string + rules: + items: + properties: + host: + type: string + http: + properties: + paths: + items: + properties: + backend: + properties: + resource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + service: + properties: + name: + type: string + port: + properties: + name: + type: string + number: + format: int32 + type: integer + type: object + required: + - name + type: object + type: object + path: + type: string + pathType: + type: string + required: + - backend + type: object + type: array + x-kubernetes-list-type: atomic + required: + - paths + type: object + type: object + type: array + x-kubernetes-list-type: atomic + tls: + items: + properties: + hosts: + items: + type: string + type: array + x-kubernetes-list-type: atomic + secretName: + type: string + type: object + type: array + x-kubernetes-list-type: atomic + type: object + ingressAnnotations: + additionalProperties: + type: string + type: object + jvm.options: + type: string + kind: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + timeoutSeconds: + format: int32 + type: integer + type: object + log4j.config: + type: string + maxSurge: + format: int32 + type: integer + maxUnavailable: + format: int32 + type: integer + nodeConfigMountPath: + type: string + nodeType: + type: string + persistentVolumeClaim: + items: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + storageClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + status: + properties: + accessModes: + items: + type: string + type: array + capacity: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + conditions: + items: + properties: + lastProbeTime: + format: date-time + type: string + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + phase: + type: string + type: object + type: object + type: array + podAnnotations: + additionalProperties: + type: string + type: object + podDisruptionBudgetSpec: + properties: + maxUnavailable: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + minAvailable: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + type: object + podLabels: + additionalProperties: + type: string + type: object + podManagementPolicy: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + timeoutSeconds: + format: int32 + type: integer + type: object + replicas: + format: int32 + minimum: 0 + type: integer + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + runtime.properties: + type: string + securityContext: + properties: + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + runAsUserName: + type: string + type: object + type: object + services: + items: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + allocateLoadBalancerNodePorts: + type: boolean + clusterIP: + type: string + clusterIPs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + externalIPs: + items: + type: string + type: array + externalName: + type: string + externalTrafficPolicy: + type: string + healthCheckNodePort: + format: int32 + type: integer + ipFamilies: + items: + type: string + type: array + x-kubernetes-list-type: atomic + ipFamilyPolicy: + type: string + loadBalancerIP: + type: string + loadBalancerSourceRanges: + items: + type: string + type: array + ports: + items: + properties: + appProtocol: + type: string + name: + type: string + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + default: TCP + type: string + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + x-kubernetes-list-map-keys: + - port + - protocol + x-kubernetes-list-type: map + publishNotReadyAddresses: + type: boolean + selector: + additionalProperties: + type: string + type: object + sessionAffinity: + type: string + sessionAffinityConfig: + properties: + clientIP: + properties: + timeoutSeconds: + format: int32 + type: integer + type: object + type: object + topologyKeys: + items: + type: string + type: array + type: + type: string + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + loadBalancer: + properties: + ingress: + items: + properties: + hostname: + type: string + ip: + type: string + ports: + items: + properties: + error: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + port: + format: int32 + type: integer + protocol: + default: TCP + type: string + required: + - port + - protocol + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: array + type: object + type: object + type: object + type: array + startUpProbes: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + timeoutSeconds: + format: int32 + type: integer + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + tolerations: + items: + properties: + effect: + type: string + key: + type: string + operator: + type: string + tolerationSeconds: + format: int64 + type: integer + value: + type: string + type: object + type: array + topologySpreadConstraints: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + maxSkew: + format: int32 + type: integer + topologyKey: + type: string + whenUnsatisfiable: + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + updateStrategy: + properties: + rollingUpdate: + properties: + partition: + format: int32 + type: integer + type: object + type: + type: string + type: object + volumeClaimTemplates: + items: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + storageClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + status: + properties: + accessModes: + items: + type: string + type: array + capacity: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + conditions: + items: + properties: + lastProbeTime: + format: date-time + type: string + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + phase: + type: string + type: object + type: object + type: array + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + volumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + type: string + kind: + type: string + readOnly: + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + type: string + type: object + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + type: string + type: object + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + properties: + readOnly: + type: boolean + volumeClaimTemplate: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + storageClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec + type: object + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + wwids: + items: + type: string + type: array + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + type: string + monitors: + items: + type: string + type: array + pool: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + user: + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + sslEnabled: + type: boolean + storageMode: + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + required: + - druid.port + - nodeConfigMountPath + - nodeType + - replicas + - runtime.properties + type: object + type: object + podAnnotations: + additionalProperties: + type: string + type: object + podLabels: + additionalProperties: + type: string + type: object + podManagementPolicy: + type: string + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + timeoutSeconds: + format: int32 + type: integer + type: object + rollingDeploy: + type: boolean + securityContext: + properties: + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + runAsUserName: + type: string + type: object + type: object + serviceAccount: + type: string + services: + items: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + allocateLoadBalancerNodePorts: + type: boolean + clusterIP: + type: string + clusterIPs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + externalIPs: + items: + type: string + type: array + externalName: + type: string + externalTrafficPolicy: + type: string + healthCheckNodePort: + format: int32 + type: integer + ipFamilies: + items: + type: string + type: array + x-kubernetes-list-type: atomic + ipFamilyPolicy: + type: string + loadBalancerIP: + type: string + loadBalancerSourceRanges: + items: + type: string + type: array + ports: + items: + properties: + appProtocol: + type: string + name: + type: string + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + default: TCP + type: string + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + x-kubernetes-list-map-keys: + - port + - protocol + x-kubernetes-list-type: map + publishNotReadyAddresses: + type: boolean + selector: + additionalProperties: + type: string + type: object + sessionAffinity: + type: string + sessionAffinityConfig: + properties: + clientIP: + properties: + timeoutSeconds: + format: int32 + type: integer + type: object + type: object + topologyKeys: + items: + type: string + type: array + type: + type: string + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + loadBalancer: + properties: + ingress: + items: + properties: + hostname: + type: string + ip: + type: string + ports: + items: + properties: + error: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + port: + format: int32 + type: integer + protocol: + default: TCP + type: string + required: + - port + - protocol + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: array + type: object + type: object + type: object + type: array + startScript: + type: string + startUpProbes: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + timeoutSeconds: + format: int32 + type: integer + type: object + tolerations: + items: + properties: + effect: + type: string + key: + type: string + operator: + type: string + tolerationSeconds: + format: int64 + type: integer + value: + type: string + type: object + type: array + updateStrategy: + properties: + rollingUpdate: + properties: + partition: + format: int32 + type: integer + type: object + type: + type: string + type: object + volumeClaimTemplates: + items: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + storageClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + status: + properties: + accessModes: + items: + type: string + type: array + capacity: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + conditions: + items: + properties: + lastProbeTime: + format: date-time + type: string + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + phase: + type: string + type: object + type: object + type: array + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + volumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + type: string + kind: + type: string + readOnly: + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + type: string + type: object + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + type: string + type: object + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + properties: + readOnly: + type: boolean + volumeClaimTemplate: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + storageClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec + type: object + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + wwids: + items: + type: string + type: array + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + type: string + monitors: + items: + type: string + type: array + pool: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + user: + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + sslEnabled: + type: boolean + storageMode: + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + zookeeper: + properties: + spec: + format: byte + type: string + type: + type: string + required: + - spec + - type + type: object + required: + - common.runtime.properties + - commonConfigMountPath + - nodes + - startScript + type: object + status: + properties: + configMaps: + items: + type: string + type: array + deployments: + items: + type: string + type: array + druidNodeStatus: + properties: + druidNode: + type: string + druidNodeConditionStatus: + type: string + druidNodeConditionType: + type: string + reason: + type: string + type: object + hpAutoscalers: + items: + type: string + type: array + ingress: + items: + type: string + type: array + persistentVolumeClaims: + items: + type: string + type: array + podDisruptionBudgets: + items: + type: string + type: array + pods: + items: + type: string + type: array + services: + items: + type: string + type: array + statefulSets: + items: + type: string + type: array + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: Druid + listKind: DruidList + plural: druids + singular: druid + conditions: + - lastTransitionTime: "2022-06-21T10:47:15Z" + message: no conflicts found + reason: NoConflicts + status: "True" + type: NamesAccepted + - lastTransitionTime: "2022-06-21T10:47:16Z" + message: the initial names have been accepted + reason: InitialNamesAccepted + status: "True" + type: Established + storedVersions: + - v1alpha1 diff --git a/knowlg-automation/helm_charts/druid-cluster/templates/_helpers.tpl b/knowlg-automation/helm_charts/druid-cluster/templates/_helpers.tpl new file mode 100644 index 000000000..f05bab676 --- /dev/null +++ b/knowlg-automation/helm_charts/druid-cluster/templates/_helpers.tpl @@ -0,0 +1,51 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "druid-cluster.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{- define "name" -}} +{{- default .Release.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "druid-cluster.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "druid-cluster.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +App config names +*/}} +{{- define "druid-cluster.appConfigName" -}} +{{- $.Release.Name -}} +{{- end -}} + +{{/* +Selector labels +*/}} +{{- define "druid-cluster.selectorLabels" -}} +app.kubernetes.io/name: {{ include "druid-cluster.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} diff --git a/knowlg-automation/helm_charts/druid-cluster/templates/druid_statefulset.yaml b/knowlg-automation/helm_charts/druid-cluster/templates/druid_statefulset.yaml new file mode 100644 index 000000000..8ac2f43f2 --- /dev/null +++ b/knowlg-automation/helm_charts/druid-cluster/templates/druid_statefulset.yaml @@ -0,0 +1,476 @@ +# This spec only works on a single node kubernetes cluster(e.g. typical k8s cluster setup for dev using kind/minikube or single node AWS EKS cluster etc) +# as it uses local disk as "deep storage". +# +apiVersion: "druid.apache.org/v1alpha1" +kind: "Druid" +metadata: + name: "{{ .Values.druid_cluster_type }}-{{ .Values.druid_env }}" +spec: + image: "{{ .Values.druid_image }}" + startScript: /druid.sh + podLabels: + environment: {{ .Values.druid_env }} + release: alpha + podAnnotations: + dummykey: dummyval + readinessProbe: + httpGet: + path: /status/health + port: 8088 + securityContext: + fsGroup: 1000 + runAsUser: 1000 + runAsGroup: 1000 + services: + - spec: + type: ClusterIP + clusterIP: None + commonConfigMountPath: "/opt/druid/conf/druid/cluster/_common" + jvm.options: |- + -server + -XX:+UseG1GC + -XX:+ExitOnOutOfMemoryError + -XX:MaxDirectMemorySize=10240g + -Duser.timezone=UTC + -Dfile.encoding=UTF-8 + -Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager + -Djava.io.tmpdir=/var/tmp + log4j.config: |- + + + + + + + + + + + + + + common.runtime.properties: | + druid.extensions.loadList=[{{ .Values.druid_extensions_loadList }}] + # druid.extensions.directory=/opt/druid/extensions + # Logging + # Log all runtime properties on startup. Disable to avoid logging properties on startup: + druid.startup.logging.logProperties=true + # Zookeeper + druid.zk.service.host={{ .Release.Name }}-zookeeper-headless.{{ .Values.druid_namespace }}.svc.cluster.local + druid.zk.paths.base=/druid + # Metadata storage + # For PostgreSQL: + druid.metadata.storage.type={{ .Values.druid_metadata_storage_type }} + druid.metadata.storage.connector.connectURI={{ .Values.druid_metadata_storage_connector_connectURI }} + druid.metadata.storage.connector.user={{ .Values.druid_metadata_storage_connector_user }} + druid.metadata.storage.connector.password={{ .Values.druid_metadata_storage_connector_password }} + + # Deep storage + druid.storage.type={{ .Values.druid_deepstorage_type }} + # AWS + druid.s3.accessKey = {{ .Values.s3_access_key }} + druid.s3.secretKey = {{ .Values.s3_secret_key }} + druid.storage.bucket={{ .Values.s3_bucket }} + druid.storage.baseKey=druid/segments + # Azure + druid.azure.account = {{ .Values.azure_storage_account_name }} + druid.azure.key = {{ .Values.azure_storage_account_key }} + druid.azure.container = {{ .Values.azure_storage_container }} + #druid.storage.storageDirectory = {{ .Values.druid_storage_directory }} + #druid.storage.disableAcl=true + + # # Indexing service logs + # # For local disk (only viable in a cluster if this is a network mount): + druid.indexer.logs.type={{ .Values.druid_indexer_logs_type }} + druid.indexer.logs.container={{ .Values.druid_indexer_logs_container }} + druid.indexer.logs.prefix={{ .Values.druid_indexer_logs_prefix }} + # + # + # Service discovery + druid.selectors.indexing.serviceName={{ .Values.druid_selectors_indexing_serviceName }} + druid.selectors.coordinator.serviceName={{ .Values.druid_selectors_coordinator_serviceName }} + # Monitoring + # druid.monitoring.monitors=[{{ .Values.druid_monitoring_monitors }}] + # druid.emitter=composing + # druid.emitter.composing.emitters=[{{ .Values.druid_emitter_composing_emitters }}] + # druid.emitter.logging.logLevel={{ .Values.druid_emitter_logging_logLevel }} + {{- if .Values.druid_monitoring -}} + druid.emitter.graphite.port={{ .Values.druid_emitter_graphite_port }} + druid.emitter.graphite.hostname={{ .Values.druid_emitter_graphite_hostname }} + druid.emitter.graphite.protocol={{ .Values.druid_emitter_graphite_protocol }} + druid.emitter.graphite.eventConverter={{ .Values.druid_emitter_graphite_eventConverter }} + {{- end -}} + # Storage type of double columns + # ommiting this will lead to index double as float at the storage layer + druid.indexing.doubleStorage={{ .Values.druid_indexing_doubleStorage }} + #Writing query logs into file + druid.request.logging.type={{ .Values.druid_request_logging_type }} + druid.request.logging.dir={{ .Values.druid_request_logging_dir }} + druid.javascript.enabled=true + druid.sql.enable={{ .Values.druid_sql_enable }} + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + + nodes: + brokers: + # Optionally specify for running broker as Deployment + kind: Deployment + nodeType: "broker" + druid.port: {{ .Values.druid_broker_port }} + nodeConfigMountPath: "/opt/druid/conf/druid/cluster/query/broker" + replicas: {{ .Values.druid_broker_replicas }} + runtime.properties: | + druid.service={{ .Values.druid_broker_service }} + # HTTP server threads + druid.broker.http.numConnections={{ .Values.druid_broker_http_numConnections }} + druid.server.http.numThreads={{ .Values.druid_server_http_numThreads }} + # Processing threads and buffers + druid.processing.buffer.sizeBytes={{ .Values.druid_broker_processing_buffer_sizeBytes }} + druid.processing.numThreads={{ .Values.druid_broker_processing_numThreads }} + druid.processing.numMergeBuffers={{ .Values.druid_broker_processing_numMergeBuffers }} + druid.javascript.enabled=true + druid.sql.enable={{ .Values.druid_sql_enable }} + extra.jvm.options: |+ + -Xms{{ .Values.druid_broker_min_heap_size }} + -Xmx{{ .Values.druid_broker_max_heap_size }} + -XX:MaxDirectMemorySize={{ .Values.druid_broker_max_direct_size }} + readinessProbe: + httpGet: + path: /status/health + port: {{ .Values.druid_broker_port }} + + # hpAutoscaler: + # maxReplicas: 2 + # minReplicas: 1 + # scaleTargetRef: + # apiVersion: apps/v1 + # kind: StatefulSet + # name: druid-tiny-cluster-brokers + # metrics: + # - type: Resource + # resource: + # name: cpu + # target: + # type: Utilization + # averageUtilization: 50 + + resources: + requests: + memory: {{ .Values.druid_broker_pod_memory_request }} + cpu: {{ .Values.druid_broker_pod_cpu_request }} + limits: + memory: {{ .Values.druid_broker_pod_memory_limit }} + cpu: {{ .Values.druid_broker_pod_cpu_limit }} + + coordinators: + # Optionally specify for running coordinator as Deployment + kind: Deployment + nodeType: "coordinator" + druid.port: {{ .Values.druid_coordinator_port }} + nodeConfigMountPath: "/opt/druid/conf/druid/cluster/master/coordinator-overlord" + replicas: {{ .Values.druid_coordinator_replicas }} + runtime.properties: | + druid.service={{ .Values.druid_coordinator_service }} + druid.coordinator.startDelay={{ .Values.druid_coordinator_startDelay }} + druid.coordinator.period={{ .Values.druid_coordinator_period }} + druid.coordinator.balancer.strategy={{ .Values.druid_coordinator_balancer_strategy }} + druid.coordinator.asOverlord.enabled=false + extra.jvm.options: |- + -Xms{{ .Values.druid_coordinator_min_heap_size }} + -Xmx{{ .Values.druid_coordinator_max_heap_size }} + + readinessProbe: + httpGet: + path: /status/health + port: {{ .Values.druid_coordinator_port }} + + resources: + requests: + memory: {{ .Values.druid_coordinator_pod_memory_request }} + cpu: {{ .Values.druid_coordinator_pod_cpu_request }} + limits: + memory: {{ .Values.druid_coordinator_pod_memory_limit }} + cpu: {{ .Values.druid_coordinator_pod_cpu_limit }} + + overlords: + # Optionally specify for running coordinator as Deployment + kind: Deployment + nodeType: "overlord" + druid.port: {{ .Values.druid_overlord_port }} + nodeConfigMountPath: "/opt/druid/conf/druid/cluster/master/coordinator-overlord" + replicas: {{ .Values.druid_overlord_replicas }} + runtime.properties: | + druid.service={{ .Values.druid_overlord_service }} + druid.indexer.queue.startDelay={{ .Values.druid_indexer_queue_startDelay }} + druid.indexer.runner.type={{ .Values.druid_indexer_runner_type }} + druid.indexer.storage.type={{ .Values.druid_indexer_storage_type }} + # Additional parameters for minor compaction + druid.indexer.tasklock.forceTimeChunkLock={{ .Values.druid_indexer_tasklock_forceTimeChunkLock }} + extra.jvm.options: |- + -Xms{{ .Values.druid_overlord_min_heap_size }} + -Xmx{{ .Values.druid_overlord_max_heap_size }} + + readinessProbe: + httpGet: + path: /status/health + port: {{ .Values.druid_overlord_port }} + + resources: + requests: + memory: {{ .Values.druid_overlord_pod_memory_request }} + cpu: {{ .Values.druid_overlord_pod_cpu_request }} + limits: + memory: {{ .Values.druid_overlord_pod_memory_limit }} + cpu: {{ .Values.druid_overlord_pod_cpu_limit }} + + historicals: + nodeType: "historical" + druid.port: {{ .Values.druid_historical_port }} + nodeConfigMountPath: "/opt/druid/conf/druid/cluster/data/historical" + replicas: {{ .Values.druid_historical_replicas }} + readinessProbe: + httpGet: + path: /status/health + port: {{ .Values.druid_historical_port }} + runtime.properties: | + druid.service={{ .Values.druid_historical_service }} + # HTTP server threads + druid.server.http.numThreads={{ .Values.druid_server_http_numThreads }} + # Processing threads and buffers + druid.processing.buffer.sizeBytes={{ .Values.druid_historical_processing_buffer_sizeBytes }} + druid.processing.numThreads={{ .Values.druid_historical_processing_numThreads }} + druid.processing.numMergeBuffers={{ .Values.druid_historical_processing_numMergeBuffers }} + # Segmentstorage + druid.segmentCache.locations=[{{ .Values.druid_segmentCache_locations }}] + druid.segmentCache.numLoadingThreads={{ .Values.druid_segmentCache_numLoadingThreads }} + # Caching + druid.historical.cache.useCache={{ .Values.druid_historical_cache_useCache }} + druid.historical.cache.populateCache={{ .Values.druid_historical_cache_populateCache }} + druid.historical.cache.unCacheable=[{{ .Values.druid_historical_cache_unCacheable }}] + druid.cache.type={{ .Values.druid_cache_type }} + druid.cache.sizeInBytes={{ .Values.druid_historical_cache_size }} + # druid.cache.expireAfter={{ .Values.druid_historical_cache_expiry }} + extra.jvm.options: |- + -Xms{{ .Values.druid_historical_min_heap_size }} + -Xmx{{ .Values.druid_historical_max_heap_size }} + -XX:MaxDirectMemorySize={{ .Values.druid_historical_max_direct_size }} + securityContext: + fsGroup: 0 + runAsUser: 0 + runAsGroup: 0 + volumeMounts: + - mountPath: {{ .Values.mount_path }} + name: historical-volume + volumeClaimTemplates: + - metadata: + name: historical-volume + spec: + storageClassName: "{{- .Values.storageClass }}" + accessModes: + - ReadWriteOnce + resources: + requests: + storage: {{ .Values.druid_historical_persistent_volume_size }} + + resources: + requests: + memory: {{ .Values.druid_historical_pod_memory_request }} + cpu: {{ .Values.druid_historical_pod_cpu_request }} + limits: + memory: {{ .Values.druid_historical_pod_memory_limit }} + cpu: {{ .Values.druid_historical_pod_cpu_limit }} + + # middlemanagers: + # nodeType: "middleManager" + # druid.port: {{ .Values.druid_middlemanager_port }} + # nodeConfigMountPath: "/opt/druid/conf/druid/cluster/data/middleManager" + # replicas: {{ .Values.druid_middlemanager_replicas }} + # runtime.properties: | + # druid.service={{ .Values.druid_middlemanager_service }} + # # Number of tasks per middleManager + # druid.worker.capacity={{ .Values.druid_worker_capacity }} + # # Task launch parameters + # druid.indexer.runner.javaOpts={{ .Values.druid_indexer_runner_javaOpts }} + # druid.indexer.task.baseTaskDir={{ .Values.druid_indexer_task_baseTaskDir }} + # # Peon properties + # druid.indexer.fork.property.druid.processing.buffer.sizeBytes={{ .Values.druid_indexer_fork_property_druid_processing_buffer_sizeBytes }} + # druid.indexer.fork.property.druid.processing.numThreads={{ .Values.druid_indexer_fork_property_druid_processing_numThreads }} + # druid.indexer.fork.property.druid.server.http.numThreads={{ .Values.druid_indexer_fork_property_druid_server_http_numThreads }} + # #Additional Parameters + # druid.indexer.task.restoreTasksOnRestart={{ .Values.druid_indexer_task_restoreTasksOnRestart }} + # druid.indexer.task.defaultHadoopCoordinates=[\"org.apache.hadoop:hadoop-client:2.8.3\"] + # extra.jvm.options: |+ + # -Xmx{{ .Values.druid_middlemanager_heap_size }} + # -Xms{{ .Values.druid_middlemanager_heap_size }} + # # services: + # # - spec: + # # clusterIP: None + # # ports: + # # - name: middlemanager-port + # # port: {{ .Values.druid_middlemanager_port }} + # # targetPort: {{ .Values.druid_middlemanager_port }} + # # type: ClusterIP + # readinessProbe: + # initialDelaySeconds: 30 + # httpGet: + # path: /status/health + # port: {{ .Values.druid_middlemanager_port }} + + # securityContext: + # fsGroup: 0 + # runAsUser: 0 + # runAsGroup: 0 + # volumeMounts: + # - mountPath: {{ .Values.mount_path }} + # name: middlemanager-volume + # volumeClaimTemplates: + # - metadata: + # name: middlemanager-volume + # spec: + # storageClassName: "{{- .Values.storageClass }}" + # accessModes: + # - ReadWriteOnce + # resources: + # requests: + # storage: {{ .Values.druid_middlemanager_persistent_volume_size }} + + # resources: + # requests: + # memory: 1Gi + # cpu: 256m + # limits: + # memory: 2Gi + # cpu: 1 + + indexers: + nodeType: "indexer" + druid.port: {{ .Values.druid_indexer_port }} + nodeConfigMountPath: "/opt/druid/conf/druid/cluster/data/indexer" + replicas: {{ .Values.druid_indexer_replicas }} + runtime.properties: | + druid.service={{ .Values.druid_indexer_service }} + # Number of tasks per indexer + druid.worker.capacity={{ .Values.druid_indexer_worker_capacity }} + # Task launch parameters + druid.indexer.runner.javaOpts={{ .Values.druid_indexer_runner_javaOpts }} + druid.indexer.task.baseTaskDir={{ .Values.druid_indexer_task_baseTaskDir }} + # Merge jobs + druid.worker.numConcurrentMerges={{ .Values.druid_indexer_numConcurrentMerges }} + # Processing resource properties + # testing auto for sizeBytes + druid.processing.buffer.sizeBytes={{ .Values.druid_indexer_fork_property_druid_processing_buffer_sizeBytes }} + druid.processing.numThreads={{ .Values.druid_indexer_fork_property_druid_processing_numThreads }} + druid.processing.numMergeBuffers={{ .Values.druid_indexer_fork_property_druid_processing_numMergeBuffers }} + druid.server.http.numThreads={{ .Values.druid_indexer_fork_property_druid_server_http_numThreads }} + #Additional Parameters + druid.indexer.task.restoreTasksOnRestart={{ .Values.druid_indexer_task_restoreTasksOnRestart }} + druid.indexer.task.defaultHadoopCoordinates=[\"org.apache.hadoop:hadoop-client:2.8.3\"] + extra.jvm.options: |- + -Xms{{ .Values.druid_indexer_min_heap_size }} + -Xmx{{ .Values.druid_indexer_max_heap_size }} + -XX:MaxDirectMemorySize={{ .Values.druid_indexer_max_direct_size }} + readinessProbe: + initialDelaySeconds: {{ .Values.indexer_intial_delay }} + httpGet: + path: /status/health + port: {{ .Values.druid_indexer_port }} + + securityContext: + fsGroup: 0 + runAsUser: 0 + runAsGroup: 0 + volumeMounts: + - mountPath: {{ .Values.mount_path }} + name: indexer-volume + volumeClaimTemplates: + - metadata: + name: indexer-volume + spec: + storageClassName: "{{- .Values.storageClass }}" + accessModes: + - ReadWriteOnce + resources: + requests: + storage: {{ .Values.druid_indexer_persistent_volume_size }} + resources: + requests: + memory: {{ .Values.druid_indexer_pod_memory_request }} + cpu: {{ .Values.druid_indexer_pod_cpu_request }} + limits: + memory: {{ .Values.druid_indexer_pod_memory_limit }} + cpu: {{ .Values.druid_indexer_pod_cpu_limit }} + + routers: + nodeType: "router" + druid.port: {{ .Values.druid_router_plaintextPort }} + nodeConfigMountPath: "/opt/druid/conf/druid/cluster/query/router" + replicas: {{ .Values.druid_router_replicas }} + services: + - spec: + type: ClusterIP + ports: + - name: druidrouterport + port: 8888 + targetPort: 8888 + protocol: TCP + # services: + # - spec: + # type: ClusterIP + # ports: + # - name: druidrouterport + # port: 80 + # targetPort: 8888 + # protocol: TCP + # ingressAnnotations: + # name: router-ingress + # nginx.ingress.kubernetes.io/rewrite-target: /$1 + # ingress: + # ingressClassName: nginx + # rules: + # - host: "*.nip.io" + # http: + # paths: + # - path: / + # pathType: Prefix + # backend: + # service: + # name: druid-{{ .Values.druid_cluster_type }}-{{ .Values.druid_env }}-routers + # port: + # name: druidrouterport + + runtime.properties: | + druid.service={{ .Values.druid_router_service }} + # druid.plaintextPort={{ .Values.druid_router_plaintextPort }} + # HTTP proxy + druid.router.http.numConnections={{ .Values.druid_router_http_numConnections }} + druid.router.http.readTimeout={{ .Values.druid_router_http_readTimeout }} + druid.router.http.numMaxThreads={{ .Values.druid_router_http_numMaxThreads }} + druid.server.http.numThreads={{ .Values.druid_server_http_numThreads }} + # Service discovery + druid.router.defaultBrokerServiceName={{ .Values.druid_broker_service }} + druid.router.coordinatorServiceName={{ .Values.druid_coordinator_service }} + # Management proxy to coordinator / overlord: required for unified web console. + druid.router.managementProxy.enabled={{ .Values.druid_router_managementProxy_enabled }} + extra.jvm.options: |- + -Xms{{ .Values.druid_router_min_heap_size }} + -Xmx{{ .Values.druid_router_max_heap_size }} + readinessProbe: + httpGet: + path: /status/health + port: {{ .Values.druid_router_plaintextPort }} + + resources: + requests: + memory: {{ .Values.druid_router_pod_memory_request }} + cpu: {{ .Values.druid_router_pod_cpu_request }} + limits: + memory: {{ .Values.druid_router_pod_memory_limit }} + cpu: {{ .Values.druid_router_pod_cpu_limit }} + diff --git a/knowlg-automation/helm_charts/druid-cluster/templates/historical_hpa.yaml b/knowlg-automation/helm_charts/druid-cluster/templates/historical_hpa.yaml new file mode 100644 index 000000000..1a8ca602e --- /dev/null +++ b/knowlg-automation/helm_charts/druid-cluster/templates/historical_hpa.yaml @@ -0,0 +1,29 @@ +# apiVersion: autoscaling/v2beta2 +# kind: HorizontalPodAutoscaler +# metadata: +# name: "{{ .Release.Namespace }}-historicals" +# namespace: {{ .Values.namespace }} +# spec: +# scaleTargetRef: +# apiVersion: apps/v1 +# kind: StatefulSet +# name: "{{ .Release.Namespace }}-{{ .Values.druid_env }}-historicals" +# # behavior: +# # scaleUp: +# # stabilizationWindowSeconds: 10 +# # scaleDown: +# # stabilizationWindowSeconds: 20 +# minReplicas: 1 +# maxReplicas: 2 +# metrics: +# - type: Object +# object: +# metric: +# name: pv_used_percentage +# describedObject: +# apiVersion: v1 +# kind: persistentvolumeclaim +# name: "historical-volume-{{ .Release.Namespace }}-{{ .Values.druid_env }}-historicals-0" +# target: +# type: Utilization +# averageValue: 20 diff --git a/knowlg-automation/helm_charts/druid-cluster/values.yaml b/knowlg-automation/helm_charts/druid-cluster/values.yaml new file mode 100644 index 000000000..3e4f4afbc --- /dev/null +++ b/knowlg-automation/helm_charts/druid-cluster/values.yaml @@ -0,0 +1,345 @@ +druid_env: "dev" +druid_cluster_type: "raw" +druid_image: "apache/druid:25.0.0" +druid_namespace: "druid" +dp_vault_pgdb_admin_password: postgres +druid_monitoring: False +mount_path: /druid/data +storageClass: "default" + +######################### Druid common variables ######################## +druid_directory: "/opt/druid" +druid_extensions_loadList: '"postgresql-metadata-storage", "druid-kafka-indexing-service"' + +# Logging +# Log all runtime properties on startup. Disable to avoid logging properties on startup: +druid.startup.logging.logProperties: true + +# Druid Metadata Store +druid_metadata_storage_type: "postgresql" +druid_metadata_storage_connector_connectURI: "jdbc:postgresql://postgresql-hl.postgresql.svc.cluster.local:5432/druid_raw" +druid_metadata_storage_connector_user: "druid" +druid_metadata_storage_connector_password: "druid" + +# Druid Storage Type +druid_deepstorage_type: "local" +druid_storage_directory: "/data" + +# AWS S3 Details +s3_access_key: "" +s3_secret_key: "" +s3_bucket: "" + +# Azure Container Details +azure_storage_account_name: "" +azure_storage_account_key: "" +azure_storage_container: "" + +# Indexing service logs +# For local disk (only viable in a cluster_type if this is a network mount): +druid_indexer_logs_type: "file" +druid_indexer_logs_prefix: "druid-task-logs" +druid_indexer_logs_container: "" + +# Service discovery +druid_selectors_indexing_serviceName: druid/overlord +druid_selectors_coordinator_serviceName: druid/coordinator + +# Monitoring +druid_monitoring_monitors: '"com.metamx.metrics.JvmMonitor","org.apache.druid.java.util.metrics.JvmMonitor"' +druid_emitter: composing +druid_emitter_composing_emitters: '"logging"' +druid_emitter_logging_logLevel: INFO + + +# Storage type of double columns +# ommiting this will lead to index double as float at the storage layer +druid_indexing_doubleStorage: double + +#Writing query logs into file +druid_request_logging_type: composing +druid_request_logging_dir: "/data/druid/log" + +druid_javascript_enabled: true +druid_sql_enable: true + +####################### Druid Broker Variables ########################## + +druid_broker_service: druid/broker +druid_broker_port: 8082 +druid_broker_min_heap_size: 256M +druid_broker_max_heap_size: 512M +druid_broker_max_direct_size: 192M + +# Processing threads and buffers +druid_broker_processing_buffer_sizeBytes: 64MiB +druid_broker_processing_numThreads: 1 +druid_broker_processing_numMergeBuffers: 1 + +druid_broker_pod_memory_request: 256Mi +druid_broker_pod_memory_limit: 750Mi +druid_broker_pod_cpu_request: 250m +druid_broker_pod_cpu_limit: 500m + +# HTTP server threads +druid_broker_http_numConnections: 5 +druid_server_http_numThreads: 25 + +druid_javascript.enabled: true +druid_sql_enable: true + +druid_broker_replicas: 1 + +##################### Druid Coordinator Variables ####################### + +druid_coordinator_service: druid/coordinator +druid_coordinator_port: 8081 + +druid_coordinator_min_heap_size: 256M +druid_coordinator_max_heap_size: 512M + +druid_coordinator_pod_memory_request: 256Mi +druid_coordinator_pod_memory_limit: 512Mi +druid_coordinator_pod_cpu_request: 250m +druid_coordinator_pod_cpu_limit: 500m + +druid_coordinator_startDelay: PT30S +druid_coordinator_period: PT30S +druid_coordinator_balancer_strategy: diskNormalized + +druid_coordinator_replicas: 1 + +####################### Druid Overlord Variables ######################## + +druid_overlord_service: druid/overlord +druid_overlord_port: 8090 +druid_overlord_min_heap_size: 256M +druid_overlord_max_heap_size: 512M + +druid_overlord_pod_memory_request: 256Mi +druid_overlord_pod_memory_limit: 512Mi +druid_overlord_pod_cpu_request: 250m +druid_overlord_pod_cpu_limit: 500m + +druid_indexer_queue_startDelay: PT30S + +druid_indexer_runner_type: remote +druid_indexer_storage_type: metadata + +# Additional parameters for minor compaction +druid_indexer_tasklock_forceTimeChunkLock: false + +druid_overlord_replicas: 1 + +###################### Druid Historical Variables ####################### + +druid_historical_service: druid/historical +druid_historical_port: 8084 +druid_historical_min_heap_size: 256M +druid_historical_max_heap_size: 512M +druid_historical_max_direct_size: 320M + +druid_historical_pod_memory_request: 512Mi +druid_historical_pod_memory_limit: 850Mi +druid_historical_pod_cpu_request: 500m +druid_historical_pod_cpu_limit: 1 + +# HTTP server threads +druid_server_http_numThreads: 25 + +# Processing threads and buffers +druid_historical_processing_buffer_sizeBytes: 64MiB +druid_historical_processing_numThreads: 2 +druid_historical_processing_numMergeBuffers: 2 + +druid_query_ondiskstorage_enabled: True +druid_query_groupBy_maxMergingDictionarySize: 100000000 +druid_query_groupBy_maxOnDiskStorage: 524288000 + +# Segmentstorage (default 1 gb storage) +druid_segmentCache_locations: '{"path": "/druid/data/store", "maxSize": 1073741824}' +druid_historical_persistent_volume_size: 1G + +druid_segmentCache_numLoadingThreads: 4 + +# Caching +druid_historical_cache_useCache: False +druid_historical_cache_populateCache: true +druid_historical_cache_unCacheable: '"select", "scan"' +druid_cache_type: caffeine +druid_historical_cache_size: 128MiB +druid_historical_cache_expiry: 3600000 + +druid_historical_replicas: 1 + +#################### Druid Middlemanager Variables ###################### + +druid_middlemanager_service: druid/middlemanager +druid_middlemanager_port: 8091 +druid_middlemanager_heap_size: 128M + +druid_middlemanager_persistent_volume_size: 1G + +# Number of tasks per middleManager +druid_worker_capacity: 2 +druid_middlemanager_peon_heap_size: 256M + +# Task launch parameters +druid_indexer_runner_javaOpts: "-server -Xms512m -Xmx512m -XX:+UseG1GC -XX:MaxGCPauseMillis=100" +druid_indexer_task_baseTaskDir: "/var/task" + +# Peon properties +druid_indexer_fork_property_druid_processing_buffer_sizeBytes: 25MiB +druid_indexer_fork_property_druid_processing_numThreads: 2 +druid_indexer_fork_property_druid_server_http_numThreads: 25 + +#Additional Parameters +druid_indexer_task_restoreTasksOnRestart: true + +druid_middlemanager_replicas: 1 + + +#################### Druid Indexer Variables ###################### + +druid_indexer_service: druid/indexer +druid_indexer_port: 8091 +druid_indexer_min_heap_size: 256M +druid_indexer_max_heap_size: 512M +druid_indexer_max_direct_size: 256M + +# Peon properties +druid_indexer_fork_property_druid_processing_buffer_sizeBytes: 64M +druid_indexer_fork_property_druid_processing_numThreads: 2 # Number of cores - 1 (or 1) +druid_indexer_fork_property_druid_processing_numMergeBuffers: 1 +druid_indexer_fork_property_druid_server_http_numThreads: 25 # max(10, (Number of cores * 17) / 16 + 2) + 30 + +druid_indexer_pod_memory_request: 512Mi +druid_indexer_pod_memory_limit: 768Mi +druid_indexer_pod_cpu_request: 500m +druid_indexer_pod_cpu_limit: 1 +druid_indexer_numConcurrentMerges: 1 + +druid_indexer_persistent_volume_size: 1Gi + +indexer_intial_delay: 30 + +# Number of tasks per indexer +druid_indexer_worker_capacity: 2 #(Number of available processors - 1) + +# Task launch parameters +# druid_indexer_runner_javaOpts: "-server -Xms512m -Xmx1024m -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:MaxDirectMemorySize=512m" +druid_indexer_task_baseTaskDir: "/druid/data/task" + + + +#Additional Parameters +druid_indexer_task_restoreTasksOnRestart: true + +druid_indexer_replicas: 1 + +######################## Druid Router Variables ######################### + +druid_router_service: druid/router +druid_router_plaintextPort: 8888 + +druid_router_min_heap_size: 256M +druid_router_max_heap_size: 512M + +druid_router_pod_memory_request: 256Mi +druid_router_pod_memory_limit: 512Mi +druid_router_pod_cpu_request: 250m +druid_router_pod_cpu_limit: 512m + +# HTTP proxy +druid_router_http_numConnections: 25 +druid_router_http_readTimeout: PT5M +druid_router_http_numMaxThreads: 25 +druid_server_http_numThreads: 25 + +# Service discovery +druid_router_defaultBrokerServiceName: druid/broker +druid_router_coordinatorServiceName: druid/coordinator + +# Management proxy to coordinator / overlord: required for unified web console. +druid_router_managementProxy_enabled: True + +druid_router_replicas: 1 + +#################### Zookeeper Variables ###################### + +zookeeper: + image: + registry: docker.io + repository: bitnami/zookeeper + tag: 3.6-debian-10 + pullPolicy: Always + + persistence: + size: 1Gi + + heapSize: 128 + replicaCount: 1 + + resources: + requests: + memory: 128Mi + cpu: 512m + + tickTime: 2000 + initLimit: 10 + syncLimit: 5 + preAllocSize: 65536 + snapCount: 100000 + maxClientCnxns: 60 + fourlwCommandsWhitelist: srvr, mntr, ruok + listenOnAllIPs: false + allowAnonymousLogin: true + autopurge: + snapRetainCount: 3 + purgeInterval: 0 + maxSessionTimeout: 40000 + + allowAnonymousLogin: true + + minServerId: 1 + + securityContext: + enabled: true + fsGroup: 1001 + runAsUser: 1001 + + livenessProbe: + enabled: true + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 + probeCommandTimeout: 2 + + readinessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 + probeCommandTimeout: 2 + + service: + type: ClusterIP + port: 2181 + followerPort: 2888 + electionPort: 3888 + nodePorts: + client: "" + clientTls: "" + publishNotReadyAddresses: true + tlsClientPort: 3181 + disableBaseClientPort: false + annotations: {} + headless: + annotations: {} + + + \ No newline at end of file diff --git a/knowlg-automation/helm_charts/druid-operator/Chart.yaml b/knowlg-automation/helm_charts/druid-operator/Chart.yaml new file mode 100644 index 000000000..6588f1e73 --- /dev/null +++ b/knowlg-automation/helm_charts/druid-operator/Chart.yaml @@ -0,0 +1,23 @@ +apiVersion: v2 +name: druid-operator +description: Druid Kubernetes Operator + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.1 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +appVersion: 0.0.7 diff --git a/knowlg-automation/helm_charts/druid-operator/templates/NOTES.txt b/knowlg-automation/helm_charts/druid-operator/templates/NOTES.txt new file mode 100644 index 000000000..88a89946c --- /dev/null +++ b/knowlg-automation/helm_charts/druid-operator/templates/NOTES.txt @@ -0,0 +1 @@ +Refer to https://github.com/druid-io/druid-operator/blob/master/docs/README.md to get started. diff --git a/knowlg-automation/helm_charts/druid-operator/templates/_helpers.tpl b/knowlg-automation/helm_charts/druid-operator/templates/_helpers.tpl new file mode 100644 index 000000000..628079bfe --- /dev/null +++ b/knowlg-automation/helm_charts/druid-operator/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "druid-operator.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "druid-operator.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "druid-operator.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "druid-operator.labels" -}} +helm.sh/chart: {{ include "druid-operator.chart" . }} +{{ include "druid-operator.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "druid-operator.selectorLabels" -}} +app.kubernetes.io/name: {{ include "druid-operator.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "druid-operator.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "druid-operator.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/knowlg-automation/helm_charts/druid-operator/templates/crds/druid.apache.org_druids.yaml b/knowlg-automation/helm_charts/druid-operator/templates/crds/druid.apache.org_druids.yaml new file mode 100644 index 000000000..ed176311c --- /dev/null +++ b/knowlg-automation/helm_charts/druid-operator/templates/crds/druid.apache.org_druids.yaml @@ -0,0 +1,4467 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.6.0-beta.0 + creationTimestamp: null + name: druids.druid.apache.org +spec: + group: druid.apache.org + names: + kind: Druid + listKind: DruidList + plural: druids + singular: druid + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + affinity: + properties: + nodeAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + preference: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + properties: + nodeSelectorTerms: + items: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + podAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + common.runtime.properties: + type: string + commonConfigMountPath: + type: string + containerSecurityContext: + properties: + allowPrivilegeEscalation: + type: boolean + capabilities: + properties: + add: + items: + type: string + type: array + drop: + items: + type: string + type: array + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + runAsUserName: + type: string + type: object + type: object + deepStorage: + properties: + spec: + format: byte + type: string + type: + type: string + required: + - spec + - type + type: object + deleteOrphanPvc: + type: boolean + disablePVCDeletionFinalizer: + type: boolean + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + type: object + required: + - name + type: object + type: array + envFrom: + items: + properties: + configMapRef: + properties: + name: + type: string + optional: + type: boolean + type: object + prefix: + type: string + secretRef: + properties: + name: + type: string + optional: + type: boolean + type: object + type: object + type: array + forceDeleteStsPodOnError: + type: boolean + ignored: + type: boolean + image: + type: string + imagePullPolicy: + type: string + imagePullSecrets: + items: + properties: + name: + type: string + type: object + type: array + jvm.options: + type: string + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + timeoutSeconds: + format: int32 + type: integer + type: object + log4j.config: + type: string + metadataStore: + properties: + spec: + format: byte + type: string + type: + type: string + required: + - spec + - type + type: object + nodeSelector: + additionalProperties: + type: string + type: object + nodes: + additionalProperties: + properties: + affinity: + properties: + nodeAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + preference: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + properties: + nodeSelectorTerms: + items: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + podAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + containerSecurityContext: + properties: + allowPrivilegeEscalation: + type: boolean + capabilities: + properties: + add: + items: + type: string + type: array + drop: + items: + type: string + type: array + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + runAsUserName: + type: string + type: object + type: object + druid.port: + format: int32 + type: integer + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + type: object + required: + - name + type: object + type: array + envFrom: + items: + properties: + configMapRef: + properties: + name: + type: string + optional: + type: boolean + type: object + prefix: + type: string + secretRef: + properties: + name: + type: string + optional: + type: boolean + type: object + type: object + type: array + extra.jvm.options: + type: string + hpAutoscaler: + properties: + behavior: + properties: + scaleDown: + properties: + policies: + items: + properties: + periodSeconds: + format: int32 + type: integer + type: + type: string + value: + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + selectPolicy: + type: string + stabilizationWindowSeconds: + format: int32 + type: integer + type: object + scaleUp: + properties: + policies: + items: + properties: + periodSeconds: + format: int32 + type: integer + type: + type: string + value: + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + selectPolicy: + type: string + stabilizationWindowSeconds: + format: int32 + type: integer + type: object + type: object + maxReplicas: + format: int32 + type: integer + metrics: + items: + properties: + containerResource: + properties: + container: + type: string + name: + type: string + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: + type: string + value: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + required: + - type + type: object + required: + - container + - name + - target + type: object + external: + properties: + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: + type: string + value: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + required: + - type + type: object + required: + - metric + - target + type: object + object: + properties: + describedObject: + properties: + apiVersion: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: + type: string + value: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + required: + - type + type: object + required: + - describedObject + - metric + - target + type: object + pods: + properties: + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: + type: string + value: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + required: + - type + type: object + required: + - metric + - target + type: object + resource: + properties: + name: + type: string + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: + type: string + value: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + required: + - type + type: object + required: + - name + - target + type: object + type: + type: string + required: + - type + type: object + type: array + minReplicas: + format: int32 + type: integer + scaleTargetRef: + properties: + apiVersion: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + required: + - maxReplicas + - scaleTargetRef + type: object + image: + type: string + imagePullPolicy: + type: string + imagePullSecrets: + items: + properties: + name: + type: string + type: object + type: array + ingress: + properties: + defaultBackend: + properties: + resource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + service: + properties: + name: + type: string + port: + properties: + name: + type: string + number: + format: int32 + type: integer + type: object + required: + - name + type: object + type: object + ingressClassName: + type: string + rules: + items: + properties: + host: + type: string + http: + properties: + paths: + items: + properties: + backend: + properties: + resource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + service: + properties: + name: + type: string + port: + properties: + name: + type: string + number: + format: int32 + type: integer + type: object + required: + - name + type: object + type: object + path: + type: string + pathType: + type: string + required: + - backend + type: object + type: array + x-kubernetes-list-type: atomic + required: + - paths + type: object + type: object + type: array + x-kubernetes-list-type: atomic + tls: + items: + properties: + hosts: + items: + type: string + type: array + x-kubernetes-list-type: atomic + secretName: + type: string + type: object + type: array + x-kubernetes-list-type: atomic + type: object + ingressAnnotations: + additionalProperties: + type: string + type: object + jvm.options: + type: string + kind: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + timeoutSeconds: + format: int32 + type: integer + type: object + log4j.config: + type: string + maxSurge: + format: int32 + type: integer + maxUnavailable: + format: int32 + type: integer + nodeConfigMountPath: + type: string + nodeType: + type: string + persistentVolumeClaim: + items: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + storageClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + status: + properties: + accessModes: + items: + type: string + type: array + capacity: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + conditions: + items: + properties: + lastProbeTime: + format: date-time + type: string + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + phase: + type: string + type: object + type: object + type: array + podAnnotations: + additionalProperties: + type: string + type: object + podDisruptionBudgetSpec: + properties: + maxUnavailable: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + minAvailable: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + type: object + podLabels: + additionalProperties: + type: string + type: object + podManagementPolicy: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + timeoutSeconds: + format: int32 + type: integer + type: object + replicas: + format: int32 + minimum: 0 + type: integer + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + runtime.properties: + type: string + securityContext: + properties: + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + runAsUserName: + type: string + type: object + type: object + services: + items: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + allocateLoadBalancerNodePorts: + type: boolean + clusterIP: + type: string + clusterIPs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + externalIPs: + items: + type: string + type: array + externalName: + type: string + externalTrafficPolicy: + type: string + healthCheckNodePort: + format: int32 + type: integer + ipFamilies: + items: + type: string + type: array + x-kubernetes-list-type: atomic + ipFamilyPolicy: + type: string + loadBalancerIP: + type: string + loadBalancerSourceRanges: + items: + type: string + type: array + ports: + items: + properties: + appProtocol: + type: string + name: + type: string + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + default: TCP + type: string + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + x-kubernetes-list-map-keys: + - port + - protocol + x-kubernetes-list-type: map + publishNotReadyAddresses: + type: boolean + selector: + additionalProperties: + type: string + type: object + sessionAffinity: + type: string + sessionAffinityConfig: + properties: + clientIP: + properties: + timeoutSeconds: + format: int32 + type: integer + type: object + type: object + topologyKeys: + items: + type: string + type: array + type: + type: string + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + loadBalancer: + properties: + ingress: + items: + properties: + hostname: + type: string + ip: + type: string + ports: + items: + properties: + error: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + port: + format: int32 + type: integer + protocol: + default: TCP + type: string + required: + - port + - protocol + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: array + type: object + type: object + type: object + type: array + startUpProbes: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + timeoutSeconds: + format: int32 + type: integer + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + tolerations: + items: + properties: + effect: + type: string + key: + type: string + operator: + type: string + tolerationSeconds: + format: int64 + type: integer + value: + type: string + type: object + type: array + topologySpreadConstraints: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + maxSkew: + format: int32 + type: integer + topologyKey: + type: string + whenUnsatisfiable: + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + updateStrategy: + properties: + rollingUpdate: + properties: + partition: + format: int32 + type: integer + type: object + type: + type: string + type: object + volumeClaimTemplates: + items: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + storageClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + status: + properties: + accessModes: + items: + type: string + type: array + capacity: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + conditions: + items: + properties: + lastProbeTime: + format: date-time + type: string + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + phase: + type: string + type: object + type: object + type: array + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + volumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + type: string + kind: + type: string + readOnly: + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + type: string + type: object + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + type: string + type: object + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + properties: + readOnly: + type: boolean + volumeClaimTemplate: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + storageClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec + type: object + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + wwids: + items: + type: string + type: array + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + type: string + monitors: + items: + type: string + type: array + pool: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + user: + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + sslEnabled: + type: boolean + storageMode: + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + required: + - druid.port + - nodeConfigMountPath + - nodeType + - replicas + - runtime.properties + type: object + type: object + podAnnotations: + additionalProperties: + type: string + type: object + podLabels: + additionalProperties: + type: string + type: object + podManagementPolicy: + type: string + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + timeoutSeconds: + format: int32 + type: integer + type: object + rollingDeploy: + type: boolean + securityContext: + properties: + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + runAsUserName: + type: string + type: object + type: object + serviceAccount: + type: string + services: + items: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + allocateLoadBalancerNodePorts: + type: boolean + clusterIP: + type: string + clusterIPs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + externalIPs: + items: + type: string + type: array + externalName: + type: string + externalTrafficPolicy: + type: string + healthCheckNodePort: + format: int32 + type: integer + ipFamilies: + items: + type: string + type: array + x-kubernetes-list-type: atomic + ipFamilyPolicy: + type: string + loadBalancerIP: + type: string + loadBalancerSourceRanges: + items: + type: string + type: array + ports: + items: + properties: + appProtocol: + type: string + name: + type: string + nodePort: + format: int32 + type: integer + port: + format: int32 + type: integer + protocol: + default: TCP + type: string + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + x-kubernetes-list-map-keys: + - port + - protocol + x-kubernetes-list-type: map + publishNotReadyAddresses: + type: boolean + selector: + additionalProperties: + type: string + type: object + sessionAffinity: + type: string + sessionAffinityConfig: + properties: + clientIP: + properties: + timeoutSeconds: + format: int32 + type: integer + type: object + type: object + topologyKeys: + items: + type: string + type: array + type: + type: string + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + loadBalancer: + properties: + ingress: + items: + properties: + hostname: + type: string + ip: + type: string + ports: + items: + properties: + error: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + port: + format: int32 + type: integer + protocol: + default: TCP + type: string + required: + - port + - protocol + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: array + type: object + type: object + type: object + type: array + startScript: + type: string + startUpProbes: + properties: + exec: + properties: + command: + items: + type: string + type: array + type: object + failureThreshold: + format: int32 + type: integer + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + timeoutSeconds: + format: int32 + type: integer + type: object + tolerations: + items: + properties: + effect: + type: string + key: + type: string + operator: + type: string + tolerationSeconds: + format: int64 + type: integer + value: + type: string + type: object + type: array + updateStrategy: + properties: + rollingUpdate: + properties: + partition: + format: int32 + type: integer + type: object + type: + type: string + type: object + volumeClaimTemplates: + items: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + storageClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + status: + properties: + accessModes: + items: + type: string + type: array + capacity: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + conditions: + items: + properties: + lastProbeTime: + format: date-time + type: string + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + phase: + type: string + type: object + type: object + type: array + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + volumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + type: string + kind: + type: string + readOnly: + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + type: string + type: object + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + type: string + type: object + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + properties: + readOnly: + type: boolean + volumeClaimTemplate: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + storageClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec + type: object + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + wwids: + items: + type: string + type: array + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + type: string + monitors: + items: + type: string + type: array + pool: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + user: + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + sslEnabled: + type: boolean + storageMode: + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + zookeeper: + properties: + spec: + format: byte + type: string + type: + type: string + required: + - spec + - type + type: object + required: + - common.runtime.properties + - commonConfigMountPath + - nodes + - startScript + type: object + status: + properties: + configMaps: + items: + type: string + type: array + deployments: + items: + type: string + type: array + druidNodeStatus: + properties: + druidNode: + type: string + druidNodeConditionStatus: + type: string + druidNodeConditionType: + type: string + reason: + type: string + type: object + hpAutoscalers: + items: + type: string + type: array + ingress: + items: + type: string + type: array + persistentVolumeClaims: + items: + type: string + type: array + podDisruptionBudgets: + items: + type: string + type: array + pods: + items: + type: string + type: array + services: + items: + type: string + type: array + statefulSets: + items: + type: string + type: array + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/knowlg-automation/helm_charts/druid-operator/templates/deployment.yaml b/knowlg-automation/helm_charts/druid-operator/templates/deployment.yaml new file mode 100644 index 000000000..f4bdc5ae8 --- /dev/null +++ b/knowlg-automation/helm_charts/druid-operator/templates/deployment.yaml @@ -0,0 +1,59 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + namespace: {{ .Release.Namespace }} + name: {{ include "druid-operator.fullname" . }} + labels: + {{- include "druid-operator.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "druid-operator.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "druid-operator.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "druid-operator.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + command: + - /manager + imagePullPolicy: {{ .Values.image.pullPolicy }} + env: + {{- range $key, $value := .Values.env }} + - name: {{ $key }} + value: {{ tpl $value $ | quote }} + {{- end }} + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + resources: + {{- toYaml .Values.resources | nindent 12 }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/knowlg-automation/helm_charts/druid-operator/templates/role.yaml b/knowlg-automation/helm_charts/druid-operator/templates/role.yaml new file mode 100644 index 000000000..b12ed0d85 --- /dev/null +++ b/knowlg-automation/helm_charts/druid-operator/templates/role.yaml @@ -0,0 +1,95 @@ +{{- if .Values.rbac.create }} +apiVersion: rbac.authorization.k8s.io/v1 +{{- if .Values.env.WATCH_NAMESPACE }} +kind: Role +{{- else }} +kind: ClusterRole +{{- end }} +metadata: +{{- if .Values.env.WATCH_NAMESPACE }} + namespace: {{ .Values.env.WATCH_NAMESPACE }} +{{- end }} + name: {{ template "druid-operator.fullname" . }} + labels: + {{- include "druid-operator.labels" . | nindent 4 }} +rules: + - apiGroups: + - "" + resources: + - pods + - configmaps + - services + - persistentvolumeclaims + verbs: + - list + - watch + - create + - update + - delete + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch + - apiGroups: + - apps + resources: + - statefulsets + - deployments + verbs: + - list + - watch + - create + - update + - delete + - apiGroups: + - autoscaling + resources: + - horizontalpodautoscalers + verbs: + - list + - watch + - create + - update + - patch + - apiGroups: + - policy + resources: + - poddisruptionbudgets + verbs: + - list + - watch + - create + - update + - apiGroups: + - networking.k8s.io + resources: + - ingresses + verbs: + - list + - watch + - create + - update + - apiGroups: + - druid.apache.org + resources: + - druids + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - druid.apache.org + resources: + - druids/status + verbs: + - get + - update + - patch +{{- end }} diff --git a/knowlg-automation/helm_charts/druid-operator/templates/role_binding.yaml b/knowlg-automation/helm_charts/druid-operator/templates/role_binding.yaml new file mode 100644 index 000000000..2587fe604 --- /dev/null +++ b/knowlg-automation/helm_charts/druid-operator/templates/role_binding.yaml @@ -0,0 +1,23 @@ +{{- if .Values.rbac.create }} +apiVersion: rbac.authorization.k8s.io/v1 +{{- if .Values.env.WATCH_NAMESPACE }} +kind: RoleBinding +{{- else }} +kind: ClusterRoleBinding +{{- end }} +metadata: +{{- if .Values.env.WATCH_NAMESPACE }} + namespace: {{ .Values.env.WATCH_NAMESPACE }} +{{- end }} + name: {{ template "druid-operator.fullname" . }} + labels: + {{- include "druid-operator.labels" . | nindent 4 }} +subjects: +- kind: ServiceAccount + name: {{ include "druid-operator.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} +roleRef: + kind: {{ if .Values.env.WATCH_NAMESPACE }} Role {{ else }} ClusterRole {{ end }} + name: {{ template "druid-operator.fullname" . }} + apiGroup: rbac.authorization.k8s.io +{{- end }} diff --git a/knowlg-automation/helm_charts/druid-operator/templates/serviceaccount.yaml b/knowlg-automation/helm_charts/druid-operator/templates/serviceaccount.yaml new file mode 100644 index 000000000..d1af85fe9 --- /dev/null +++ b/knowlg-automation/helm_charts/druid-operator/templates/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + namespace: {{ .Release.Namespace }} + name: {{ include "druid-operator.serviceAccountName" . }} + labels: + {{- include "druid-operator.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/knowlg-automation/helm_charts/druid-operator/values.yaml b/knowlg-automation/helm_charts/druid-operator/values.yaml new file mode 100644 index 000000000..75ab618ec --- /dev/null +++ b/knowlg-automation/helm_charts/druid-operator/values.yaml @@ -0,0 +1,63 @@ +# Default values for druid-operator. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +env: + DENY_LIST: "default,kube-system" # Comma-separated list of namespaces to ignore + RECONCILE_WAIT: "10s" # Reconciliation delay + WATCH_NAMESPACE: "" # Namespace to watch or empty string to watch all namespaces + +replicaCount: 1 + +image: + repository: druidio/druid-operator + pullPolicy: IfNotPresent + # Overrides the image tag whose default is the chart appVersion. + tag: "" + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +rbac: + create: true + +serviceAccount: + # Specifies whether a service account should be created + create: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + +podAnnotations: {} + +podSecurityContext: {} + # fsGroup: 2000 + +securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +nodeSelector: {} + +tolerations: [] + +affinity: {} diff --git a/knowlg-automation/helm_charts/elasticsearch/.helmignore b/knowlg-automation/helm_charts/elasticsearch/.helmignore new file mode 100644 index 000000000..f0c131944 --- /dev/null +++ b/knowlg-automation/helm_charts/elasticsearch/.helmignore @@ -0,0 +1,21 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj diff --git a/knowlg-automation/helm_charts/elasticsearch/Chart.lock b/knowlg-automation/helm_charts/elasticsearch/Chart.lock new file mode 100644 index 000000000..6f6b4a208 --- /dev/null +++ b/knowlg-automation/helm_charts/elasticsearch/Chart.lock @@ -0,0 +1,9 @@ +dependencies: +- name: kibana + repository: https://charts.bitnami.com/bitnami + version: 10.2.16 +- name: common + repository: https://charts.bitnami.com/bitnami + version: 2.2.4 +digest: sha256:4d5edc18f6f4c69d66d3e2c1723fcd7f722141237052c3852562232c141bfcf3 +generated: "2023-03-18T21:45:26.026217282Z" diff --git a/knowlg-automation/helm_charts/elasticsearch/Chart.yaml b/knowlg-automation/helm_charts/elasticsearch/Chart.yaml new file mode 100644 index 000000000..5fb0a720b --- /dev/null +++ b/knowlg-automation/helm_charts/elasticsearch/Chart.yaml @@ -0,0 +1,28 @@ +annotations: + category: Analytics +apiVersion: v2 +appVersion: 8.6.0 +dependencies: +- condition: global.kibanaEnabled + name: kibana + repository: https://charts.bitnami.com/bitnami + version: 10.x.x +- name: common + repository: https://charts.bitnami.com/bitnami + tags: + - bitnami-common + version: 2.x.x +description: Elasticsearch is a distributed search and analytics engine. It is used + for web search, log monitoring, and real-time analytics. Ideal for Big Data applications. +home: https://github.com/bitnami/charts/tree/main/bitnami/elasticsearch +icon: https://bitnami.com/assets/stacks/elasticsearch/img/elasticsearch-stack-220x234.png +keywords: +- elasticsearch +maintainers: +- name: Bitnami + url: https://github.com/bitnami/charts +name: elasticsearch +sources: +- https://github.com/bitnami/containers/tree/main/bitnami/elasticsearch +- https://www.elastic.co/products/elasticsearch +version: 19.5.8 \ No newline at end of file diff --git a/knowlg-automation/helm_charts/elasticsearch/README.md b/knowlg-automation/helm_charts/elasticsearch/README.md new file mode 100644 index 000000000..b95192856 --- /dev/null +++ b/knowlg-automation/helm_charts/elasticsearch/README.md @@ -0,0 +1,969 @@ + + +# Bitnami Elasticsearch Stack + +Elasticsearch is a distributed search and analytics engine. It is used for web search, log monitoring, and real-time analytics. Ideal for Big Data applications. + +[Overview of Elasticsearch](https://www.elastic.co/products/elasticsearch) + +Trademarks: This software listing is packaged by Bitnami. The respective trademarks mentioned in the offering are owned by the respective companies, and use of them does not imply any affiliation or endorsement. + +## TL;DR + +```console +helm repo add my-repo https://charts.bitnami.com/bitnami +helm install my-release my-repo/elasticsearch +``` + +## Introduction + +This chart bootstraps a [Elasticsearch](https://github.com/bitnami/containers/tree/main/bitnami/elasticsearch) deployment on a [Kubernetes](https://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager. + +Bitnami charts can be used with [Kubeapps](https://kubeapps.dev/) for deployment and management of Helm Charts in clusters. + +## Prerequisites + +- Kubernetes 1.19+ +- Helm 3.2.0+ +- PV provisioner support in the underlying infrastructure + +## Installing the Chart + +To install the chart with the release name `my-release`: + +```console +helm repo add my-repo https://charts.bitnami.com/bitnami +helm install my-release my-repo/elasticsearch +``` + +These commands deploy Elasticsearch on the Kubernetes cluster in the default configuration. The [Parameters](#parameters) section lists the parameters that can be configured during installation. + +> **Tip**: List all releases using `helm list` + +## Uninstalling the Chart + +To uninstall/delete the `my-release` release: + +```console +helm delete my-release +``` + +The command removes all the Kubernetes components associated with the chart and deletes the release. Remove also the chart using `--purge` option: + +```console +helm delete --purge my-release +``` + +## Parameters + +### Global parameters + +| Name | Description | Value | +| -------------------------------------------- | ----------------------------------------------------------------------------------------------------- | --------------- | +| `global.imageRegistry` | Global Docker image registry | `""` | +| `global.imagePullSecrets` | Global Docker registry secret names as an array | `[]` | +| `global.storageClass` | Global StorageClass for Persistent Volume(s) | `""` | +| `global.elasticsearch.service.name` | Elasticsearch service name to be used in the Kibana subchart (ignored if kibanaEnabled=false) | `elasticsearch` | +| `global.elasticsearch.service.ports.restAPI` | Elasticsearch service restAPI port to be used in the Kibana subchart (ignored if kibanaEnabled=false) | `9200` | +| `global.kibanaEnabled` | Whether or not to enable Kibana | `false` | + +### Common parameters + +| Name | Description | Value | +| ------------------------ | --------------------------------------------------------------------------------------- | --------------- | +| `kubeVersion` | Override Kubernetes version | `""` | +| `nameOverride` | String to partially override common.names.fullname | `""` | +| `fullnameOverride` | String to fully override common.names.fullname | `""` | +| `commonLabels` | Labels to add to all deployed objects | `{}` | +| `commonAnnotations` | Annotations to add to all deployed objects | `{}` | +| `clusterDomain` | Kubernetes cluster domain name | `cluster.local` | +| `extraDeploy` | Array of extra objects to deploy with the release | `[]` | +| `namespaceOverride` | String to fully override common.names.namespace | `""` | +| `diagnosticMode.enabled` | Enable diagnostic mode (all probes will be disabled and the command will be overridden) | `false` | +| `diagnosticMode.command` | Command to override all containers in the deployment | `["sleep"]` | +| `diagnosticMode.args` | Args to override all containers in the deployment | `["infinity"]` | + +### Elasticsearch cluster Parameters + +| Name | Description | Value | +| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------ | +| `clusterName` | Elasticsearch cluster name | `elastic` | +| `containerPorts.restAPI` | Elasticsearch REST API port | `9200` | +| `containerPorts.transport` | Elasticsearch Transport port | `9300` | +| `plugins` | Comma, semi-colon or space separated list of plugins to install at initialization | `""` | +| `snapshotRepoPath` | File System snapshot repository path | `""` | +| `config` | Override elasticsearch configuration | `{}` | +| `extraConfig` | Append extra configuration to the elasticsearch node configuration | `{}` | +| `extraHosts` | A list of external hosts which are part of this cluster | `[]` | +| `extraVolumes` | A list of volumes to be added to the pod | `[]` | +| `extraVolumeMounts` | A list of volume mounts to be added to the pod | `[]` | +| `initScripts` | Dictionary of init scripts. Evaluated as a template. | `{}` | +| `initScriptsCM` | ConfigMap with the init scripts. Evaluated as a template. | `""` | +| `initScriptsSecret` | Secret containing `/docker-entrypoint-initdb.d` scripts to be executed at initialization time that contain sensitive data. Evaluated as a template. | `""` | +| `extraEnvVars` | Array containing extra env vars to be added to all pods (evaluated as a template) | `[]` | +| `extraEnvVarsCM` | ConfigMap containing extra env vars to be added to all pods (evaluated as a template) | `""` | +| `extraEnvVarsSecret` | Secret containing extra env vars to be added to all pods (evaluated as a template) | `""` | +| `sidecars` | Add additional sidecar containers to the all elasticsearch node pod(s) | `[]` | +| `initContainers` | Add additional init containers to the all elasticsearch node pod(s) | `[]` | +| `useIstioLabels` | Use this variable to add Istio labels to all pods | `true` | +| `image.registry` | Elasticsearch image registry | `docker.io` | +| `image.repository` | Elasticsearch image repository | `bitnami/elasticsearch` | +| `image.tag` | Elasticsearch image tag (immutable tags are recommended) | `8.6.2-debian-11-r10` | +| `image.digest` | Elasticsearch image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag | `""` | +| `image.pullPolicy` | Elasticsearch image pull policy | `IfNotPresent` | +| `image.pullSecrets` | Elasticsearch image pull secrets | `[]` | +| `image.debug` | Enable Elasticsearch image debug mode | `false` | +| `security.enabled` | Enable X-Pack Security settings | `false` | +| `security.elasticPassword` | Password for 'elastic' user | `""` | +| `security.existingSecret` | Name of the existing secret containing the Elasticsearch password and | `""` | +| `security.fipsMode` | Configure elasticsearch with FIPS 140 compliant mode | `false` | +| `security.tls.restEncryption` | Enable SSL/TLS encryption for Elasticsearch REST API. | `true` | +| `security.tls.autoGenerated` | Create self-signed TLS certificates. | `false` | +| `security.tls.verificationMode` | Verification mode for SSL communications. | `full` | +| `security.tls.master.existingSecret` | Existing secret containing the certificates for the master nodes | `""` | +| `security.tls.data.existingSecret` | Existing secret containing the certificates for the data nodes | `""` | +| `security.tls.ingest.existingSecret` | Existing secret containing the certificates for the ingest nodes | `""` | +| `security.tls.coordinating.existingSecret` | Existing secret containing the certificates for the coordinating nodes | `""` | +| `security.tls.keystoreFilename` | Name of the keystore file | `elasticsearch.keystore.jks` | +| `security.tls.truststoreFilename` | Name of the truststore | `elasticsearch.truststore.jks` | +| `security.tls.usePemCerts` | Use this variable if your secrets contain PEM certificates instead of JKS/PKCS12 | `false` | +| `security.tls.passwordsSecret` | Existing secret containing the Keystore and Truststore passwords, or key password if PEM certs are used | `""` | +| `security.tls.keystorePassword` | Password to access the JKS/PKCS12 keystore or PEM key when they are password-protected. | `""` | +| `security.tls.truststorePassword` | Password to access the JKS/PKCS12 truststore when they are password-protected. | `""` | +| `security.tls.keyPassword` | Password to access the PEM key when they are password-protected. | `""` | +| `security.tls.secretKeystoreKey` | Name of the secret key containing the Keystore password | `""` | +| `security.tls.secretTruststoreKey` | Name of the secret key containing the Truststore password | `""` | +| `security.tls.secretKey` | Name of the secret key containing the PEM key password | `""` | + +### Traffic Exposure Parameters + +| Name | Description | Value | +| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | +| `service.type` | Elasticsearch service type | `ClusterIP` | +| `service.ports.restAPI` | Elasticsearch service REST API port | `9200` | +| `service.ports.transport` | Elasticsearch service transport port | `9300` | +| `service.nodePorts.restAPI` | Node port for REST API | `""` | +| `service.nodePorts.transport` | Node port for REST API | `""` | +| `service.clusterIP` | Elasticsearch service Cluster IP | `""` | +| `service.loadBalancerIP` | Elasticsearch service Load Balancer IP | `""` | +| `service.loadBalancerSourceRanges` | Elasticsearch service Load Balancer sources | `[]` | +| `service.externalTrafficPolicy` | Elasticsearch service external traffic policy | `Cluster` | +| `service.annotations` | Additional custom annotations for Elasticsearch service | `{}` | +| `service.extraPorts` | Extra ports to expose in Elasticsearch service (normally used with the `sidecars` value) | `[]` | +| `service.sessionAffinity` | Session Affinity for Kubernetes service, can be "None" or "ClientIP" | `None` | +| `service.sessionAffinityConfig` | Additional settings for the sessionAffinity | `{}` | +| `ingress.enabled` | Enable ingress record generation for Elasticsearch | `false` | +| `ingress.pathType` | Ingress path type | `ImplementationSpecific` | +| `ingress.apiVersion` | Force Ingress API version (automatically detected if not set) | `""` | +| `ingress.hostname` | Default host for the ingress record | `elasticsearch.local` | +| `ingress.path` | Default path for the ingress record | `/` | +| `ingress.annotations` | Additional annotations for the Ingress resource. To enable certificate autogeneration, place here your cert-manager annotations. | `{}` | +| `ingress.tls` | Enable TLS configuration for the host defined at `ingress.hostname` parameter | `false` | +| `ingress.selfSigned` | Create a TLS secret for this ingress record using self-signed certificates generated by Helm | `false` | +| `ingress.ingressClassName` | IngressClass that will be be used to implement the Ingress (Kubernetes 1.18+) | `""` | +| `ingress.extraHosts` | An array with additional hostname(s) to be covered with the ingress record | `[]` | +| `ingress.extraPaths` | An array with additional arbitrary paths that may need to be added to the ingress under the main host | `[]` | +| `ingress.extraTls` | TLS configuration for additional hostname(s) to be covered with this ingress record | `[]` | +| `ingress.secrets` | Custom TLS certificates as secrets | `[]` | +| `ingress.extraRules` | Additional rules to be covered with this ingress record | `[]` | + +### Master-elegible nodes parameters + +| Name | Description | Value | +| ---------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | +| `master.masterOnly` | Deploy the Elasticsearch master-elegible nodes as master-only nodes. Recommended for high-demand deployments. | `true` | +| `master.replicaCount` | Number of master-elegible replicas to deploy | `2` | +| `master.nameOverride` | String to partially override elasticsearch.master.fullname | `""` | +| `master.fullnameOverride` | String to fully override elasticsearch.master.fullname | `""` | +| `master.servicenameOverride` | String to fully override elasticsearch.master.servicename | `""` | +| `master.annotations` | Annotations for the master statefulset | `{}` | +| `master.updateStrategy.type` | Master-elegible nodes statefulset stategy type | `RollingUpdate` | +| `master.resources.limits` | The resources limits for elasticsearch containers | `{}` | +| `master.resources.requests` | The requested resources for elasticsearch containers | `{}` | +| `master.heapSize` | Elasticsearch master-eligible node heap size. | `128m` | +| `master.podSecurityContext.enabled` | Enabled master-elegible pods' Security Context | `true` | +| `master.podSecurityContext.fsGroup` | Set master-elegible pod's Security Context fsGroup | `1001` | +| `master.containerSecurityContext.enabled` | Enabled master-elegible containers' Security Context | `true` | +| `master.containerSecurityContext.runAsUser` | Set master-elegible containers' Security Context runAsUser | `1001` | +| `master.containerSecurityContext.runAsNonRoot` | Set master-elegible containers' Security Context runAsNonRoot | `true` | +| `master.hostAliases` | master-elegible pods host aliases | `[]` | +| `master.podLabels` | Extra labels for master-elegible pods | `{}` | +| `master.podAnnotations` | Annotations for master-elegible pods | `{}` | +| `master.podAffinityPreset` | Pod affinity preset. Ignored if `master.affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `master.podAntiAffinityPreset` | Pod anti-affinity preset. Ignored if `master.affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `master.nodeAffinityPreset.type` | Node affinity preset type. Ignored if `master.affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `master.nodeAffinityPreset.key` | Node label key to match. Ignored if `master.affinity` is set | `""` | +| `master.nodeAffinityPreset.values` | Node label values to match. Ignored if `master.affinity` is set | `[]` | +| `master.affinity` | Affinity for master-elegible pods assignment | `{}` | +| `master.nodeSelector` | Node labels for master-elegible pods assignment | `{}` | +| `master.tolerations` | Tolerations for master-elegible pods assignment | `[]` | +| `master.priorityClassName` | master-elegible pods' priorityClassName | `""` | +| `master.schedulerName` | Name of the k8s scheduler (other than default) for master-elegible pods | `""` | +| `master.terminationGracePeriodSeconds` | In seconds, time the given to the Elasticsearch Master pod needs to terminate gracefully | `""` | +| `master.topologySpreadConstraints` | Topology Spread Constraints for pod assignment spread across your cluster among failure-domains. Evaluated as a template | `[]` | +| `master.podManagementPolicy` | podManagementPolicy to manage scaling operation of Elasticsearch master pods | `Parallel` | +| `master.startupProbe.enabled` | Enable/disable the startup probe (master nodes pod) | `false` | +| `master.startupProbe.initialDelaySeconds` | Delay before startup probe is initiated (master nodes pod) | `90` | +| `master.startupProbe.periodSeconds` | How often to perform the probe (master nodes pod) | `10` | +| `master.startupProbe.timeoutSeconds` | When the probe times out (master nodes pod) | `5` | +| `master.startupProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed (master nodes pod) | `1` | +| `master.startupProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded | `5` | +| `master.livenessProbe.enabled` | Enable/disable the liveness probe (master-eligible nodes pod) | `true` | +| `master.livenessProbe.initialDelaySeconds` | Delay before liveness probe is initiated (master-eligible nodes pod) | `90` | +| `master.livenessProbe.periodSeconds` | How often to perform the probe (master-eligible nodes pod) | `10` | +| `master.livenessProbe.timeoutSeconds` | When the probe times out (master-eligible nodes pod) | `5` | +| `master.livenessProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed (master-eligible nodes pod) | `1` | +| `master.livenessProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded | `5` | +| `master.readinessProbe.enabled` | Enable/disable the readiness probe (master-eligible nodes pod) | `true` | +| `master.readinessProbe.initialDelaySeconds` | Delay before readiness probe is initiated (master-eligible nodes pod) | `90` | +| `master.readinessProbe.periodSeconds` | How often to perform the probe (master-eligible nodes pod) | `10` | +| `master.readinessProbe.timeoutSeconds` | When the probe times out (master-eligible nodes pod) | `5` | +| `master.readinessProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed (master-eligible nodes pod) | `1` | +| `master.readinessProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded | `5` | +| `master.customStartupProbe` | Override default startup probe | `{}` | +| `master.customLivenessProbe` | Override default liveness probe | `{}` | +| `master.customReadinessProbe` | Override default readiness probe | `{}` | +| `master.command` | Override default container command (useful when using custom images) | `[]` | +| `master.args` | Override default container args (useful when using custom images) | `[]` | +| `master.lifecycleHooks` | for the master-elegible container(s) to automate configuration before or after startup | `{}` | +| `master.extraEnvVars` | Array with extra environment variables to add to master-elegible nodes | `[]` | +| `master.extraEnvVarsCM` | Name of existing ConfigMap containing extra env vars for master-elegible nodes | `""` | +| `master.extraEnvVarsSecret` | Name of existing Secret containing extra env vars for master-elegible nodes | `""` | +| `master.extraVolumes` | Optionally specify extra list of additional volumes for the master-elegible pod(s) | `[]` | +| `master.extraVolumeMounts` | Optionally specify extra list of additional volumeMounts for the master-elegible container(s) | `[]` | +| `master.sidecars` | Add additional sidecar containers to the master-elegible pod(s) | `[]` | +| `master.initContainers` | Add additional init containers to the master-elegible pod(s) | `[]` | +| `master.persistence.enabled` | Enable persistence using a `PersistentVolumeClaim` | `true` | +| `master.persistence.storageClass` | Persistent Volume Storage Class | `""` | +| `master.persistence.existingClaim` | Existing Persistent Volume Claim | `""` | +| `master.persistence.existingVolume` | Existing Persistent Volume for use as volume match label selector to the `volumeClaimTemplate`. Ignored when `master.persistence.selector` is set. | `""` | +| `master.persistence.selector` | Configure custom selector for existing Persistent Volume. Overwrites `master.persistence.existingVolume` | `{}` | +| `master.persistence.annotations` | Persistent Volume Claim annotations | `{}` | +| `master.persistence.accessModes` | Persistent Volume Access Modes | `["ReadWriteOnce"]` | +| `master.persistence.size` | Persistent Volume Size | `8Gi` | +| `master.serviceAccount.create` | Specifies whether a ServiceAccount should be created | `false` | +| `master.serviceAccount.name` | Name of the service account to use. If not set and create is true, a name is generated using the fullname template. | `""` | +| `master.serviceAccount.automountServiceAccountToken` | Automount service account token for the server service account | `true` | +| `master.serviceAccount.annotations` | Annotations for service account. Evaluated as a template. Only used if `create` is `true`. | `{}` | +| `master.autoscaling.enabled` | Whether enable horizontal pod autoscale | `false` | +| `master.autoscaling.minReplicas` | Configure a minimum amount of pods | `3` | +| `master.autoscaling.maxReplicas` | Configure a maximum amount of pods | `11` | +| `master.autoscaling.targetCPU` | Define the CPU target to trigger the scaling actions (utilization percentage) | `""` | +| `master.autoscaling.targetMemory` | Define the memory target to trigger the scaling actions (utilization percentage) | `""` | + +### Data-only nodes parameters + +| Name | Description | Value | +| -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------- | +| `data.replicaCount` | Number of data-only replicas to deploy | `2` | +| `data.nameOverride` | String to partially override elasticsearch.data.fullname | `""` | +| `data.fullnameOverride` | String to fully override elasticsearch.data.fullname | `""` | +| `data.servicenameOverride` | String to fully override elasticsearch.data.servicename | `""` | +| `data.annotations` | Annotations for the data statefulset | `{}` | +| `data.updateStrategy.type` | Data-only nodes statefulset stategy type | `RollingUpdate` | +| `data.resources.limits` | The resources limits for the data containers | `{}` | +| `data.resources.requests` | The requested resources for the data containers | `{}` | +| `data.heapSize` | Elasticsearch data node heap size. | `1024m` | +| `data.podSecurityContext.enabled` | Enabled data pods' Security Context | `true` | +| `data.podSecurityContext.fsGroup` | Set data pod's Security Context fsGroup | `1001` | +| `data.containerSecurityContext.enabled` | Enabled data containers' Security Context | `true` | +| `data.containerSecurityContext.runAsUser` | Set data containers' Security Context runAsUser | `1001` | +| `data.containerSecurityContext.runAsNonRoot` | Set data containers' Security Context runAsNonRoot | `true` | +| `data.hostAliases` | data pods host aliases | `[]` | +| `data.podLabels` | Extra labels for data pods | `{}` | +| `data.podAnnotations` | Annotations for data pods | `{}` | +| `data.podAffinityPreset` | Pod affinity preset. Ignored if `data.affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `data.podAntiAffinityPreset` | Pod anti-affinity preset. Ignored if `data.affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `data.nodeAffinityPreset.type` | Node affinity preset type. Ignored if `data.affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `data.nodeAffinityPreset.key` | Node label key to match. Ignored if `data.affinity` is set | `""` | +| `data.nodeAffinityPreset.values` | Node label values to match. Ignored if `data.affinity` is set | `[]` | +| `data.affinity` | Affinity for data pods assignment | `{}` | +| `data.nodeSelector` | Node labels for data pods assignment | `{}` | +| `data.tolerations` | Tolerations for data pods assignment | `[]` | +| `data.priorityClassName` | data pods' priorityClassName | `""` | +| `data.schedulerName` | Name of the k8s scheduler (other than default) for data pods | `""` | +| `data.terminationGracePeriodSeconds` | In seconds, time the given to the Elasticsearch data pod needs to terminate gracefully | `""` | +| `data.topologySpreadConstraints` | Topology Spread Constraints for pod assignment spread across your cluster among failure-domains. Evaluated as a template | `[]` | +| `data.podManagementPolicy` | podManagementPolicy to manage scaling operation of Elasticsearch data pods | `Parallel` | +| `data.startupProbe.enabled` | Enable/disable the startup probe (data nodes pod) | `false` | +| `data.startupProbe.initialDelaySeconds` | Delay before startup probe is initiated (data nodes pod) | `90` | +| `data.startupProbe.periodSeconds` | How often to perform the probe (data nodes pod) | `10` | +| `data.startupProbe.timeoutSeconds` | When the probe times out (data nodes pod) | `5` | +| `data.startupProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed (data nodes pod) | `1` | +| `data.startupProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded | `5` | +| `data.livenessProbe.enabled` | Enable/disable the liveness probe (data nodes pod) | `true` | +| `data.livenessProbe.initialDelaySeconds` | Delay before liveness probe is initiated (data nodes pod) | `90` | +| `data.livenessProbe.periodSeconds` | How often to perform the probe (data nodes pod) | `10` | +| `data.livenessProbe.timeoutSeconds` | When the probe times out (data nodes pod) | `5` | +| `data.livenessProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed (data nodes pod) | `1` | +| `data.livenessProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded | `5` | +| `data.readinessProbe.enabled` | Enable/disable the readiness probe (data nodes pod) | `true` | +| `data.readinessProbe.initialDelaySeconds` | Delay before readiness probe is initiated (data nodes pod) | `90` | +| `data.readinessProbe.periodSeconds` | How often to perform the probe (data nodes pod) | `10` | +| `data.readinessProbe.timeoutSeconds` | When the probe times out (data nodes pod) | `5` | +| `data.readinessProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed (data nodes pod) | `1` | +| `data.readinessProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded | `5` | +| `data.customStartupProbe` | Override default startup probe | `{}` | +| `data.customLivenessProbe` | Override default liveness probe | `{}` | +| `data.customReadinessProbe` | Override default readiness probe | `{}` | +| `data.command` | Override default container command (useful when using custom images) | `[]` | +| `data.args` | Override default container args (useful when using custom images) | `[]` | +| `data.lifecycleHooks` | for the data container(s) to automate configuration before or after startup | `{}` | +| `data.extraEnvVars` | Array with extra environment variables to add to data nodes | `[]` | +| `data.extraEnvVarsCM` | Name of existing ConfigMap containing extra env vars for data nodes | `""` | +| `data.extraEnvVarsSecret` | Name of existing Secret containing extra env vars for data nodes | `""` | +| `data.extraVolumes` | Optionally specify extra list of additional volumes for the data pod(s) | `[]` | +| `data.extraVolumeMounts` | Optionally specify extra list of additional volumeMounts for the data container(s) | `[]` | +| `data.sidecars` | Add additional sidecar containers to the data pod(s) | `[]` | +| `data.initContainers` | Add additional init containers to the data pod(s) | `[]` | +| `data.persistence.enabled` | Enable persistence using a `PersistentVolumeClaim` | `true` | +| `data.persistence.storageClass` | Persistent Volume Storage Class | `""` | +| `data.persistence.existingClaim` | Existing Persistent Volume Claim | `""` | +| `data.persistence.existingVolume` | Existing Persistent Volume for use as volume match label selector to the `volumeClaimTemplate`. Ignored when `data.persistence.selector` is set. | `""` | +| `data.persistence.selector` | Configure custom selector for existing Persistent Volume. Overwrites `data.persistence.existingVolume` | `{}` | +| `data.persistence.annotations` | Persistent Volume Claim annotations | `{}` | +| `data.persistence.accessModes` | Persistent Volume Access Modes | `["ReadWriteOnce"]` | +| `data.persistence.size` | Persistent Volume Size | `8Gi` | +| `data.serviceAccount.create` | Specifies whether a ServiceAccount should be created | `false` | +| `data.serviceAccount.name` | Name of the service account to use. If not set and create is true, a name is generated using the fullname template. | `""` | +| `data.serviceAccount.automountServiceAccountToken` | Automount service account token for the server service account | `true` | +| `data.serviceAccount.annotations` | Annotations for service account. Evaluated as a template. Only used if `create` is `true`. | `{}` | +| `data.autoscaling.enabled` | Whether enable horizontal pod autoscale | `false` | +| `data.autoscaling.minReplicas` | Configure a minimum amount of pods | `3` | +| `data.autoscaling.maxReplicas` | Configure a maximum amount of pods | `11` | +| `data.autoscaling.targetCPU` | Define the CPU target to trigger the scaling actions (utilization percentage) | `""` | +| `data.autoscaling.targetMemory` | Define the memory target to trigger the scaling actions (utilization percentage) | `""` | + +### Coordinating-only nodes parameters + +| Name | Description | Value | +| ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | --------------- | +| `coordinating.replicaCount` | Number of coordinating-only replicas to deploy | `2` | +| `coordinating.nameOverride` | String to partially override elasticsearch.coordinating.fullname | `""` | +| `coordinating.fullnameOverride` | String to fully override elasticsearch.coordinating.fullname | `""` | +| `coordinating.servicenameOverride` | String to fully override elasticsearch.coordinating.servicename | `""` | +| `coordinating.annotations` | Annotations for the coordinating-only statefulset | `{}` | +| `coordinating.updateStrategy.type` | Coordinating-only nodes statefulset stategy type | `RollingUpdate` | +| `coordinating.resources.limits` | The resources limits for the coordinating-only containers | `{}` | +| `coordinating.resources.requests` | The requested resources for the coordinating-only containers | `{}` | +| `coordinating.heapSize` | Elasticsearch coordinating node heap size. | `128m` | +| `coordinating.podSecurityContext.enabled` | Enabled coordinating-only pods' Security Context | `true` | +| `coordinating.podSecurityContext.fsGroup` | Set coordinating-only pod's Security Context fsGroup | `1001` | +| `coordinating.containerSecurityContext.enabled` | Enabled coordinating-only containers' Security Context | `true` | +| `coordinating.containerSecurityContext.runAsUser` | Set coordinating-only containers' Security Context runAsUser | `1001` | +| `coordinating.containerSecurityContext.runAsNonRoot` | Set coordinating-only containers' Security Context runAsNonRoot | `true` | +| `coordinating.hostAliases` | coordinating-only pods host aliases | `[]` | +| `coordinating.podLabels` | Extra labels for coordinating-only pods | `{}` | +| `coordinating.podAnnotations` | Annotations for coordinating-only pods | `{}` | +| `coordinating.podAffinityPreset` | Pod affinity preset. Ignored if `coordinating.affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `coordinating.podAntiAffinityPreset` | Pod anti-affinity preset. Ignored if `coordinating.affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `coordinating.nodeAffinityPreset.type` | Node affinity preset type. Ignored if `coordinating.affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `coordinating.nodeAffinityPreset.key` | Node label key to match. Ignored if `coordinating.affinity` is set | `""` | +| `coordinating.nodeAffinityPreset.values` | Node label values to match. Ignored if `coordinating.affinity` is set | `[]` | +| `coordinating.affinity` | Affinity for coordinating-only pods assignment | `{}` | +| `coordinating.nodeSelector` | Node labels for coordinating-only pods assignment | `{}` | +| `coordinating.tolerations` | Tolerations for coordinating-only pods assignment | `[]` | +| `coordinating.priorityClassName` | coordinating-only pods' priorityClassName | `""` | +| `coordinating.schedulerName` | Name of the k8s scheduler (other than default) for coordinating-only pods | `""` | +| `coordinating.terminationGracePeriodSeconds` | In seconds, time the given to the Elasticsearch coordinating pod needs to terminate gracefully | `""` | +| `coordinating.topologySpreadConstraints` | Topology Spread Constraints for pod assignment spread across your cluster among failure-domains. Evaluated as a template | `[]` | +| `coordinating.podManagementPolicy` | podManagementPolicy to manage scaling operation of Elasticsearch coordinating pods | `Parallel` | +| `coordinating.startupProbe.enabled` | Enable/disable the startup probe (coordinating-only nodes pod) | `false` | +| `coordinating.startupProbe.initialDelaySeconds` | Delay before startup probe is initiated (coordinating-only nodes pod) | `90` | +| `coordinating.startupProbe.periodSeconds` | How often to perform the probe (coordinating-only nodes pod) | `10` | +| `coordinating.startupProbe.timeoutSeconds` | When the probe times out (coordinating-only nodes pod) | `5` | +| `coordinating.startupProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed (coordinating-only nodes pod) | `1` | +| `coordinating.startupProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded | `5` | +| `coordinating.livenessProbe.enabled` | Enable/disable the liveness probe (coordinating-only nodes pod) | `true` | +| `coordinating.livenessProbe.initialDelaySeconds` | Delay before liveness probe is initiated (coordinating-only nodes pod) | `90` | +| `coordinating.livenessProbe.periodSeconds` | How often to perform the probe (coordinating-only nodes pod) | `10` | +| `coordinating.livenessProbe.timeoutSeconds` | When the probe times out (coordinating-only nodes pod) | `5` | +| `coordinating.livenessProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed (coordinating-only nodes pod) | `1` | +| `coordinating.livenessProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded | `5` | +| `coordinating.readinessProbe.enabled` | Enable/disable the readiness probe (coordinating-only nodes pod) | `true` | +| `coordinating.readinessProbe.initialDelaySeconds` | Delay before readiness probe is initiated (coordinating-only nodes pod) | `90` | +| `coordinating.readinessProbe.periodSeconds` | How often to perform the probe (coordinating-only nodes pod) | `10` | +| `coordinating.readinessProbe.timeoutSeconds` | When the probe times out (coordinating-only nodes pod) | `5` | +| `coordinating.readinessProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed (coordinating-only nodes pod) | `1` | +| `coordinating.readinessProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded | `5` | +| `coordinating.customStartupProbe` | Override default startup probe | `{}` | +| `coordinating.customLivenessProbe` | Override default liveness probe | `{}` | +| `coordinating.customReadinessProbe` | Override default readiness probe | `{}` | +| `coordinating.command` | Override default container command (useful when using custom images) | `[]` | +| `coordinating.args` | Override default container args (useful when using custom images) | `[]` | +| `coordinating.lifecycleHooks` | for the coordinating-only container(s) to automate configuration before or after startup | `{}` | +| `coordinating.extraEnvVars` | Array with extra environment variables to add to coordinating-only nodes | `[]` | +| `coordinating.extraEnvVarsCM` | Name of existing ConfigMap containing extra env vars for coordinating-only nodes | `""` | +| `coordinating.extraEnvVarsSecret` | Name of existing Secret containing extra env vars for coordinating-only nodes | `""` | +| `coordinating.extraVolumes` | Optionally specify extra list of additional volumes for the coordinating-only pod(s) | `[]` | +| `coordinating.extraVolumeMounts` | Optionally specify extra list of additional volumeMounts for the coordinating-only container(s) | `[]` | +| `coordinating.sidecars` | Add additional sidecar containers to the coordinating-only pod(s) | `[]` | +| `coordinating.initContainers` | Add additional init containers to the coordinating-only pod(s) | `[]` | +| `coordinating.serviceAccount.create` | Specifies whether a ServiceAccount should be created | `false` | +| `coordinating.serviceAccount.name` | Name of the service account to use. If not set and create is true, a name is generated using the fullname template. | `""` | +| `coordinating.serviceAccount.automountServiceAccountToken` | Automount service account token for the server service account | `true` | +| `coordinating.serviceAccount.annotations` | Annotations for service account. Evaluated as a template. Only used if `create` is `true`. | `{}` | +| `coordinating.autoscaling.enabled` | Whether enable horizontal pod autoscale | `false` | +| `coordinating.autoscaling.minReplicas` | Configure a minimum amount of pods | `3` | +| `coordinating.autoscaling.maxReplicas` | Configure a maximum amount of pods | `11` | +| `coordinating.autoscaling.targetCPU` | Define the CPU target to trigger the scaling actions (utilization percentage) | `""` | +| `coordinating.autoscaling.targetMemory` | Define the memory target to trigger the scaling actions (utilization percentage) | `""` | + +### Ingest-only nodes parameters + +| Name | Description | Value | +| ---------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | ---------------------------- | +| `ingest.enabled` | Enable ingest nodes | `true` | +| `ingest.replicaCount` | Number of ingest-only replicas to deploy | `2` | +| `ingest.nameOverride` | String to partially override elasticsearch.ingest.fullname | `""` | +| `ingest.fullnameOverride` | String to fully override elasticsearch.ingest.fullname | `""` | +| `ingest.servicenameOverride` | String to fully override ingest.master.servicename | `""` | +| `ingest.annotations` | Annotations for the ingest statefulset | `{}` | +| `ingest.containerPorts.restAPI` | Elasticsearch REST API port | `9200` | +| `ingest.containerPorts.transport` | Elasticsearch Transport port | `9300` | +| `ingest.updateStrategy.type` | Ingest-only nodes statefulset stategy type | `RollingUpdate` | +| `ingest.resources.limits` | The resources limits for the ingest-only containers | `{}` | +| `ingest.resources.requests` | The requested resources for the ingest-only containers | `{}` | +| `ingest.heapSize` | Elasticsearch ingest-only node heap size. | `128m` | +| `ingest.podSecurityContext.enabled` | Enabled ingest-only pods' Security Context | `true` | +| `ingest.podSecurityContext.fsGroup` | Set ingest-only pod's Security Context fsGroup | `1001` | +| `ingest.containerSecurityContext.enabled` | Enabled ingest-only containers' Security Context | `true` | +| `ingest.containerSecurityContext.runAsUser` | Set ingest-only containers' Security Context runAsUser | `1001` | +| `ingest.containerSecurityContext.runAsNonRoot` | Set ingest-only containers' Security Context runAsNonRoot | `true` | +| `ingest.hostAliases` | ingest-only pods host aliases | `[]` | +| `ingest.podLabels` | Extra labels for ingest-only pods | `{}` | +| `ingest.podAnnotations` | Annotations for ingest-only pods | `{}` | +| `ingest.podAffinityPreset` | Pod affinity preset. Ignored if `ingest.affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `ingest.podAntiAffinityPreset` | Pod anti-affinity preset. Ignored if `ingest.affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `ingest.nodeAffinityPreset.type` | Node affinity preset type. Ignored if `ingest.affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `ingest.nodeAffinityPreset.key` | Node label key to match. Ignored if `ingest.affinity` is set | `""` | +| `ingest.nodeAffinityPreset.values` | Node label values to match. Ignored if `ingest.affinity` is set | `[]` | +| `ingest.affinity` | Affinity for ingest-only pods assignment | `{}` | +| `ingest.nodeSelector` | Node labels for ingest-only pods assignment | `{}` | +| `ingest.tolerations` | Tolerations for ingest-only pods assignment | `[]` | +| `ingest.priorityClassName` | ingest-only pods' priorityClassName | `""` | +| `ingest.schedulerName` | Name of the k8s scheduler (other than default) for ingest-only pods | `""` | +| `ingest.terminationGracePeriodSeconds` | In seconds, time the given to the Elasticsearch ingest pod needs to terminate gracefully | `""` | +| `ingest.topologySpreadConstraints` | Topology Spread Constraints for pod assignment spread across your cluster among failure-domains. Evaluated as a template | `[]` | +| `ingest.podManagementPolicy` | podManagementPolicy to manage scaling operation of Elasticsearch ingest pods | `Parallel` | +| `ingest.startupProbe.enabled` | Enable/disable the startup probe (ingest-only nodes pod) | `false` | +| `ingest.startupProbe.initialDelaySeconds` | Delay before startup probe is initiated (ingest-only nodes pod) | `90` | +| `ingest.startupProbe.periodSeconds` | How often to perform the probe (ingest-only nodes pod) | `10` | +| `ingest.startupProbe.timeoutSeconds` | When the probe times out (ingest-only nodes pod) | `5` | +| `ingest.startupProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed (ingest-only nodes pod) | `1` | +| `ingest.startupProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded | `5` | +| `ingest.livenessProbe.enabled` | Enable/disable the liveness probe (ingest-only nodes pod) | `true` | +| `ingest.livenessProbe.initialDelaySeconds` | Delay before liveness probe is initiated (ingest-only nodes pod) | `90` | +| `ingest.livenessProbe.periodSeconds` | How often to perform the probe (ingest-only nodes pod) | `10` | +| `ingest.livenessProbe.timeoutSeconds` | When the probe times out (ingest-only nodes pod) | `5` | +| `ingest.livenessProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed (ingest-only nodes pod) | `1` | +| `ingest.livenessProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded | `5` | +| `ingest.readinessProbe.enabled` | Enable/disable the readiness probe (ingest-only nodes pod) | `true` | +| `ingest.readinessProbe.initialDelaySeconds` | Delay before readiness probe is initiated (ingest-only nodes pod) | `90` | +| `ingest.readinessProbe.periodSeconds` | How often to perform the probe (ingest-only nodes pod) | `10` | +| `ingest.readinessProbe.timeoutSeconds` | When the probe times out (ingest-only nodes pod) | `5` | +| `ingest.readinessProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed (ingest-only nodes pod) | `1` | +| `ingest.readinessProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded | `5` | +| `ingest.customStartupProbe` | Override default startup probe | `{}` | +| `ingest.customLivenessProbe` | Override default liveness probe | `{}` | +| `ingest.customReadinessProbe` | Override default readiness probe | `{}` | +| `ingest.command` | Override default container command (useful when using custom images) | `[]` | +| `ingest.args` | Override default container args (useful when using custom images) | `[]` | +| `ingest.lifecycleHooks` | for the ingest-only container(s) to automate configuration before or after startup | `{}` | +| `ingest.extraEnvVars` | Array with extra environment variables to add to ingest-only nodes | `[]` | +| `ingest.extraEnvVarsCM` | Name of existing ConfigMap containing extra env vars for ingest-only nodes | `""` | +| `ingest.extraEnvVarsSecret` | Name of existing Secret containing extra env vars for ingest-only nodes | `""` | +| `ingest.extraVolumes` | Optionally specify extra list of additional volumes for the ingest-only pod(s) | `[]` | +| `ingest.extraVolumeMounts` | Optionally specify extra list of additional volumeMounts for the ingest-only container(s) | `[]` | +| `ingest.sidecars` | Add additional sidecar containers to the ingest-only pod(s) | `[]` | +| `ingest.initContainers` | Add additional init containers to the ingest-only pod(s) | `[]` | +| `ingest.serviceAccount.create` | Specifies whether a ServiceAccount should be created | `false` | +| `ingest.serviceAccount.name` | Name of the service account to use. If not set and create is true, a name is generated using the fullname template. | `""` | +| `ingest.serviceAccount.automountServiceAccountToken` | Automount service account token for the server service account | `true` | +| `ingest.serviceAccount.annotations` | Annotations for service account. Evaluated as a template. Only used if `create` is `true`. | `{}` | +| `ingest.autoscaling.enabled` | Whether enable horizontal pod autoscale | `false` | +| `ingest.autoscaling.minReplicas` | Configure a minimum amount of pods | `3` | +| `ingest.autoscaling.maxReplicas` | Configure a maximum amount of pods | `11` | +| `ingest.autoscaling.targetCPU` | Define the CPU target to trigger the scaling actions (utilization percentage) | `""` | +| `ingest.autoscaling.targetMemory` | Define the memory target to trigger the scaling actions (utilization percentage) | `""` | +| `ingest.service.enabled` | Enable Ingest-only service | `false` | +| `ingest.service.type` | Elasticsearch ingest-only service type | `ClusterIP` | +| `ingest.service.ports.restAPI` | Elasticsearch service REST API port | `9200` | +| `ingest.service.ports.transport` | Elasticsearch service transport port | `9300` | +| `ingest.service.nodePorts.restAPI` | Node port for REST API | `""` | +| `ingest.service.nodePorts.transport` | Node port for REST API | `""` | +| `ingest.service.clusterIP` | Elasticsearch ingest-only service Cluster IP | `""` | +| `ingest.service.loadBalancerIP` | Elasticsearch ingest-only service Load Balancer IP | `""` | +| `ingest.service.loadBalancerSourceRanges` | Elasticsearch ingest-only service Load Balancer sources | `[]` | +| `ingest.service.externalTrafficPolicy` | Elasticsearch ingest-only service external traffic policy | `Cluster` | +| `ingest.service.extraPorts` | Extra ports to expose (normally used with the `sidecar` value) | `[]` | +| `ingest.service.annotations` | Additional custom annotations for Elasticsearch ingest-only service | `{}` | +| `ingest.service.sessionAffinity` | Session Affinity for Kubernetes service, can be "None" or "ClientIP" | `None` | +| `ingest.service.sessionAffinityConfig` | Additional settings for the sessionAffinity | `{}` | +| `ingest.ingress.enabled` | Enable ingress record generation for Elasticsearch | `false` | +| `ingest.ingress.pathType` | Ingress path type | `ImplementationSpecific` | +| `ingest.ingress.apiVersion` | Force Ingress API version (automatically detected if not set) | `""` | +| `ingest.ingress.hostname` | Default host for the ingress record | `elasticsearch-ingest.local` | +| `ingest.ingress.path` | Default path for the ingress record | `/` | +| `ingest.ingress.annotations` | Additional annotations for the Ingress resource. To enable certificate autogeneration, place here your cert-manager annotations. | `{}` | +| `ingest.ingress.tls` | Enable TLS configuration for the host defined at `ingress.hostname` parameter | `false` | +| `ingest.ingress.selfSigned` | Create a TLS secret for this ingress record using self-signed certificates generated by Helm | `false` | +| `ingest.ingress.ingressClassName` | IngressClass that will be be used to implement the Ingress (Kubernetes 1.18+) | `""` | +| `ingest.ingress.extraHosts` | An array with additional hostname(s) to be covered with the ingress record | `[]` | +| `ingest.ingress.extraPaths` | An array with additional arbitrary paths that may need to be added to the ingress under the main host | `[]` | +| `ingest.ingress.extraTls` | TLS configuration for additional hostname(s) to be covered with this ingress record | `[]` | +| `ingest.ingress.secrets` | Custom TLS certificates as secrets | `[]` | +| `ingest.ingress.extraRules` | Additional rules to be covered with this ingress record | `[]` | + +### Metrics parameters + +| Name | Description | Value | +| ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | -------------------------------- | +| `metrics.enabled` | Enable prometheus exporter | `false` | +| `metrics.nameOverride` | Metrics pod name | `""` | +| `metrics.fullnameOverride` | String to fully override common.names.fullname | `""` | +| `metrics.image.registry` | Metrics exporter image registry | `docker.io` | +| `metrics.image.repository` | Metrics exporter image repository | `bitnami/elasticsearch-exporter` | +| `metrics.image.tag` | Metrics exporter image tag | `1.5.0-debian-11-r80` | +| `metrics.image.digest` | Metrics exporter image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag | `""` | +| `metrics.image.pullPolicy` | Metrics exporter image pull policy | `IfNotPresent` | +| `metrics.image.pullSecrets` | Metrics exporter image pull secrets | `[]` | +| `metrics.annotations` | Annotations for metrics | `{}` | +| `metrics.extraArgs` | Extra arguments to add to the default exporter command | `[]` | +| `metrics.hostAliases` | Add deployment host aliases | `[]` | +| `metrics.schedulerName` | Name of the k8s scheduler (other than default) | `""` | +| `metrics.priorityClassName` | Elasticsearch metrics exporter pods' priorityClassName | `""` | +| `metrics.service.type` | Metrics exporter endpoint service type | `ClusterIP` | +| `metrics.service.port` | Metrics exporter endpoint service port | `9114` | +| `metrics.service.annotations` | Provide any additional annotations which may be required. | `{}` | +| `metrics.podAffinityPreset` | Metrics Pod affinity preset. Ignored if `affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `metrics.podAntiAffinityPreset` | Metrics Pod anti-affinity preset. Ignored if `affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `metrics.nodeAffinityPreset.type` | Metrics Node affinity preset type. Ignored if `affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `metrics.nodeAffinityPreset.key` | Metrics Node label key to match Ignored if `affinity` is set. | `""` | +| `metrics.nodeAffinityPreset.values` | Metrics Node label values to match. Ignored if `affinity` is set. | `[]` | +| `metrics.affinity` | Metrics Affinity for pod assignment | `{}` | +| `metrics.nodeSelector` | Metrics Node labels for pod assignment | `{}` | +| `metrics.tolerations` | Metrics Tolerations for pod assignment | `[]` | +| `metrics.topologySpreadConstraints` | Topology Spread Constraints for pod assignment spread across your cluster among failure-domains. Evaluated as a template | `[]` | +| `metrics.resources.limits` | The resources limits for the container | `{}` | +| `metrics.resources.requests` | The requested resources for the container | `{}` | +| `metrics.livenessProbe.enabled` | Enable/disable the liveness probe (metrics pod) | `true` | +| `metrics.livenessProbe.initialDelaySeconds` | Delay before liveness probe is initiated (metrics pod) | `60` | +| `metrics.livenessProbe.periodSeconds` | How often to perform the probe (metrics pod) | `10` | +| `metrics.livenessProbe.timeoutSeconds` | When the probe times out (metrics pod) | `5` | +| `metrics.livenessProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded | `5` | +| `metrics.livenessProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed (metrics pod) | `1` | +| `metrics.readinessProbe.enabled` | Enable/disable the readiness probe (metrics pod) | `true` | +| `metrics.readinessProbe.initialDelaySeconds` | Delay before readiness probe is initiated (metrics pod) | `5` | +| `metrics.readinessProbe.periodSeconds` | How often to perform the probe (metrics pod) | `10` | +| `metrics.readinessProbe.timeoutSeconds` | When the probe times out (metrics pod) | `1` | +| `metrics.readinessProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded | `5` | +| `metrics.readinessProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed (metrics pod) | `1` | +| `metrics.startupProbe.enabled` | Enable/disable the startup probe (metrics pod) | `false` | +| `metrics.startupProbe.initialDelaySeconds` | Delay before startup probe is initiated (metrics pod) | `5` | +| `metrics.startupProbe.periodSeconds` | How often to perform the probe (metrics pod) | `10` | +| `metrics.startupProbe.timeoutSeconds` | When the probe times out (metrics pod) | `1` | +| `metrics.startupProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded | `5` | +| `metrics.startupProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed (metrics pod) | `1` | +| `metrics.customStartupProbe` | Custom liveness probe for the Web component | `{}` | +| `metrics.customLivenessProbe` | Custom liveness probe for the Web component | `{}` | +| `metrics.customReadinessProbe` | Custom readiness probe for the Web component | `{}` | +| `metrics.podAnnotations` | Metrics exporter pod Annotation and Labels | `{}` | +| `metrics.podLabels` | Extra labels to add to Pod | `{}` | +| `metrics.podSecurityContext.enabled` | Enabled Elasticsearch metrics exporter pods' Security Context | `true` | +| `metrics.podSecurityContext.fsGroup` | Set Elasticsearch metrics exporter pod's Security Context fsGroup | `1001` | +| `metrics.containerSecurityContext.enabled` | Enabled Elasticsearch metrics exporter containers' Security Context | `true` | +| `metrics.containerSecurityContext.runAsUser` | Set Elasticsearch metrics exporter containers' Security Context runAsUser | `1001` | +| `metrics.containerSecurityContext.runAsNonRoot` | Set Elasticsearch metrics exporter container's Security Context runAsNonRoot | `true` | +| `metrics.command` | Override default container command (useful when using custom images) | `[]` | +| `metrics.args` | Override default container args (useful when using custom images) | `[]` | +| `metrics.extraEnvVars` | Array with extra environment variables to add to Elasticsearch metrics exporter nodes | `[]` | +| `metrics.extraEnvVarsCM` | Name of existing ConfigMap containing extra env vars for Elasticsearch metrics exporter nodes | `""` | +| `metrics.extraEnvVarsSecret` | Name of existing Secret containing extra env vars for Elasticsearch metrics exporter nodes | `""` | +| `metrics.extraVolumes` | Optionally specify extra list of additional volumes for the Elasticsearch metrics exporter pod(s) | `[]` | +| `metrics.extraVolumeMounts` | Optionally specify extra list of additional volumeMounts for the Elasticsearch metrics exporter container(s) | `[]` | +| `metrics.sidecars` | Add additional sidecar containers to the Elasticsearch metrics exporter pod(s) | `[]` | +| `metrics.initContainers` | Add additional init containers to the Elasticsearch metrics exporter pod(s) | `[]` | +| `metrics.serviceMonitor.enabled` | Create ServiceMonitor Resource for scraping metrics using PrometheusOperator | `false` | +| `metrics.serviceMonitor.namespace` | Namespace which Prometheus is running in | `""` | +| `metrics.serviceMonitor.jobLabel` | The name of the label on the target service to use as the job name in prometheus. | `""` | +| `metrics.serviceMonitor.interval` | Interval at which metrics should be scraped | `""` | +| `metrics.serviceMonitor.scrapeTimeout` | Timeout after which the scrape is ended | `""` | +| `metrics.serviceMonitor.relabelings` | RelabelConfigs to apply to samples before scraping | `[]` | +| `metrics.serviceMonitor.metricRelabelings` | MetricRelabelConfigs to apply to samples before ingestion | `[]` | +| `metrics.serviceMonitor.selector` | ServiceMonitor selector labels | `{}` | +| `metrics.serviceMonitor.labels` | Extra labels for the ServiceMonitor | `{}` | +| `metrics.serviceMonitor.honorLabels` | honorLabels chooses the metric's labels on collisions with target labels | `false` | +| `metrics.prometheusRule.enabled` | Creates a Prometheus Operator PrometheusRule (also requires `metrics.enabled` to be `true` and `metrics.prometheusRule.rules`) | `false` | +| `metrics.prometheusRule.namespace` | Namespace for the PrometheusRule Resource (defaults to the Release Namespace) | `""` | +| `metrics.prometheusRule.additionalLabels` | Additional labels that can be used so PrometheusRule will be discovered by Prometheus | `{}` | +| `metrics.prometheusRule.rules` | Prometheus Rule definitions | `[]` | + +### Init Container Parameters + +| Name | Description | Value | +| -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | +| `volumePermissions.enabled` | Enable init container that changes volume permissions in the data directory (for cases where the default k8s `runAsUser` and `fsUser` values do not work) | `false` | +| `volumePermissions.image.registry` | Init container volume-permissions image registry | `docker.io` | +| `volumePermissions.image.repository` | Init container volume-permissions image name | `bitnami/bitnami-shell` | +| `volumePermissions.image.tag` | Init container volume-permissions image tag | `11-debian-11-r97` | +| `volumePermissions.image.digest` | Init container volume-permissions image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag | `""` | +| `volumePermissions.image.pullPolicy` | Init container volume-permissions image pull policy | `IfNotPresent` | +| `volumePermissions.image.pullSecrets` | Init container volume-permissions image pull secrets | `[]` | +| `volumePermissions.resources.limits` | The resources limits for the container | `{}` | +| `volumePermissions.resources.requests` | The requested resources for the container | `{}` | +| `sysctlImage.enabled` | Enable kernel settings modifier image | `true` | +| `sysctlImage.registry` | Kernel settings modifier image registry | `docker.io` | +| `sysctlImage.repository` | Kernel settings modifier image repository | `bitnami/bitnami-shell` | +| `sysctlImage.tag` | Kernel settings modifier image tag | `11-debian-11-r97` | +| `sysctlImage.digest` | Kernel settings modifier image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag | `""` | +| `sysctlImage.pullPolicy` | Kernel settings modifier image pull policy | `IfNotPresent` | +| `sysctlImage.pullSecrets` | Kernel settings modifier image pull secrets | `[]` | +| `sysctlImage.resources.limits` | The resources limits for the container | `{}` | +| `sysctlImage.resources.requests` | The requested resources for the container | `{}` | + +### Kibana Parameters + +| Name | Description | Value | +| ---------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------- | +| `kibana.elasticsearch.hosts` | Array containing hostnames for the ES instances. Used to generate the URL | `[]` | +| `kibana.elasticsearch.port` | Port to connect Kibana and ES instance. Used to generate the URL | `{{ include "elasticsearch.service.ports.restAPI" . }}` | + +Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example, + +```console +helm install my-release \ + --set name=my-elastic,client.service.port=8080 \ + my-repo/elasticsearch +``` + +The above command sets the Elasticsearch cluster name to `my-elastic` and REST port number to `8080`. + +Alternatively, a YAML file that specifies the values for the parameters can be provided while installing the chart. For example, + +```console +helm install my-release -f values.yaml my-repo/elasticsearch +``` + +> **Tip**: You can use the default [values.yaml](values.yaml). + +## Configuration and installation details + +### [Rolling VS Immutable tags](https://docs.bitnami.com/containers/how-to/understand-rolling-tags-containers/) + +It is strongly recommended to use immutable tags in a production environment. This ensures your deployment does not change automatically if the same tag is updated with a different image. + +Bitnami will release a new chart updating its containers if a new version of the main container, significant changes, or critical vulnerabilities exist. + +### Change ElasticSearch version + +To modify the ElasticSearch version used in this chart you can specify a [valid image tag](https://hub.docker.com/r/bitnami/elasticsearch/tags/) using the `image.tag` parameter. For example, `image.tag=X.Y.Z`. This approach is also applicable to other images like exporters. + +### Default kernel settings + +Currently, Elasticsearch requires some changes in the kernel of the host machine to work as expected. If those values are not set in the underlying operating system, the ES containers fail to boot with ERROR messages. More information about these requirements can be found in the links below: + +- [File Descriptor requirements](https://www.elastic.co/guide/en/elasticsearch/reference/current/file-descriptors.html) +- [Virtual memory requirements](https://www.elastic.co/guide/en/elasticsearch/reference/current/vm-max-map-count.html) + +This chart uses a **privileged** initContainer to change those settings in the Kernel by running: `sysctl -w vm.max_map_count=262144 && sysctl -w fs.file-max=65536`. +You can disable the initContainer using the `sysctlImage.enabled=false` parameter. + +### Enable bundled Kibana + +This Elasticsearch chart contains Kibana as subchart, you can enable it just setting the `global.kibanaEnabled=true` parameter. +To see the notes with some operational instructions from the Kibana chart, please use the `--render-subchart-notes` as part of your `helm install` command, in this way you can see the Kibana and ES notes in your terminal. + +When enabling the bundled kibana subchart, there are a few gotchas that you should be aware of listed below. + +#### Elasticsearch rest Encryption + +When enabling elasticsearch' rest endpoint encryption you will also need to set `kibana.elasticsearch.security.tls.enabled` to the SAME value along with some additional values shown below for an "out of the box experience": + +```yaml +security: + enabled: true + # PASSWORD must be the same value passed to elasticsearch to get an "out of the box" experience + elasticPassword: "" + tls: + # AutoGenerate TLS certs for elastic + autoGenerated: true + +kibana: + elasticsearch: + security: + auth: + enabled: true + # default in the elasticsearch chart is elastic + kibanaUsername: "" + kibanaPassword: "" + tls: + # Instruct kibana to connect to elastic over https + enabled: true + # Bit of a catch 22, as you will need to know the name upfront of your release + existingSecret: RELEASENAME-elasticsearch-coordinating-crt # or just 'elasticsearch-coordinating-crt' if the release name happens to be 'elasticsearch' + # As the certs are auto-generated, they are pemCerts so set to true + usePemCerts: true +``` + +At a bare-minimum, when working with kibana and elasticsearch together the following values MUST be the same, otherwise things will fail: + +```yaml +security: + tls: + restEncryption: true + +# assumes global.kibanaEnabled=true +kibana: + elasticsearch: + security: + tls: + enabled: true +``` + +### Adding extra environment variables + +In case you want to add extra environment variables (useful for advanced operations like custom init scripts), you can use the `extraEnvVars` property. + +```yaml +extraEnvVars: + - name: ELASTICSEARCH_VERSION + value: 7.0 +``` + +Alternatively, you can use a ConfigMap or a Secret with the environment variables. To do so, use the `extraEnvVarsCM` or the `extraEnvVarsSecret` values. + +### Using custom init scripts + +For advanced operations, the Bitnami Elasticsearch charts allows using custom init scripts that will be mounted inside `/docker-entrypoint.init-db`. You can include the file directly in your `values.yaml` with `initScripts`, or use a ConfigMap or a Secret (in case of sensitive data) for mounting these extra scripts. In this case you use the `initScriptsCM` and `initScriptsSecret` values. + +```console +initScriptsCM=special-scripts +initScriptsSecret=special-scripts-sensitive +``` + +### Snapshot and restore operations + +As it's described in the [official documentation](https://www.elastic.co/guide/en/elasticsearch/reference/current/snapshots-register-repository.html#snapshots-filesystem-repository), it's necessary to register a snapshot repository before you can perform snapshot and restore operations. + +This chart allows you to configure Elasticsearch to use a shared file system to store snapshots. To do so, you need to mount a RWX volume on every Elasticsearch node, and set the parameter `snapshotRepoPath` with the path where the volume is mounted. In the example below, you can find the values to set when using a NFS Perstitent Volume: + +```yaml +extraVolumes: + - name: snapshot-repository + nfs: + server: nfs.example.com # Please change this to your NFS server + path: /share1 +extraVolumeMounts: + - name: snapshot-repository + mountPath: /snapshots +snapshotRepoPath: "/snapshots" +``` + +### Sidecars and Init Containers + +If you have a need for additional containers to run within the same pod as Elasticsearch components (e.g. an additional metrics or logging exporter), you can do so via the `XXX.sidecars` parameter(s), where XXX is placeholder you need to replace with the actual component(s). Simply define your container according to the Kubernetes container spec. + +```yaml +sidecars: + - name: your-image-name + image: your-image + imagePullPolicy: Always + ports: + - name: portname + containerPort: 1234 +``` + +Similarly, you can add extra init containers using the `initContainers` parameter. + +```yaml +initContainers: + - name: your-image-name + image: your-image + imagePullPolicy: Always + ports: + - name: portname +``` + +### Setting Pod's affinity + +This chart allows you to set your custom affinity using the `XXX.affinity` parameter(s). Find more information about Pod's affinity in the [kubernetes documentation](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity). + +As an alternative, you can use of the preset configurations for pod affinity, pod anti-affinity, and node affinity available at the [bitnami/common](https://github.com/bitnami/charts/tree/main/bitnami/common#affinities) chart. To do so, set the `XXX.podAffinityPreset`, `XXX.podAntiAffinityPreset`, or `XXX.nodeAffinityPreset` parameters. + +## Persistence + +The [Bitnami Elasticsearch](https://github.com/bitnami/containers/tree/main/bitnami/elasticsearch) image stores the Elasticsearch data at the `/bitnami/elasticsearch/data` path of the container. + +By default, the chart mounts a [Persistent Volume](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) at this location. The volume is created using dynamic volume provisioning. See the [Parameters](#parameters) section to configure the PVC. + +### Adjust permissions of persistent volume mountpoint + +As the image run as non-root by default, it is necessary to adjust the ownership of the persistent volume so that the container can write data into it. + +By default, the chart is configured to use Kubernetes Security Context to automatically change the ownership of the volume. However, this feature does not work in all Kubernetes distributions. +As an alternative, this chart supports using an initContainer to change the ownership of the volume before mounting it in the final destination. + +You can enable this initContainer by setting `volumePermissions.enabled` to `true`. + +## Troubleshooting + +Find more information about how to deal with common errors related to Bitnami's Helm charts in [this troubleshooting guide](https://docs.bitnami.com/general/how-to/troubleshoot-helm-chart-issues). + +## Upgrading + +### To 19.0.0 + +The new version of this chart no longer supports elasticsearch-curator, this repository has been deprecated. + +### To 18.0.0 + +This major release refactors the bitnami/elasticsearch chart, adding some organization and functional changes. + +- Each role has now the same structure: its Statefulset, a headless service (for FQDN, it gives each node an individual Advertised name, required for TLS verification), its own ServiceAccount (BWC), and HorizontalPodAutoscaling. +- Previously, the chart would alternate between a Coordinating service and an All-nodes service for traffic exposure. This logic has been replaced with a single Traffic exposure service, that will have coordinating-only nodes as backend pods, or master pods if no coordinating nodes are enabled. +- Master-eligible nodes can now be deployed as multi-role nodes using the setting masterOnly. This allows the creation of different topologies, smaller clusters with HA (3 multi-role master-eligible nodes), and single-node deployments. +- Renamed several values to be in line with the rest of the catalog. + +This major release also upgrades Elasticsearch to its version 8.x.x and the updates Kibana subchart. + +- Upgrade to Elasticsearch 8 +- Upgrade Kibana subchart. + +In addition, several modifications have been performed adding missing features and renaming values, in order to get aligned with the rest of the assets in the Bitnami charts repository. + +The following values have been modified: + +- `coordinating.service.*` have been renamed as `service.*`. This service will be backed by coordinating nodes if enabled, or master nodes if not. +- `master.service.*` has been removed. +- `data.service.*` has been removed. +- `master.ingress.*` has been renamed as `ingress.*`. This ingress will be backed by the coordinating/master service previously mentioned. +- In addition, an Ingest-only service and ingress have been added, for use cases where separated ingrestion and search channels are needed. +- `global.coordinating.name` have been renamed as `global.elasticsaerch.service.name`. +- `name` has been renamed as `clusterName`. +- `extraEnvVarsConfigMap` has been renamed as `extraEnvVarsCM`. +- `{master/data/ingest/coordinating}.replicas` has been renamed as `{master/data/ingest/coordinating}.replicaCount`. +- `{master/data/ingest/coordinating}.securityContext` has been separated in two different values: `podSecurityContext` and `containerSecurityContext`. +- `{master/data/ingest/coordinating}.updateStrategy` is now interpreted as an object. `rollingUpdatePartition` has been removed and has to be configured inside the updateStrategy object when needed. +- Default values for `kibana.elasticsearch.hosts` and `kibana.elasticsearch.port` have been modified to use the new helpers. +- `{master/data/ingest/coordinating/curator/metrics}.name` has been renamed as `{master/data/ingest/coordinating/curator}.nameOverride`. + +### To 17.0.0 + +This version bumps in a major the version of the Kibana Helm Chart bundled as dependecy, [here](https://github.com/bitnami/charts/tree/main/bitnami/kibana#to-900) you can see the changes implemented in this Kibana major version. + +### To 16.0.0 + +This version replaces the Ingest and Coordinating Deployments with Statefulsets. This change is required so Coordinating and Ingest nodes have their services associated, required for TLS hostname verification. + +We haven't encountered any issues during our upgrade test, but we recommend creating volumes backups before upgrading this major version, especially for users with additional volumes and custom configurations. + +Additionally, this version adds support for X-Pack Security features such as TLS/SSL encryption and basic authentication. + +### To 15.0.0 + +From this version onwards, Elasticsearch container components are now licensed under the [Elastic License](https://www.elastic.co/licensing/elastic-license) that is not currently accepted as an Open Source license by the Open Source Initiative (OSI). + +Also, from now on, the Helm Chart will include the X-Pack plugin installed by default. + +Regular upgrade is compatible from previous versions. + +### To 14.0.0 + +This version standardizes the way of defining Ingress rules in the Kibana subchart. When configuring a single hostname for the Ingress rule, set the `kibana.ingress.hostname` value. When defining more than one, set the `kibana.ingress.extraHosts` array. Apart from this case, no issues are expected to appear when upgrading. + +### To 13.0.0 + +[On November 13, 2020, Helm v2 support was formally finished](https://github.com/helm/charts#status-of-the-project), this major version is the result of the required changes applied to the Helm Chart to be able to incorporate the different features added in Helm v3 and to be consistent with the Helm project itself regarding the Helm v2 EOL. + +#### What changes were introduced in this major version? + +- Previous versions of this Helm Chart use `apiVersion: v1` (installable by both Helm 2 and 3), this Helm Chart was updated to `apiVersion: v2` (installable by Helm 3 only). [Here](https://helm.sh/docs/topics/charts/#the-apiversion-field) you can find more information about the `apiVersion` field. +- Move dependency information from the *requirements.yaml* to the *Chart.yaml* +- After running `helm dependency update`, a *Chart.lock* file is generated containing the same structure used in the previous *requirements.lock* +- The different fields present in the *Chart.yaml* file has been ordered alphabetically in a homogeneous way for all the Bitnami Helm Charts + +#### Considerations when upgrading to this version + +- If you want to upgrade to this version from a previous one installed with Helm v3, you shouldn't face any issues +- If you want to upgrade to this version using Helm v2, this scenario is not supported as this version doesn't support Helm v2 anymore +- If you installed the previous version with Helm v2 and wants to upgrade to this version with Helm v3, please refer to the [official Helm documentation](https://helm.sh/docs/topics/v2_v3_migration/#migration-use-cases) about migrating from Helm v2 to v3 + +#### Useful links + +- +- +- + +### To 12.0.0 + +Several changes were introduced that breaks backwards compatibility: + +- Ports names were prefixed with the protocol to comply with Istio (see ). +- Labels are adapted to follow the Helm charts best practices. +- Elasticsearch data pods are now deployed in parallel in order to bootstrap the cluster and be discovered. + +### To 11.0.0 + +Elasticsearch master pods are now deployed in parallel in order to bootstrap the cluster and be discovered. + +The field `podManagementPolicy` can't be updated in a StatefulSet, so you need to destroy it before you upgrade the chart to this version. + +```console +kubectl delete statefulset elasticsearch-master +helm upgrade my-repo/elasticsearch +``` + +### TO 10.0.0 + +In this version, Kibana was added as dependent chart. More info about how to enable and work with this bundled Kibana in the ["Enable bundled Kibana"](#enable-bundled-kibana) section. + +### To 9.0.0 + +Elasticsearch master nodes store the cluster status at `/bitnami/elasticsearch/data`. Among other things this includes the UUID of the elasticsearch cluster. Without a persistent data store for this data, the UUID of a cluster could change if k8s node(s) hosting the es master nodes go down and are scheduled on some other master node. In the event that this happens, the data nodes will no longer be able to join a cluster as the uuid changed resulting in a broken cluster. + +To resolve such issues, PVC's are now attached for master node data persistence. + +--- + +Helm performs a lookup for the object based on its group (apps), version (v1), and kind (Deployment). Also known as its GroupVersionKind, or GVK. Changing the GVK is considered a compatibility breaker from Kubernetes' point of view, so you cannot "upgrade" those objects to the new GVK in-place. Earlier versions of Helm 3 did not perform the lookup correctly which has since been fixed to match the spec. + +In [4dfac075aacf74405e31ae5b27df4369e84eb0b0](https://github.com/bitnami/charts/commit/4dfac075aacf74405e31ae5b27df4369e84eb0b0) the `apiVersion` of the deployment resources was updated to `apps/v1` in tune with the api's deprecated, resulting in compatibility breakage. + +### To 7.4.0 + +This version also introduces `bitnami/common`, a [library chart](https://helm.sh/docs/topics/library_charts/#helm) as a dependency. More documentation about this new utility could be found [here](https://github.com/bitnami/charts/tree/main/bitnami/common#bitnami-common-library-chart). Please, make sure that you have updated the chart dependencies before executing any upgrade. + +### To 7.0.0 + +This version enabled by default the initContainer that modify some kernel settings to meet the Elasticsearch requirements. More info in the ["Default kernel settings"](#default-kernel-settings) section. +You can disable the initContainer using the `sysctlImage.enabled=false` parameter. + +### To 3.0.0 + +Backwards compatibility is not guaranteed unless you modify the labels used on the chart's deployments. +Use the workaround below to upgrade from versions previous to 3.0.0. The following example assumes that the release name is elasticsearch: + +```console +kubectl patch deployment elasticsearch-coordinating --type=json -p='[{"op": "remove", "path": "/spec/selector/matchLabels/chart"}]' +kubectl patch deployment elasticsearch-ingest --type=json -p='[{"op": "remove", "path": "/spec/selector/matchLabels/chart"}]' +kubectl patch deployment elasticsearch-master --type=json -p='[{"op": "remove", "path": "/spec/selector/matchLabels/chart"}]' +kubectl patch deployment elasticsearch-metrics --type=json -p='[{"op": "remove", "path": "/spec/selector/matchLabels/chart"}]' +kubectl delete statefulset elasticsearch-data --cascade=false +``` + +## License + +Copyright © 2023 Bitnami + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/knowlg-automation/helm_charts/elasticsearch/charts/common-2.2.4.tgz b/knowlg-automation/helm_charts/elasticsearch/charts/common-2.2.4.tgz new file mode 100644 index 0000000000000000000000000000000000000000..9016f32e2cfd76371466da567554893ec977e7c3 GIT binary patch literal 12364 zcmV-SFtg7eiwG0|00000|0w_~VMtOiV@ORlOnEsqVl!4SWK%V1T2nbTPgYhoO;>Dc zVQyr3R8em|NM&qo0PMa0ciT3yDBhp>SImb_Z>(%cNp|Aw^{)4G-6Y-hX`AHPX>#^8 z_q7lSNvJ7;1wc94#P4r^2j58Wo4(kxiSPHK_BU@({&%nSxVL}xRa_McA?6X62Q zl5e*E3KJ|`BZk-l{(+MjD5irkWiVkh&qz7} z&5n@J35j5Y#RbMGjEG3lj6fvI^Uwo$J0)EI1i~Su0%Dvcbb&?*hS36u;5ZqF-jvSp zAxwqH_+h_4A!3@3!idiL24r7@>{SbzoP-I)t1}o(M+1_x!CjU>LJ{jK2|7|>G5DIhLWiYfEgnh$KJ3OM z65yZXg%AGsuV4G%>v_nB-Gp!f{&&ZxzWn!p`@J3nmzSQx=F_J@H@LC|StVn5j2Xl6 zi=2^kau!W-oF^omoJ>+$yngu+N4c_6J%9joC7ecB{+7=SiD>%rW5zJ&)>_t20Oxpd z2%kPdc#aovd0Bh`pc!UJP$unZ=e6$Uq4Nen_KKl&f+5tOtB_X2(&tAgkEvX#HW75YBLdBSG0)%w~q*!PhjF zOGfV+16Pch6m}ncEiE@Pf_m_DpU4>S9}vn$ZJVa&>g#%1BXj`bgySky%`clk)io+m z4ZWApUeJstbh7vXZ$NsdQaJ$bJ4NNS$y=$a@dfuKe3q3_o=DluN0_BpU>*|MpHePO zKJ2gRrc(47jV+t?<=?3wo3mX;{31^y^}c(67N9keVd9u$bd;ZaFJAHwoOQMQ4YQ$_W&2TkO!J}Z?YA@!NnIf(>o z6a}X(M#B!* zsmj+8cDF=t^J#l~QdybA?~YWKGym;L<>od<&@O%3lf6H?EV;JeU0>{4gzkzWx3<95 zn!HhATOwXtldRjZvUE#ZFtVJvaC1 zDyII?h2a)lQYvtYDXr%Gw&p?j+{N>UvUX^p*akWwyQ@G2w6@obLSb2`fq!{GmQ814;&f#0-y zy7?gN6#$j(0={mtKOc^;KtsPu)~>Qmr}nC}O0l@0?A&yGJt~>vuo1|e-t=X z(X938We_FlBs~AKG7Gm!x`@&AcRE^=Xtwli^43TitLu@<7Tq3sbhg3_r|SxzQ(AQYw6 zC}RDIFT=3y4gcr08*WplThYSzRJ-lD>-4+h=fJQ@m|9b+*a$@%=$q@$_qxGcOerJ( zQVDz;k#Je{?UC~N>5Fv<7_oQ*`dy#(P)16&--vV_HMd8#<2NT~n9WIq*Q6cFh^??b z2?yICA;S~G1>2neI_qwajQ^0VO;dpqctc}2&IRQWN-$fKs?!W@V3F=`O3otI>&~!} za(fM08O%vK>Cg96krl*kROlk>%F62x2M57&HC4!Q$vKX7mahp60O7D7?*A$Dj^P3= zKv0OLI6ChreSvgdFG&TXaUS6q@>GzdD6k*yhXc4EVp{X4B^X2H$8a$v(G(=95lK0q z6i^(i_$^An|8#lj0~ATlp=a`nC0c~LZ6gi{7@pvdD##5aB8b1whYYmyn_Ty1?Y zxltgdNPq;X6xaysa-rw_%+UlN>P6c*N=U4XftRv@!2k}A8Y@d>A#O6OLfLSFLj|%} zaZ}&)Ds+a#G!!(nSPcd~_#8(Ji{g)0DjCKf6P(1ViY^)q27Wm|H!vLD6o$hMFdP;b zHc&B=tOp4f;CT}hw_C+(WXp&&Nzkh<@JpNE)aH%b1|dTX>Fm1z>a9TlW-184)(qiC zi2xltrR6kD(g~M_gPQN8&GA@HK15}!Yt|zIE@=8tfS59RVJAu1uE7!lcwYQfw)X}x zoC;g0+dy1J(sG5fPAZjz@H-``N?K@A!Ck*aY1~rZT8!Z-PB7v)JmEiUJSyuC4-}4u zriVx1e@NdPpPjvbefr`IUY?%5K0Sng(p<}9#^{{H7#1{V5amo!p_Jd6kW0IQna2I@ zMvsgsS$XMyNTL3`q5^B>faf>`G!~cv!_v4yNuv7x7-$kh#BhvLK~TbBh8B_mQjBA5 zn29M~*th36lQtt{(n#S#_GV6#e1;)P5Sj66o&a=FSMEvVA&I+qPmr zqS!(e(}-90P)%@(8A|$7dJzcP7mVhk1oJ7SBADW2rZ-6hg!3Hp{!ZEXK(k+PF(nBO zig7XJ&B*X8bn)9-C6hZ+Db{4j&oD+LxnI@)nxCfqM`mcUy}cjbthWE24u((b_TRz% z{@>eqHn9Jks7uCZCKaG$6}3CH&HONor2gl_E@*2*{XC^qlzD{K{t;8GOM|ah@BMm0 zfHE+;=H_a9h1F)lObcm2$@wrWFbM4@w{S)|5wxSY{a9AuSQ9xac??J_7&0YIBX- z=c5)tExoQaUhPS9O7g$u{cpMa&##rOkDY^Q1+PZztJ#7Ezd6Kp0+vQ}UeM?qGdNM~ zUX0M0vh_i>#$h+c8N(40I6edwKC19J!)c7!n>!I`;ZOEbxcgB;)zePLmwUWR zTD7axT2)OE{{b(c0@|+=B1&>DFw;w6Ose^{s;wwq-3=`g9-wH3{hq7d{#}xhn)bih zn0|ZfKf~d0ud)8~bno8&zl~>u^&iKU=K`fMVzEB_fOo0`!%Mcf)&b1}2?`shuEgXA z9y)W;6eqKgPxU#aORA{vrcSdxm0*xY#;&sI6xL8{me;B!4n~V=4U=@;9`iH@Aqc8s z_cS$|zCxbiDCFk62>1L4mG;5sQMJS>+ExybxGs4&CDs2xh4^pwH1)q`iY==DmGPga z&+6wtK6$!-zyJSMo(=T>%a57VP|~7JO@KrtnyaJZJJpn{V}i1X*$S`fDs5uTPg(Ny z95Y5@44w;GHVCF*((Th#$G zp;WvJwN@N_D{W`DBl>R}i1-W)X9Gr%*w-ah@a#_#;ON858N4WSQ!0 z=)K1px+*VZ8xnI~Y(e03hTW)K={0J%B_T6nf>2^gIP4lyy?Avd%c@9NhNE8Sos4ZR zpaxQGvT$fm$^e(5yV^z}kvG^br@1WHa3n3C*pCIT1hp-wjRI@Y-E|=!q$USsZGLs| zEXbjuRHx1Y*UHA{vD=l_+*o$cVN82!nry~| zqRVn7FH@J)W!{|zg}CE zTA^-Rq>ht>UbtD+iWPY#XA7O?PBnRAthY4I@EI7}NuqV9kG6i+hPx4ktWQ8;ud9p$JnU^m ziZ5;Ysi$fG>-{FTxBfdk*xPTM|2}wjAOE?PX9N4cSpL-^OiUJW<@$1z=yEkh0_nZr}cw7*_ zr>cxlbbf(YTrR+qgos6`F!&Qm$MiZkAnn-R>vk1Q8^hu5;VS8P^p{{k#@nS8dbM71kdlj9d zPiZ~JD)rmHS1h-oSkCdHeLLtYC*C9rS$(0Zt`_g}@`U_ApGAClq%U}=h;571RF7P4 z$W66IHR-bnQMULK$}+qC!O2UNxEujVWBhShtp^3t-KnK3h0TIkEo;RFUdn)B!3dsX z$ysWTYQivz7Z52^K3+>E%k8pyMa4<&s%&<)E^}1|!8f~A<9F3llk5AZhb?=m_xv9C z2~HceoL#*oS}R5=7y55DsE(&RBC`VEeSpu(~b^+-M}PL+a-DP~yb!&@2?4qQ;l z?#pDM2DwET^z}6^p&(HEssE~h{jCbEu9WO9Pn&h$qLdy34kw4w1mk}s1%>?(-jgJO zj6~-!CX5Tc?;8uuOhTZM&sYP~sOm9+K;;*e+EU`IznGp*3~$E3P#V)&K{yRY4<`ih z9i<6IX%*D_DONjn1(l8^@WpII_1fGTpv&k*i}`@}hrDX2D$SC4hjMbz0$j{O4KCFRYWHLe zsD*`PTQpB&$Ww_$u`FPD&r9R!z%?8sNvFtE$noQPzp;_gf~cloYp3@^I8~yNEF5X6k<0Y>7*c#; zC8%JO5{lx3EouQ9E#{OT4j?q$FA5tTu$=XSHc{&HXf8;?!wD8Y;Klch&ZJg%uixT6 zgf-RgOkkz6($(xcd!IvR!>WXA+P!H(P1CJ?SlYTCo)<_UP@bYB`4EZE1FY@W16B ze26L5=bjn0S4{Hy`-jxG-L$PQJA%Pv26v3Jvjwf}Wk*?O?0ILW36hP|Ht`6{1bMj{yYU^maQW~tBSY@J{IF8NjEc|=GYUVd455-%A4juggHA*n>cpnG;YHh zo~E8mHuWz}JMYZKPNTMF3#|jcQeV*RR0otV?E`M=`>Znf?C1y1$!*%O@tv0y!WXy-)oeD@?8*N0eD zS3523n05B%Ua{Z3r>XzT^KP~^1FX>hgM-1dy8eH%|Lk7>-^R0%{!e4fiWLRDU{fs5 zSPoj2NZoRj5>(GpYMBb!o$sz$pxH!CgE!Xj=VdUo)O;dK>=*agt+v}E-734`yzqSf ziDpT!z4jeCd8sM?I@W)i`@aTzgU0@^`}<#S=h;C1^&USQxoq?sD5h@6B6PC$KNq)C(OAI}svgv;Nv~R_(o|FVp!u#O!6(_+v(y zFt?g(0GF3X4|dP<5spNnh+~5SPTKy`t++L3+6l~>Ls^U)4FQGUIZdUmJn}!O_)jQH z=rcerFE4%g3?szx!4nA7si5_E%6BJE4U`jHQeRX~WR?&Wpy2Wkbwz+Qe@%y-8ft~$IpX;HMpd=Y5Y!J7theFL8J*C| z^8>p|?1Ema2%*u_$InvuLT2!soyVTGnVYy$groIl+2k4o(GO zCOS^X@bFvy(1a9$q+qY_f3SQpwiYGbwc*tgnJR}kAC9^rqLskuT#jFvj-ab3h_ED# zssr?vhE5_#B}5ODnUW@WsYFO+049*qG%%&tVY%+Sg<2}#IbIB_e5exmHs!=hd-ViR z(|whbQ+#81jv2>4$d_tOK>akOs$Aw4tq=0mawx*qHRoO>Yr$cMTJ~tzTsB`CX*WU# zcf%09f4<1mwEr&^%l!W2D91NBTfdogR}cfMMB9oSGC`G^ZqPQrp}OnW+mZRdOg!=Y-KzZ=6!wGrDge zGPJ>r+e?U460bU^DKM=BvX#Io={d2B`#Ix($kWjOPPSVA*9dWr(wL!ZO$ICU|I@+n zpr-$y9Xz{V|GSlE1O0EZ{xCeZTv4XXlvl<6DP>D`+brT` zUCJa`LwKRxyhGjJ&N*^j;yO-eZTnk`3sXWcAR8MiEAULZ?^=L#*&2j+^fSlo4Z~yd zaV;?CcoFJKHApr)*v%U*ouM*+t=48Wg;kjqlxf4^L4EI^I)S>ri&U+#_UuPP5Pc|Z zb+=$6iicYMfV-B7ungqdfVMU8MsmxpY&P~*yR{Gc`h#KNtT761Y_NMoY{!<+ zz7k}Vny+J9M_Y*dx^%Z`&3e2yqPaU2SPP=C70j{VcV7D3POKR7x|FpIXZ6c{%H{pD z!vC8gMo>JuW+-rl|M%=*uYUjQv;8Oc{@<-USMmRTl3elPyRCbFmfP-(?`J#O>G`SM zFW2+>Y#TS@^Rs0W!7Ww47r9E`gkNX27k* za<*s;tb_Lzf;kp!3%siYa@GX1TWQ2 zbA#em>B4p7%$<=o5v-1STMo=g`Q2b-OEdec9Muy26(j0u$+i)5-+$RK+=}=QO(*n* z<3Gc}lg9bq&-M=n_wk?Gc&-%xkzDcOyZ>N#@|W!gfeg#togk3g+wMq^M*EtvAh)R- z3J5jHyIDZU)Za=#s0yniAhbM;Qw7lx##tFus&%S6sC3r?LNAa&BgE_VWApm90!nAt zZtQ4=lJvKgkdAXPeOdB~1E=7NjxdyZ{j!_QHi?(GSgi0xZy@@mV-PM1cQPzyRNcQp zSWH)Nh3q=57I0Pvt-Ev*X1nUZTTy)GYcEP~=# z`7K7^3?lcjw{?w;?Zw`d@ip3t+qeeaSBVK2uwQrV&2(aealdNptpmz+V{ZoR9gn@O z9ii#6pl>olvt>W0vk$AnG>$-BAxv|lA<<d&WeyWoi z`Bd$#g!o)YSIqdTAamt>FBi&vw%2{OSJT5+68&!w)cVr7UKP!Gr=m(l{Jk^*|iV@qoXF}h8+n+##x zjCfr;a-rqwE6?X@H>1D0(Of@7SZKq$oyT?6XL6gq*f+CNR`a;Ekd~4v1?W1JeSjHAGZ+ zmqmHk{aOuGYJ2;Zmw}EIZ8*NtlI)`P)>;EAVRf?kg3**_u~y1 znI;z1@QR_3Gmsl`p*xD@=vCtgElt$NV+pZ04Ja&S&b3D1X2T21dv=!s4OcUtV;qqf z7x&L$iI|)b&1FFb&t-F7li6~PLwmzfCQGb1-#`@mD9xyIp%6X zt0jQ08M$%?>u!gyY_OwY5^gvI9cJYX21B;%9<1G?8_^zR(VvS2(bXD6*~(4PT*9~D z%2?Jg8LM|XwN+}COgmY-F$=Y~cmtZmHCC^tN~-4WSIot&{n?&&a|yknrmI6;0cy+o z&TTEby2@+yZNFl6>Fm=Amg!)LtS06zKP)TvW#Y%n#$N`hZ*k((N%ysRl>QhAGlr>hSCal*<9T|DQp&eYKL)6&rAcolOZQPT+u4NK- zjm-dmJA&bp-r6J4YIaIm*-j!I%-YbOhOZ!h>`?K8tcjvOJM*&v&|4$C~2lf1~y{CKk{{L+}S8@N1J1om6 zM}LHyzP`5IRe2@Y&uKay45V_|t($zsEx+oO-{tEU$qdFs3I+pXCU>+p+-_^bqP2BQ zzAj5o-*enbo3<`A?>w#z17yx*Z+qDuR2e@0?m@jZpYHu z>xie6W<_T_rM+Da6P#j(1djXbZ?iPlzOQOu7c+e`$izsX_9nXX9@eny%c)9V5giuM zv5LdAntM1&Ck=O@ZNX*sj`^k1zPeAHeevs86s#1<*&=Yu zme#QI=e^L}uF%w6jRtfGs@=0i)djdt|C~Uw>|v%nka4()P*!E99<#ja&FrPbb#1QH zCbjLD6QlBdH|`YrrSsFXn=qX>&DHLCW1aiK92Gl8r8t(O5 zP#PDS!)T0J{KU4@MKx9FEq$5J-y!BENp_%7`Y`aebeAJtM6>qchx9{gvk|}LAJP_2 zM>no9mm_B=5cWC}y{Zua4gTS`{9#kA6&Ez4IEIHoAk8A_ZXG?4#f=t_SfDfxr59GR zMmhSAcwf+rM7*EqqQ9!CkoR|FnVyP&PZ-TJsZuo8K|&`af)N%M zSSlQYh^`lU-p&rZ{qZj^PCf6p-+q&pwHM}w=; zcHkrxjK;ZIhwwb5rL`rJF*+wP=722GZKw~XILR>Ma4{v(6e5&L8%V|f)lP>wPG|-x zzAysWn&kp`G{rNdzU;us3{CJUrJ`g4h9`szwvgi`78yPS zuKXYmAjUjmL^(r;@PbC?YRyF#cf33zfl9|Gp^uB z<5WD<_~-?AULu4TLkkap2&}^b%Q3ZHZL$Dvkxn%b$(05ArXAQVx85LL2q5o;o)_a$ z?vQ(=*XSCg(S1?tA!G~-n!^RnlNb_mj^!7QG2oAaAt33PvYEEAw2I~KAnDaR$=k;% zkdzCQM%Vxw;X`kyU`veR0sA4U5?XAro<{ zbau8d(9iMWCzMGu>aY&Wye}*&4Ku@$)I8^Sp)|jm2;`U4X<2?GX%`Y2e>zf5&oW|b z+onKu2V7)#0E*zAEM#+Ce!z=@l#-UJ!xFJvYO43r7riivm%wq#iSjO$EULq3ToZw$ zfDGb~fzufVL1vgs02e4#LOGGO1(nd5&Jf5b=VU|@A{M$?*?Td?gz-8xq-kMCcc;|k zeM2ktJ_v$#d(cqj>c${If<`zoGZf0Q^{eV3e4U{`a$Fdd#mzQ3p({YxRJr2Xr#QktOKP+|=TMPZo@!8q?*QYOv51@m6-!nQpbY6lXw8{V* zSHCPFmmMA5V-vY;981u?<*&Y2P;K%YFP64=?u3fhZP09hDl-i)FBSc~9d{i;k+mjK zRpDPHP4LcLQ8WVf>h;@~XQB8gDo)NdR%{1@+g-CA46m?iyY3;ht1|Vjy?(34=4322 z(wMSH&YYANYwBjFwptXBI*TNUvh9>ALkVVrH+@s3ijxGS^$W9wS%guM>)Jpa`TpY? zEsuO(as0zt(KSQk^#&eTLH|{PsD;bBL;hYNb@*dOnE(gO(Su#-7e*pcmF*H*p!8gZ zB3x|lEqMU~3cqujO7xEWPwGOX(BZ;MU#d66@xc=a;-1$=g5i}&Fmy>U+zttbH$Z}6 zodhDKIjo90Wn7QlKoO3<+(-#$-IkYFNe?iJVDL zag5bssP5{%wxARgDTl`7RWXgCDboWWg5zW?ohrm)V^6Prz3*cu-1#JeI2<2}LoaXys1+6jqqrYQH2MW2su=xsXWP@+3 z+$8bfkZJeV8rzQ)Vbyg1NUcD?6mb|yuW{oj3iLh#wC%4kB1%(uxB)RLvgc^+HtjpqhNfooF;P|Nc8s9zxzQ(xd=*B^ho#r=0aSjH?0*$ z37zzH863!-s5XQ7ULdIXbHD#LHLd+fBAiNx@;S{GIz9hCBAPA!BB!x^D>a^H%EgeU zu}LmJ&QLVP7Q$n&`+)Ys0qn{GzWL<${^CitnW2T^K&g1u@GHs2A0wP8$&ctPONjQK z?W|!ygx)_506G##3A@D7PIIOdAmMq68i)P<#l=O46pA5blYXL$@cxe{&tJYedl~G7 zgTHy+&uM}=FXze8LQj#AQq~E&0I5SJ3~Sv^Qn+A5sFhnz$KnDp?8Sr&Mn<`)kjLUg zIJgC*N>0H)K7*4pAHF+2J2`voy+3*TkJmrHh4;s&r^m0}p1eGR*QfCO^{W>rZ%qQ54|&tD~;PImyXAh2ojV|ax}p(p>xbq ywUCY3jBq_oqBQmrG9$t$!$x<)uV@kX{<(kdpZn(u&;JVm0RR64N9#oZLID6;h-UHt literal 0 HcmV?d00001 diff --git a/knowlg-automation/helm_charts/elasticsearch/charts/kibana-10.2.16.tgz b/knowlg-automation/helm_charts/elasticsearch/charts/kibana-10.2.16.tgz new file mode 100644 index 0000000000000000000000000000000000000000..313e3d0240367593658eb44da6ea7d65e714a854 GIT binary patch literal 42078 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 z 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 literal 0 HcmV?d00001 diff --git a/knowlg-automation/helm_charts/elasticsearch/templates/NOTES.txt b/knowlg-automation/helm_charts/elasticsearch/templates/NOTES.txt new file mode 100644 index 000000000..6b7e75c27 --- /dev/null +++ b/knowlg-automation/helm_charts/elasticsearch/templates/NOTES.txt @@ -0,0 +1,121 @@ +CHART NAME: {{ .Chart.Name }} +CHART VERSION: {{ .Chart.Version }} +APP VERSION: {{ .Chart.AppVersion }} + +{{- if contains .Values.service.type "LoadBalancer" }} + +------------------------------------------------------------------------------- + WARNING + + By specifying "service.type=LoadBalancer" you have most likely + exposed the Elasticsearch service externally. + + Please note that Elasticsearch does not implement a authentication + mechanism to secure your cluster. For security reasons, we strongly + suggest that you switch to "ClusterIP" or "NodePort". +------------------------------------------------------------------------------- +{{- end }} +{{- if not .Values.sysctlImage.enabled }} + +------------------------------------------------------------------------------- + WARNING + + Elasticsearch requires some changes in the kernel of the host machine to + work as expected. If those values are not set in the underlying operating + system, the ES containers fail to boot with ERROR messages. + + To check whether the host machine meets the requirements, run the command + below: + + kubectl logs --namespace {{ include "common.names.namespace" . }} $(kubectl get --namespace {{ include "common.names.namespace" . }} \ + pods -l app={{ template "common.names.name" . }},role=master -o jsonpath='{.items[0].metadata.name}') \ + elasticsearch + + You can adapt the Kernel parameters on you cluster as described in the + official documentation: + + https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster + + As an alternative, you can specify "sysctlImage.enabled=true" to use a + privileged initContainer to change those settings in the Kernel: + + helm upgrade --namespace {{ include "common.names.namespace" . }} {{ .Release.Name }} my-repo/elasticsearch --set sysctlImage.enabled=true + + Note that this requires the ability to run privileged containers, which is likely not + the case on many secure clusters. To cover this use case, you can also set some parameters + in the config file to customize the default settings: + + https://www.elastic.co/guide/en/elasticsearch/reference/current/index-modules-store.html + https://www.elastic.co/guide/en/cloud-on-k8s/master/k8s-virtual-memory.html + + For that, you can place the desired parameters by using the "config" block present in the values.yaml + +{{- else if .Values.sysctlImage.enabled }} + +------------------------------------------------------------------------------- + WARNING + + Elasticsearch requires some changes in the kernel of the host machine to + work as expected. If those values are not set in the underlying operating + system, the ES containers fail to boot with ERROR messages. + + More information about these requirements can be found in the links below: + + https://www.elastic.co/guide/en/elasticsearch/reference/current/file-descriptors.html + https://www.elastic.co/guide/en/elasticsearch/reference/current/vm-max-map-count.html + + This chart uses a privileged initContainer to change those settings in the Kernel + by running: sysctl -w vm.max_map_count=262144 && sysctl -w fs.file-max=65536 + +{{- end }} + +** Please be patient while the chart is being deployed ** + +{{- if .Values.diagnosticMode.enabled }} +The chart has been deployed in diagnostic mode. All probes have been disabled and the command has been overwritten with: + + command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 4 }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 4 }} + +Get the list of pods by executing: + + kubectl get pods --namespace {{ include "common.names.namespace" . }} -l app.kubernetes.io/instance={{ .Release.Name }} + +Access the pod you want to debug by executing + + kubectl exec --namespace {{ include "common.names.namespace" . }} -ti -- bash + +In order to replicate the container startup scripts execute this command: + + /opt/bitnami/scripts/elasticsearch/entrypoint.sh /opt/bitnami/scripts/elasticsearch/run.sh + +{{- else }} + + Elasticsearch can be accessed within the cluster on port {{ include "elasticsearch.service.ports.restAPI" . }} at {{ template "elasticsearch.service.name" . }}.{{ include "common.names.namespace" . }}.svc.{{ .Values.clusterDomain }} + + To access from outside the cluster execute the following commands: + +{{- if contains "NodePort" .Values.service.type }} + + export NODE_PORT=$(kubectl get --namespace {{ include "common.names.namespace" . }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "elasticsearch.service.name" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ include "common.names.namespace" . }} -o jsonpath="{.items[0].status.addresses[0].address}") + curl http://$NODE_IP:$NODE_PORT/ +{{- else if contains "LoadBalancer" .Values.service.type }} + + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + Watch the status with: 'kubectl get svc --namespace {{ include "common.names.namespace" . }} -w {{ template "elasticsearch.service.name" . }}' + + export SERVICE_IP=$(kubectl get svc --namespace {{ include "common.names.namespace" . }} {{ template "elasticsearch.service.name" . }} --template "{{ "{{ range (index .status.loadBalancer.ingress 0) }}{{ . }}{{ end }}" }}") + curl http://$SERVICE_IP:{{ include "elasticsearch.service.ports.restAPI" . }}/ +{{- else if contains "ClusterIP" .Values.service.type }} + + kubectl port-forward --namespace {{ include "common.names.namespace" . }} svc/{{ template "elasticsearch.service.name" . }} {{ include "elasticsearch.service.ports.restAPI" . }}:9200 & + curl http://127.0.0.1:9200/ +{{- end }} + +{{- include "common.warnings.rollingTag" .Values.image }} +{{- include "common.warnings.rollingTag" .Values.volumePermissions.image }} +{{- include "common.warnings.rollingTag" .Values.sysctlImage }} + +{{- end }} +{{ include "elasticsearch.validateValues" . }} diff --git a/knowlg-automation/helm_charts/elasticsearch/templates/_helpers.tpl b/knowlg-automation/helm_charts/elasticsearch/templates/_helpers.tpl new file mode 100644 index 000000000..d0c27d458 --- /dev/null +++ b/knowlg-automation/helm_charts/elasticsearch/templates/_helpers.tpl @@ -0,0 +1,585 @@ +{{/* vim: set filetype=mustache: */}} + +{{/* +Return the proper ES image name +*/}} +{{- define "elasticsearch.image" -}} +{{ include "common.images.image" (dict "imageRoot" .Values.image "global" .Values.global) }} +{{- end -}} + +{{/* +Return the proper Docker Image Registry Secret Names +*/}} +{{- define "elasticsearch.imagePullSecrets" -}} +{{ include "common.images.pullSecrets" (dict "images" (list .Values.image .Values.metrics.image .Values.sysctlImage .Values.volumePermissions.image) "global" .Values.global) }} +{{- end -}} + +{{/* +Return the proper ES exporter image name +*/}} +{{- define "elasticsearch.metrics.image" -}} +{{ include "common.images.image" (dict "imageRoot" .Values.metrics.image "global" .Values.global) }} +{{- end -}} + +{{/* +Return the proper sysctl image name +*/}} +{{- define "elasticsearch.sysctl.image" -}} +{{ include "common.images.image" (dict "imageRoot" .Values.sysctlImage "global" .Values.global) }} +{{- end -}} + +{{/* +Return the proper image name (for the init container volume-permissions image) +*/}} +{{- define "elasticsearch.volumePermissions.image" -}} +{{ include "common.images.image" (dict "imageRoot" .Values.volumePermissions.image "global" .Values.global) }} +{{- end -}} + + +{{/* +Name for the Elasticsearch service +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +Required for the Kibana subchart to find Elasticsearch service. +*/}} +{{- define "elasticsearch.service.name" -}} +{{- if .Values.global.kibanaEnabled -}} + {{- $name := .Values.global.elasticsearch.service.name -}} + {{- if contains $name .Release.Name -}} + {{- .Release.Name | trunc 63 | trimSuffix "-" -}} + {{- else -}} + {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} + {{- end -}} +{{- else -}} + {{- printf "%s" ( include "common.names.fullname" . ) | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} + +{{/* +Port number for the Elasticsearch service REST API port +Required for the Kibana subchart to find Elasticsearch service. +*/}} +{{- define "elasticsearch.service.ports.restAPI" -}} +{{- if .Values.global.kibanaEnabled -}} +{{- printf "%d" (int .Values.global.elasticsearch.service.ports.restAPI) -}} +{{- else -}} +{{- printf "%d" (int .Values.service.ports.restAPI) -}} +{{- end -}} +{{- end -}} + +{{/* +Create a default fully qualified master name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "elasticsearch.master.fullname" -}} +{{- $name := default "master" .Values.master.nameOverride -}} +{{- if .Values.master.fullnameOverride -}} +{{- .Values.master.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" (include "common.names.fullname" .) $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} + +{{/* +Create a default master service name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "elasticsearch.master.servicename" -}} +{{- if .Values.master.servicenameOverride -}} +{{- .Values.master.servicenameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-hl" (include "elasticsearch.master.fullname" .) | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} + +{{/* +Create a default fully qualified coordinating name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "elasticsearch.coordinating.fullname" -}} +{{- $name := default "coordinating" .Values.coordinating.nameOverride -}} +{{- if .Values.coordinating.fullnameOverride -}} +{{- .Values.coordinating.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" (include "common.names.fullname" .) $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} + +{{/* +Create a default coordinating service name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "elasticsearch.coordinating.servicename" -}} +{{- if .Values.coordinating.servicenameOverride -}} +{{- .Values.coordinating.servicenameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-hl" (include "elasticsearch.coordinating.fullname" .) | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} + +{{/* +Create a default fully qualified data name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "elasticsearch.data.fullname" -}} +{{- $name := default "data" .Values.data.nameOverride -}} +{{- if .Values.data.fullnameOverride -}} +{{- .Values.data.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" (include "common.names.fullname" .) $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} + +{{/* +Create a default data service name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "elasticsearch.data.servicename" -}} +{{- if .Values.data.servicenameOverride -}} +{{- .Values.data.servicenameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-hl" (include "elasticsearch.data.fullname" .) | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} + +{{/* +Create a default fully qualified ingest name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "elasticsearch.ingest.fullname" -}} +{{- $name := default "ingest" .Values.ingest.nameOverride -}} +{{- if .Values.ingest.fullnameOverride -}} +{{- .Values.ingest.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" (include "common.names.fullname" .) $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} + +{{/* +Create a default ingest service name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "elasticsearch.ingest.servicename" -}} +{{- if .Values.ingest.servicenameOverride -}} +{{- .Values.ingest.servicenameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-hl" (include "elasticsearch.ingest.fullname" .) | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} + +{{/* +Create a default fully qualified metrics name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "elasticsearch.metrics.fullname" -}} +{{- $name := default "metrics" .Values.metrics.nameOverride -}} +{{- if .Values.metrics.fullnameOverride -}} +{{- .Values.metrics.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" (include "common.names.fullname" .) $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} + +{{/* +Returns true if at least one master-elegible node replica has been configured. +*/}} +{{- define "elasticsearch.master.enabled" -}} +{{- if or .Values.master.autoscaling.enabled (gt (int .Values.master.replicaCount) 0) -}} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Returns true if at least one coordinating-only node replica has been configured. +*/}} +{{- define "elasticsearch.coordinating.enabled" -}} +{{- if or .Values.coordinating.autoscaling.enabled (gt (int .Values.coordinating.replicaCount) 0) -}} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Returns true if at least one data-only node replica has been configured. +*/}} +{{- define "elasticsearch.data.enabled" -}} +{{- if or .Values.data.autoscaling.enabled (gt (int .Values.data.replicaCount) 0) -}} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Returns true if at least one ingest-only node replica has been configured. +*/}} +{{- define "elasticsearch.ingest.enabled" -}} +{{- if and .Values.ingest.enabled (or .Values.ingest.autoscaling.enabled (gt (int .Values.ingest.replicaCount) 0)) -}} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Return the hostname of every ElasticSearch seed node +*/}} +{{- define "elasticsearch.hosts" -}} +{{- $clusterDomain := .Values.clusterDomain }} +{{- $releaseNamespace := include "common.names.namespace" . }} +{{- if (include "elasticsearch.master.enabled" .) -}} +{{- $masterFullname := include "elasticsearch.master.servicename" .}} +{{- $masterFullname }}.{{ $releaseNamespace }}.svc.{{ $clusterDomain }}, +{{- end -}} +{{- if (include "elasticsearch.coordinating.enabled" .) -}} +{{- $coordinatingFullname := include "elasticsearch.coordinating.servicename" .}} +{{- $coordinatingFullname }}.{{ $releaseNamespace }}.svc.{{ $clusterDomain }}, +{{- end -}} +{{- if (include "elasticsearch.data.enabled" .) -}} +{{- $dataFullname := include "elasticsearch.data.servicename" .}} +{{- $dataFullname }}.{{ $releaseNamespace }}.svc.{{ $clusterDomain }}, +{{- end -}} +{{- if (include "elasticsearch.ingest.enabled" .) -}} +{{- $ingestFullname := include "elasticsearch.ingest.servicename" .}} +{{- $ingestFullname }}.{{ $releaseNamespace }}.svc.{{ $clusterDomain }}, +{{- end -}} +{{- range .Values.extraHosts }} +{{- . }}, +{{- end }} +{{- end -}} + +{{/* +Get the initialization scripts volume name. +*/}} +{{- define "elasticsearch.initScripts" -}} +{{- printf "%s-init-scripts" (include "common.names.fullname" .) -}} +{{- end -}} + +{{/* +Get the initialization scripts ConfigMap name. +*/}} +{{- define "elasticsearch.initScriptsCM" -}} +{{- printf "%s" .Values.initScriptsCM -}} +{{- end -}} + +{{/* +Get the initialization scripts Secret name. +*/}} +{{- define "elasticsearch.initScriptsSecret" -}} +{{- printf "%s" .Values.initScriptsSecret -}} +{{- end -}} + +{{/* + Create the name of the master service account to use + */}} +{{- define "elasticsearch.master.serviceAccountName" -}} +{{- if .Values.master.serviceAccount.create -}} + {{ default (include "elasticsearch.master.fullname" .) .Values.master.serviceAccount.name }} +{{- else -}} + {{ default "default" .Values.master.serviceAccount.name }} +{{- end -}} +{{- end -}} + +{{/* + Create the name of the coordinating-only service account to use + */}} +{{- define "elasticsearch.coordinating.serviceAccountName" -}} +{{- if .Values.coordinating.serviceAccount.create -}} + {{ default (include "elasticsearch.coordinating.fullname" .) .Values.coordinating.serviceAccount.name }} +{{- else -}} + {{ default "default" .Values.coordinating.serviceAccount.name }} +{{- end -}} +{{- end -}} + +{{/* + Create the name of the data service account to use + */}} +{{- define "elasticsearch.data.serviceAccountName" -}} +{{- if .Values.data.serviceAccount.create -}} + {{ default (include "elasticsearch.data.fullname" .) .Values.data.serviceAccount.name }} +{{- else -}} + {{ default "default" .Values.data.serviceAccount.name }} +{{- end -}} +{{- end -}} + +{{/* + Create the name of the ingest service account to use + */}} +{{- define "elasticsearch.ingest.serviceAccountName" -}} +{{- if .Values.ingest.serviceAccount.create -}} + {{ default (include "elasticsearch.ingest.fullname" .) .Values.ingest.serviceAccount.name }} +{{- else -}} + {{ default "default" .Values.ingest.serviceAccount.name }} +{{- end -}} +{{- end -}} + +{{/* +Return the elasticsearch TLS credentials secret for master nodes. +*/}} +{{- define "elasticsearch.master.tlsSecretName" -}} +{{- $secretName := .Values.security.tls.master.existingSecret -}} +{{- if $secretName -}} + {{- printf "%s" (tpl $secretName $) -}} +{{- else -}} + {{- printf "%s-crt" (include "elasticsearch.master.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{/* +Return the elasticsearch TLS credentials secret for data nodes. +*/}} +{{- define "elasticsearch.data.tlsSecretName" -}} +{{- $secretName := .Values.security.tls.data.existingSecret -}} +{{- if $secretName -}} + {{- printf "%s" (tpl $secretName $) -}} +{{- else -}} + {{- printf "%s-crt" (include "elasticsearch.data.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{/* +Return the elasticsearch TLS credentials secret for ingest nodes. +*/}} +{{- define "elasticsearch.ingest.tlsSecretName" -}} +{{- $secretName := .Values.security.tls.ingest.existingSecret -}} +{{- if $secretName -}} + {{- printf "%s" (tpl $secretName $) -}} +{{- else -}} + {{- printf "%s-crt" (include "elasticsearch.ingest.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{/* +Return the elasticsearch TLS credentials secret for coordinating-only nodes. +*/}} +{{- define "elasticsearch.coordinating.tlsSecretName" -}} +{{- $secretName := .Values.security.tls.coordinating.existingSecret -}} +{{- if $secretName -}} + {{- printf "%s" (tpl $secretName $) -}} +{{- else -}} + {{- printf "%s-crt" (include "elasticsearch.coordinating.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{/* +Return true if a TLS credentials secret object should be created +*/}} +{{- define "elasticsearch.createTlsSecret" -}} +{{- if and .Values.security.enabled .Values.security.tls.autoGenerated (not (include "elasticsearch.security.tlsSecretsProvided" .)) }} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Return true if an authentication credentials secret object should be created +*/}} +{{- define "elasticsearch.createSecret" -}} +{{- if and .Values.security.enabled (not .Values.security.existingSecret) }} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Return the Elasticsearch authentication credentials secret name +*/}} +{{- define "elasticsearch.secretName" -}} +{{- default (include "common.names.fullname" .) .Values.security.existingSecret -}} +{{- end -}} + +{{/* +Return true if a TLS password secret object should be created +*/}} +{{- define "elasticsearch.createTlsPasswordsSecret" -}} +{{- if and .Values.security.enabled (not .Values.security.tls.passwordsSecret) (or .Values.security.tls.keystorePassword .Values.security.tls.truststorePassword .Values.security.tls.keyPassword ) }} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Return the Elasticsearch TLS password secret name +*/}} +{{- define "elasticsearch.tlsPasswordsSecret" -}} +{{- default (printf "%s-tls-pass" (include "common.names.fullname" .)) .Values.security.tls.passwordsSecret -}} +{{- end -}} + +{{/* +Returns the name of the secret key containing the Keystore password +*/}} +{{- define "elasticsearch.keystorePasswordKey" -}} +{{- if .Values.security.tls.secretKeystoreKey -}} +{{- printf "%s" .Values.security.tls.secretKeystoreKey -}} +{{- else -}} +{{- print "keystore-password"}} +{{- end -}} +{{- end -}} + + +{{/* +Returns the name of the secret key containing the Truststore password +*/}} +{{- define "elasticsearch.truststorePasswordKey" -}} +{{- if .Values.security.tls.secretTruststoreKey -}} +{{- printf "%s" .Values.security.tls.secretTruststoreKey -}} +{{- else -}} +{{- print "truststore-password"}} +{{- end -}} +{{- end -}} + +{{/* +Returns the name of the secret key containing the PEM key password +*/}} +{{- define "elasticsearch.keyPasswordKey" -}} +{{- if .Values.security.tls.secretKey -}} +{{- printf "%s" .Values.security.tls.secretKey -}} +{{- else -}} +{{- print "key-password"}} +{{- end -}} +{{- end -}} + +{{/* +Add environment variables to configure database values +*/}} +{{- define "elasticsearch.configure.security" -}} +- name: ELASTICSEARCH_ENABLE_SECURITY + value: "true" +- name: ELASTICSEARCH_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "elasticsearch.secretName" . }} + key: elasticsearch-password +- name: ELASTICSEARCH_ENABLE_FIPS_MODE + value: {{ .Values.security.fipsMode | quote }} +- name: ELASTICSEARCH_TLS_VERIFICATION_MODE + value: {{ .Values.security.tls.verificationMode | quote }} +- name: ELASTICSEARCH_ENABLE_REST_TLS + value: {{ ternary "true" "false" .Values.security.tls.restEncryption | quote }} +{{- if or (include "elasticsearch.createTlsSecret" .) .Values.security.tls.usePemCerts }} +- name: ELASTICSEARCH_TLS_USE_PEM + value: "true" +{{- else }} +- name: ELASTICSEARCH_KEYSTORE_LOCATION + value: "/opt/bitnami/elasticsearch/config/certs/{{ .Values.security.tls.keystoreFilename }}" +- name: ELASTICSEARCH_TRUSTSTORE_LOCATION + value: "/opt/bitnami/elasticsearch/config/certs/{{ .Values.security.tls.truststoreFilename }}" +{{- end }} +{{- if and (not .Values.security.tls.usePemCerts) (or .Values.security.tls.keystorePassword .Values.security.tls.passwordsSecret) }} +- name: ELASTICSEARCH_KEYSTORE_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "elasticsearch.tlsPasswordsSecret" . }} + key: {{ include "elasticsearch.keystorePasswordKey" . | quote }} +{{- end }} +{{- if and (not .Values.security.tls.usePemCerts) (or .Values.security.tls.truststorePassword .Values.security.tls.passwordsSecret) }} +- name: ELASTICSEARCH_TRUSTSTORE_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "elasticsearch.tlsPasswordsSecret" . }} + key: {{ include "elasticsearch.truststorePasswordKey" . | quote }} +{{- end }} +{{- if and .Values.security.tls.usePemCerts (or .Values.security.tls.keyPassword .Values.security.tls.passwordsSecret) }} +- name: ELASTICSEARCH_KEY_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "elasticsearch.tlsPasswordsSecret" . }} + key: {{ include "elasticsearch.keyPasswordKey" . | quote }} +{{- end }} +{{- end -}} + +{{/* +Returns true if at least 1 existing secret was provided +*/}} +{{- define "elasticsearch.security.tlsSecretsProvided" -}} +{{- $masterSecret := (and (include "elasticsearch.master.enabled" .) .Values.security.tls.master.existingSecret) -}} +{{- $coordinatingSecret := (and (include "elasticsearch.coordinating.enabled" .) .Values.security.tls.coordinating.existingSecret) -}} +{{- $dataSecret := (and (include "elasticsearch.data.enabled" .) .Values.security.tls.data.existingSecret) -}} +{{- $ingestSecret := (and (include "elasticsearch.ingest.enabled" .) .Values.security.tls.ingest.existingSecret) -}} +{{- if or $masterSecret $coordinatingSecret $dataSecret $ingestSecret }} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* Validate values of Elasticsearch - Existing secret not provided for master nodes */}} +{{- define "elasticsearch.validateValues.security.missingTlsSecrets.master" -}} +{{- $masterSecret := (and (include "elasticsearch.master.enabled" .) (not .Values.security.tls.master.existingSecret)) -}} +{{- if and .Values.security.enabled (include "elasticsearch.security.tlsSecretsProvided" .) $masterSecret -}} +elasticsearch: security.tls.master.existingSecret + Missing secret containing the TLS certificates for the Elasticsearch master nodes. + Provide the certificates using --set .Values.security.tls.master.existingSecret="my-secret". +{{- end -}} +{{- end -}} + +{{/* Validate values of Elasticsearch - Existing secret not provided for coordinating-only nodes */}} +{{- define "elasticsearch.validateValues.security.missingTlsSecrets.coordinating" -}} +{{- $coordinatingSecret := (and (include "elasticsearch.coordinating.enabled" .) (not .Values.security.tls.coordinating.existingSecret)) -}} +{{- if and .Values.security.enabled (include "elasticsearch.security.tlsSecretsProvided" .) $coordinatingSecret -}} +elasticsearch: security.tls.coordinating.existingSecret + Missing secret containing the TLS certificates for the Elasticsearch coordinating-only nodes. + Provide the certificates using --set .Values.security.tls.coordinating.existingSecret="my-secret". +{{- end -}} +{{- end -}} + +{{/* Validate values of Elasticsearch - Existing secret not provided for data nodes */}} +{{- define "elasticsearch.validateValues.security.missingTlsSecrets.data" -}} +{{- $dataSecret := (and (include "elasticsearch.data.enabled" .) (not .Values.security.tls.data.existingSecret)) -}} +{{- if and .Values.security.enabled (include "elasticsearch.security.tlsSecretsProvided" .) $dataSecret -}} +elasticsearch: security.tls.data.existingSecret + Missing secret containing the TLS certificates for the Elasticsearch data nodes. + Provide the certificates using --set .Values.security.tls.data.existingSecret="my-secret". +{{- end -}} +{{- end -}} + +{{/* Validate values of Elasticsearch - Existing secret not provided for ingest nodes */}} +{{- define "elasticsearch.validateValues.security.missingTlsSecrets.ingest" -}} +{{- $ingestSecret := (and (include "elasticsearch.ingest.enabled" .) (not .Values.security.tls.ingest.existingSecret)) -}} +{{- if and .Values.security.enabled (include "elasticsearch.security.tlsSecretsProvided" .) $ingestSecret -}} +elasticsearch: security.tls.ingest.existingSecret + Missing secret containing the TLS certificates for the Elasticsearch ingest nodes. + Provide the certificates using --set .Values.security.tls.ingest.existingSecret="my-secret". +{{- end -}} +{{- end -}} + +{{/* Validate values of Elasticsearch - TLS enabled but no certificates provided */}} +{{- define "elasticsearch.validateValues.security.tls" -}} +{{- if and .Values.security.enabled (not .Values.security.tls.autoGenerated) (not (include "elasticsearch.security.tlsSecretsProvided" .)) -}} +elasticsearch: security.tls + In order to enable X-Pack Security, it is necessary to configure TLS. + Three different mechanisms can be used: + - Provide an existing secret containing the Keystore and Truststore for each role + - Provide an existing secret containing the PEM certificates for each role and enable `security.tls.usePemCerts=true` + - Enable using auto-generated certificates with `security.tls.autoGenerated=true` + Existing secrets containing either JKS/PKCS12 or PEM certificates can be provided using --set Values.security.tls.master.existingSecret=master-certs, + --set Values.security.tls.data.existingSecret=data-certs, --set Values.security.tls.coordinating.existingSecret=coordinating-certs, --set Values.security.tls.ingest.existingSecret=ingest-certs +{{- end -}} +{{- end -}} + +{{/* Validate at least Elasticsearch one master node is configured */}} +{{- define "elasticsearch.validateValues.master.replicas" -}} +{{- if not (include "elasticsearch.master.enabled" .) -}} +elasticsearch: master.replicas + Elasticsearch needs at least one master-elegible node to form a cluster. +{{- end -}} +{{- end -}} + +{{/* +Compile all warnings into a single message, and call fail. +*/}} +{{- define "elasticsearch.validateValues" -}} +{{- $messages := list -}} +{{- $messages := append $messages (include "elasticsearch.validateValues.master.replicas" .) -}} +{{- $messages := append $messages (include "elasticsearch.validateValues.security.tls" .) -}} +{{- $messages := append $messages (include "elasticsearch.validateValues.security.missingTlsSecrets.master" .) -}} +{{- $messages := append $messages (include "elasticsearch.validateValues.security.missingTlsSecrets.data" .) -}} +{{- $messages := append $messages (include "elasticsearch.validateValues.security.missingTlsSecrets.coordinating" .) -}} +{{- $messages := append $messages (include "elasticsearch.validateValues.security.missingTlsSecrets.ingest" .) -}} +{{- $messages := without $messages "" -}} +{{- $message := join "\n" $messages -}} + +{{- if $message -}} +{{- printf "\nVALUES VALIDATION:\n%s" $message | fail -}} +{{- end -}} +{{- end -}} + +{{/* +Sysctl set if less then +*/}} +{{- define "elasticsearch.sysctlIfLess" -}} +CURRENT=`sysctl -n {{ .key }}`; +DESIRED="{{ .value }}"; +if [ "$DESIRED" -gt "$CURRENT" ]; then + sysctl -w {{ .key }}={{ .value }}; +fi; +{{- end -}} diff --git a/knowlg-automation/helm_charts/elasticsearch/templates/configmap.yaml b/knowlg-automation/helm_charts/elasticsearch/templates/configmap.yaml new file mode 100644 index 000000000..3efba80cc --- /dev/null +++ b/knowlg-automation/helm_charts/elasticsearch/templates/configmap.yaml @@ -0,0 +1,23 @@ +{{- if or .Values.config .Values.extraConfig }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "common.names.fullname" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +data: + {{- if .Values.config }} + elasticsearch.yml: |- + {{- include "common.tplvalues.render" ( dict "value" .Values.config "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.extraConfig }} + my_elasticsearch.yml: |- + {{- include "common.tplvalues.render" ( dict "value" .Values.extraConfig "context" $ ) | nindent 4 }} + {{- end }} +{{- end }} diff --git a/knowlg-automation/helm_charts/elasticsearch/templates/coordinating/hpa.yaml b/knowlg-automation/helm_charts/elasticsearch/templates/coordinating/hpa.yaml new file mode 100644 index 000000000..9de3b8efc --- /dev/null +++ b/knowlg-automation/helm_charts/elasticsearch/templates/coordinating/hpa.yaml @@ -0,0 +1,47 @@ +{{- if and (include "elasticsearch.coordinating.enabled" .) .Values.coordinating.autoscaling.enabled }} +apiVersion: {{ include "common.capabilities.hpa.apiVersion" ( dict "context" $ ) }} +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "elasticsearch.coordinating.fullname" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: coordinating-only + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" (dict "value" .Values.commonLabels "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + scaleTargetRef: + apiVersion: {{ template "common.capabilities.statefulset.apiVersion" . }} + kind: StatefulSet + name: {{ include "elasticsearch.coordinating.fullname" . }} + minReplicas: {{ .Values.coordinating.autoscaling.minReplicas }} + maxReplicas: {{ .Values.coordinating.autoscaling.maxReplicas }} + metrics: + {{- if .Values.coordinating.autoscaling.targetCPU }} + - type: Resource + resource: + name: cpu + {{- if semverCompare "<1.23-0" (include "common.capabilities.kubeVersion" .) }} + targetAverageUtilization: {{ .Values.coordinating.autoscaling.targetCPU }} + {{- else }} + target: + type: Utilization + averageUtilization: {{ .Values.coordinating.autoscaling.targetCPU }} + {{- end }} + {{- end }} + {{- if .Values.coordinating.autoscaling.targetMemory }} + - type: Resource + resource: + name: memory + {{- if semverCompare "<1.23-0" (include "common.capabilities.kubeVersion" .) }} + targetAverageUtilization: {{ .Values.coordinating.autoscaling.targetMemory }} + {{- else }} + target: + type: Utilization + averageUtilization: {{ .Values.coordinating.autoscaling.targetMemory }} + {{- end }} + {{- end }} +{{- end }} diff --git a/knowlg-automation/helm_charts/elasticsearch/templates/coordinating/serviceaccount.yaml b/knowlg-automation/helm_charts/elasticsearch/templates/coordinating/serviceaccount.yaml new file mode 100644 index 000000000..ba0861a32 --- /dev/null +++ b/knowlg-automation/helm_charts/elasticsearch/templates/coordinating/serviceaccount.yaml @@ -0,0 +1,22 @@ +{{- if and (include "elasticsearch.coordinating.enabled" .) .Values.coordinating.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "elasticsearch.coordinating.serviceAccountName" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: coordinating-only + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" (dict "value" .Values.commonLabels "context" $) | nindent 4 }} + {{- end }} + {{- if or .Values.coordinating.serviceAccount.annotations .Values.commonAnnotations }} + annotations: + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" (dict "value" .Values.commonAnnotations "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.coordinating.serviceAccount.annotations }} + {{- include "common.tplvalues.render" (dict "value" .Values.coordinating.serviceAccount.annotations "context" $) | nindent 4 }} + {{- end }} + {{- end }} +automountServiceAccountToken: {{ .Values.coordinating.serviceAccount.automountServiceAccountToken }} +{{- end -}} diff --git a/knowlg-automation/helm_charts/elasticsearch/templates/coordinating/statefulset.yaml b/knowlg-automation/helm_charts/elasticsearch/templates/coordinating/statefulset.yaml new file mode 100644 index 000000000..692dc890f --- /dev/null +++ b/knowlg-automation/helm_charts/elasticsearch/templates/coordinating/statefulset.yaml @@ -0,0 +1,313 @@ +{{- if (include "elasticsearch.coordinating.enabled" .) }} +apiVersion: {{ template "common.capabilities.statefulset.apiVersion" . }} +kind: StatefulSet +metadata: + name: {{ include "elasticsearch.coordinating.fullname" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: coordinating-only + {{- if .Values.useIstioLabels }} + ## Istio Labels: https://istio.io/docs/ops/deployment/requirements/ + app: coordinating-only + {{- end }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if or .Values.coordinating.annotations .Values.commonAnnotations }} + annotations: + {{- if .Values.coordinating.annotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.coordinating.annotations "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- end }} +spec: + {{- if not .Values.coordinating.autoscaling.enabled }} + replicas: {{ .Values.coordinating.replicaCount }} + {{- end }} + selector: + matchLabels: {{- include "common.labels.matchLabels" . | nindent 6 }} + app.kubernetes.io/component: coordinating-only + {{- if .Values.coordinating.updateStrategy }} + updateStrategy: {{- toYaml .Values.coordinating.updateStrategy | nindent 4 }} + {{- end }} + serviceName: {{ include "elasticsearch.coordinating.servicename" . }} + podManagementPolicy: {{ .Values.coordinating.podManagementPolicy }} + template: + metadata: + labels: {{- include "common.labels.standard" . | nindent 8 }} + app.kubernetes.io/component: coordinating-only + {{- if .Values.useIstioLabels }} + ## Istio Labels: https://istio.io/docs/ops/deployment/requirements/ + app: coordinating-only + {{- end }} + {{- if .Values.coordinating.podLabels }} + {{- include "common.tplvalues.render" (dict "value" .Values.coordinating.podLabels "context" $) | nindent 8 }} + {{- end }} + annotations: + {{- if and (include "elasticsearch.createTlsSecret" .) (not .Values.security.tls.coordinating.existingSecret) }} + checksum/tls: {{ include (print $.Template.BasePath "/tls-secret.yaml") . | sha256sum }} + {{- end }} + {{- if .Values.coordinating.podAnnotations }} + {{- include "common.tplvalues.render" (dict "value" .Values.coordinating.podAnnotations "context" $) | nindent 8 }} + {{- end }} + spec: + serviceAccountName: {{ template "elasticsearch.coordinating.serviceAccountName" . }} + {{- include "elasticsearch.imagePullSecrets" . | nindent 6 }} + {{- if .Values.coordinating.hostAliases }} + hostAliases: {{- include "common.tplvalues.render" (dict "value" .Values.coordinating.hostAliases "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.coordinating.affinity }} + affinity: {{- include "common.tplvalues.render" (dict "value" .Values.coordinating.affinity "context" $) | nindent 8 }} + {{- else }} + affinity: + podAffinity: {{- include "common.affinities.pods" (dict "type" .Values.coordinating.podAffinityPreset "component" "coordinating-only" "context" $) | nindent 10 }} + podAntiAffinity: {{- include "common.affinities.pods" (dict "type" .Values.coordinating.podAntiAffinityPreset "component" "coordinating-only" "context" $) | nindent 10 }} + nodeAffinity: {{- include "common.affinities.nodes" (dict "type" .Values.coordinating.nodeAffinityPreset.type "key" .Values.coordinating.nodeAffinityPreset.key "values" .Values.coordinating.nodeAffinityPreset.values) | nindent 10 }} + {{- end }} + {{- if .Values.coordinating.nodeSelector }} + nodeSelector: {{- include "common.tplvalues.render" (dict "value" .Values.coordinating.nodeSelector "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.coordinating.tolerations }} + tolerations: {{- include "common.tplvalues.render" (dict "value" .Values.coordinating.tolerations "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.coordinating.schedulerName }} + schedulerName: {{ .Values.coordinating.schedulerName }} + {{- end }} + {{- if .Values.coordinating.topologySpreadConstraints }} + topologySpreadConstraints: {{- include "common.tplvalues.render" (dict "value" .Values.coordinating.topologySpreadConstraints "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.coordinating.priorityClassName }} + priorityClassName: {{ .Values.coordinating.priorityClassName | quote }} + {{- end }} + {{- if .Values.coordinating.terminationGracePeriodSeconds }} + terminationGracePeriodSeconds: {{ .Values.coordinating.terminationGracePeriodSeconds }} + {{- end }} + {{- if .Values.coordinating.podSecurityContext.enabled }} + securityContext: {{- omit .Values.coordinating.podSecurityContext "enabled" | toYaml | nindent 8 }} + {{- end }} + {{- if or .Values.coordinating.initContainers .Values.sysctlImage.enabled }} + initContainers: + {{- if .Values.sysctlImage.enabled }} + ## Image that performs the sysctl operation to modify Kernel settings (needed sometimes to avoid boot errors) + - name: sysctl + image: {{ include "elasticsearch.sysctl.image" . }} + imagePullPolicy: {{ .Values.image.pullPolicy | quote }} + command: + - /bin/bash + - -ec + - | + {{- include "elasticsearch.sysctlIfLess" (dict "key" "vm.max_map_count" "value" "262144") | nindent 14 }} + {{- include "elasticsearch.sysctlIfLess" (dict "key" "fs.file-max" "value" "65536") | nindent 14 }} + securityContext: + privileged: true + runAsUser: 0 + {{- if .Values.sysctlImage.resources }} + resources: {{- toYaml .Values.sysctlImage.resources | nindent 12 }} + {{- end }} + {{- end }} + {{- if .Values.coordinating.initContainers }} + {{- include "common.tplvalues.render" (dict "value" .Values.coordinating.initContainers "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.initContainers }} + {{- include "common.tplvalues.render" (dict "value" .Values.initContainers "context" $) | nindent 8 }} + {{- end }} + {{- end }} + containers: + - name: elasticsearch + image: {{ include "elasticsearch.image" . }} + imagePullPolicy: {{ .Values.image.pullPolicy | quote }} + {{- if .Values.coordinating.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.coordinating.containerSecurityContext "enabled" | toYaml | nindent 12 }} + {{- end }} + {{- if .Values.diagnosticMode.enabled }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 12 }} + {{- else if .Values.coordinating.command }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.coordinating.command "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.diagnosticMode.enabled }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 12 }} + {{- else if .Values.coordinating.args }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.coordinating.args "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.coordinating.lifecycleHooks }} + lifecycle: {{- include "common.tplvalues.render" (dict "value" .Values.coordinating.lifecycleHooks "context" $) | nindent 12 }} + {{- end }} + env: + - name: MY_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: BITNAMI_DEBUG + value: {{ ternary "true" "false" (or .Values.image.debug .Values.diagnosticMode.enabled) | quote }} + - name: ELASTICSEARCH_CLUSTER_NAME + value: {{ .Values.clusterName | quote }} + - name: ELASTICSEARCH_IS_DEDICATED_NODE + value: "yes" + - name: ELASTICSEARCH_NODE_ROLES + value: "" + - name: ELASTICSEARCH_TRANSPORT_PORT_NUMBER + value: {{ .Values.containerPorts.transport | quote }} + - name: ELASTICSEARCH_HTTP_PORT_NUMBER + value: {{ .Values.containerPorts.restAPI | quote }} + - name: ELASTICSEARCH_CLUSTER_HOSTS + value: {{ include "elasticsearch.hosts" . | quote }} + - name: ELASTICSEARCH_TOTAL_NODES + value: {{ add (ternary .Values.master.autoscaling.minReplicas .Values.master.replicaCount .Values.master.autoscaling.enabled) (ternary .Values.data.autoscaling.minReplicas .Values.data.replicaCount .Values.data.autoscaling.enabled) | quote }} + - name: ELASTICSEARCH_CLUSTER_MASTER_HOSTS + {{- $elasticsearchMasterFullname := include "elasticsearch.master.fullname" . }} + {{- $replicas := int (ternary .Values.master.autoscaling.minReplicas .Values.master.replicaCount .Values.master.autoscaling.enabled) }} + value: {{ range $i, $e := until $replicas }}{{ printf "%s-%d" $elasticsearchMasterFullname $e }} {{ end }} + - name: ELASTICSEARCH_MINIMUM_MASTER_NODES + value: {{ add (div (ternary .Values.master.autoscaling.minReplicas .Values.master.replicaCount .Values.master.autoscaling.enabled) 2) 1 | quote }} + - name: ELASTICSEARCH_ADVERTISED_HOSTNAME + value: "$(MY_POD_NAME).{{ (include "elasticsearch.coordinating.servicename" .)}}.{{ include "common.names.namespace" . }}.svc.{{ .Values.clusterDomain }}" + {{- if .Values.plugins }} + - name: ELASTICSEARCH_PLUGINS + value: {{ .Values.plugins | quote }} + {{- end }} + {{- if .Values.coordinating.heapSize }} + - name: ELASTICSEARCH_HEAP_SIZE + value: {{ .Values.coordinating.heapSize | quote }} + {{- end }} + {{- if .Values.security.enabled }} + {{- include "elasticsearch.configure.security" . | nindent 12 }} + {{- end }} + {{- if .Values.coordinating.extraEnvVars }} + {{- include "common.tplvalues.render" ( dict "value" .Values.coordinating.extraEnvVars "context" $ ) | nindent 12 }} + {{- end }} + {{- if .Values.extraEnvVars }} + {{- include "common.tplvalues.render" ( dict "value" .Values.extraEnvVars "context" $ ) | nindent 12 }} + {{- end }} + {{- if or .Values.extraEnvVarsCM .Values.extraEnvVarsSecret .Values.coordinating.extraEnvVarsCM .Values.coordinating.extraEnvVarsSecret }} + envFrom: + {{- if .Values.extraEnvVarsCM }} + - configMapRef: + name: {{ include "common.tplvalues.render" ( dict "value" .Values.extraEnvVarsCM "context" $ ) }} + {{- end }} + {{- if .Values.coordinating.extraEnvVarsCM }} + - configMapRef: + name: {{ include "common.tplvalues.render" ( dict "value" .Values.coordinating.extraEnvVarsCM "context" $ ) }} + {{- end }} + {{- if .Values.extraEnvVarsSecret }} + - secretRef: + name: {{ include "common.tplvalues.render" ( dict "value" .Values.extraEnvVarsSecret "context" $ ) }} + {{- end }} + {{- if .Values.coordinating.extraEnvVarsSecret }} + - secretRef: + name: {{ include "common.tplvalues.render" ( dict "value" .Values.coordinating.extraEnvVarsSecret "context" $ ) }} + {{- end }} + {{- end }} + ports: + - name: rest-api + containerPort: {{ .Values.containerPorts.restAPI }} + - name: transport + containerPort: {{ .Values.containerPorts.transport }} + {{- if not .Values.diagnosticMode.enabled }} + {{- if .Values.coordinating.customStartupProbe }} + startupProbe: {{- include "common.tplvalues.render" (dict "value" .Values.coordinating.customStartupProbe "context" $) | nindent 12 }} + {{- else if .Values.coordinating.startupProbe.enabled }} + startupProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.coordinating.startupProbe "enabled") "context" $) | nindent 12 }} + tcpSocket: + port: rest-api + {{- end }} + {{- if .Values.coordinating.customLivenessProbe }} + livenessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.coordinating.customLivenessProbe "context" $) | nindent 12 }} + {{- else if .Values.coordinating.livenessProbe.enabled }} + livenessProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.coordinating.livenessProbe "enabled") "context" $) | nindent 12 }} + exec: + command: + - /opt/bitnami/scripts/elasticsearch/healthcheck.sh + {{- end }} + {{- if .Values.coordinating.customReadinessProbe }} + readinessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.coordinating.customReadinessProbe "context" $) | nindent 12 }} + {{- else if .Values.coordinating.readinessProbe.enabled }} + readinessProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.coordinating.readinessProbe "enabled") "context" $) | nindent 12 }} + exec: + command: + - /opt/bitnami/scripts/elasticsearch/healthcheck.sh + {{- end }} + {{- end }} + {{- if .Values.coordinating.resources }} + resources: {{- toYaml .Values.coordinating.resources | nindent 12 }} + {{- end }} + volumeMounts: + - name: data + mountPath: /bitnami/elasticsearch/data + {{- if .Values.config }} + - mountPath: /opt/bitnami/elasticsearch/config/elasticsearch.yml + name: config + subPath: elasticsearch.yml + {{- end }} + {{- if .Values.extraConfig }} + - mountPath: /opt/bitnami/elasticsearch/config/my_elasticsearch.yml + name: config + subPath: my_elasticsearch.yml + {{- end }} + {{- if .Values.security.enabled }} + - name: elasticsearch-certificates + mountPath: /opt/bitnami/elasticsearch/config/certs + readOnly: true + {{- end }} + {{- if .Values.initScripts }} + - name: custom-init-scripts + mountPath: /docker-entrypoint-initdb.d/init-scripts + {{- end }} + {{- if .Values.initScriptsCM }} + - name: custom-init-scripts-cm + mountPath: /docker-entrypoint-initdb.d/init-scripts-cm + {{- end }} + {{- if .Values.initScriptsSecret }} + - name: custom-init-scripts-secret + mountPath: /docker-entrypoint-initdb.d/init-scripts-secret + {{- end }} + {{- if .Values.coordinating.extraVolumeMounts }} + {{- include "common.tplvalues.render" (dict "value" .Values.coordinating.extraVolumeMounts "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.extraVolumeMounts }} + {{- include "common.tplvalues.render" (dict "value" .Values.extraVolumeMounts "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.coordinating.sidecars }} + {{- include "common.tplvalues.render" ( dict "value" .Values.coordinating.sidecars "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.sidecars }} + {{- include "common.tplvalues.render" ( dict "value" .Values.sidecars "context" $) | nindent 8 }} + {{- end }} + volumes: + - name: "data" + emptyDir: {} + {{- if or .Values.config .Values.extraConfig }} + - name: config + configMap: + name: {{ include "common.names.fullname" . }} + {{- end }} + {{- if .Values.security.enabled }} + - name: elasticsearch-certificates + secret: + secretName: {{ include "elasticsearch.coordinating.tlsSecretName" . }} + defaultMode: 256 + {{- end }} + {{- if .Values.initScripts }} + - name: custom-init-scripts + configMap: + name: {{ template "elasticsearch.initScripts" . }} + {{- end }} + {{- if .Values.initScriptsCM }} + - name: custom-init-scripts-cm + configMap: + name: {{ template "elasticsearch.initScriptsCM" . }} + {{- end }} + {{- if .Values.initScriptsSecret }} + - name: custom-init-scripts-secret + secret: + secretName: {{ template "elasticsearch.initScriptsSecret" . }} + defaultMode: 0755 + {{- end }} + {{- if .Values.coordinating.extraVolumes }} + {{- include "common.tplvalues.render" (dict "value" .Values.coordinating.extraVolumes "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.extraVolumes }} + {{- include "common.tplvalues.render" (dict "value" .Values.extraVolumes "context" $) | nindent 8 }} + {{- end }} +{{- end }} diff --git a/knowlg-automation/helm_charts/elasticsearch/templates/coordinating/svc-headless.yaml b/knowlg-automation/helm_charts/elasticsearch/templates/coordinating/svc-headless.yaml new file mode 100644 index 000000000..a62484fe8 --- /dev/null +++ b/knowlg-automation/helm_charts/elasticsearch/templates/coordinating/svc-headless.yaml @@ -0,0 +1,28 @@ +{{- if (include "elasticsearch.coordinating.enabled" .) }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "elasticsearch.coordinating.servicename" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: coordinating-only + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" (dict "value" .Values.commonLabels "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" (dict "value" .Values.commonAnnotations "context" $) | nindent 4 }} + {{- end }} +spec: + type: ClusterIP + clusterIP: None + publishNotReadyAddresses: true + ports: + - name: tcp-rest-api + port: {{ .Values.containerPorts.restAPI }} + targetPort: rest-api + - name: tcp-transport + port: {{ .Values.containerPorts.transport }} + targetPort: transport + selector: {{- include "common.labels.matchLabels" . | nindent 4 }} + app.kubernetes.io/component: coordinating-only +{{- end }} diff --git a/knowlg-automation/helm_charts/elasticsearch/templates/data/hpa.yaml b/knowlg-automation/helm_charts/elasticsearch/templates/data/hpa.yaml new file mode 100644 index 000000000..62c247822 --- /dev/null +++ b/knowlg-automation/helm_charts/elasticsearch/templates/data/hpa.yaml @@ -0,0 +1,47 @@ +{{- if and (include "elasticsearch.data.enabled" .) .Values.data.autoscaling.enabled }} +apiVersion: {{ include "common.capabilities.hpa.apiVersion" ( dict "context" $ ) }} +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "elasticsearch.data.fullname" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: data + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" (dict "value" .Values.commonLabels "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + scaleTargetRef: + apiVersion: {{ template "common.capabilities.statefulset.apiVersion" . }} + kind: StatefulSet + name: {{ include "elasticsearch.data.fullname" . }} + minReplicas: {{ .Values.data.autoscaling.minReplicas }} + maxReplicas: {{ .Values.data.autoscaling.maxReplicas }} + metrics: + {{- if .Values.data.autoscaling.targetCPU }} + - type: Resource + resource: + name: cpu + {{- if semverCompare "<1.23-0" (include "common.capabilities.kubeVersion" .) }} + targetAverageUtilization: {{ .Values.data.autoscaling.targetCPU }} + {{- else }} + target: + type: Utilization + averageUtilization: {{ .Values.data.autoscaling.targetCPU }} + {{- end }} + {{- end }} + {{- if .Values.data.autoscaling.targetMemory }} + - type: Resource + resource: + name: memory + {{- if semverCompare "<1.23-0" (include "common.capabilities.kubeVersion" .) }} + targetAverageUtilization: {{ .Values.data.autoscaling.targetMemory }} + {{- else }} + target: + type: Utilization + averageUtilization: {{ .Values.data.autoscaling.targetMemory }} + {{- end }} + {{- end }} +{{- end }} diff --git a/knowlg-automation/helm_charts/elasticsearch/templates/data/serviceaccount.yaml b/knowlg-automation/helm_charts/elasticsearch/templates/data/serviceaccount.yaml new file mode 100644 index 000000000..bd71bd2d9 --- /dev/null +++ b/knowlg-automation/helm_charts/elasticsearch/templates/data/serviceaccount.yaml @@ -0,0 +1,22 @@ +{{- if and (include "elasticsearch.data.enabled" .) .Values.data.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "elasticsearch.data.serviceAccountName" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: data + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" (dict "value" .Values.commonLabels "context" $) | nindent 4 }} + {{- end }} + {{- if or .Values.data.serviceAccount.annotations .Values.commonAnnotations }} + annotations: + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" (dict "value" .Values.commonAnnotations "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.data.serviceAccount.annotations }} + {{- include "common.tplvalues.render" (dict "value" .Values.data.serviceAccount.annotations "context" $) | nindent 4 }} + {{- end }} + {{- end }} +automountServiceAccountToken: {{ .Values.data.serviceAccount.automountServiceAccountToken }} +{{- end -}} diff --git a/knowlg-automation/helm_charts/elasticsearch/templates/data/statefulset.yaml b/knowlg-automation/helm_charts/elasticsearch/templates/data/statefulset.yaml new file mode 100644 index 000000000..164275641 --- /dev/null +++ b/knowlg-automation/helm_charts/elasticsearch/templates/data/statefulset.yaml @@ -0,0 +1,373 @@ +{{- if (include "elasticsearch.data.enabled" .) }} +apiVersion: {{ template "common.capabilities.statefulset.apiVersion" . }} +kind: StatefulSet +metadata: + name: {{ include "elasticsearch.data.fullname" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: data + {{- if .Values.useIstioLabels }} + ## Istio Labels: https://istio.io/docs/ops/deployment/requirements/ + app: data + {{- end }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if or .Values.data.annotations .Values.commonAnnotations }} + annotations: + {{- if .Values.data.annotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.data.annotations "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- end }} +spec: + {{- if not .Values.data.autoscaling.enabled }} + replicas: {{ .Values.data.replicaCount }} + {{- end }} + podManagementPolicy: {{ .Values.data.podManagementPolicy }} + selector: + matchLabels: {{- include "common.labels.matchLabels" . | nindent 6 }} + app.kubernetes.io/component: data + serviceName: {{ include "elasticsearch.data.servicename" . }} + {{- if .Values.data.updateStrategy }} + updateStrategy: {{- toYaml .Values.data.updateStrategy | nindent 4 }} + {{- end }} + template: + metadata: + labels: {{- include "common.labels.standard" . | nindent 8 }} + app.kubernetes.io/component: data + {{- if .Values.useIstioLabels }} + ## Istio Labels: https://istio.io/docs/ops/deployment/requirements/ + app: data + {{- end }} + {{- if .Values.data.podLabels }} + {{- include "common.tplvalues.render" (dict "value" .Values.data.podLabels "context" $) | nindent 8 }} + {{- end }} + annotations: + {{- if and (include "elasticsearch.createTlsSecret" .) (not .Values.security.tls.data.existingSecret) }} + checksum/tls: {{ include (print $.Template.BasePath "/tls-secret.yaml") . | sha256sum }} + {{- end }} + {{- if .Values.data.podAnnotations }} + {{- include "common.tplvalues.render" (dict "value" .Values.data.podAnnotations "context" $) | nindent 8 }} + {{- end }} + spec: + serviceAccountName: {{ template "elasticsearch.data.serviceAccountName" . }} + {{- include "elasticsearch.imagePullSecrets" . | nindent 6 }} + {{- if .Values.data.hostAliases }} + hostAliases: {{- include "common.tplvalues.render" (dict "value" .Values.data.hostAliases "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.data.affinity }} + affinity: {{- include "common.tplvalues.render" (dict "value" .Values.data.affinity "context" $) | nindent 8 }} + {{- else }} + affinity: + podAffinity: {{- include "common.affinities.pods" (dict "type" .Values.data.podAffinityPreset "component" "data" "context" $) | nindent 10 }} + podAntiAffinity: {{- include "common.affinities.pods" (dict "type" .Values.data.podAntiAffinityPreset "component" "data" "context" $) | nindent 10 }} + nodeAffinity: {{- include "common.affinities.nodes" (dict "type" .Values.data.nodeAffinityPreset.type "key" .Values.data.nodeAffinityPreset.key "values" .Values.data.nodeAffinityPreset.values) | nindent 10 }} + {{- end }} + {{- if .Values.data.nodeSelector }} + nodeSelector: {{- include "common.tplvalues.render" (dict "value" .Values.data.nodeSelector "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.data.tolerations }} + tolerations: {{- include "common.tplvalues.render" (dict "value" .Values.data.tolerations "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.data.schedulerName }} + schedulerName: {{ .Values.data.schedulerName }} + {{- end }} + {{- if .Values.data.topologySpreadConstraints }} + topologySpreadConstraints: {{- include "common.tplvalues.render" (dict "value" .Values.data.topologySpreadConstraints "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.data.priorityClassName }} + priorityClassName: {{ .Values.data.priorityClassName | quote }} + {{- end }} + {{- if .Values.data.podSecurityContext.enabled }} + securityContext: {{- omit .Values.data.podSecurityContext "enabled" | toYaml | nindent 8 }} + {{- end }} + {{- if .Values.data.terminationGracePeriodSeconds }} + terminationGracePeriodSeconds: {{ .Values.data.terminationGracePeriodSeconds }} + {{- end }} + {{- if or .Values.data.initContainers .Values.sysctlImage.enabled (and .Values.volumePermissions.enabled .Values.data.persistence.enabled) }} + initContainers: + {{- if .Values.sysctlImage.enabled }} + ## Image that performs the sysctl operation to modify Kernel settings (needed sometimes to avoid boot errors) + - name: sysctl + image: {{ include "elasticsearch.sysctl.image" . }} + imagePullPolicy: {{ .Values.image.pullPolicy | quote }} + command: + - /bin/bash + - -ec + - | + {{- include "elasticsearch.sysctlIfLess" (dict "key" "vm.max_map_count" "value" "262144") | nindent 14 }} + {{- include "elasticsearch.sysctlIfLess" (dict "key" "fs.file-max" "value" "65536") | nindent 14 }} + securityContext: + privileged: true + runAsUser: 0 + {{- if .Values.sysctlImage.resources }} + resources: {{- toYaml .Values.sysctlImage.resources | nindent 12 }} + {{- end }} + {{- end }} + {{- if and .Values.volumePermissions.enabled .Values.data.persistence.enabled }} + - name: volume-permissions + image: {{ include "elasticsearch.volumePermissions.image" . }} + imagePullPolicy: {{ .Values.volumePermissions.image.pullPolicy | quote }} + command: + - /bin/bash + - -ec + - | + mkdir -p /bitnami/elasticsearch/data + chown {{ .Values.data.containerSecurityContext.runAsUser }}:{{ .Values.data.podSecurityContext.fsGroup }} /bitnami/elasticsearch/data + find /bitnami/elasticsearch/data -mindepth 1 -maxdepth 1 -not -name ".snapshot" -not -name "lost+found" | xargs -r chown -R {{ .Values.data.containerSecurityContext.runAsUser }}:{{ .Values.data.podSecurityContext.fsGroup }} + securityContext: + runAsUser: 0 + {{- if .Values.volumePermissions.resources }} + resources: {{- toYaml .Values.volumePermissions.resources | nindent 12 }} + {{- end }} + volumeMounts: + - name: data + mountPath: /bitnami/elasticsearch/data + {{- end }} + {{- if .Values.data.initContainers }} + {{- include "common.tplvalues.render" (dict "value" .Values.data.initContainers "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.initContainers }} + {{- include "common.tplvalues.render" (dict "value" .Values.initContainers "context" $) | nindent 8 }} + {{- end }} + {{- end }} + containers: + - name: elasticsearch + image: {{ include "elasticsearch.image" . }} + imagePullPolicy: {{ .Values.image.pullPolicy | quote }} + {{- if .Values.data.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.data.containerSecurityContext "enabled" | toYaml | nindent 12 }} + {{- end }} + {{- if .Values.diagnosticMode.enabled }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 12 }} + {{- else if .Values.data.command }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.data.command "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.diagnosticMode.enabled }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 12 }} + {{- else if .Values.data.args }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.data.args "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.data.lifecycleHooks }} + lifecycle: {{- include "common.tplvalues.render" (dict "value" .Values.data.lifecycleHooks "context" $) | nindent 12 }} + {{- end }} + env: + - name: BITNAMI_DEBUG + value: {{ ternary "true" "false" (or .Values.image.debug .Values.diagnosticMode.enabled) | quote }} + - name: MY_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: ELASTICSEARCH_IS_DEDICATED_NODE + value: "yes" + - name: ELASTICSEARCH_NODE_ROLES + value: "data" + - name: ELASTICSEARCH_TRANSPORT_PORT_NUMBER + value: {{ .Values.containerPorts.transport | quote }} + - name: ELASTICSEARCH_HTTP_PORT_NUMBER + value: {{ .Values.containerPorts.restAPI | quote }} + - name: ELASTICSEARCH_CLUSTER_NAME + value: {{ .Values.clusterName | quote }} + - name: ELASTICSEARCH_CLUSTER_HOSTS + value: {{ include "elasticsearch.hosts" . | quote }} + - name: ELASTICSEARCH_TOTAL_NODES + value: {{ add (ternary .Values.master.autoscaling.minReplicas .Values.master.replicaCount .Values.master.autoscaling.enabled) (ternary .Values.data.autoscaling.minReplicas .Values.data.replicaCount .Values.data.autoscaling.enabled) | quote }} + - name: ELASTICSEARCH_CLUSTER_MASTER_HOSTS + {{- $elasticsearchMasterFullname := include "elasticsearch.master.fullname" . }} + {{- $replicas := int (ternary .Values.master.autoscaling.minReplicas .Values.master.replicaCount .Values.master.autoscaling.enabled) }} + value: {{ range $i, $e := until $replicas }}{{ printf "%s-%d" $elasticsearchMasterFullname $e }} {{ end }} + - name: ELASTICSEARCH_MINIMUM_MASTER_NODES + value: {{ add (div (ternary .Values.master.autoscaling.minReplicas .Values.master.replicaCount .Values.master.autoscaling.enabled) 2) 1 | quote }} + - name: ELASTICSEARCH_ADVERTISED_HOSTNAME + value: "$(MY_POD_NAME).{{ (include "elasticsearch.data.servicename" .)}}.{{ include "common.names.namespace" . }}.svc.{{ .Values.clusterDomain }}" + {{- if .Values.plugins }} + - name: ELASTICSEARCH_PLUGINS + value: {{ .Values.plugins | quote }} + {{- end }} + {{- if .Values.snapshotRepoPath }} + - name: ELASTICSEARCH_FS_SNAPSHOT_REPO_PATH + value: {{ .Values.snapshotRepoPath | quote }} + {{- end }} + {{- if .Values.data.heapSize }} + - name: ELASTICSEARCH_HEAP_SIZE + value: {{ .Values.data.heapSize | quote }} + {{- end }} + {{- if .Values.security.enabled }} + {{- include "elasticsearch.configure.security" . | nindent 12 }} + {{- end }} + {{- if .Values.data.extraEnvVars }} + {{- include "common.tplvalues.render" ( dict "value" .Values.data.extraEnvVars "context" $ ) | nindent 12 }} + {{- end }} + {{- if .Values.extraEnvVars }} + {{- include "common.tplvalues.render" ( dict "value" .Values.extraEnvVars "context" $ ) | nindent 12 }} + {{- end }} + {{- if or .Values.extraEnvVarsCM .Values.extraEnvVarsSecret .Values.data.extraEnvVarsCM .Values.data.extraEnvVarsSecret }} + envFrom: + {{- if .Values.extraEnvVarsCM }} + - configMapRef: + name: {{ include "common.tplvalues.render" ( dict "value" .Values.extraEnvVarsCM "context" $ ) }} + {{- end }} + {{- if .Values.data.extraEnvVarsCM }} + - configMapRef: + name: {{ include "common.tplvalues.render" ( dict "value" .Values.data.extraEnvVarsCM "context" $ ) }} + {{- end }} + {{- if .Values.extraEnvVarsSecret }} + - secretRef: + name: {{ include "common.tplvalues.render" ( dict "value" .Values.extraEnvVarsSecret "context" $ ) }} + {{- end }} + {{- if .Values.data.extraEnvVarsSecret }} + - secretRef: + name: {{ include "common.tplvalues.render" ( dict "value" .Values.data.extraEnvVarsSecret "context" $ ) }} + {{- end }} + {{- end }} + ports: + - name: rest-api + containerPort: {{ .Values.containerPorts.restAPI }} + - name: transport + containerPort: {{ .Values.containerPorts.transport }} + {{- if not .Values.diagnosticMode.enabled }} + {{- if .Values.data.customStartupProbe }} + startupProbe: {{- include "common.tplvalues.render" (dict "value" .Values.data.customStartupProbe "context" $) | nindent 12 }} + {{- else if .Values.data.startupProbe.enabled }} + startupProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.data.startupProbe "enabled") "context" $) | nindent 12 }} + tcpSocket: + port: rest-api + {{- end }} + {{- if .Values.data.customLivenessProbe }} + livenessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.data.customLivenessProbe "context" $) | nindent 12 }} + {{- else if .Values.data.livenessProbe.enabled }} + livenessProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.data.livenessProbe "enabled") "context" $) | nindent 12 }} + exec: + command: + - /opt/bitnami/scripts/elasticsearch/healthcheck.sh + {{- end }} + {{- if .Values.data.customReadinessProbe }} + readinessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.data.customReadinessProbe "context" $) | nindent 12 }} + {{- else if .Values.data.readinessProbe.enabled }} + readinessProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.data.readinessProbe "enabled") "context" $) | nindent 12 }} + exec: + command: + - /opt/bitnami/scripts/elasticsearch/healthcheck.sh + {{- end }} + {{- end }} + {{- if .Values.data.resources }} + resources: {{- toYaml .Values.data.resources | nindent 12 }} + {{- end }} + volumeMounts: + - name: data + mountPath: /bitnami/elasticsearch/data + {{- if .Values.config }} + - mountPath: /opt/bitnami/elasticsearch/config/elasticsearch.yml + name: config + subPath: elasticsearch.yml + {{- end }} + {{- if .Values.extraConfig }} + - mountPath: /opt/bitnami/elasticsearch/config/my_elasticsearch.yml + name: config + subPath: my_elasticsearch.yml + {{- end }} + {{- if .Values.security.enabled }} + - name: elasticsearch-certificates + mountPath: /opt/bitnami/elasticsearch/config/certs + readOnly: true + {{- end }} + {{- if .Values.initScripts }} + - name: custom-init-scripts + mountPath: /docker-entrypoint-initdb.d/init-scripts + {{- end }} + {{- if .Values.initScriptsCM }} + - name: custom-init-scripts-cm + mountPath: /docker-entrypoint-initdb.d/init-scripts-cm + {{- end }} + {{- if .Values.initScriptsSecret }} + - name: custom-init-scripts-secret + mountPath: /docker-entrypoint-initdb.d/init-scripts-secret + {{- end }} + {{- if .Values.data.extraVolumeMounts }} + {{- include "common.tplvalues.render" (dict "value" .Values.data.extraVolumeMounts "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.extraVolumeMounts }} + {{- include "common.tplvalues.render" (dict "value" .Values.extraVolumeMounts "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.data.sidecars }} + {{- include "common.tplvalues.render" ( dict "value" .Values.data.sidecars "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.sidecars }} + {{- include "common.tplvalues.render" ( dict "value" .Values.sidecars "context" $) | nindent 8 }} + {{- end }} + volumes: + {{- if or .Values.config .Values.extraConfig }} + - name: config + configMap: + name: {{ template "common.names.fullname" . }} + {{- end }} + {{- if .Values.security.enabled }} + - name: elasticsearch-certificates + secret: + secretName: {{ template "elasticsearch.data.tlsSecretName" . }} + defaultMode: 256 + {{- end }} + {{- if .Values.initScripts }} + - name: custom-init-scripts + configMap: + name: {{ template "elasticsearch.initScripts" . }} + {{- end }} + {{- if .Values.initScriptsCM }} + - name: custom-init-scripts-cm + configMap: + name: {{ template "elasticsearch.initScriptsCM" . }} + {{- end }} + {{- if .Values.initScriptsSecret }} + - name: custom-init-scripts-secret + secret: + secretName: {{ template "elasticsearch.initScriptsSecret" . }} + defaultMode: 0755 + {{- end }} + {{- if .Values.data.extraVolumes }} + {{- include "common.tplvalues.render" (dict "value" .Values.data.extraVolumes "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.extraVolumes }} + {{- include "common.tplvalues.render" (dict "value" .Values.extraVolumes "context" $) | nindent 8 }} + {{- end }} + {{- if not .Values.data.persistence.enabled }} + - name: "data" + emptyDir: {} + {{- else if .Values.data.persistence.existingClaim }} + - name: "data" + persistentVolumeClaim: + claimName: {{ .Values.data.persistence.existingClaim }} + {{- else }} + volumeClaimTemplates: + - metadata: + name: "data" + annotations: + {{- if .Values.data.persistence.annotations }} + {{- include "common.tplvalues.render" (dict "value" .Values.data.persistence.annotations "context" $) | nindent 10 }} + {{- end }} + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" (dict "value" .Values.commonAnnotations "context" $) | nindent 10 }} + {{- end }} + {{- if .Values.commonLabels }} + labels: {{- include "common.tplvalues.render" (dict "value" .Values.commonLabels "context" $) | nindent 10 }} + {{- end }} + spec: + accessModes: + {{- range .Values.data.persistence.accessModes }} + - {{ . | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.data.persistence.size | quote }} + {{- if .Values.data.persistence.selector }} + selector: {{- include "common.tplvalues.render" (dict "value" .Values.data.persistence.selector "context" $) | nindent 10 }} + {{- else if .Values.data.persistence.existingVolume }} + selector: + matchLabels: + volume: {{ .Values.data.persistence.existingVolume }} + {{- end }} + {{- include "common.storage.class" (dict "persistence" .Values.data.persistence "global" .Values.global) | nindent 8 }} + {{- end }} +{{- end }} diff --git a/knowlg-automation/helm_charts/elasticsearch/templates/data/svc-headless.yaml b/knowlg-automation/helm_charts/elasticsearch/templates/data/svc-headless.yaml new file mode 100644 index 000000000..3d37d8e42 --- /dev/null +++ b/knowlg-automation/helm_charts/elasticsearch/templates/data/svc-headless.yaml @@ -0,0 +1,28 @@ +{{- if (include "elasticsearch.data.enabled" .) }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "elasticsearch.data.servicename" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: data + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" (dict "value" .Values.commonLabels "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" (dict "value" .Values.commonAnnotations "context" $) | nindent 4 }} + {{- end }} +spec: + type: ClusterIP + clusterIP: None + publishNotReadyAddresses: true + ports: + - name: tcp-rest-api + port: {{ .Values.containerPorts.restAPI }} + targetPort: rest-api + - name: tcp-transport + port: {{ .Values.containerPorts.transport }} + targetPort: transport + selector: {{- include "common.labels.matchLabels" . | nindent 4 }} + app.kubernetes.io/component: data +{{- end }} diff --git a/knowlg-automation/helm_charts/elasticsearch/templates/extra-list.yaml b/knowlg-automation/helm_charts/elasticsearch/templates/extra-list.yaml new file mode 100644 index 000000000..9ac65f9e1 --- /dev/null +++ b/knowlg-automation/helm_charts/elasticsearch/templates/extra-list.yaml @@ -0,0 +1,4 @@ +{{- range .Values.extraDeploy }} +--- +{{ include "common.tplvalues.render" (dict "value" . "context" $) }} +{{- end }} diff --git a/knowlg-automation/helm_charts/elasticsearch/templates/ingest/hpa.yaml b/knowlg-automation/helm_charts/elasticsearch/templates/ingest/hpa.yaml new file mode 100644 index 000000000..ef57de1bc --- /dev/null +++ b/knowlg-automation/helm_charts/elasticsearch/templates/ingest/hpa.yaml @@ -0,0 +1,47 @@ +{{- if and (include "elasticsearch.ingest.enabled" .) .Values.ingest.autoscaling.enabled }} +apiVersion: {{ include "common.capabilities.hpa.apiVersion" ( dict "context" $ ) }} +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "elasticsearch.ingest.fullname" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: ingest + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" (dict "value" .Values.commonLabels "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + scaleTargetRef: + apiVersion: {{ template "common.capabilities.statefulset.apiVersion" . }} + kind: StatefulSet + name: {{ include "elasticsearch.ingest.fullname" . }} + minReplicas: {{ .Values.ingest.autoscaling.minReplicas }} + maxReplicas: {{ .Values.ingest.autoscaling.maxReplicas }} + metrics: + {{- if .Values.ingest.autoscaling.targetCPU }} + - type: Resource + resource: + name: cpu + {{- if semverCompare "<1.23-0" (include "common.capabilities.kubeVersion" .) }} + targetAverageUtilization: {{ .Values.ingest.autoscaling.targetCPU }} + {{- else }} + target: + type: Utilization + averageUtilization: {{ .Values.ingest.autoscaling.targetCPU }} + {{- end }} + {{- end }} + {{- if .Values.ingest.autoscaling.targetMemory }} + - type: Resource + resource: + name: memory + {{- if semverCompare "<1.23-0" (include "common.capabilities.kubeVersion" .) }} + targetAverageUtilization: {{ .Values.ingest.autoscaling.targetMemory }} + {{- else }} + target: + type: Utilization + averageUtilization: {{ .Values.ingest.autoscaling.targetMemory }} + {{- end }} + {{- end }} +{{- end }} diff --git a/knowlg-automation/helm_charts/elasticsearch/templates/ingest/ingress.yaml b/knowlg-automation/helm_charts/elasticsearch/templates/ingest/ingress.yaml new file mode 100644 index 000000000..e4ad0a6d5 --- /dev/null +++ b/knowlg-automation/helm_charts/elasticsearch/templates/ingest/ingress.yaml @@ -0,0 +1,65 @@ +{{- if and (include "elasticsearch.ingest.enabled" .) .Values.ingest.service.enabled .Values.ingest.ingress.enabled }} +apiVersion: {{ include "common.capabilities.ingress.apiVersion" . }} +kind: Ingress +metadata: + name: {{ include "elasticsearch.ingest.fullname" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: ingest + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if or .Values.ingest.ingress.annotations .Values.commonAnnotations }} + annotations: + {{- if .Values.ingest.ingress.annotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.ingest.ingress.annotations "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- end }} +spec: + {{- if and .Values.ingest.ingress.ingressClassName (eq "true" (include "common.ingress.supportsIngressClassname" .)) }} + ingressClassName: {{ .Values.ingest.ingress.ingressClassName | quote }} + {{- end }} + rules: + {{- if .Values.ingest.ingress.hostname }} + - http: + paths: + {{- if .Values.ingest.ingress.extraPaths }} + {{- toYaml .Values.ingest.ingress.extraPaths | nindent 10 }} + {{- end }} + - path: {{ .Values.ingest.ingress.path }} + {{- if eq "true" (include "common.ingress.supportsPathType" .) }} + pathType: {{ .Values.ingest.ingress.pathType }} + {{- end }} + backend: {{- include "common.ingress.backend" (dict "serviceName" (include "elasticsearch.ingest.fullname" .) "servicePort" "tcp-rest-api" "context" $) | nindent 14 }} + {{- if ne .Values.ingest.ingress.hostname "*" }} + host: {{ .Values.ingest.ingress.hostname }} + {{- end }} + {{- end }} + {{- range .Values.ingest.ingress.extraHosts }} + - host: {{ .name }} + http: + paths: + - path: {{ default "/" .path }} + {{- if eq "true" (include "common.ingress.supportsPathType" $) }} + pathType: {{ default "ImplementationSpecific" .pathType }} + {{- end }} + backend: {{- include "common.ingress.backend" (dict "serviceName" (include "elasticsearch.ingest.fullname" $) "servicePort" "tcp-rest-api" "context" $) | nindent 14 }} + {{- end }} + {{- if .Values.ingest.ingress.extraRules }} + {{- include "common.tplvalues.render" ( dict "value" .Values.ingest.ingress.extraRules "context" $ ) | nindent 4 }} + {{- end }} + {{- if or (and .Values.ingest.ingress.tls (or (include "common.ingress.certManagerRequest" ( dict "annotations" .Values.ingest.ingress.annotations )) .Values.ingest.ingress.selfSigned)) .Values.ingest.ingress.extraTls }} + tls: + {{- if and .Values.ingest.ingress.tls (or (include "common.ingress.certManagerRequest" ( dict "annotations" .Values.ingest.ingress.annotations )) .Values.ingest.ingress.selfSigned) }} + - hosts: + - {{ .Values.ingest.ingress.hostname | quote }} + secretName: {{ printf "%s-tls" .Values.ingest.ingress.hostname }} + {{- end }} + {{- if .Values.ingest.ingress.extraTls }} + {{- include "common.tplvalues.render" (dict "value" .Values.ingest.ingress.extraTls "context" $) | nindent 4 }} + {{- end }} + {{- end }} +{{- end }} diff --git a/knowlg-automation/helm_charts/elasticsearch/templates/ingest/service.yaml b/knowlg-automation/helm_charts/elasticsearch/templates/ingest/service.yaml new file mode 100644 index 000000000..d18b67758 --- /dev/null +++ b/knowlg-automation/helm_charts/elasticsearch/templates/ingest/service.yaml @@ -0,0 +1,60 @@ +{{- if and (include "elasticsearch.ingest.enabled" .) .Values.ingest.service.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "elasticsearch.ingest.fullname" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: ingest + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + annotations: + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" (dict "value" .Values.commonAnnotations "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.ingest.service.annotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.ingest.service.annotations "context" $) | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.ingest.service.type }} + {{- if and .Values.ingest.service.clusterIP (eq .Values.ingest.service.type "ClusterIP") }} + clusterIP: {{ .Values.ingest.service.clusterIP }} + {{- end }} + {{- if or (eq .Values.ingest.service.type "LoadBalancer") (eq .Values.ingest.service.type "NodePort") }} + externalTrafficPolicy: {{ .Values.ingest.service.externalTrafficPolicy | quote }} + {{- end }} + {{- if and (eq .Values.ingest.service.type "LoadBalancer") .Values.ingest.service.loadBalancerSourceRanges }} + loadBalancerSourceRanges: {{- toYaml .Values.ingest.service.loadBalancerSourceRanges | nindent 4 }} + {{- end }} + {{- if (and (eq .Values.ingest.service.type "LoadBalancer") (not (empty .Values.ingest.service.loadBalancerIP))) }} + loadBalancerIP: {{ .Values.ingest.service.loadBalancerIP }} + {{- end }} + {{- if .Values.ingest.service.sessionAffinity }} + sessionAffinity: {{ .Values.ingest.service.sessionAffinity }} + {{- end }} + {{- if .Values.ingest.service.sessionAffinityConfig }} + sessionAffinityConfig: {{- include "common.tplvalues.render" (dict "value" .Values.ingest.service.sessionAffinityConfig "context" $) | nindent 4 }} + {{- end }} + ports: + - name: tcp-rest-api + port: {{ .Values.ingest.service.ports.restAPI }} + targetPort: rest-api + {{- if and (or (eq .Values.ingest.service.type "NodePort") (eq .Values.ingest.service.type "LoadBalancer")) (not (empty .Values.ingest.service.nodePorts.restAPI)) }} + nodePort: {{ .Values.ingest.service.nodePorts.restAPI }} + {{- else if eq .Values.ingest.service.type "ClusterIP" }} + nodePort: null + {{- end }} + - name: tcp-transport + port: {{ .Values.ingest.service.ports.transport }} + {{- if and (or (eq .Values.ingest.service.type "NodePort") (eq .Values.ingest.service.type "LoadBalancer")) (not (empty .Values.ingest.service.nodePorts.transport)) }} + nodePort: {{ .Values.ingest.service.nodePorts.transport }} + {{- else if eq .Values.ingest.service.type "ClusterIP" }} + nodePort: null + {{- end }} + {{- if .Values.ingest.service.extraPorts }} + {{- include "common.tplvalues.render" (dict "value" .Values.ingest.service.extraPorts "context" $) | nindent 4 }} + {{- end }} + selector: {{- include "common.labels.matchLabels" . | nindent 4 }} + app.kubernetes.io/component: ingest +{{- end }} diff --git a/knowlg-automation/helm_charts/elasticsearch/templates/ingest/serviceaccount.yaml b/knowlg-automation/helm_charts/elasticsearch/templates/ingest/serviceaccount.yaml new file mode 100644 index 000000000..a3bff3816 --- /dev/null +++ b/knowlg-automation/helm_charts/elasticsearch/templates/ingest/serviceaccount.yaml @@ -0,0 +1,22 @@ +{{- if and (include "elasticsearch.ingest.enabled" .) .Values.ingest.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "elasticsearch.ingest.serviceAccountName" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: ingest + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" (dict "value" .Values.commonLabels "context" $) | nindent 4 }} + {{- end }} + {{- if or .Values.ingest.serviceAccount.annotations .Values.commonAnnotations }} + annotations: + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" (dict "value" .Values.commonAnnotations "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.ingest.serviceAccount.annotations }} + {{- include "common.tplvalues.render" (dict "value" .Values.ingest.serviceAccount.annotations "context" $) | nindent 4 }} + {{- end }} + {{- end }} +automountServiceAccountToken: {{ .Values.ingest.serviceAccount.automountServiceAccountToken }} +{{- end -}} diff --git a/knowlg-automation/helm_charts/elasticsearch/templates/ingest/statefulset.yaml b/knowlg-automation/helm_charts/elasticsearch/templates/ingest/statefulset.yaml new file mode 100644 index 000000000..d39b85ad7 --- /dev/null +++ b/knowlg-automation/helm_charts/elasticsearch/templates/ingest/statefulset.yaml @@ -0,0 +1,313 @@ +{{- if (include "elasticsearch.ingest.enabled" . ) }} +apiVersion: {{ template "common.capabilities.statefulset.apiVersion" . }} +kind: StatefulSet +metadata: + name: {{ include "elasticsearch.ingest.fullname" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: ingest + {{- if .Values.useIstioLabels }} + ## Istio Labels: https://istio.io/docs/ops/deployment/requirements/ + app: ingest + {{- end }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if or .Values.ingest.annotations .Values.commonAnnotations }} + annotations: + {{- if .Values.ingest.annotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.ingest.annotations "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- end }} +spec: + {{- if not .Values.ingest.autoscaling.enabled }} + replicas: {{ .Values.ingest.replicaCount }} + {{- end }} + podManagementPolicy: {{ .Values.ingest.podManagementPolicy }} + selector: + matchLabels: {{- include "common.labels.matchLabels" . | nindent 6 }} + app.kubernetes.io/component: ingest + serviceName: {{ include "elasticsearch.ingest.servicename" . }} + {{- if .Values.ingest.updateStrategy }} + updateStrategy: {{- toYaml .Values.ingest.updateStrategy | nindent 4 }} + {{- end }} + template: + metadata: + labels: {{- include "common.labels.standard" . | nindent 8 }} + app.kubernetes.io/component: ingest + {{- if .Values.useIstioLabels }} + ## Istio Labels: https://istio.io/docs/ops/deployment/requirements/ + app: ingest + {{- end }} + {{- if .Values.ingest.podLabels }} + {{- include "common.tplvalues.render" (dict "value" .Values.ingest.podLabels "context" $) | nindent 8 }} + {{- end }} + annotations: + {{- if and (include "elasticsearch.createTlsSecret" .) (not .Values.security.tls.ingest.existingSecret) }} + checksum/tls: {{ include (print $.Template.BasePath "/tls-secret.yaml") . | sha256sum }} + {{- end }} + {{- if .Values.ingest.podAnnotations }} + {{- include "common.tplvalues.render" (dict "value" .Values.ingest.podAnnotations "context" $) | nindent 8 }} + {{- end }} + spec: + serviceAccountName: {{ template "elasticsearch.ingest.serviceAccountName" . }} + {{- include "elasticsearch.imagePullSecrets" . | nindent 6 }} + {{- if .Values.ingest.hostAliases }} + hostAliases: {{- include "common.tplvalues.render" (dict "value" .Values.ingest.hostAliases "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.ingest.affinity }} + affinity: {{- include "common.tplvalues.render" (dict "value" .Values.ingest.affinity "context" $) | nindent 8 }} + {{- else }} + affinity: + podAffinity: {{- include "common.affinities.pods" (dict "type" .Values.ingest.podAffinityPreset "component" "ingest" "context" $) | nindent 10 }} + podAntiAffinity: {{- include "common.affinities.pods" (dict "type" .Values.ingest.podAntiAffinityPreset "component" "ingest" "context" $) | nindent 10 }} + nodeAffinity: {{- include "common.affinities.nodes" (dict "type" .Values.ingest.nodeAffinityPreset.type "key" .Values.ingest.nodeAffinityPreset.key "values" .Values.ingest.nodeAffinityPreset.values) | nindent 10 }} + {{- end }} + {{- if .Values.ingest.nodeSelector }} + nodeSelector: {{- include "common.tplvalues.render" (dict "value" .Values.ingest.nodeSelector "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.ingest.tolerations }} + tolerations: {{- include "common.tplvalues.render" (dict "value" .Values.ingest.tolerations "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.ingest.schedulerName }} + schedulerName: {{ .Values.ingest.schedulerName }} + {{- end }} + {{- if .Values.ingest.topologySpreadConstraints }} + topologySpreadConstraints: {{- include "common.tplvalues.render" (dict "value" .Values.ingest.topologySpreadConstraints "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.ingest.priorityClassName }} + priorityClassName: {{ .Values.ingest.priorityClassName | quote }} + {{- end }} + {{- if .Values.ingest.podSecurityContext.enabled }} + securityContext: {{- omit .Values.ingest.podSecurityContext "enabled" | toYaml | nindent 8 }} + {{- end }} + {{- if .Values.ingest.terminationGracePeriodSeconds }} + terminationGracePeriodSeconds: {{ .Values.ingest.terminationGracePeriodSeconds }} + {{- end }} + {{- if or .Values.ingest.initContainers .Values.sysctlImage.enabled }} + initContainers: + {{- if .Values.sysctlImage.enabled }} + ## Image that performs the sysctl operation to modify Kernel settings (needed sometimes to avoid boot errors) + - name: sysctl + image: {{ include "elasticsearch.sysctl.image" . }} + imagePullPolicy: {{ .Values.image.pullPolicy | quote }} + command: + - /bin/bash + - -ec + - | + {{- include "elasticsearch.sysctlIfLess" (dict "key" "vm.max_map_count" "value" "262144") | nindent 14 }} + {{- include "elasticsearch.sysctlIfLess" (dict "key" "fs.file-max" "value" "65536") | nindent 14 }} + securityContext: + privileged: true + runAsUser: 0 + {{- if .Values.sysctlImage.resources }} + resources: {{- toYaml .Values.sysctlImage.resources | nindent 12 }} + {{- end }} + {{- end }} + {{- if .Values.ingest.initContainers }} + {{- include "common.tplvalues.render" (dict "value" .Values.ingest.initContainers "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.initContainers }} + {{- include "common.tplvalues.render" (dict "value" .Values.initContainers "context" $) | nindent 8 }} + {{- end }} + {{- end }} + containers: + - name: elasticsearch + image: {{ include "elasticsearch.image" . }} + imagePullPolicy: {{ .Values.image.pullPolicy | quote }} + {{- if .Values.ingest.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.ingest.containerSecurityContext "enabled" | toYaml | nindent 12 }} + {{- end }} + {{- if .Values.diagnosticMode.enabled }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 12 }} + {{- else if .Values.ingest.command }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.ingest.command "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.diagnosticMode.enabled }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 12 }} + {{- else if .Values.ingest.args }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.ingest.args "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.ingest.lifecycleHooks }} + lifecycle: {{- include "common.tplvalues.render" (dict "value" .Values.ingest.lifecycleHooks "context" $) | nindent 12 }} + {{- end }} + env: + - name: BITNAMI_DEBUG + value: {{ ternary "true" "false" (or .Values.image.debug .Values.diagnosticMode.enabled) | quote }} + - name: MY_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: ELASTICSEARCH_IS_DEDICATED_NODE + value: "yes" + - name: ELASTICSEARCH_NODE_ROLES + value: "ingest" + - name: ELASTICSEARCH_TRANSPORT_PORT_NUMBER + value: {{ .Values.containerPorts.transport | quote }} + - name: ELASTICSEARCH_HTTP_PORT_NUMBER + value: {{ .Values.containerPorts.restAPI | quote }} + - name: ELASTICSEARCH_CLUSTER_NAME + value: {{ .Values.clusterName | quote }} + - name: ELASTICSEARCH_CLUSTER_HOSTS + value: {{ include "elasticsearch.hosts" . | quote }} + - name: ELASTICSEARCH_TOTAL_NODES + value: {{ add (ternary .Values.master.autoscaling.minReplicas .Values.master.replicaCount .Values.master.autoscaling.enabled) (ternary .Values.data.autoscaling.minReplicas .Values.data.replicaCount .Values.data.autoscaling.enabled) | quote }} + - name: ELASTICSEARCH_CLUSTER_MASTER_HOSTS + {{- $elasticsearchMasterFullname := include "elasticsearch.master.fullname" . }} + {{- $replicas := int (ternary .Values.master.autoscaling.minReplicas .Values.master.replicaCount .Values.master.autoscaling.enabled) }} + value: {{ range $i, $e := until $replicas }}{{ printf "%s-%d" $elasticsearchMasterFullname $e }} {{ end }} + - name: ELASTICSEARCH_MINIMUM_MASTER_NODES + value: {{ add (div (ternary .Values.master.autoscaling.minReplicas .Values.master.replicaCount .Values.master.autoscaling.enabled) 2) 1 | quote }} + - name: ELASTICSEARCH_ADVERTISED_HOSTNAME + value: "$(MY_POD_NAME).{{ (include "elasticsearch.ingest.servicename" .)}}.{{ include "common.names.namespace" . }}.svc.{{ .Values.clusterDomain }}" + {{- if .Values.plugins }} + - name: ELASTICSEARCH_PLUGINS + value: {{ .Values.plugins | quote }} + {{- end }} + {{- if .Values.ingest.heapSize }} + - name: ELASTICSEARCH_HEAP_SIZE + value: {{ .Values.ingest.heapSize | quote }} + {{- end }} + {{- if .Values.security.enabled }} + {{- include "elasticsearch.configure.security" . | nindent 12 }} + {{- end }} + {{- if .Values.ingest.extraEnvVars }} + {{- include "common.tplvalues.render" ( dict "value" .Values.ingest.extraEnvVars "context" $ ) | nindent 12 }} + {{- end }} + {{- if .Values.extraEnvVars }} + {{- include "common.tplvalues.render" ( dict "value" .Values.extraEnvVars "context" $ ) | nindent 12 }} + {{- end }} + {{- if or .Values.extraEnvVarsCM .Values.extraEnvVarsSecret .Values.ingest.extraEnvVarsCM .Values.ingest.extraEnvVarsSecret }} + envFrom: + {{- if .Values.extraEnvVarsCM }} + - configMapRef: + name: {{ include "common.tplvalues.render" ( dict "value" .Values.extraEnvVarsCM "context" $ ) }} + {{- end }} + {{- if .Values.ingest.extraEnvVarsCM }} + - configMapRef: + name: {{ include "common.tplvalues.render" ( dict "value" .Values.ingest.extraEnvVarsCM "context" $ ) }} + {{- end }} + {{- if .Values.extraEnvVarsSecret }} + - secretRef: + name: {{ include "common.tplvalues.render" ( dict "value" .Values.extraEnvVarsSecret "context" $ ) }} + {{- end }} + {{- if .Values.ingest.extraEnvVarsSecret }} + - secretRef: + name: {{ include "common.tplvalues.render" ( dict "value" .Values.ingest.extraEnvVarsSecret "context" $ ) }} + {{- end }} + {{- end }} + ports: + - name: rest-api + containerPort: {{ .Values.containerPorts.restAPI }} + - name: transport + containerPort: {{ .Values.containerPorts.transport }} + {{- if not .Values.diagnosticMode.enabled }} + {{- if .Values.ingest.customStartupProbe }} + startupProbe: {{- include "common.tplvalues.render" (dict "value" .Values.ingest.customStartupProbe "context" $) | nindent 12 }} + {{- else if .Values.ingest.startupProbe.enabled }} + startupProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.ingest.startupProbe "enabled") "context" $) | nindent 12 }} + tcpSocket: + port: rest-api + {{- end }} + {{- if .Values.ingest.customLivenessProbe }} + livenessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.ingest.customLivenessProbe "context" $) | nindent 12 }} + {{- else if .Values.ingest.livenessProbe.enabled }} + livenessProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.ingest.livenessProbe "enabled") "context" $) | nindent 12 }} + exec: + command: + - /opt/bitnami/scripts/elasticsearch/healthcheck.sh + {{- end }} + {{- if .Values.ingest.customReadinessProbe }} + readinessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.ingest.customReadinessProbe "context" $) | nindent 12 }} + {{- else if .Values.ingest.readinessProbe.enabled }} + readinessProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.ingest.readinessProbe "enabled") "context" $) | nindent 12 }} + exec: + command: + - /opt/bitnami/scripts/elasticsearch/healthcheck.sh + {{- end }} + {{- end }} + {{- if .Values.ingest.resources }} + resources: {{- toYaml .Values.ingest.resources | nindent 12 }} + {{- end }} + volumeMounts: + - name: data + mountPath: /bitnami/elasticsearch/data + {{- if .Values.config }} + - mountPath: /opt/bitnami/elasticsearch/config/elasticsearch.yml + name: config + subPath: elasticsearch.yml + {{- end }} + {{- if .Values.extraConfig }} + - mountPath: /opt/bitnami/elasticsearch/config/my_elasticsearch.yml + name: config + subPath: my_elasticsearch.yml + {{- end }} + {{- if .Values.security.enabled }} + - name: elasticsearch-certificates + mountPath: /opt/bitnami/elasticsearch/config/certs + readOnly: true + {{- end }} + {{- if .Values.initScripts }} + - name: custom-init-scripts + mountPath: /docker-entrypoint-initdb.d/init-scripts + {{- end }} + {{- if .Values.initScriptsCM }} + - name: custom-init-scripts-cm + mountPath: /docker-entrypoint-initdb.d/init-scripts-cm + {{- end }} + {{- if .Values.initScriptsSecret }} + - name: custom-init-scripts-secret + mountPath: /docker-entrypoint-initdb.d/init-scripts-secret + {{- end }} + {{- if .Values.ingest.extraVolumeMounts }} + {{- include "common.tplvalues.render" (dict "value" .Values.ingest.extraVolumeMounts "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.extraVolumeMounts }} + {{- include "common.tplvalues.render" (dict "value" .Values.extraVolumeMounts "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.ingest.sidecars }} + {{- include "common.tplvalues.render" ( dict "value" .Values.ingest.sidecars "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.sidecars }} + {{- include "common.tplvalues.render" ( dict "value" .Values.sidecars "context" $) | nindent 8 }} + {{- end }} + volumes: + - name: "data" + emptyDir: {} + {{- if or .Values.config .Values.extraConfig }} + - name: config + configMap: + name: {{ template "common.names.fullname" . }} + {{- end }} + {{- if .Values.security.enabled }} + - name: elasticsearch-certificates + secret: + secretName: {{ template "elasticsearch.ingest.tlsSecretName" . }} + defaultMode: 256 + {{- end }} + {{- if .Values.initScripts }} + - name: custom-init-scripts + configMap: + name: {{ template "elasticsearch.initScripts" . }} + {{- end }} + {{- if .Values.initScriptsCM }} + - name: custom-init-scripts-cm + configMap: + name: {{ template "elasticsearch.initScriptsCM" . }} + {{- end }} + {{- if .Values.initScriptsSecret }} + - name: custom-init-scripts-secret + secret: + secretName: {{ template "elasticsearch.initScriptsSecret" . }} + defaultMode: 0755 + {{- end }} + {{- if .Values.ingest.extraVolumes }} + {{- include "common.tplvalues.render" (dict "value" .Values.ingest.extraVolumes "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.extraVolumes }} + {{- include "common.tplvalues.render" (dict "value" .Values.extraVolumes "context" $) | nindent 8 }} + {{- end }} +{{- end }} diff --git a/knowlg-automation/helm_charts/elasticsearch/templates/ingest/svc-headless.yaml b/knowlg-automation/helm_charts/elasticsearch/templates/ingest/svc-headless.yaml new file mode 100644 index 000000000..c0e0de401 --- /dev/null +++ b/knowlg-automation/helm_charts/elasticsearch/templates/ingest/svc-headless.yaml @@ -0,0 +1,28 @@ +{{- if (include "elasticsearch.ingest.enabled" .) }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "elasticsearch.ingest.servicename" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: ingest + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" (dict "value" .Values.commonLabels "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" (dict "value" .Values.commonAnnotations "context" $) | nindent 4 }} + {{- end }} +spec: + type: ClusterIP + clusterIP: None + publishNotReadyAddresses: true + ports: + - name: tcp-rest-api + port: {{ .Values.containerPorts.restAPI }} + targetPort: rest-api + - name: tcp-transport + port: {{ .Values.containerPorts.transport }} + targetPort: transport + selector: {{- include "common.labels.matchLabels" . | nindent 4 }} + app.kubernetes.io/component: ingest +{{- end }} diff --git a/knowlg-automation/helm_charts/elasticsearch/templates/ingress-tls-secrets.yaml b/knowlg-automation/helm_charts/elasticsearch/templates/ingress-tls-secrets.yaml new file mode 100644 index 000000000..50ae8c9ca --- /dev/null +++ b/knowlg-automation/helm_charts/elasticsearch/templates/ingress-tls-secrets.yaml @@ -0,0 +1,94 @@ +{{- if .Values.ingress.enabled }} +{{- if .Values.ingress.secrets }} +{{- range .Values.ingress.secrets }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ .name }} + namespace: {{ template "common.names.namespace" $ }} + labels: {{- include "common.labels.standard" $ | nindent 4 }} + {{- if $.Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" $.Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if $.Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" $.Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +type: kubernetes.io/tls +data: + tls.crt: {{ .certificate | b64enc }} + tls.key: {{ .key | b64enc }} +--- +{{- end }} +{{- end }} +{{- if and .Values.ingress.tls .Values.ingress.selfSigned }} +{{- $secretName := printf "%s-tls" .Values.ingress.hostname }} +{{- $ca := genCA "elasticsearch-ca" 365 }} +{{- $cert := genSignedCert .Values.ingress.hostname nil (list .Values.ingress.hostname) 365 $ca }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ $secretName }} + namespace: {{ template "common.names.namespace" . }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +type: kubernetes.io/tls +data: + tls.crt: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "tls.crt" "defaultValue" $cert.Cert "context" $) }} + tls.key: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "tls.key" "defaultValue" $cert.Key "context" $) }} + ca.crt: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "ca.crt" "defaultValue" $ca.Cert "context" $) }} +--- +{{- end }} +{{- end }} +{{- if .Values.ingest.ingress.enabled }} +{{- if .Values.ingest.ingress.secrets }} +{{- range .Values.ingest.ingress.secrets }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ .name }} + namespace: {{ printf "%s-ingest-ingress" (include "common.names.namespace" $ ) }} + labels: {{- include "common.labels.standard" $ | nindent 4 }} + app.kubernetes.io/component: ingest + {{- if $.Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" $.Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if $.Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" $.Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +type: kubernetes.io/tls +data: + tls.crt: {{ .certificate | b64enc }} + tls.key: {{ .key | b64enc }} +--- +{{- end }} +{{- end }} +{{- if and .Values.ingest.ingress.tls .Values.ingest.ingress.selfSigned }} +{{- $secretName := printf "%s-tls" .Values.ingest.ingress.hostname }} +{{- $ca := genCA "elasticsearch-ingest-ca" 365 }} +{{- $cert := genSignedCert .Values.ingest.ingress.hostname nil (list .Values.ingest.ingress.hostname) 365 $ca }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ $secretName }} + namespace: {{ template "common.names.namespace" $ }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: ingest + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +type: kubernetes.io/tls +data: + tls.crt: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "tls.crt" "defaultValue" $cert.Cert "context" $) }} + tls.key: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "tls.key" "defaultValue" $cert.Key "context" $) }} + ca.crt: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "ca.crt" "defaultValue" $ca.Cert "context" $) }} +--- +{{- end }} +{{- end }} diff --git a/knowlg-automation/helm_charts/elasticsearch/templates/ingress.yaml b/knowlg-automation/helm_charts/elasticsearch/templates/ingress.yaml new file mode 100644 index 000000000..c39dec2ea --- /dev/null +++ b/knowlg-automation/helm_charts/elasticsearch/templates/ingress.yaml @@ -0,0 +1,65 @@ +{{- if .Values.ingress.enabled }} +apiVersion: {{ include "common.capabilities.ingress.apiVersion" . }} +kind: Ingress +metadata: + name: {{ include "common.names.fullname" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: elasticsearch + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if or .Values.ingress.annotations .Values.commonAnnotations }} + annotations: + {{- if .Values.ingress.annotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.ingress.annotations "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- end }} +spec: + {{- if and .Values.ingress.ingressClassName (eq "true" (include "common.ingress.supportsIngressClassname" .)) }} + ingressClassName: {{ .Values.ingress.ingressClassName | quote }} + {{- end }} + rules: + {{- if .Values.ingress.hostname }} + - http: + paths: + {{- if .Values.ingress.extraPaths }} + {{- toYaml .Values.ingress.extraPaths | nindent 10 }} + {{- end }} + - path: {{ .Values.ingress.path }} + {{- if eq "true" (include "common.ingress.supportsPathType" .) }} + pathType: {{ .Values.ingress.pathType }} + {{- end }} + backend: {{- include "common.ingress.backend" (dict "serviceName" (include "common.names.fullname" .) "servicePort" "tcp-rest-api" "context" $) | nindent 14 }} + {{- if ne .Values.ingress.hostname "*" }} + host: {{ .Values.ingress.hostname }} + {{- end }} + {{- end }} + {{- range .Values.ingress.extraHosts }} + - host: {{ .name }} + http: + paths: + - path: {{ default "/" .path }} + {{- if eq "true" (include "common.ingress.supportsPathType" $) }} + pathType: {{ default "ImplementationSpecific" .pathType }} + {{- end }} + backend: {{- include "common.ingress.backend" (dict "serviceName" (include "common.names.fullname" $) "servicePort" "tcp-rest-api" "context" $) | nindent 14 }} + {{- end }} + {{- if .Values.ingress.extraRules }} + {{- include "common.tplvalues.render" ( dict "value" .Values.ingress.extraRules "context" $ ) | nindent 4 }} + {{- end }} + {{- if or (and .Values.ingress.tls (or (include "common.ingress.certManagerRequest" ( dict "annotations" .Values.ingress.annotations )) .Values.ingress.selfSigned)) .Values.ingress.extraTls }} + tls: + {{- if and .Values.ingress.tls (or (include "common.ingress.certManagerRequest" ( dict "annotations" .Values.ingress.annotations )) .Values.ingress.selfSigned) }} + - hosts: + - {{ .Values.ingress.hostname | quote }} + secretName: {{ printf "%s-tls" .Values.ingress.hostname }} + {{- end }} + {{- if .Values.ingress.extraTls }} + {{- include "common.tplvalues.render" (dict "value" .Values.ingress.extraTls "context" $) | nindent 4 }} + {{- end }} + {{- end }} +{{- end }} diff --git a/knowlg-automation/helm_charts/elasticsearch/templates/initialization-configmap.yaml b/knowlg-automation/helm_charts/elasticsearch/templates/initialization-configmap.yaml new file mode 100644 index 000000000..d84a353d6 --- /dev/null +++ b/knowlg-automation/helm_charts/elasticsearch/templates/initialization-configmap.yaml @@ -0,0 +1,16 @@ +{{- if .Values.initScripts }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ printf "%s-init-scripts" (include "common.names.fullname" .) }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +data: +{{- include "common.tplvalues.render" ( dict "value" .Values.initScripts "context" $ ) | nindent 4 }} +{{- end }} diff --git a/knowlg-automation/helm_charts/elasticsearch/templates/master/hpa.yaml b/knowlg-automation/helm_charts/elasticsearch/templates/master/hpa.yaml new file mode 100644 index 000000000..b41d43364 --- /dev/null +++ b/knowlg-automation/helm_charts/elasticsearch/templates/master/hpa.yaml @@ -0,0 +1,47 @@ +{{- if and (include "elasticsearch.master.enabled" .) .Values.master.autoscaling.enabled }} +apiVersion: {{ include "common.capabilities.hpa.apiVersion" ( dict "context" $ ) }} +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "elasticsearch.master.fullname" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: master + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" (dict "value" .Values.commonLabels "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + scaleTargetRef: + apiVersion: {{ template "common.capabilities.statefulset.apiVersion" . }} + kind: StatefulSet + name: {{ include "elasticsearch.master.fullname" . }} + minReplicas: {{ .Values.master.autoscaling.minReplicas }} + maxReplicas: {{ .Values.master.autoscaling.maxReplicas }} + metrics: + {{- if .Values.master.autoscaling.targetCPU }} + - type: Resource + resource: + name: cpu + {{- if semverCompare "<1.23-0" (include "common.capabilities.kubeVersion" .) }} + targetAverageUtilization: {{ .Values.master.autoscaling.targetCPU }} + {{- else }} + target: + type: Utilization + averageUtilization: {{ .Values.master.autoscaling.targetCPU }} + {{- end }} + {{- end }} + {{- if .Values.master.autoscaling.targetMemory }} + - type: Resource + resource: + name: memory + {{- if semverCompare "<1.23-0" (include "common.capabilities.kubeVersion" .) }} + targetAverageUtilization: {{ .Values.master.autoscaling.targetMemory }} + {{- else }} + target: + type: Utilization + averageUtilization: {{ .Values.master.autoscaling.targetMemory }} + {{- end }} + {{- end }} +{{- end }} diff --git a/knowlg-automation/helm_charts/elasticsearch/templates/master/serviceaccount.yaml b/knowlg-automation/helm_charts/elasticsearch/templates/master/serviceaccount.yaml new file mode 100644 index 000000000..a6fefec01 --- /dev/null +++ b/knowlg-automation/helm_charts/elasticsearch/templates/master/serviceaccount.yaml @@ -0,0 +1,22 @@ +{{- if and (include "elasticsearch.master.enabled" .) .Values.master.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "elasticsearch.master.serviceAccountName" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: master + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" (dict "value" .Values.commonLabels "context" $) | nindent 4 }} + {{- end }} + {{- if or .Values.master.serviceAccount.annotations .Values.commonAnnotations }} + annotations: + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" (dict "value" .Values.commonAnnotations "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.master.serviceAccount.annotations }} + {{- include "common.tplvalues.render" (dict "value" .Values.master.serviceAccount.annotations "context" $) | nindent 4 }} + {{- end }} + {{- end }} +automountServiceAccountToken: {{ .Values.master.serviceAccount.automountServiceAccountToken }} +{{- end -}} diff --git a/knowlg-automation/helm_charts/elasticsearch/templates/master/statefulset.yaml b/knowlg-automation/helm_charts/elasticsearch/templates/master/statefulset.yaml new file mode 100644 index 000000000..88c6e6671 --- /dev/null +++ b/knowlg-automation/helm_charts/elasticsearch/templates/master/statefulset.yaml @@ -0,0 +1,373 @@ +{{- if (include "elasticsearch.master.enabled" .) }} +apiVersion: {{ template "common.capabilities.statefulset.apiVersion" . }} +kind: StatefulSet +metadata: + name: {{ include "elasticsearch.master.fullname" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: master + {{- if .Values.useIstioLabels }} + ## Istio Labels: https://istio.io/docs/ops/deployment/requirements/ + app: master + {{- end }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if or .Values.master.annotations .Values.commonAnnotations }} + annotations: + {{- if .Values.master.annotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.master.annotations "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- end }} +spec: + {{- if not .Values.master.autoscaling.enabled }} + replicas: {{ .Values.master.replicaCount }} + {{- end }} + podManagementPolicy: {{ .Values.master.podManagementPolicy }} + selector: + matchLabels: {{- include "common.labels.matchLabels" . | nindent 6 }} + app.kubernetes.io/component: master + serviceName: {{ include "elasticsearch.master.servicename" . }} + {{- if .Values.master.updateStrategy }} + updateStrategy: {{- toYaml .Values.master.updateStrategy | nindent 4 }} + {{- end }} + template: + metadata: + labels: {{- include "common.labels.standard" . | nindent 8 }} + app.kubernetes.io/component: master + {{- if .Values.useIstioLabels }} + ## Istio Labels: https://istio.io/docs/ops/deployment/requirements/ + app: master + {{- end }} + {{- if .Values.master.podLabels }} + {{- include "common.tplvalues.render" (dict "value" .Values.master.podLabels "context" $) | nindent 8 }} + {{- end }} + annotations: + {{- if and (include "elasticsearch.createTlsSecret" .) (not .Values.security.tls.master.existingSecret) }} + checksum/tls: {{ include (print $.Template.BasePath "/tls-secret.yaml") . | sha256sum }} + {{- end }} + {{- if .Values.master.podAnnotations }} + {{- include "common.tplvalues.render" (dict "value" .Values.master.podAnnotations "context" $) | nindent 8 }} + {{- end }} + spec: + serviceAccountName: {{ template "elasticsearch.master.serviceAccountName" . }} + {{- include "elasticsearch.imagePullSecrets" . | nindent 6 }} + {{- if .Values.master.hostAliases }} + hostAliases: {{- include "common.tplvalues.render" (dict "value" .Values.master.hostAliases "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.master.affinity }} + affinity: {{- include "common.tplvalues.render" (dict "value" .Values.master.affinity "context" $) | nindent 8 }} + {{- else }} + affinity: + podAffinity: {{- include "common.affinities.pods" (dict "type" .Values.master.podAffinityPreset "component" "master" "context" $) | nindent 10 }} + podAntiAffinity: {{- include "common.affinities.pods" (dict "type" .Values.master.podAntiAffinityPreset "component" "master" "context" $) | nindent 10 }} + nodeAffinity: {{- include "common.affinities.nodes" (dict "type" .Values.master.nodeAffinityPreset.type "key" .Values.master.nodeAffinityPreset.key "values" .Values.master.nodeAffinityPreset.values) | nindent 10 }} + {{- end }} + {{- if .Values.master.nodeSelector }} + nodeSelector: {{- include "common.tplvalues.render" (dict "value" .Values.master.nodeSelector "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.master.tolerations }} + tolerations: {{- include "common.tplvalues.render" (dict "value" .Values.master.tolerations "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.master.schedulerName }} + schedulerName: {{ .Values.master.schedulerName }} + {{- end }} + {{- if .Values.master.topologySpreadConstraints }} + topologySpreadConstraints: {{- include "common.tplvalues.render" (dict "value" .Values.master.topologySpreadConstraints "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.master.priorityClassName }} + priorityClassName: {{ .Values.master.priorityClassName | quote }} + {{- end }} + {{- if .Values.master.podSecurityContext.enabled }} + securityContext: {{- omit .Values.master.podSecurityContext "enabled" | toYaml | nindent 8 }} + {{- end }} + {{- if .Values.master.terminationGracePeriodSeconds }} + terminationGracePeriodSeconds: {{ .Values.master.terminationGracePeriodSeconds }} + {{- end }} + {{- if or .Values.master.initContainers .Values.sysctlImage.enabled (and .Values.volumePermissions.enabled .Values.master.persistence.enabled) }} + initContainers: + {{- if .Values.sysctlImage.enabled }} + ## Image that performs the sysctl operation to modify Kernel settings (needed sometimes to avoid boot errors) + - name: sysctl + image: {{ include "elasticsearch.sysctl.image" . }} + imagePullPolicy: {{ .Values.image.pullPolicy | quote }} + command: + - /bin/bash + - -ec + - | + {{- include "elasticsearch.sysctlIfLess" (dict "key" "vm.max_map_count" "value" "262144") | nindent 14 }} + {{- include "elasticsearch.sysctlIfLess" (dict "key" "fs.file-max" "value" "65536") | nindent 14 }} + securityContext: + privileged: true + runAsUser: 0 + {{- if .Values.sysctlImage.resources }} + resources: {{- toYaml .Values.sysctlImage.resources | nindent 12 }} + {{- end }} + {{- end }} + {{- if and .Values.volumePermissions.enabled .Values.master.persistence.enabled }} + - name: volume-permissions + image: {{ include "elasticsearch.volumePermissions.image" . }} + imagePullPolicy: {{ .Values.volumePermissions.image.pullPolicy | quote }} + command: + - /bin/bash + - -ec + - | + mkdir -p /bitnami/elasticsearch/data + chown {{ .Values.master.containerSecurityContext.runAsUser }}:{{ .Values.master.podSecurityContext.fsGroup }} /bitnami/elasticsearch/data + find /bitnami/elasticsearch/data -mindepth 1 -maxdepth 1 -not -name ".snapshot" -not -name "lost+found" | xargs -r chown -R {{ .Values.master.containerSecurityContext.runAsUser }}:{{ .Values.master.podSecurityContext.fsGroup }} + securityContext: + runAsUser: 0 + {{- if .Values.volumePermissions.resources }} + resources: {{- toYaml .Values.volumePermissions.resources | nindent 12 }} + {{- end }} + volumeMounts: + - name: data + mountPath: /bitnami/elasticsearch/data + {{- end }} + {{- if .Values.master.initContainers }} + {{- include "common.tplvalues.render" (dict "value" .Values.master.initContainers "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.initContainers }} + {{- include "common.tplvalues.render" (dict "value" .Values.initContainers "context" $) | nindent 8 }} + {{- end }} + {{- end }} + containers: + - name: elasticsearch + image: {{ include "elasticsearch.image" . }} + imagePullPolicy: {{ .Values.image.pullPolicy | quote }} + {{- if .Values.master.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.master.containerSecurityContext "enabled" | toYaml | nindent 12 }} + {{- end }} + {{- if .Values.diagnosticMode.enabled }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 12 }} + {{- else if .Values.master.command }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.master.command "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.diagnosticMode.enabled }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 12 }} + {{- else if .Values.master.args }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.master.args "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.master.lifecycleHooks }} + lifecycle: {{- include "common.tplvalues.render" (dict "value" .Values.master.lifecycleHooks "context" $) | nindent 12 }} + {{- end }} + env: + - name: BITNAMI_DEBUG + value: {{ ternary "true" "false" (or .Values.image.debug .Values.diagnosticMode.enabled) | quote }} + - name: MY_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: ELASTICSEARCH_IS_DEDICATED_NODE + value: {{ ternary "yes" "no" .Values.master.masterOnly | quote }} + - name: ELASTICSEARCH_NODE_ROLES + value: "master" + - name: ELASTICSEARCH_TRANSPORT_PORT_NUMBER + value: {{ .Values.containerPorts.transport | quote }} + - name: ELASTICSEARCH_HTTP_PORT_NUMBER + value: {{ .Values.containerPorts.restAPI | quote }} + - name: ELASTICSEARCH_CLUSTER_NAME + value: {{ .Values.clusterName | quote }} + - name: ELASTICSEARCH_CLUSTER_HOSTS + value: {{ include "elasticsearch.hosts" . | quote }} + - name: ELASTICSEARCH_TOTAL_NODES + value: {{ add (ternary .Values.master.autoscaling.minReplicas .Values.master.replicaCount .Values.master.autoscaling.enabled) (ternary .Values.data.autoscaling.minReplicas .Values.data.replicaCount .Values.data.autoscaling.enabled) | quote }} + - name: ELASTICSEARCH_CLUSTER_MASTER_HOSTS + {{- $elasticsearchMasterFullname := include "elasticsearch.master.fullname" . }} + {{- $replicas := int (ternary .Values.master.autoscaling.minReplicas .Values.master.replicaCount .Values.master.autoscaling.enabled) }} + value: {{ range $i, $e := until $replicas }}{{ printf "%s-%d" $elasticsearchMasterFullname $e }} {{ end }} + - name: ELASTICSEARCH_MINIMUM_MASTER_NODES + value: {{ add (div (ternary .Values.master.autoscaling.minReplicas .Values.master.replicaCount .Values.master.autoscaling.enabled) 2) 1 | quote }} + - name: ELASTICSEARCH_ADVERTISED_HOSTNAME + value: "$(MY_POD_NAME).{{ (include "elasticsearch.master.servicename" .) | trunc 63 | trimSuffix "-" }}.{{ include "common.names.namespace" . }}.svc.{{ .Values.clusterDomain }}" + {{- if .Values.plugins }} + - name: ELASTICSEARCH_PLUGINS + value: {{ .Values.plugins | quote }} + {{- end }} + {{- if .Values.snapshotRepoPath }} + - name: ELASTICSEARCH_FS_SNAPSHOT_REPO_PATH + value: {{ .Values.snapshotRepoPath | quote }} + {{- end }} + {{- if .Values.master.heapSize }} + - name: ELASTICSEARCH_HEAP_SIZE + value: {{ .Values.master.heapSize | quote }} + {{- end }} + {{- if .Values.security.enabled }} + {{- include "elasticsearch.configure.security" . | nindent 12 }} + {{- end }} + {{- if .Values.master.extraEnvVars }} + {{- include "common.tplvalues.render" ( dict "value" .Values.master.extraEnvVars "context" $ ) | nindent 12 }} + {{- end }} + {{- if .Values.extraEnvVars }} + {{- include "common.tplvalues.render" ( dict "value" .Values.extraEnvVars "context" $ ) | nindent 12 }} + {{- end }} + {{- if or .Values.extraEnvVarsCM .Values.extraEnvVarsSecret .Values.master.extraEnvVarsCM .Values.master.extraEnvVarsSecret }} + envFrom: + {{- if .Values.extraEnvVarsCM }} + - configMapRef: + name: {{ include "common.tplvalues.render" ( dict "value" .Values.extraEnvVarsCM "context" $ ) }} + {{- end }} + {{- if .Values.master.extraEnvVarsCM }} + - configMapRef: + name: {{ include "common.tplvalues.render" ( dict "value" .Values.master.extraEnvVarsCM "context" $ ) }} + {{- end }} + {{- if .Values.extraEnvVarsSecret }} + - secretRef: + name: {{ include "common.tplvalues.render" ( dict "value" .Values.extraEnvVarsSecret "context" $ ) }} + {{- end }} + {{- if .Values.master.extraEnvVarsSecret }} + - secretRef: + name: {{ include "common.tplvalues.render" ( dict "value" .Values.master.extraEnvVarsSecret "context" $ ) }} + {{- end }} + {{- end }} + ports: + - name: rest-api + containerPort: {{ .Values.containerPorts.restAPI }} + - name: transport + containerPort: {{ .Values.containerPorts.transport }} + {{- if not .Values.diagnosticMode.enabled }} + {{- if .Values.master.customStartupProbe }} + startupProbe: {{- include "common.tplvalues.render" (dict "value" .Values.master.customStartupProbe "context" $) | nindent 12 }} + {{- else if .Values.master.startupProbe.enabled }} + startupProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.master.startupProbe "enabled") "context" $) | nindent 12 }} + tcpSocket: + port: rest-api + {{- end }} + {{- if .Values.master.customLivenessProbe }} + livenessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.master.customLivenessProbe "context" $) | nindent 12 }} + {{- else if .Values.master.livenessProbe.enabled }} + livenessProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.master.livenessProbe "enabled") "context" $) | nindent 12 }} + exec: + command: + - /opt/bitnami/scripts/elasticsearch/healthcheck.sh + {{- end }} + {{- if .Values.master.customReadinessProbe }} + readinessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.master.customReadinessProbe "context" $) | nindent 12 }} + {{- else if .Values.master.readinessProbe.enabled }} + readinessProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.master.readinessProbe "enabled") "context" $) | nindent 12 }} + exec: + command: + - /opt/bitnami/scripts/elasticsearch/healthcheck.sh + {{- end }} + {{- end }} + {{- if .Values.master.resources }} + resources: {{- toYaml .Values.master.resources | nindent 12 }} + {{- end }} + volumeMounts: + - name: data + mountPath: /bitnami/elasticsearch/data + {{- if .Values.config }} + - mountPath: /opt/bitnami/elasticsearch/config/elasticsearch.yml + name: config + subPath: elasticsearch.yml + {{- end }} + {{- if .Values.extraConfig }} + - mountPath: /opt/bitnami/elasticsearch/config/my_elasticsearch.yml + name: config + subPath: my_elasticsearch.yml + {{- end }} + {{- if .Values.security.enabled }} + - name: elasticsearch-certificates + mountPath: /opt/bitnami/elasticsearch/config/certs + readOnly: true + {{- end }} + {{- if .Values.initScripts }} + - name: custom-init-scripts + mountPath: /docker-entrypoint-initdb.d/init-scripts + {{- end }} + {{- if .Values.initScriptsCM }} + - name: custom-init-scripts-cm + mountPath: /docker-entrypoint-initdb.d/init-scripts-cm + {{- end }} + {{- if .Values.initScriptsSecret }} + - name: custom-init-scripts-secret + mountPath: /docker-entrypoint-initdb.d/init-scripts-secret + {{- end }} + {{- if .Values.master.extraVolumeMounts }} + {{- include "common.tplvalues.render" (dict "value" .Values.master.extraVolumeMounts "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.extraVolumeMounts }} + {{- include "common.tplvalues.render" (dict "value" .Values.extraVolumeMounts "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.master.sidecars }} + {{- include "common.tplvalues.render" ( dict "value" .Values.master.sidecars "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.sidecars }} + {{- include "common.tplvalues.render" ( dict "value" .Values.sidecars "context" $) | nindent 8 }} + {{- end }} + volumes: + {{- if or .Values.config .Values.extraConfig }} + - name: config + configMap: + name: {{ template "common.names.fullname" . }} + {{- end }} + {{- if .Values.security.enabled }} + - name: elasticsearch-certificates + secret: + secretName: {{ template "elasticsearch.master.tlsSecretName" . }} + defaultMode: 256 + {{- end }} + {{- if .Values.initScripts }} + - name: custom-init-scripts + configMap: + name: {{ template "elasticsearch.initScripts" . }} + {{- end }} + {{- if .Values.initScriptsCM }} + - name: custom-init-scripts-cm + configMap: + name: {{ template "elasticsearch.initScriptsCM" . }} + {{- end }} + {{- if .Values.initScriptsSecret }} + - name: custom-init-scripts-secret + secret: + secretName: {{ template "elasticsearch.initScriptsSecret" . }} + defaultMode: 0755 + {{- end }} + {{- if .Values.master.extraVolumes }} + {{- include "common.tplvalues.render" (dict "value" .Values.master.extraVolumes "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.extraVolumes }} + {{- include "common.tplvalues.render" (dict "value" .Values.extraVolumes "context" $) | nindent 8 }} + {{- end }} + {{- if not .Values.master.persistence.enabled }} + - name: "data" + emptyDir: {} + {{- else if .Values.master.persistence.existingClaim }} + - name: "data" + persistentVolumeClaim: + claimName: {{ .Values.master.persistence.existingClaim }} + {{- else }} + volumeClaimTemplates: + - metadata: + name: "data" + annotations: + {{- if .Values.master.persistence.annotations }} + {{- include "common.tplvalues.render" (dict "value" .Values.master.persistence.annotations "context" $) | nindent 10 }} + {{- end }} + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" (dict "value" .Values.commonAnnotations "context" $) | nindent 10 }} + {{- end }} + {{- if .Values.commonLabels }} + labels: {{- include "common.tplvalues.render" (dict "value" .Values.commonLabels "context" $) | nindent 10 }} + {{- end }} + spec: + accessModes: + {{- range .Values.master.persistence.accessModes }} + - {{ . | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.master.persistence.size | quote }} + {{- if .Values.master.persistence.selector }} + selector: {{- include "common.tplvalues.render" (dict "value" .Values.master.persistence.selector "context" $) | nindent 10 }} + {{- else if .Values.master.persistence.existingVolume }} + selector: + matchLabels: + volume: {{ .Values.master.persistence.existingVolume }} + {{- end }} + {{- include "common.storage.class" (dict "persistence" .Values.master.persistence "global" .Values.global) | nindent 8 }} + {{- end }} +{{- end }} diff --git a/knowlg-automation/helm_charts/elasticsearch/templates/master/svc-headless.yaml b/knowlg-automation/helm_charts/elasticsearch/templates/master/svc-headless.yaml new file mode 100644 index 000000000..7b73ad59a --- /dev/null +++ b/knowlg-automation/helm_charts/elasticsearch/templates/master/svc-headless.yaml @@ -0,0 +1,28 @@ +{{- if (include "elasticsearch.master.enabled" .) }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "elasticsearch.master.servicename" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{ include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: master + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" (dict "value" .Values.commonLabels "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" (dict "value" .Values.commonAnnotations "context" $) | nindent 4 }} + {{- end }} +spec: + type: ClusterIP + clusterIP: None + publishNotReadyAddresses: true + ports: + - name: tcp-rest-api + port: {{ .Values.containerPorts.restAPI }} + targetPort: rest-api + - name: tcp-transport + port: {{ .Values.containerPorts.transport }} + targetPort: transport + selector: {{- include "common.labels.matchLabels" . | nindent 4 }} + app.kubernetes.io/component: master +{{- end }} diff --git a/knowlg-automation/helm_charts/elasticsearch/templates/metrics/deployment.yaml b/knowlg-automation/helm_charts/elasticsearch/templates/metrics/deployment.yaml new file mode 100644 index 000000000..7ffa1e3b2 --- /dev/null +++ b/knowlg-automation/helm_charts/elasticsearch/templates/metrics/deployment.yaml @@ -0,0 +1,182 @@ +{{- if .Values.metrics.enabled }} +apiVersion: {{ template "common.capabilities.deployment.apiVersion" . }} +kind: Deployment +metadata: + name: {{ include "elasticsearch.metrics.fullname" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: metrics + {{- if .Values.useIstioLabels }} + ## Istio Labels: https://istio.io/docs/ops/deployment/requirements/ + app: metrics + {{- end }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + annotations: + {{- if .Values.metrics.annotations }} + {{- include "common.tplvalues.render" (dict "value" .Values.metrics.annotations "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + replicas: 1 + selector: + matchLabels: {{- include "common.labels.matchLabels" . | nindent 6 }} + app.kubernetes.io/component: metrics + template: + metadata: + labels: {{- include "common.labels.standard" . | nindent 8 }} + app.kubernetes.io/component: metrics + {{- if .Values.useIstioLabels }} + ## Istio Labels: https://istio.io/docs/ops/deployment/requirements/ + app: metrics + {{- end }} + {{- if .Values.metrics.podLabels }} + {{- include "common.tplvalues.render" (dict "value" .Values.metrics.podLabels "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.metrics.podAnnotations }} + annotations: {{- include "common.tplvalues.render" (dict "value" .Values.metrics.podAnnotations "context" $) | nindent 8 }} + {{- end }} + spec: + {{- include "elasticsearch.imagePullSecrets" . | nindent 6 }} + {{- if .Values.metrics.hostAliases }} + hostAliases: {{- include "common.tplvalues.render" (dict "value" .Values.metrics.hostAliases "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.metrics.affinity }} + affinity: {{- include "common.tplvalues.render" (dict "value" .Values.metrics.affinity "context" $) | nindent 8 }} + {{- else }} + affinity: + podAffinity: {{- include "common.affinities.pods" (dict "type" .Values.metrics.podAffinityPreset "component" "metrics" "context" $) | nindent 10 }} + podAntiAffinity: {{- include "common.affinities.pods" (dict "type" .Values.metrics.podAntiAffinityPreset "component" "metrics" "context" $) | nindent 10 }} + nodeAffinity: {{- include "common.affinities.nodes" (dict "type" .Values.metrics.nodeAffinityPreset.type "key" .Values.metrics.nodeAffinityPreset.key "values" .Values.metrics.nodeAffinityPreset.values) | nindent 10 }} + {{- end }} + {{- if .Values.metrics.nodeSelector }} + nodeSelector: {{- include "common.tplvalues.render" (dict "value" .Values.metrics.nodeSelector "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.metrics.tolerations }} + tolerations: {{- include "common.tplvalues.render" (dict "value" .Values.metrics.tolerations "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.metrics.priorityClassName }} + priorityClassName: {{ .Values.metrics.priorityClassName | quote }} + {{- end }} + {{- if .Values.metrics.schedulerName }} + schedulerName: {{ .Values.metrics.schedulerName | quote }} + {{- end }} + {{- if .Values.metrics.podSecurityContext.enabled }} + securityContext: {{- omit .Values.metrics.podSecurityContext "enabled" | toYaml | nindent 8 }} + {{- end }} + {{- if .Values.metrics.topologySpreadConstraints }} + topologySpreadConstraints: {{- include "common.tplvalues.render" (dict "value" .Values.metrics.topologySpreadConstraints "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.metrics.initContainers }} + initContainers: {{- include "common.tplvalues.render" (dict "value" .Values.metrics.initContainers "context" $) | nindent 8 }} + {{- end }} + containers: + - name: metrics + image: {{ include "elasticsearch.metrics.image" . }} + imagePullPolicy: {{ .Values.metrics.image.pullPolicy | quote }} + {{- if .Values.metrics.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.metrics.containerSecurityContext "enabled" | toYaml | nindent 12 }} + {{- end }} + {{- if .Values.diagnosticMode.enabled }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 12 }} + {{- else }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.metrics.command "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.diagnosticMode.enabled }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 12 }} + {{- else if .Values.metrics.args }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.metrics.args "context" $) | nindent 12 }} + {{- else }} + args: + {{- $protocol := (ternary "https" "http" (and .Values.security.enabled .Values.security.tls.restEncryption)) }} + - --es.uri={{$protocol}}://{{ include "common.names.fullname" . }}:{{ include "elasticsearch.service.ports.restAPI" . }} + - --es.all + {{- if .Values.security.tls.restEncryption }} + - --es.ssl-skip-verify + {{- end }} + {{- if .Values.metrics.extraArgs }} + {{- include "common.tplvalues.render" (dict "value" .Values.metrics.extraArgs "context" $) | nindent 12 }} + {{- end }} + {{- end }} + env: + {{- if .Values.security.enabled }} + - name: ES_USERNAME + value: "elastic" + - name: ES_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "elasticsearch.secretName" . }} + key: elasticsearch-password + {{- end }} + {{- if .Values.metrics.extraEnvVars }} + {{- include "common.tplvalues.render" (dict "value" .Values.metrics.extraEnvVars "context" $) | nindent 12 }} + {{- end }} + envFrom: + {{- if .Values.metrics.extraEnvVarsCM }} + - configMapRef: + name: {{ include "common.tplvalues.render" (dict "value" .Values.metrics.extraEnvVarsCM "context" $) }} + {{- end }} + {{- if .Values.metrics.extraEnvVarsSecret }} + - secretRef: + name: {{ include "common.tplvalues.render" (dict "value" .Values.metrics.extraEnvVarsSecret "context" $) }} + {{- end }} + ports: + - name: metrics + containerPort: 9114 + {{- if not .Values.diagnosticMode.enabled }} + {{- if .Values.metrics.customLivenessProbe }} + livenessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.metrics.customLivenessProbe "context" $) | nindent 12 }} + {{- else if .Values.metrics.livenessProbe.enabled }} + livenessProbe: + initialDelaySeconds: {{ .Values.metrics.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.metrics.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.metrics.livenessProbe.timeoutSeconds }} + successThreshold: {{ .Values.metrics.livenessProbe.successThreshold }} + failureThreshold: {{ .Values.metrics.livenessProbe.failureThreshold }} + httpGet: + path: /metrics + port: metrics + {{- end }} + {{- if .Values.metrics.customReadinessProbe }} + readinessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.metrics.customReadinessProbe "context" $) | nindent 12 }} + {{- else if .Values.metrics.readinessProbe.enabled }} + readinessProbe: + initialDelaySeconds: {{ .Values.metrics.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.metrics.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.metrics.readinessProbe.timeoutSeconds }} + successThreshold: {{ .Values.metrics.readinessProbe.successThreshold }} + failureThreshold: {{ .Values.metrics.readinessProbe.failureThreshold }} + httpGet: + path: /metrics + port: metrics + {{- end }} + {{- if .Values.metrics.customStartupProbe }} + startupProbe: {{- include "common.tplvalues.render" (dict "value" .Values.metrics.customStartupProbe "context" $) | nindent 12 }} + {{- else if .Values.metrics.startupProbe.enabled }} + startupProbe: + initialDelaySeconds: {{ .Values.metrics.startupProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.metrics.startupProbe.periodSeconds }} + timeoutSeconds: {{ .Values.metrics.startupProbe.timeoutSeconds }} + successThreshold: {{ .Values.metrics.startupProbe.successThreshold }} + failureThreshold: {{ .Values.metrics.startupProbe.failureThreshold }} + httpGet: + path: /metrics + port: metrics + {{- end }} + {{- end }} + {{- if .Values.metrics.resources }} + resources: {{- toYaml .Values.metrics.resources | nindent 12 }} + {{- end }} + {{- if .Values.metrics.extraVolumeMounts }} + volumeMounts: {{- include "common.tplvalues.render" (dict "value" .Values.metrics.extraVolumeMounts "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.metrics.sidecars }} + {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.sidecars "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.metrics.extraVolumes }} + volumes: {{- include "common.tplvalues.render" (dict "value" .Values.metrics.extraVolumes "context" $) | nindent 8 }} + {{- end }} +{{- end }} diff --git a/knowlg-automation/helm_charts/elasticsearch/templates/metrics/prometheusrule.yaml b/knowlg-automation/helm_charts/elasticsearch/templates/metrics/prometheusrule.yaml new file mode 100644 index 000000000..c235dab4f --- /dev/null +++ b/knowlg-automation/helm_charts/elasticsearch/templates/metrics/prometheusrule.yaml @@ -0,0 +1,22 @@ +{{- if and .Values.metrics.enabled .Values.metrics.prometheusRule.enabled }} +apiVersion: monitoring.coreos.com/v1 +kind: PrometheusRule +metadata: + name: {{ include "common.names.fullname" . }} + namespace: {{ default .Release.Namespace .Values.metrics.prometheusRule.namespace | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: metrics + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.metrics.prometheusRule.additionalLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.prometheusRule.additionalLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + groups: + - name: {{ include "common.names.fullname" . }} + rules: {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.prometheusRule.rules "context" $ ) | nindent 6 }} +{{- end }} diff --git a/knowlg-automation/helm_charts/elasticsearch/templates/metrics/service.yaml b/knowlg-automation/helm_charts/elasticsearch/templates/metrics/service.yaml new file mode 100644 index 000000000..bf3d5208c --- /dev/null +++ b/knowlg-automation/helm_charts/elasticsearch/templates/metrics/service.yaml @@ -0,0 +1,24 @@ +{{- if .Values.metrics.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "elasticsearch.metrics.fullname" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: metrics + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + annotations: {{ include "common.tplvalues.render" ( dict "value" .Values.metrics.service.annotations "context" $) | nindent 4 }} + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.metrics.service.type }} + ports: + - name: http-metrics + port: {{ .Values.metrics.service.port }} + targetPort: metrics + selector: {{- include "common.labels.matchLabels" . | nindent 4 }} + app.kubernetes.io/component: metrics +{{- end }} diff --git a/knowlg-automation/helm_charts/elasticsearch/templates/metrics/servicemonitor.yaml b/knowlg-automation/helm_charts/elasticsearch/templates/metrics/servicemonitor.yaml new file mode 100644 index 000000000..7d0f394ee --- /dev/null +++ b/knowlg-automation/helm_charts/elasticsearch/templates/metrics/servicemonitor.yaml @@ -0,0 +1,46 @@ +{{- if and .Values.metrics.enabled .Values.metrics.serviceMonitor.enabled }} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ include "elasticsearch.metrics.fullname" . }} + namespace: {{ default (include "common.names.namespace" .) .Values.metrics.serviceMonitor.namespace | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: metrics + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" (dict "value" .Values.commonLabels "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.metrics.serviceMonitor.labels }} + {{- include "common.tplvalues.render" (dict "value" .Values.metrics.serviceMonitor.labels "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" (dict "value" .Values.commonAnnotations "context" $) | nindent 4 }} + {{- end }} +spec: + jobLabel: {{ .Values.metrics.serviceMonitor.jobLabel | quote }} + selector: + matchLabels: {{- include "common.labels.matchLabels" . | nindent 6 }} + {{- if .Values.metrics.serviceMonitor.selector }} + {{- include "common.tplvalues.render" (dict "value" .Values.metrics.serviceMonitor.selector "context" $) | nindent 6 }} + {{- end }} + app.kubernetes.io/component: metrics + endpoints: + - port: http-metrics + {{- if .Values.metrics.serviceMonitor.interval }} + interval: {{ .Values.metrics.serviceMonitor.interval }} + {{- end }} + {{- if .Values.metrics.serviceMonitor.scrapeTimeout }} + scrapeTimeout: {{ .Values.metrics.serviceMonitor.scrapeTimeout }} + {{- end }} + {{- if .Values.metrics.serviceMonitor.honorLabels }} + honorLabels: {{ .Values.metrics.serviceMonitor.honorLabels }} + {{- end }} + {{- if .Values.metrics.serviceMonitor.metricRelabelings }} + metricRelabelings: {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.serviceMonitor.metricRelabelings "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.metrics.serviceMonitor.relabelings }} + relabelings: {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.serviceMonitor.relabelings "context" $) | nindent 8 }} + {{- end }} + namespaceSelector: + matchNames: + - {{ include "common.names.namespace" . | quote }} +{{- end }} diff --git a/knowlg-automation/helm_charts/elasticsearch/templates/secrets.yaml b/knowlg-automation/helm_charts/elasticsearch/templates/secrets.yaml new file mode 100644 index 000000000..8fda48827 --- /dev/null +++ b/knowlg-automation/helm_charts/elasticsearch/templates/secrets.yaml @@ -0,0 +1,43 @@ +{{- if (include "elasticsearch.createSecret" . ) }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "common.names.fullname" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +type: Opaque +data: + elasticsearch-password: {{ include "common.secrets.passwords.manage" (dict "secret" (include "common.names.fullname" .) "key" "elasticsearch-password" "length" 16 "providedValues" (list "security.elasticPassword") "context" $) }} +{{- end }} +{{- if (include "elasticsearch.createTlsPasswordsSecret" . ) }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ printf "%s-tls-pass" (include "common.names.fullname" .) }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +type: Opaque +data: + {{- if .Values.security.tls.keystorePassword }} + keystore-password: {{ default "" .Values.security.tls.keystorePassword | b64enc | quote }} + {{- end }} + {{- if .Values.security.tls.truststorePassword }} + truststore-password: {{ default "" .Values.security.tls.truststorePassword | b64enc | quote }} + {{- end }} + {{- if .Values.security.tls.keyPassword }} + key-password: {{ default "" .Values.security.tls.keyPassword | b64enc | quote }} + {{- end }} +{{- end }} diff --git a/knowlg-automation/helm_charts/elasticsearch/templates/service.yaml b/knowlg-automation/helm_charts/elasticsearch/templates/service.yaml new file mode 100644 index 000000000..cb3e67938 --- /dev/null +++ b/knowlg-automation/helm_charts/elasticsearch/templates/service.yaml @@ -0,0 +1,66 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "elasticsearch.service.name" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if (include "elasticsearch.coordinating.enabled" .) }} + app.kubernetes.io/component: coordinating-only + {{- else }} + app.kubernetes.io/component: master + {{- end }} + annotations: + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" (dict "value" .Values.commonAnnotations "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.service.annotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.service.annotations "context" $) | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.service.type }} + {{- if and .Values.service.clusterIP (eq .Values.service.type "ClusterIP") }} + clusterIP: {{ .Values.service.clusterIP }} + {{- end }} + {{- if or (eq .Values.service.type "LoadBalancer") (eq .Values.service.type "NodePort") }} + externalTrafficPolicy: {{ .Values.service.externalTrafficPolicy | quote }} + {{- end }} + {{- if and (eq .Values.service.type "LoadBalancer") .Values.service.loadBalancerSourceRanges }} + loadBalancerSourceRanges: {{- toYaml .Values.service.loadBalancerSourceRanges | nindent 4 }} + {{- end }} + {{- if (and (eq .Values.service.type "LoadBalancer") (not (empty .Values.service.loadBalancerIP))) }} + loadBalancerIP: {{ .Values.service.loadBalancerIP }} + {{- end }} + {{- if .Values.service.sessionAffinity }} + sessionAffinity: {{ .Values.service.sessionAffinity }} + {{- end }} + {{- if .Values.service.sessionAffinityConfig }} + sessionAffinityConfig: {{- include "common.tplvalues.render" (dict "value" .Values.service.sessionAffinityConfig "context" $) | nindent 4 }} + {{- end }} + ports: + - name: tcp-rest-api + port: {{ include "elasticsearch.service.ports.restAPI" . }} + targetPort: rest-api + {{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePorts.restAPI)) }} + nodePort: {{ .Values.service.nodePorts.restAPI }} + {{- else if eq .Values.service.type "ClusterIP" }} + nodePort: null + {{- end }} + - name: tcp-transport + port: {{ .Values.service.ports.transport }} + {{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePorts.transport)) }} + nodePort: {{ .Values.service.nodePorts.transport }} + {{- else if eq .Values.service.type "ClusterIP" }} + nodePort: null + {{- end }} + {{- if .Values.service.extraPorts }} + {{- include "common.tplvalues.render" (dict "value" .Values.service.extraPorts "context" $) | nindent 4 }} + {{- end }} + selector: {{- include "common.labels.matchLabels" . | nindent 4 }} + {{- if (include "elasticsearch.coordinating.enabled" .) }} + app.kubernetes.io/component: coordinating-only + {{- else }} + app.kubernetes.io/component: master + {{- end }} diff --git a/knowlg-automation/helm_charts/elasticsearch/templates/tls-secret.yaml b/knowlg-automation/helm_charts/elasticsearch/templates/tls-secret.yaml new file mode 100644 index 000000000..985f4ce68 --- /dev/null +++ b/knowlg-automation/helm_charts/elasticsearch/templates/tls-secret.yaml @@ -0,0 +1,117 @@ +{{- if (include "elasticsearch.createTlsSecret" .) }} +{{- $ca := genCA "elasticsearch-ca" 365 }} +{{- $releaseNamespace := include "common.names.namespace" . }} +{{- $clusterDomain := .Values.clusterDomain }} + +{{- if and (include "elasticsearch.master.enabled" .) (not .Values.security.tls.master.existingSecret) }} +{{- $fullname := include "elasticsearch.master.fullname" . }} +{{- $serviceName := include "elasticsearch.master.servicename" . }} +{{- $altNames := list (printf "*.%s.%s.svc.%s" $serviceName $releaseNamespace $clusterDomain) (printf "%s.%s.svc.%s" $serviceName $releaseNamespace $clusterDomain) $fullname "127.0.0.1" "localhost" }} +{{- if not (include "elasticsearch.coordinating.enabled" .) }} +{{- $altNames = append $altNames (include "elasticsearch.service.name" .) }} +{{- $altNames = append $altNames (printf "%s.%s.svc.%s" (include "elasticsearch.service.name" .) $releaseNamespace $clusterDomain) }} +{{- end }} +{{- $cert := genSignedCert $fullname nil $altNames 365 $ca }} +{{- $secretName := printf "%s-crt" (include "elasticsearch.master.fullname" .) }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ $secretName }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + app.kubernetes.io/component: master + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +type: kubernetes.io/tls +data: + tls.crt: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "tls.crt" "defaultValue" $cert.Cert "context" $) }} + tls.key: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "tls.key" "defaultValue" $cert.Key "context" $) }} + ca.crt: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "ca.crt" "defaultValue" $ca.Cert "context" $) }} +{{- end }} +{{- if and (include "elasticsearch.data.enabled" .) (not .Values.security.tls.data.existingSecret) }} +{{- $fullname := include "elasticsearch.data.fullname" . }} +{{- $serviceName := include "elasticsearch.data.servicename" . }} +{{- $altNames := list (printf "*.%s.%s.svc.%s" $serviceName $releaseNamespace $clusterDomain) (printf "%s.%s.svc.%s" $serviceName $releaseNamespace $clusterDomain) $fullname "127.0.0.1" "localhost" }} +{{- $cert := genSignedCert $fullname nil $altNames 365 $ca }} +{{- $secretName := printf "%s-crt" (include "elasticsearch.data.fullname" .) }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ $secretName }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + app.kubernetes.io/component: data + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +type: kubernetes.io/tls +data: + tls.crt: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "tls.crt" "defaultValue" $cert.Cert "context" $) }} + tls.key: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "tls.key" "defaultValue" $cert.Key "context" $) }} + ca.crt: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "ca.crt" "defaultValue" $ca.Cert "context" $) }} +{{- end }} +{{- if and (include "elasticsearch.coordinating.enabled" .) (not .Values.security.tls.coordinating.existingSecret) }} +{{- $fullname := include "elasticsearch.coordinating.fullname" . }} +{{- $serviceName := include "elasticsearch.coordinating.servicename" . }} +{{- $altNames := list (include "elasticsearch.service.name" .) (printf "%s.%s.svc.%s" (include "elasticsearch.service.name" .) $releaseNamespace $clusterDomain) (printf "*.%s.%s.svc.%s" $serviceName $releaseNamespace $clusterDomain) (printf "%s.%s.svc.%s" $serviceName $releaseNamespace $clusterDomain) $fullname "127.0.0.1" "localhost" }} +{{- $cert := genSignedCert $fullname nil $altNames 365 $ca }} +{{- $secretName := printf "%s-crt" (include "elasticsearch.coordinating.fullname" .) }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ $secretName }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + app.kubernetes.io/component: coordinating-only + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +type: kubernetes.io/tls +data: + tls.crt: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "tls.crt" "defaultValue" $cert.Cert "context" $) }} + tls.key: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "tls.key" "defaultValue" $cert.Key "context" $) }} + ca.crt: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "ca.crt" "defaultValue" $ca.Cert "context" $) }} +{{- end }} +{{- if and (include "elasticsearch.ingest.enabled" .) (not .Values.security.tls.ingest.existingSecret) }} +{{- $fullname := include "elasticsearch.ingest.fullname" . }} +{{- $serviceName := include "elasticsearch.ingest.servicename" . }} +{{- $altNames := list (printf "*.%s.%s.svc.%s" $serviceName $releaseNamespace $clusterDomain) (printf "%s.%s.svc.%s" $serviceName $releaseNamespace $clusterDomain) $fullname "127.0.0.1" "localhost" }} +{{- if .Values.ingest.service.enabled }} +{{- $altNames = append $altNames (include "elasticsearch.ingest.fullname" .) }} +{{- $altNames = append $altNames (printf "%s.%s.svc.%s" (include "elasticsearch.ingest.fullname" .) $releaseNamespace $clusterDomain) }} +{{- end }} +{{- $cert := genSignedCert $fullname nil $altNames 365 $ca }} +{{- $secretName := printf "%s-crt" (include "elasticsearch.ingest.fullname" .) }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ $secretName }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + app.kubernetes.io/component: ingest + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +type: kubernetes.io/tls +data: + tls.crt: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "tls.crt" "defaultValue" $cert.Cert "context" $) }} + tls.key: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "tls.key" "defaultValue" $cert.Key "context" $) }} + ca.crt: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "ca.crt" "defaultValue" $ca.Cert "context" $) }} +{{- end }} +{{- end }} diff --git a/knowlg-automation/helm_charts/elasticsearch/values.yaml b/knowlg-automation/helm_charts/elasticsearch/values.yaml new file mode 100644 index 000000000..02d04d174 --- /dev/null +++ b/knowlg-automation/helm_charts/elasticsearch/values.yaml @@ -0,0 +1,2145 @@ +## @section Global parameters +## Global Docker image parameters +## Please, note that this will override the image parameters, including dependencies, configured to use the global value +## Current available global Docker image parameters: imageRegistry, imagePullSecrets and storageClass + +## @param global.imageRegistry Global Docker image registry +## @param global.imagePullSecrets Global Docker registry secret names as an array +## @param global.storageClass Global StorageClass for Persistent Volume(s) +## @param global.elasticsearch.service.name Elasticsearch service name to be used in the Kibana subchart (ignored if kibanaEnabled=false) +## @param global.elasticsearch.service.ports.restAPI Elasticsearch service restAPI port to be used in the Kibana subchart (ignored if kibanaEnabled=false) +## @param global.kibanaEnabled Whether or not to enable Kibana +## +global: + imageRegistry: "" + ## E.g. + ## imagePullSecrets: + ## - myRegistryKeySecretName + ## + imagePullSecrets: [] + storageClass: "" + elasticsearch: + service: + name: elasticsearch + ports: + restAPI: 9200 + kibanaEnabled: false + +## @section Common parameters + +## @param kubeVersion Override Kubernetes version +## +kubeVersion: "" +## @param nameOverride String to partially override common.names.fullname +## +nameOverride: "" +## @param fullnameOverride String to fully override common.names.fullname +## +fullnameOverride: "" +## @param commonLabels Labels to add to all deployed objects +## +commonLabels: {} +## @param commonAnnotations Annotations to add to all deployed objects +## +commonAnnotations: {} +## @param clusterDomain Kubernetes cluster domain name +## +clusterDomain: cluster.local +## @param extraDeploy Array of extra objects to deploy with the release +## +extraDeploy: [] +## @param namespaceOverride String to fully override common.names.namespace +## +namespaceOverride: "" + +## Enable diagnostic mode in the deployment +## +diagnosticMode: + ## @param diagnosticMode.enabled Enable diagnostic mode (all probes will be disabled and the command will be overridden) + ## + enabled: false + ## @param diagnosticMode.command Command to override all containers in the deployment + ## + command: + - sleep + ## @param diagnosticMode.args Args to override all containers in the deployment + ## + args: + - infinity + +## @section Elasticsearch cluster Parameters + +## @param clusterName Elasticsearch cluster name +## +clusterName: elastic +## @param containerPorts.restAPI Elasticsearch REST API port +## @param containerPorts.transport Elasticsearch Transport port +## +containerPorts: + restAPI: 9200 + transport: 9300 +## @param plugins Comma, semi-colon or space separated list of plugins to install at initialization +## ref: https://github.com/bitnami/containers/tree/main/bitnami/elasticsearch#environment-variables +## +plugins: "" +## @param snapshotRepoPath File System snapshot repository path +## ref: https://github.com/bitnami/containers/tree/main/bitnami/elasticsearch#environment-variables +## +snapshotRepoPath: "" +## @param config Override elasticsearch configuration +## +config: {} +## @param extraConfig Append extra configuration to the elasticsearch node configuration +## Use this instead of `config` to add more configuration +## See below example: +## extraConfig: +## node: +## store: +## allow_mmap: false +## ref: https://www.elastic.co/guide/en/elasticsearch/reference/current/settings.html +## +extraConfig: {} +## @param extraHosts A list of external hosts which are part of this cluster +## Example Use Case: When you have a cluster with nodes spaned acorss multiple K8s or namespaces +## extraHosts: +## - datacenter2-elasticsearch-master-hl.namespace2.svc +## - datacenter2-elasticsearch-data-hl.namespace2.svc +extraHosts: [] +## @param extraVolumes A list of volumes to be added to the pod +## Example Use Case: mount ssl certificates when elasticsearch has tls enabled +## extraVolumes: +## - name: es-certs +## secret: +## defaultMode: 420 +## secretName: es-certs +extraVolumes: [] +## @param extraVolumeMounts A list of volume mounts to be added to the pod +## extraVolumeMounts: +## - name: es-certs +## mountPath: /certs +## readOnly: true +extraVolumeMounts: [] +## @param initScripts Dictionary of init scripts. Evaluated as a template. +## Specify dictionary of scripts to be run at first boot +## Alternatively, you can put your scripts under the files/docker-entrypoint-initdb.d directory +## For example: +## initScripts: +## my_init_script.sh: | +## #!/bin/sh +## echo "Do something." +## +initScripts: {} +## @param initScriptsCM ConfigMap with the init scripts. Evaluated as a template. +## Note: This will override initScripts +## +initScriptsCM: "" +## @param initScriptsSecret Secret containing `/docker-entrypoint-initdb.d` scripts to be executed at initialization time that contain sensitive data. Evaluated as a template. +## +initScriptsSecret: "" +## @param extraEnvVars Array containing extra env vars to be added to all pods (evaluated as a template) +## For example: +## extraEnvVars: +## - name: MY_ENV_VAR +## value: env_var_value +## +extraEnvVars: [] +## @param extraEnvVarsCM ConfigMap containing extra env vars to be added to all pods (evaluated as a template) +## +extraEnvVarsCM: "" +## @param extraEnvVarsSecret Secret containing extra env vars to be added to all pods (evaluated as a template) +## +extraEnvVarsSecret: "" +## @param sidecars Add additional sidecar containers to the all elasticsearch node pod(s) +## e.g: +## sidecars: +## - name: your-image-name +## image: your-image +## imagePullPolicy: Always +## ports: +## - name: portname +## containerPort: 1234 +## +sidecars: [] +## @param initContainers Add additional init containers to the all elasticsearch node pod(s) +## ref: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ +## e.g: +## initContainers: +## - name: your-image-name +## image: your-image +## imagePullPolicy: Always +## command: ['sh', '-c', 'echo "hello world"'] +## +initContainers: [] +## @param useIstioLabels Use this variable to add Istio labels to all pods +## +useIstioLabels: true + +## Bitnami Elasticsearch image +## @param image.registry Elasticsearch image registry +## @param image.repository Elasticsearch image repository +## @param image.tag Elasticsearch image tag (immutable tags are recommended) +## @param image.digest Elasticsearch image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag +## @param image.pullPolicy Elasticsearch image pull policy +## @param image.pullSecrets Elasticsearch image pull secrets +## @param image.debug Enable Elasticsearch image debug mode +## +image: + registry: docker.io + repository: bitnami/elasticsearch + tag: 8.6.2-debian-11-r10 + digest: "" + ## Specify a imagePullPolicy + ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' + ## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images + ## + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## e.g: + ## pullSecrets: + ## - myRegistryKeySecretName + ## + pullSecrets: [] + ## Enable debug mode + ## + debug: false + +## X-Pack security parameters +## Note: TLS configuration is required in order to configure password authentication +## +security: + ## @param security.enabled Enable X-Pack Security settings + ## + enabled: false + ## @param security.elasticPassword Password for 'elastic' user + ## Ref: https://github.com/bitnami/containers/tree/main/bitnami/elasticsearch#security + ## + elasticPassword: "" + ## @param security.existingSecret Name of the existing secret containing the Elasticsearch password and + ## + existingSecret: "" + ## FIPS mode + ## @param security.fipsMode Configure elasticsearch with FIPS 140 compliant mode + ## Ref: https://www.elastic.co/guide/en/elasticsearch/reference/current/fips-140-compliance.html + ## + fipsMode: false + ## TLS configuration + ## + tls: + ## @param security.tls.restEncryption Enable SSL/TLS encryption for Elasticsearch REST API. + ## + restEncryption: true + ## @param security.tls.autoGenerated Create self-signed TLS certificates. + ## NOTE: If autoGenerated certs are enabled and a new node type is enabled using helm upgrade, make sure you remove previously existing Elasticsearch TLS secrets. + ## Otherwise, the new node certs won't match the existing certs. + ## + autoGenerated: false + ## @param security.tls.verificationMode Verification mode for SSL communications. + ## Supported values: full, certificate, none. + ## Ref: https://www.elastic.co/guide/en/elasticsearch/reference/current/security-settings.html + ## + verificationMode: "full" + ## @param security.tls.master.existingSecret Existing secret containing the certificates for the master nodes + ## @param security.tls.data.existingSecret Existing secret containing the certificates for the data nodes + ## @param security.tls.ingest.existingSecret Existing secret containing the certificates for the ingest nodes + ## @param security.tls.coordinating.existingSecret Existing secret containing the certificates for the coordinating nodes + ## + master: + existingSecret: "" + data: + existingSecret: "" + ingest: + existingSecret: "" + coordinating: + existingSecret: "" + ## @param security.tls.keystoreFilename Name of the keystore file + ## + keystoreFilename: elasticsearch.keystore.jks + ## @param security.tls.truststoreFilename Name of the truststore + ## + truststoreFilename: elasticsearch.truststore.jks + ## @param security.tls.usePemCerts Use this variable if your secrets contain PEM certificates instead of JKS/PKCS12 + ## Ignored when using autoGenerated certs. + ## + usePemCerts: false + ## @param security.tls.passwordsSecret Existing secret containing the Keystore and Truststore passwords, or key password if PEM certs are used + ## + passwordsSecret: "" + ## @param security.tls.keystorePassword Password to access the JKS/PKCS12 keystore or PEM key when they are password-protected. + ## Ignored if security.tls.passwordsSecret is provided. + ## + keystorePassword: "" + ## @param security.tls.truststorePassword Password to access the JKS/PKCS12 truststore when they are password-protected. + ## Ignored if security.tls.passwordsSecret is provided. + ## + truststorePassword: "" + ## @param security.tls.keyPassword Password to access the PEM key when they are password-protected. + ## Ignored if security.tls.passwordsSecret is provided. + ## + keyPassword: "" + ## @param security.tls.secretKeystoreKey Name of the secret key containing the Keystore password + ## + secretKeystoreKey: "" + ## @param security.tls.secretTruststoreKey Name of the secret key containing the Truststore password + ## + secretTruststoreKey: "" + ## @param security.tls.secretKey Name of the secret key containing the PEM key password + ## + secretKey: "" + +## @section Traffic Exposure Parameters +## + +## Elasticsearch service parameters +## +service: + ## @param service.type Elasticsearch service type + ## + type: ClusterIP + ## @param service.ports.restAPI Elasticsearch service REST API port + ## @param service.ports.transport Elasticsearch service transport port + ## + ports: + restAPI: 9200 + transport: 9300 + ## Node ports to expose + ## @param service.nodePorts.restAPI Node port for REST API + ## @param service.nodePorts.transport Node port for REST API + ## NOTE: choose port between <30000-32767> + ## + nodePorts: + restAPI: "" + transport: "" + ## @param service.clusterIP Elasticsearch service Cluster IP + ## e.g.: + ## clusterIP: None + ## + clusterIP: "" + ## @param service.loadBalancerIP Elasticsearch service Load Balancer IP + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-loadbalancer + ## + loadBalancerIP: "" + ## @param service.loadBalancerSourceRanges Elasticsearch service Load Balancer sources + ## ref: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/#restrict-access-for-loadbalancer-service + ## e.g: + ## loadBalancerSourceRanges: + ## - 10.10.10.0/24 + ## + loadBalancerSourceRanges: [] + ## @param service.externalTrafficPolicy Elasticsearch service external traffic policy + ## ref http://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip + ## + externalTrafficPolicy: Cluster + ## @param service.annotations Additional custom annotations for Elasticsearch service + ## + annotations: {} + ## @param service.extraPorts Extra ports to expose in Elasticsearch service (normally used with the `sidecars` value) + ## + extraPorts: [] + ## @param service.sessionAffinity Session Affinity for Kubernetes service, can be "None" or "ClientIP" + ## If "ClientIP", consecutive client requests will be directed to the same Pod + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies + ## + sessionAffinity: None + ## @param service.sessionAffinityConfig Additional settings for the sessionAffinity + ## sessionAffinityConfig: + ## clientIP: + ## timeoutSeconds: 300 + ## + sessionAffinityConfig: {} + +## Elasticsearch ingress parameters +## ref: http://kubernetes.io/docs/user-guide/ingress/ +## +ingress: + ## @param ingress.enabled Enable ingress record generation for Elasticsearch + ## + enabled: false + ## @param ingress.pathType Ingress path type + ## + pathType: ImplementationSpecific + ## @param ingress.apiVersion Force Ingress API version (automatically detected if not set) + ## + apiVersion: "" + ## @param ingress.hostname Default host for the ingress record + ## + hostname: elasticsearch.local + ## @param ingress.path Default path for the ingress record + ## NOTE: You may need to set this to '/*' in order to use this with ALB ingress controllers + ## + path: / + ## @param ingress.annotations Additional annotations for the Ingress resource. To enable certificate autogeneration, place here your cert-manager annotations. + ## Use this parameter to set the required annotations for cert-manager, see + ## ref: https://cert-manager.io/docs/usage/ingress/#supported-annotations + ## e.g: + ## annotations: + ## kubernetes.io/ingress.class: nginx + ## cert-manager.io/cluster-issuer: cluster-issuer-name + ## + annotations: {} + ## @param ingress.tls Enable TLS configuration for the host defined at `ingress.hostname` parameter + ## TLS certificates will be retrieved from a TLS secret with name: `{{- printf "%s-tls" .Values.ingress.hostname }}` + ## You can: + ## - Use the `ingress.secrets` parameter to create this TLS secret + ## - Rely on cert-manager to create it by setting the corresponding annotations + ## - Rely on Helm to create self-signed certificates by setting `ingress.selfSigned=true` + ## + tls: false + ## @param ingress.selfSigned Create a TLS secret for this ingress record using self-signed certificates generated by Helm + ## + selfSigned: false + ## @param ingress.ingressClassName IngressClass that will be be used to implement the Ingress (Kubernetes 1.18+) + ## This is supported in Kubernetes 1.18+ and required if you have more than one IngressClass marked as the default for your cluster . + ## ref: https://kubernetes.io/blog/2020/04/02/improvements-to-the-ingress-api-in-kubernetes-1.18/ + ## + ingressClassName: "" + ## @param ingress.extraHosts An array with additional hostname(s) to be covered with the ingress record + ## e.g: + ## extraHosts: + ## - name: elasticsearch.local + ## path: / + ## + extraHosts: [] + ## @param ingress.extraPaths An array with additional arbitrary paths that may need to be added to the ingress under the main host + ## e.g: + ## extraPaths: + ## - path: /* + ## backend: + ## serviceName: ssl-redirect + ## servicePort: use-annotation + ## + extraPaths: [] + ## @param ingress.extraTls TLS configuration for additional hostname(s) to be covered with this ingress record + ## ref: https://kubernetes.io/docs/concepts/services-networking/ingress/#tls + ## e.g: + ## extraTls: + ## - hosts: + ## - elasticsearch.local + ## secretName: elasticsearch.local-tls + ## + extraTls: [] + ## @param ingress.secrets Custom TLS certificates as secrets + ## NOTE: 'key' and 'certificate' are expected in PEM format + ## NOTE: 'name' should line up with a 'secretName' set further up + ## If it is not set and you're using cert-manager, this is unneeded, as it will create a secret for you with valid certificates + ## If it is not set and you're NOT using cert-manager either, self-signed certificates will be created valid for 365 days + ## It is also possible to create and manage the certificates outside of this helm chart + ## Please see README.md for more information + ## e.g: + ## secrets: + ## - name: elasticsearch.local-tls + ## key: |- + ## -----BEGIN RSA PRIVATE KEY----- + ## ... + ## -----END RSA PRIVATE KEY----- + ## certificate: |- + ## -----BEGIN CERTIFICATE----- + ## ... + ## -----END CERTIFICATE----- + ## + secrets: [] + ## @param ingress.extraRules Additional rules to be covered with this ingress record + ## ref: https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-rules + ## e.g: + ## extraRules: + ## - host: example.local + ## http: + ## path: / + ## backend: + ## service: + ## name: example-svc + ## port: + ## name: http + ## + extraRules: [] + +## @section Master-elegible nodes parameters + +master: + ## @param master.masterOnly Deploy the Elasticsearch master-elegible nodes as master-only nodes. Recommended for high-demand deployments. + ## If you are + masterOnly: true + ## @param master.replicaCount Number of master-elegible replicas to deploy + ## + replicaCount: 1 + ## @param master.nameOverride String to partially override elasticsearch.master.fullname + ## + nameOverride: "" + ## @param master.fullnameOverride String to fully override elasticsearch.master.fullname + ## + fullnameOverride: "" + ## @param master.servicenameOverride String to fully override elasticsearch.master.servicename + ## + servicenameOverride: "" + ## @param master.annotations [object] Annotations for the master statefulset + ## + annotations: {} + ## @param master.updateStrategy.type Master-elegible nodes statefulset stategy type + ## ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#update-strategies + ## + updateStrategy: + type: RollingUpdate + ## Elasticsearch resource requests and limits + ## ref: http://kubernetes.io/docs/user-guide/compute-resources/ + ## We usually recommend not to specify default resources and to leave this as a conscious + ## choice for the user. This also increases chances charts run on environments with little + ## resources, such as Minikube. If you do want to specify resources, uncomment the following + ## lines, adjust them as necessary, and remove the curly braces after 'resources:'. + ## @param master.resources.limits The resources limits for elasticsearch containers + ## @param master.resources.requests The requested resources for elasticsearch containers + ## + resources: + ## Example: + ## limits: + ## cpu: 500m + ## memory: 1Gi + limits: {} + ## Examples: + ## requests: + ## cpu: 250m + ## memory: 256Mi + requests: {} + ## @param master.heapSize Elasticsearch master-eligible node heap size. + ## Note: The recommended heapSize is half of the container's memory. + ## If omitted, it will be automatically set. + ## Example: + ## heapSize: 128m + ## + heapSize: 128m + ## Configure Pods Security Context + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod + ## @param master.podSecurityContext.enabled Enabled master-elegible pods' Security Context + ## @param master.podSecurityContext.fsGroup Set master-elegible pod's Security Context fsGroup + ## + podSecurityContext: + enabled: true + fsGroup: 1001 + ## Configure Container Security Context + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod + ## @param master.containerSecurityContext.enabled Enabled master-elegible containers' Security Context + ## @param master.containerSecurityContext.runAsUser Set master-elegible containers' Security Context runAsUser + ## @param master.containerSecurityContext.runAsNonRoot Set master-elegible containers' Security Context runAsNonRoot + ## + containerSecurityContext: + enabled: true + runAsUser: 1001 + runAsNonRoot: true + ## @param master.hostAliases master-elegible pods host aliases + ## https://kubernetes.io/docs/concepts/services-networking/add-entries-to-pod-etc-hosts-with-host-aliases/ + ## + hostAliases: [] + ## @param master.podLabels Extra labels for master-elegible pods + ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + ## + podLabels: {} + ## @param master.podAnnotations Annotations for master-elegible pods + ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ + ## + podAnnotations: {} + ## @param master.podAffinityPreset Pod affinity preset. Ignored if `master.affinity` is set. Allowed values: `soft` or `hard` + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity + ## + podAffinityPreset: "" + ## @param master.podAntiAffinityPreset Pod anti-affinity preset. Ignored if `master.affinity` is set. Allowed values: `soft` or `hard` + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity + ## + podAntiAffinityPreset: "" + ## Node master.affinity preset + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity + ## + nodeAffinityPreset: + ## @param master.nodeAffinityPreset.type Node affinity preset type. Ignored if `master.affinity` is set. Allowed values: `soft` or `hard` + ## + type: "" + ## @param master.nodeAffinityPreset.key Node label key to match. Ignored if `master.affinity` is set + ## + key: "" + ## @param master.nodeAffinityPreset.values Node label values to match. Ignored if `master.affinity` is set + ## E.g. + ## values: + ## - e2e-az1 + ## - e2e-az2 + ## + values: [] + ## @param master.affinity Affinity for master-elegible pods assignment + ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity + ## NOTE: `master.podAffinityPreset`, `master.podAntiAffinityPreset`, and `master.nodeAffinityPreset` will be ignored when it's set + ## + affinity: {} + ## @param master.nodeSelector Node labels for master-elegible pods assignment + ## ref: https://kubernetes.io/docs/user-guide/node-selection/ + ## + nodeSelector: {} + ## @param master.tolerations Tolerations for master-elegible pods assignment + ## ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ + ## + tolerations: [] + ## @param master.priorityClassName master-elegible pods' priorityClassName + ## + priorityClassName: "" + ## @param master.schedulerName Name of the k8s scheduler (other than default) for master-elegible pods + ## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ + ## + schedulerName: "" + ## @param master.terminationGracePeriodSeconds In seconds, time the given to the Elasticsearch Master pod needs to terminate gracefully + ## ref: https://kubernetes.io/docs/concepts/workloads/pods/pod/#termination-of-pods + ## + terminationGracePeriodSeconds: "" + ## @param master.topologySpreadConstraints Topology Spread Constraints for pod assignment spread across your cluster among failure-domains. Evaluated as a template + ## Ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/#spread-constraints-for-pods + ## + topologySpreadConstraints: [] + ## @param master.podManagementPolicy podManagementPolicy to manage scaling operation of Elasticsearch master pods + ## ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#pod-management-policies + ## + podManagementPolicy: "Parallel" + ## Configure extra options for Elasticsearch master-elegible containers' liveness, readiness and startup probes + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes + ## @param master.startupProbe.enabled Enable/disable the startup probe (master nodes pod) + ## @param master.startupProbe.initialDelaySeconds Delay before startup probe is initiated (master nodes pod) + ## @param master.startupProbe.periodSeconds How often to perform the probe (master nodes pod) + ## @param master.startupProbe.timeoutSeconds When the probe times out (master nodes pod) + ## @param master.startupProbe.successThreshold Minimum consecutive successes for the probe to be considered successful after having failed (master nodes pod) + ## @param master.startupProbe.failureThreshold Minimum consecutive failures for the probe to be considered failed after having succeeded + ## + startupProbe: + enabled: false + initialDelaySeconds: 90 + periodSeconds: 10 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 5 + ## @param master.livenessProbe.enabled Enable/disable the liveness probe (master-eligible nodes pod) + ## @param master.livenessProbe.initialDelaySeconds Delay before liveness probe is initiated (master-eligible nodes pod) + ## @param master.livenessProbe.periodSeconds How often to perform the probe (master-eligible nodes pod) + ## @param master.livenessProbe.timeoutSeconds When the probe times out (master-eligible nodes pod) + ## @param master.livenessProbe.successThreshold Minimum consecutive successes for the probe to be considered successful after having failed (master-eligible nodes pod) + ## @param master.livenessProbe.failureThreshold Minimum consecutive failures for the probe to be considered failed after having succeeded + ## + livenessProbe: + enabled: true + initialDelaySeconds: 90 + periodSeconds: 10 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 5 + ## @param master.readinessProbe.enabled Enable/disable the readiness probe (master-eligible nodes pod) + ## @param master.readinessProbe.initialDelaySeconds Delay before readiness probe is initiated (master-eligible nodes pod) + ## @param master.readinessProbe.periodSeconds How often to perform the probe (master-eligible nodes pod) + ## @param master.readinessProbe.timeoutSeconds When the probe times out (master-eligible nodes pod) + ## @param master.readinessProbe.successThreshold Minimum consecutive successes for the probe to be considered successful after having failed (master-eligible nodes pod) + ## @param master.readinessProbe.failureThreshold Minimum consecutive failures for the probe to be considered failed after having succeeded + ## + readinessProbe: + enabled: true + initialDelaySeconds: 90 + periodSeconds: 10 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 5 + ## @param master.customStartupProbe Override default startup probe + ## + customStartupProbe: {} + ## @param master.customLivenessProbe Override default liveness probe + ## + customLivenessProbe: {} + ## @param master.customReadinessProbe Override default readiness probe + ## + customReadinessProbe: {} + ## @param master.command Override default container command (useful when using custom images) + ## + command: [] + ## @param master.args Override default container args (useful when using custom images) + ## + args: [] + ## @param master.lifecycleHooks for the master-elegible container(s) to automate configuration before or after startup + ## + lifecycleHooks: {} + ## @param master.extraEnvVars Array with extra environment variables to add to master-elegible nodes + ## e.g: + ## extraEnvVars: + ## - name: FOO + ## value: "bar" + ## + extraEnvVars: [] + ## @param master.extraEnvVarsCM Name of existing ConfigMap containing extra env vars for master-elegible nodes + ## + extraEnvVarsCM: "" + ## @param master.extraEnvVarsSecret Name of existing Secret containing extra env vars for master-elegible nodes + ## + extraEnvVarsSecret: "" + ## @param master.extraVolumes Optionally specify extra list of additional volumes for the master-elegible pod(s) + ## + extraVolumes: [] + ## @param master.extraVolumeMounts Optionally specify extra list of additional volumeMounts for the master-elegible container(s) + ## + extraVolumeMounts: [] + ## @param master.sidecars Add additional sidecar containers to the master-elegible pod(s) + ## e.g: + ## sidecars: + ## - name: your-image-name + ## image: your-image + ## imagePullPolicy: Always + ## ports: + ## - name: portname + ## containerPort: 1234 + ## + sidecars: [] + ## @param master.initContainers Add additional init containers to the master-elegible pod(s) + ## ref: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ + ## e.g: + ## initContainers: + ## - name: your-image-name + ## image: your-image + ## imagePullPolicy: Always + ## command: ['sh', '-c', 'echo "hello world"'] + ## + initContainers: [] + ## Enable persistence using Persistent Volume Claims + ## ref: https://kubernetes.io/docs/user-guide/persistent-volumes/ + ## + persistence: + ## @param master.persistence.enabled Enable persistence using a `PersistentVolumeClaim` + ## + enabled: true + ## @param master.persistence.storageClass Persistent Volume Storage Class + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. (gp2 on AWS, standard on + ## GKE, AWS & OpenStack) + ## + storageClass: "" + ## @param master.persistence.existingClaim Existing Persistent Volume Claim + ## then accept the value as an existing Persistent Volume Claim to which + ## the container should be bound + ## + existingClaim: "" + ## @param master.persistence.existingVolume Existing Persistent Volume for use as volume match label selector to the `volumeClaimTemplate`. Ignored when `master.persistence.selector` is set. + ## + existingVolume: "" + ## @param master.persistence.selector Configure custom selector for existing Persistent Volume. Overwrites `master.persistence.existingVolume` + ## selector: + ## matchLabels: + ## volume: + ## + selector: {} + ## @param master.persistence.annotations Persistent Volume Claim annotations + ## + annotations: {} + ## @param master.persistence.accessModes Persistent Volume Access Modes + ## + accessModes: + - ReadWriteOnce + ## @param master.persistence.size Persistent Volume Size + ## + size: 8Gi + ## Pods Service Account + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ + ## @param master.serviceAccount.create Specifies whether a ServiceAccount should be created + ## @param master.serviceAccount.name Name of the service account to use. If not set and create is true, a name is generated using the fullname template. + ## @param master.serviceAccount.automountServiceAccountToken Automount service account token for the server service account + ## @param master.serviceAccount.annotations Annotations for service account. Evaluated as a template. Only used if `create` is `true`. + ## + serviceAccount: + create: false + name: "" + automountServiceAccountToken: true + annotations: {} + ## Enable HorizontalPodAutoscaler for Elasticsearch Master pods + ## ref: https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/ + ## @param master.autoscaling.enabled Whether enable horizontal pod autoscale + ## @param master.autoscaling.minReplicas Configure a minimum amount of pods + ## @param master.autoscaling.maxReplicas Configure a maximum amount of pods + ## @param master.autoscaling.targetCPU Define the CPU target to trigger the scaling actions (utilization percentage) + ## @param master.autoscaling.targetMemory Define the memory target to trigger the scaling actions (utilization percentage) + ## + autoscaling: + enabled: false + minReplicas: 3 + maxReplicas: 11 + targetCPU: "" + targetMemory: "" + +## @section Data-only nodes parameters + +data: + ## @param data.replicaCount Number of data-only replicas to deploy + ## + replicaCount: 1 + ## @param data.nameOverride String to partially override elasticsearch.data.fullname + ## + nameOverride: "" + ## @param data.fullnameOverride String to fully override elasticsearch.data.fullname + ## + fullnameOverride: "" + ## @param data.servicenameOverride String to fully override elasticsearch.data.servicename + ## + servicenameOverride: "" + ## @param data.annotations [object] Annotations for the data statefulset + ## + annotations: {} + ## @param data.updateStrategy.type Data-only nodes statefulset stategy type + ## ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#update-strategies + ## + updateStrategy: + type: RollingUpdate + ## Elasticsearch resource requests and limits + ## ref: http://kubernetes.io/docs/user-guide/compute-resources/ + ## @param data.resources.limits The resources limits for the data containers + ## @param data.resources.requests [object] The requested resources for the data containers + ## + resources: + limits: {} + requests: + cpu: 25m + memory: 2048Mi + ## @param data.heapSize Elasticsearch data node heap size. + ## Note: The recommended heapSize is half of the container's memory. + ## If omitted, it will be automatically set. + ## Example: + ## heapSize: 128m + ## + heapSize: 1024m + ## Configure Pods Security Context + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod + ## @param data.podSecurityContext.enabled Enabled data pods' Security Context + ## @param data.podSecurityContext.fsGroup Set data pod's Security Context fsGroup + ## + podSecurityContext: + enabled: true + fsGroup: 1001 + ## Configure Container Security Context + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod + ## @param data.containerSecurityContext.enabled Enabled data containers' Security Context + ## @param data.containerSecurityContext.runAsUser Set data containers' Security Context runAsUser + ## @param data.containerSecurityContext.runAsNonRoot Set data containers' Security Context runAsNonRoot + ## + containerSecurityContext: + enabled: true + runAsUser: 1001 + runAsNonRoot: true + ## @param data.hostAliases data pods host aliases + ## https://kubernetes.io/docs/concepts/services-networking/add-entries-to-pod-etc-hosts-with-host-aliases/ + ## + hostAliases: [] + ## @param data.podLabels Extra labels for data pods + ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + ## + podLabels: {} + ## @param data.podAnnotations Annotations for data pods + ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ + ## + podAnnotations: {} + ## @param data.podAffinityPreset Pod affinity preset. Ignored if `data.affinity` is set. Allowed values: `soft` or `hard` + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity + ## + podAffinityPreset: "" + ## @param data.podAntiAffinityPreset Pod anti-affinity preset. Ignored if `data.affinity` is set. Allowed values: `soft` or `hard` + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity + ## + podAntiAffinityPreset: "" + ## Node data.affinity preset + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity + ## + nodeAffinityPreset: + ## @param data.nodeAffinityPreset.type Node affinity preset type. Ignored if `data.affinity` is set. Allowed values: `soft` or `hard` + ## + type: "" + ## @param data.nodeAffinityPreset.key Node label key to match. Ignored if `data.affinity` is set + ## + key: "" + ## @param data.nodeAffinityPreset.values Node label values to match. Ignored if `data.affinity` is set + ## E.g. + ## values: + ## - e2e-az1 + ## - e2e-az2 + ## + values: [] + ## @param data.affinity Affinity for data pods assignment + ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity + ## NOTE: `data.podAffinityPreset`, `data.podAntiAffinityPreset`, and `data.nodeAffinityPreset` will be ignored when it's set + ## + affinity: {} + ## @param data.nodeSelector Node labels for data pods assignment + ## ref: https://kubernetes.io/docs/user-guide/node-selection/ + ## + nodeSelector: {} + ## @param data.tolerations Tolerations for data pods assignment + ## ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ + ## + tolerations: [] + ## @param data.priorityClassName data pods' priorityClassName + ## + priorityClassName: "" + ## @param data.schedulerName Name of the k8s scheduler (other than default) for data pods + ## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ + ## + schedulerName: "" + ## @param data.terminationGracePeriodSeconds In seconds, time the given to the Elasticsearch data pod needs to terminate gracefully + ## ref: https://kubernetes.io/docs/concepts/workloads/pods/pod/#termination-of-pods + ## + terminationGracePeriodSeconds: "" + ## @param data.topologySpreadConstraints Topology Spread Constraints for pod assignment spread across your cluster among failure-domains. Evaluated as a template + ## Ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/#spread-constraints-for-pods + ## + topologySpreadConstraints: [] + ## @param data.podManagementPolicy podManagementPolicy to manage scaling operation of Elasticsearch data pods + ## ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#pod-management-policies + ## + podManagementPolicy: "Parallel" + ## Configure extra options for Elasticsearch data containers' liveness, readiness and startup probes + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes + ## @param data.startupProbe.enabled Enable/disable the startup probe (data nodes pod) + ## @param data.startupProbe.initialDelaySeconds Delay before startup probe is initiated (data nodes pod) + ## @param data.startupProbe.periodSeconds How often to perform the probe (data nodes pod) + ## @param data.startupProbe.timeoutSeconds When the probe times out (data nodes pod) + ## @param data.startupProbe.successThreshold Minimum consecutive successes for the probe to be considered successful after having failed (data nodes pod) + ## @param data.startupProbe.failureThreshold Minimum consecutive failures for the probe to be considered failed after having succeeded + ## + startupProbe: + enabled: false + initialDelaySeconds: 90 + periodSeconds: 10 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 5 + ## @param data.livenessProbe.enabled Enable/disable the liveness probe (data nodes pod) + ## @param data.livenessProbe.initialDelaySeconds Delay before liveness probe is initiated (data nodes pod) + ## @param data.livenessProbe.periodSeconds How often to perform the probe (data nodes pod) + ## @param data.livenessProbe.timeoutSeconds When the probe times out (data nodes pod) + ## @param data.livenessProbe.successThreshold Minimum consecutive successes for the probe to be considered successful after having failed (data nodes pod) + ## @param data.livenessProbe.failureThreshold Minimum consecutive failures for the probe to be considered failed after having succeeded + ## + livenessProbe: + enabled: true + initialDelaySeconds: 90 + periodSeconds: 10 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 5 + ## @param data.readinessProbe.enabled Enable/disable the readiness probe (data nodes pod) + ## @param data.readinessProbe.initialDelaySeconds Delay before readiness probe is initiated (data nodes pod) + ## @param data.readinessProbe.periodSeconds How often to perform the probe (data nodes pod) + ## @param data.readinessProbe.timeoutSeconds When the probe times out (data nodes pod) + ## @param data.readinessProbe.successThreshold Minimum consecutive successes for the probe to be considered successful after having failed (data nodes pod) + ## @param data.readinessProbe.failureThreshold Minimum consecutive failures for the probe to be considered failed after having succeeded + ## + readinessProbe: + enabled: true + initialDelaySeconds: 90 + periodSeconds: 10 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 5 + ## @param data.customStartupProbe Override default startup probe + ## + customStartupProbe: {} + ## @param data.customLivenessProbe Override default liveness probe + ## + customLivenessProbe: {} + ## @param data.customReadinessProbe Override default readiness probe + ## + customReadinessProbe: {} + ## @param data.command Override default container command (useful when using custom images) + ## + command: [] + ## @param data.args Override default container args (useful when using custom images) + ## + args: [] + ## @param data.lifecycleHooks for the data container(s) to automate configuration before or after startup + ## + lifecycleHooks: {} + ## @param data.extraEnvVars Array with extra environment variables to add to data nodes + ## e.g: + ## extraEnvVars: + ## - name: FOO + ## value: "bar" + ## + extraEnvVars: [] + ## @param data.extraEnvVarsCM Name of existing ConfigMap containing extra env vars for data nodes + ## + extraEnvVarsCM: "" + ## @param data.extraEnvVarsSecret Name of existing Secret containing extra env vars for data nodes + ## + extraEnvVarsSecret: "" + ## @param data.extraVolumes Optionally specify extra list of additional volumes for the data pod(s) + ## + extraVolumes: [] + ## @param data.extraVolumeMounts Optionally specify extra list of additional volumeMounts for the data container(s) + ## + extraVolumeMounts: [] + ## @param data.sidecars Add additional sidecar containers to the data pod(s) + ## e.g: + ## sidecars: + ## - name: your-image-name + ## image: your-image + ## imagePullPolicy: Always + ## ports: + ## - name: portname + ## containerPort: 1234 + ## + sidecars: [] + ## @param data.initContainers Add additional init containers to the data pod(s) + ## ref: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ + ## e.g: + ## initContainers: + ## - name: your-image-name + ## image: your-image + ## imagePullPolicy: Always + ## command: ['sh', '-c', 'echo "hello world"'] + ## + initContainers: [] + ## Enable persistence using Persistent Volume Claims + ## ref: https://kubernetes.io/docs/user-guide/persistent-volumes/ + ## + persistence: + ## @param data.persistence.enabled Enable persistence using a `PersistentVolumeClaim` + ## + enabled: true + ## @param data.persistence.storageClass Persistent Volume Storage Class + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. (gp2 on AWS, standard on + ## GKE, AWS & OpenStack) + ## + storageClass: "" + ## @param data.persistence.existingClaim Existing Persistent Volume Claim + ## then accept the value as an existing Persistent Volume Claim to which + ## the container should be bound + ## + existingClaim: "" + ## @param data.persistence.existingVolume Existing Persistent Volume for use as volume match label selector to the `volumeClaimTemplate`. Ignored when `data.persistence.selector` is set. + ## + existingVolume: "" + ## @param data.persistence.selector Configure custom selector for existing Persistent Volume. Overwrites `data.persistence.existingVolume` + ## selector: + ## matchLabels: + ## volume: + ## + selector: {} + ## @param data.persistence.annotations Persistent Volume Claim annotations + ## + annotations: {} + ## @param data.persistence.accessModes Persistent Volume Access Modes + ## + accessModes: + - ReadWriteOnce + ## @param data.persistence.size Persistent Volume Size + ## + size: 8Gi + ## Pods Service Account + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ + ## @param data.serviceAccount.create Specifies whether a ServiceAccount should be created + ## @param data.serviceAccount.name Name of the service account to use. If not set and create is true, a name is generated using the fullname template. + ## @param data.serviceAccount.automountServiceAccountToken Automount service account token for the server service account + ## @param data.serviceAccount.annotations Annotations for service account. Evaluated as a template. Only used if `create` is `true`. + ## + serviceAccount: + create: false + name: "" + automountServiceAccountToken: true + annotations: {} + ## Enable HorizontalPodAutoscaler for Elasticsearch data pods + ## ref: https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/ + ## @param data.autoscaling.enabled Whether enable horizontal pod autoscale + ## @param data.autoscaling.minReplicas Configure a minimum amount of pods + ## @param data.autoscaling.maxReplicas Configure a maximum amount of pods + ## @param data.autoscaling.targetCPU Define the CPU target to trigger the scaling actions (utilization percentage) + ## @param data.autoscaling.targetMemory Define the memory target to trigger the scaling actions (utilization percentage) + ## + autoscaling: + enabled: false + minReplicas: 3 + maxReplicas: 11 + targetCPU: "" + targetMemory: "" + +## @section Coordinating-only nodes parameters + +coordinating: + ## @param coordinating.replicaCount Number of coordinating-only replicas to deploy + ## + replicaCount: 1 + ## @param coordinating.nameOverride String to partially override elasticsearch.coordinating.fullname + ## + nameOverride: "" + ## @param coordinating.fullnameOverride String to fully override elasticsearch.coordinating.fullname + ## + fullnameOverride: "" + ## @param coordinating.servicenameOverride String to fully override elasticsearch.coordinating.servicename + ## + servicenameOverride: "" + ## @param coordinating.annotations [object] Annotations for the coordinating-only statefulset + ## + annotations: {} + ## @param coordinating.updateStrategy.type Coordinating-only nodes statefulset stategy type + ## ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#update-strategies + ## + updateStrategy: + type: RollingUpdate + ## Elasticsearch resource requests and limits + ## ref: http://kubernetes.io/docs/user-guide/compute-resources/ + ## @param coordinating.resources.limits The resources limits for the coordinating-only containers + ## @param coordinating.resources.requests [object] The requested resources for the coordinating-only containers + ## + resources: + limits: {} + requests: + cpu: 25m + memory: 256Mi + ## @param coordinating.heapSize Elasticsearch coordinating node heap size. + ## Note: The recommended heapSize is half of the container's memory. + ## If omitted, it will be automatically set. + ## Example: + ## heapSize: 128m + ## + heapSize: 128m + ## Configure Pods Security Context + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod + ## @param coordinating.podSecurityContext.enabled Enabled coordinating-only pods' Security Context + ## @param coordinating.podSecurityContext.fsGroup Set coordinating-only pod's Security Context fsGroup + ## + podSecurityContext: + enabled: true + fsGroup: 1001 + ## Configure Container Security Context + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod + ## @param coordinating.containerSecurityContext.enabled Enabled coordinating-only containers' Security Context + ## @param coordinating.containerSecurityContext.runAsUser Set coordinating-only containers' Security Context runAsUser + ## @param coordinating.containerSecurityContext.runAsNonRoot Set coordinating-only containers' Security Context runAsNonRoot + ## + containerSecurityContext: + enabled: true + runAsUser: 1001 + runAsNonRoot: true + ## @param coordinating.hostAliases coordinating-only pods host aliases + ## https://kubernetes.io/docs/concepts/services-networking/add-entries-to-pod-etc-hosts-with-host-aliases/ + ## + hostAliases: [] + ## @param coordinating.podLabels Extra labels for coordinating-only pods + ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + ## + podLabels: {} + ## @param coordinating.podAnnotations Annotations for coordinating-only pods + ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ + ## + podAnnotations: {} + ## @param coordinating.podAffinityPreset Pod affinity preset. Ignored if `coordinating.affinity` is set. Allowed values: `soft` or `hard` + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity + ## + podAffinityPreset: "" + ## @param coordinating.podAntiAffinityPreset Pod anti-affinity preset. Ignored if `coordinating.affinity` is set. Allowed values: `soft` or `hard` + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity + ## + podAntiAffinityPreset: "" + ## Node coordinating.affinity preset + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity + ## + nodeAffinityPreset: + ## @param coordinating.nodeAffinityPreset.type Node affinity preset type. Ignored if `coordinating.affinity` is set. Allowed values: `soft` or `hard` + ## + type: "" + ## @param coordinating.nodeAffinityPreset.key Node label key to match. Ignored if `coordinating.affinity` is set + ## + key: "" + ## @param coordinating.nodeAffinityPreset.values Node label values to match. Ignored if `coordinating.affinity` is set + ## E.g. + ## values: + ## - e2e-az1 + ## - e2e-az2 + ## + values: [] + ## @param coordinating.affinity Affinity for coordinating-only pods assignment + ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity + ## NOTE: `coordinating.podAffinityPreset`, `coordinating.podAntiAffinityPreset`, and `coordinating.nodeAffinityPreset` will be ignored when it's set + ## + affinity: {} + ## @param coordinating.nodeSelector Node labels for coordinating-only pods assignment + ## ref: https://kubernetes.io/docs/user-guide/node-selection/ + ## + nodeSelector: {} + ## @param coordinating.tolerations Tolerations for coordinating-only pods assignment + ## ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ + ## + tolerations: [] + ## @param coordinating.priorityClassName coordinating-only pods' priorityClassName + ## + priorityClassName: "" + ## @param coordinating.schedulerName Name of the k8s scheduler (other than default) for coordinating-only pods + ## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ + ## + schedulerName: "" + ## @param coordinating.terminationGracePeriodSeconds In seconds, time the given to the Elasticsearch coordinating pod needs to terminate gracefully + ## ref: https://kubernetes.io/docs/concepts/workloads/pods/pod/#termination-of-pods + ## + terminationGracePeriodSeconds: "" + ## @param coordinating.topologySpreadConstraints Topology Spread Constraints for pod assignment spread across your cluster among failure-domains. Evaluated as a template + ## Ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/#spread-constraints-for-pods + ## + topologySpreadConstraints: [] + ## @param coordinating.podManagementPolicy podManagementPolicy to manage scaling operation of Elasticsearch coordinating pods + ## ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#pod-management-policies + ## + podManagementPolicy: "Parallel" + ## Configure extra options for Elasticsearch coordinating-only containers' liveness, readiness and startup probes + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes + ## @param coordinating.startupProbe.enabled Enable/disable the startup probe (coordinating-only nodes pod) + ## @param coordinating.startupProbe.initialDelaySeconds Delay before startup probe is initiated (coordinating-only nodes pod) + ## @param coordinating.startupProbe.periodSeconds How often to perform the probe (coordinating-only nodes pod) + ## @param coordinating.startupProbe.timeoutSeconds When the probe times out (coordinating-only nodes pod) + ## @param coordinating.startupProbe.successThreshold Minimum consecutive successes for the probe to be considered successful after having failed (coordinating-only nodes pod) + ## @param coordinating.startupProbe.failureThreshold Minimum consecutive failures for the probe to be considered failed after having succeeded + ## + startupProbe: + enabled: false + initialDelaySeconds: 90 + periodSeconds: 10 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 5 + ## @param coordinating.livenessProbe.enabled Enable/disable the liveness probe (coordinating-only nodes pod) + ## @param coordinating.livenessProbe.initialDelaySeconds Delay before liveness probe is initiated (coordinating-only nodes pod) + ## @param coordinating.livenessProbe.periodSeconds How often to perform the probe (coordinating-only nodes pod) + ## @param coordinating.livenessProbe.timeoutSeconds When the probe times out (coordinating-only nodes pod) + ## @param coordinating.livenessProbe.successThreshold Minimum consecutive successes for the probe to be considered successful after having failed (coordinating-only nodes pod) + ## @param coordinating.livenessProbe.failureThreshold Minimum consecutive failures for the probe to be considered failed after having succeeded + ## + livenessProbe: + enabled: true + initialDelaySeconds: 90 + periodSeconds: 10 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 5 + ## @param coordinating.readinessProbe.enabled Enable/disable the readiness probe (coordinating-only nodes pod) + ## @param coordinating.readinessProbe.initialDelaySeconds Delay before readiness probe is initiated (coordinating-only nodes pod) + ## @param coordinating.readinessProbe.periodSeconds How often to perform the probe (coordinating-only nodes pod) + ## @param coordinating.readinessProbe.timeoutSeconds When the probe times out (coordinating-only nodes pod) + ## @param coordinating.readinessProbe.successThreshold Minimum consecutive successes for the probe to be considered successful after having failed (coordinating-only nodes pod) + ## @param coordinating.readinessProbe.failureThreshold Minimum consecutive failures for the probe to be considered failed after having succeeded + ## + readinessProbe: + enabled: true + initialDelaySeconds: 90 + periodSeconds: 10 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 5 + ## @param coordinating.customStartupProbe Override default startup probe + ## + customStartupProbe: {} + ## @param coordinating.customLivenessProbe Override default liveness probe + ## + customLivenessProbe: {} + ## @param coordinating.customReadinessProbe Override default readiness probe + ## + customReadinessProbe: {} + ## @param coordinating.command Override default container command (useful when using custom images) + ## + command: [] + ## @param coordinating.args Override default container args (useful when using custom images) + ## + args: [] + ## @param coordinating.lifecycleHooks for the coordinating-only container(s) to automate configuration before or after startup + ## + lifecycleHooks: {} + ## @param coordinating.extraEnvVars Array with extra environment variables to add to coordinating-only nodes + ## e.g: + ## extraEnvVars: + ## - name: FOO + ## value: "bar" + ## + extraEnvVars: [] + ## @param coordinating.extraEnvVarsCM Name of existing ConfigMap containing extra env vars for coordinating-only nodes + ## + extraEnvVarsCM: "" + ## @param coordinating.extraEnvVarsSecret Name of existing Secret containing extra env vars for coordinating-only nodes + ## + extraEnvVarsSecret: "" + ## @param coordinating.extraVolumes Optionally specify extra list of additional volumes for the coordinating-only pod(s) + ## + extraVolumes: [] + ## @param coordinating.extraVolumeMounts Optionally specify extra list of additional volumeMounts for the coordinating-only container(s) + ## + extraVolumeMounts: [] + ## @param coordinating.sidecars Add additional sidecar containers to the coordinating-only pod(s) + ## e.g: + ## sidecars: + ## - name: your-image-name + ## image: your-image + ## imagePullPolicy: Always + ## ports: + ## - name: portname + ## containerPort: 1234 + ## + sidecars: [] + ## @param coordinating.initContainers Add additional init containers to the coordinating-only pod(s) + ## ref: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ + ## e.g: + ## initContainers: + ## - name: your-image-name + ## image: your-image + ## imagePullPolicy: Always + ## command: ['sh', '-c', 'echo "hello world"'] + ## + initContainers: [] + ## Pods Service Account + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ + ## @param coordinating.serviceAccount.create Specifies whether a ServiceAccount should be created + ## @param coordinating.serviceAccount.name Name of the service account to use. If not set and create is true, a name is generated using the fullname template. + ## @param coordinating.serviceAccount.automountServiceAccountToken Automount service account token for the server service account + ## @param coordinating.serviceAccount.annotations Annotations for service account. Evaluated as a template. Only used if `create` is `true`. + ## + serviceAccount: + create: false + name: "" + automountServiceAccountToken: true + annotations: {} + ## Enable HorizontalPodAutoscaler for Elasticsearch coordinating pods + ## ref: https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/ + ## @param coordinating.autoscaling.enabled Whether enable horizontal pod autoscale + ## @param coordinating.autoscaling.minReplicas Configure a minimum amount of pods + ## @param coordinating.autoscaling.maxReplicas Configure a maximum amount of pods + ## @param coordinating.autoscaling.targetCPU Define the CPU target to trigger the scaling actions (utilization percentage) + ## @param coordinating.autoscaling.targetMemory Define the memory target to trigger the scaling actions (utilization percentage) + ## + autoscaling: + enabled: false + minReplicas: 3 + maxReplicas: 11 + targetCPU: "" + targetMemory: "" + +## @section Ingest-only nodes parameters + +ingest: + ## @param ingest.enabled Enable ingest nodes + ## + enabled: true + ## @param ingest.replicaCount Number of ingest-only replicas to deploy + ## + replicaCount: 1 + ## @param ingest.nameOverride String to partially override elasticsearch.ingest.fullname + ## + nameOverride: "" + ## @param ingest.fullnameOverride String to fully override elasticsearch.ingest.fullname + ## + fullnameOverride: "" + ## @param ingest.servicenameOverride String to fully override ingest.master.servicename + ## + servicenameOverride: "" + ## @param ingest.annotations [object] Annotations for the ingest statefulset + ## + annotations: {} + ## @param ingest.containerPorts.restAPI Elasticsearch REST API port + ## @param ingest.containerPorts.transport Elasticsearch Transport port + ## + containerPorts: + restAPI: 9200 + transport: 9300 + ## @param ingest.updateStrategy.type Ingest-only nodes statefulset stategy type + ## ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#update-strategies + ## + updateStrategy: + type: RollingUpdate + ## Elasticsearch resource requests and limits + ## ref: http://kubernetes.io/docs/user-guide/compute-resources/ + ## @param ingest.resources.limits The resources limits for the ingest-only containers + ## @param ingest.resources.requests [object] The requested resources for the ingest-only containers + ## + resources: + limits: {} + requests: + cpu: 25m + memory: 256Mi + ## @param ingest.heapSize Elasticsearch ingest-only node heap size. + ## Note: The recommended heapSize is half of the container's memory. + ## If omitted, it will be automatically set. + ## Example: + ## heapSize: 128m + ## + heapSize: 128m + ## Configure Pods Security Context + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod + ## @param ingest.podSecurityContext.enabled Enabled ingest-only pods' Security Context + ## @param ingest.podSecurityContext.fsGroup Set ingest-only pod's Security Context fsGroup + ## + podSecurityContext: + enabled: true + fsGroup: 1001 + ## Configure Container Security Context + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod + ## @param ingest.containerSecurityContext.enabled Enabled ingest-only containers' Security Context + ## @param ingest.containerSecurityContext.runAsUser Set ingest-only containers' Security Context runAsUser + ## @param ingest.containerSecurityContext.runAsNonRoot Set ingest-only containers' Security Context runAsNonRoot + ## + containerSecurityContext: + enabled: true + runAsUser: 1001 + runAsNonRoot: true + ## @param ingest.hostAliases ingest-only pods host aliases + ## https://kubernetes.io/docs/concepts/services-networking/add-entries-to-pod-etc-hosts-with-host-aliases/ + ## + hostAliases: [] + ## @param ingest.podLabels Extra labels for ingest-only pods + ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + ## + podLabels: {} + ## @param ingest.podAnnotations Annotations for ingest-only pods + ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ + ## + podAnnotations: {} + ## @param ingest.podAffinityPreset Pod affinity preset. Ignored if `ingest.affinity` is set. Allowed values: `soft` or `hard` + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity + ## + podAffinityPreset: "" + ## @param ingest.podAntiAffinityPreset Pod anti-affinity preset. Ignored if `ingest.affinity` is set. Allowed values: `soft` or `hard` + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity + ## + podAntiAffinityPreset: "" + ## Node ingest.affinity preset + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity + ## + nodeAffinityPreset: + ## @param ingest.nodeAffinityPreset.type Node affinity preset type. Ignored if `ingest.affinity` is set. Allowed values: `soft` or `hard` + ## + type: "" + ## @param ingest.nodeAffinityPreset.key Node label key to match. Ignored if `ingest.affinity` is set + ## + key: "" + ## @param ingest.nodeAffinityPreset.values Node label values to match. Ignored if `ingest.affinity` is set + ## E.g. + ## values: + ## - e2e-az1 + ## - e2e-az2 + ## + values: [] + ## @param ingest.affinity Affinity for ingest-only pods assignment + ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity + ## NOTE: `ingest.podAffinityPreset`, `ingest.podAntiAffinityPreset`, and `ingest.nodeAffinityPreset` will be ignored when it's set + ## + affinity: {} + ## @param ingest.nodeSelector Node labels for ingest-only pods assignment + ## ref: https://kubernetes.io/docs/user-guide/node-selection/ + ## + nodeSelector: {} + ## @param ingest.tolerations Tolerations for ingest-only pods assignment + ## ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ + ## + tolerations: [] + ## @param ingest.priorityClassName ingest-only pods' priorityClassName + ## + priorityClassName: "" + ## @param ingest.schedulerName Name of the k8s scheduler (other than default) for ingest-only pods + ## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ + ## + schedulerName: "" + ## @param ingest.terminationGracePeriodSeconds In seconds, time the given to the Elasticsearch ingest pod needs to terminate gracefully + ## ref: https://kubernetes.io/docs/concepts/workloads/pods/pod/#termination-of-pods + ## + terminationGracePeriodSeconds: "" + ## @param ingest.topologySpreadConstraints Topology Spread Constraints for pod assignment spread across your cluster among failure-domains. Evaluated as a template + ## Ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/#spread-constraints-for-pods + ## + topologySpreadConstraints: [] + ## @param ingest.podManagementPolicy podManagementPolicy to manage scaling operation of Elasticsearch ingest pods + ## ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#pod-management-policies + ## + podManagementPolicy: "Parallel" + ## Configure extra options for Elasticsearch ingest-only containers' liveness, readiness and startup probes + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes + ## @param ingest.startupProbe.enabled Enable/disable the startup probe (ingest-only nodes pod) + ## @param ingest.startupProbe.initialDelaySeconds Delay before startup probe is initiated (ingest-only nodes pod) + ## @param ingest.startupProbe.periodSeconds How often to perform the probe (ingest-only nodes pod) + ## @param ingest.startupProbe.timeoutSeconds When the probe times out (ingest-only nodes pod) + ## @param ingest.startupProbe.successThreshold Minimum consecutive successes for the probe to be considered successful after having failed (ingest-only nodes pod) + ## @param ingest.startupProbe.failureThreshold Minimum consecutive failures for the probe to be considered failed after having succeeded + ## + startupProbe: + enabled: false + initialDelaySeconds: 90 + periodSeconds: 10 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 5 + ## @param ingest.livenessProbe.enabled Enable/disable the liveness probe (ingest-only nodes pod) + ## @param ingest.livenessProbe.initialDelaySeconds Delay before liveness probe is initiated (ingest-only nodes pod) + ## @param ingest.livenessProbe.periodSeconds How often to perform the probe (ingest-only nodes pod) + ## @param ingest.livenessProbe.timeoutSeconds When the probe times out (ingest-only nodes pod) + ## @param ingest.livenessProbe.successThreshold Minimum consecutive successes for the probe to be considered successful after having failed (ingest-only nodes pod) + ## @param ingest.livenessProbe.failureThreshold Minimum consecutive failures for the probe to be considered failed after having succeeded + ## + livenessProbe: + enabled: true + initialDelaySeconds: 90 + periodSeconds: 10 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 5 + ## @param ingest.readinessProbe.enabled Enable/disable the readiness probe (ingest-only nodes pod) + ## @param ingest.readinessProbe.initialDelaySeconds Delay before readiness probe is initiated (ingest-only nodes pod) + ## @param ingest.readinessProbe.periodSeconds How often to perform the probe (ingest-only nodes pod) + ## @param ingest.readinessProbe.timeoutSeconds When the probe times out (ingest-only nodes pod) + ## @param ingest.readinessProbe.successThreshold Minimum consecutive successes for the probe to be considered successful after having failed (ingest-only nodes pod) + ## @param ingest.readinessProbe.failureThreshold Minimum consecutive failures for the probe to be considered failed after having succeeded + ## + readinessProbe: + enabled: true + initialDelaySeconds: 90 + periodSeconds: 10 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 5 + ## @param ingest.customStartupProbe Override default startup probe + ## + customStartupProbe: {} + ## @param ingest.customLivenessProbe Override default liveness probe + ## + customLivenessProbe: {} + ## @param ingest.customReadinessProbe Override default readiness probe + ## + customReadinessProbe: {} + ## @param ingest.command Override default container command (useful when using custom images) + ## + command: [] + ## @param ingest.args Override default container args (useful when using custom images) + ## + args: [] + ## @param ingest.lifecycleHooks for the ingest-only container(s) to automate configuration before or after startup + ## + lifecycleHooks: {} + ## @param ingest.extraEnvVars Array with extra environment variables to add to ingest-only nodes + ## e.g: + ## extraEnvVars: + ## - name: FOO + ## value: "bar" + ## + extraEnvVars: [] + ## @param ingest.extraEnvVarsCM Name of existing ConfigMap containing extra env vars for ingest-only nodes + ## + extraEnvVarsCM: "" + ## @param ingest.extraEnvVarsSecret Name of existing Secret containing extra env vars for ingest-only nodes + ## + extraEnvVarsSecret: "" + ## @param ingest.extraVolumes Optionally specify extra list of additional volumes for the ingest-only pod(s) + ## + extraVolumes: [] + ## @param ingest.extraVolumeMounts Optionally specify extra list of additional volumeMounts for the ingest-only container(s) + ## + extraVolumeMounts: [] + ## @param ingest.sidecars Add additional sidecar containers to the ingest-only pod(s) + ## e.g: + ## sidecars: + ## - name: your-image-name + ## image: your-image + ## imagePullPolicy: Always + ## ports: + ## - name: portname + ## containerPort: 1234 + ## + sidecars: [] + ## @param ingest.initContainers Add additional init containers to the ingest-only pod(s) + ## ref: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ + ## e.g: + ## initContainers: + ## - name: your-image-name + ## image: your-image + ## imagePullPolicy: Always + ## command: ['sh', '-c', 'echo "hello world"'] + ## + initContainers: [] + ## Pods Service Account + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ + ## @param ingest.serviceAccount.create Specifies whether a ServiceAccount should be created + ## @param ingest.serviceAccount.name Name of the service account to use. If not set and create is true, a name is generated using the fullname template. + ## @param ingest.serviceAccount.automountServiceAccountToken Automount service account token for the server service account + ## @param ingest.serviceAccount.annotations Annotations for service account. Evaluated as a template. Only used if `create` is `true`. + ## + serviceAccount: + create: false + name: "" + automountServiceAccountToken: true + annotations: {} + ## Enable HorizontalPodAutoscaler for Elasticsearch ingest-only pods + ## ref: https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/ + ## @param ingest.autoscaling.enabled Whether enable horizontal pod autoscale + ## @param ingest.autoscaling.minReplicas Configure a minimum amount of pods + ## @param ingest.autoscaling.maxReplicas Configure a maximum amount of pods + ## @param ingest.autoscaling.targetCPU Define the CPU target to trigger the scaling actions (utilization percentage) + ## @param ingest.autoscaling.targetMemory Define the memory target to trigger the scaling actions (utilization percentage) + ## + autoscaling: + enabled: false + minReplicas: 3 + maxReplicas: 11 + targetCPU: "" + targetMemory: "" + + ## Elasticsearch Ingest-only Service + ## Recommended for heavy ingestion, improves performance by sending ingest traffic directly into the ingest nodes. + ## NOTE: Ingest nodes will only accept index requests with an associated pipeline, any other request won't be rerouted. + ## + service: + ## @param ingest.service.enabled Enable Ingest-only service + ## + enabled: false + ## @param ingest.service.type Elasticsearch ingest-only service type + ## + type: ClusterIP + ## @param ingest.service.ports.restAPI Elasticsearch service REST API port + ## @param ingest.service.ports.transport Elasticsearch service transport port + ## + ports: + restAPI: 9200 + transport: 9300 + ## Node ports to expose + ## @param ingest.service.nodePorts.restAPI Node port for REST API + ## @param ingest.service.nodePorts.transport Node port for REST API + ## NOTE: choose port between <30000-32767> + ## + nodePorts: + restAPI: "" + transport: "" + ## @param ingest.service.clusterIP Elasticsearch ingest-only service Cluster IP + ## e.g.: + ## clusterIP: None + ## + clusterIP: "" + ## @param ingest.service.loadBalancerIP Elasticsearch ingest-only service Load Balancer IP + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-loadbalancer + ## + loadBalancerIP: "" + ## @param ingest.service.loadBalancerSourceRanges Elasticsearch ingest-only service Load Balancer sources + ## ref: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/#restrict-access-for-loadbalancer-service + ## e.g: + ## loadBalancerSourceRanges: + ## - 10.10.10.0/24 + ## + loadBalancerSourceRanges: [] + ## @param ingest.service.externalTrafficPolicy Elasticsearch ingest-only service external traffic policy + ## ref http://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip + ## + externalTrafficPolicy: Cluster + ## @param ingest.service.extraPorts Extra ports to expose (normally used with the `sidecar` value) + ## + extraPorts: [] + ## @param ingest.service.annotations Additional custom annotations for Elasticsearch ingest-only service + ## + annotations: {} + ## @param ingest.service.sessionAffinity Session Affinity for Kubernetes service, can be "None" or "ClientIP" + ## If "ClientIP", consecutive client requests will be directed to the same Pod + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies + ## + sessionAffinity: None + ## @param ingest.service.sessionAffinityConfig Additional settings for the sessionAffinity + ## sessionAffinityConfig: + ## clientIP: + ## timeoutSeconds: 300 + ## + sessionAffinityConfig: {} + ## Elasticsearch Ingest-only ingress parameters + ## ref: http://kubernetes.io/docs/user-guide/ingress/ + ## + ingress: + ## @param ingest.ingress.enabled Enable ingress record generation for Elasticsearch + ## + enabled: false + ## @param ingest.ingress.pathType Ingress path type + ## + pathType: ImplementationSpecific + ## @param ingest.ingress.apiVersion Force Ingress API version (automatically detected if not set) + ## + apiVersion: "" + ## @param ingest.ingress.hostname Default host for the ingress record + ## + hostname: elasticsearch-ingest.local + ## @param ingest.ingress.path Default path for the ingress record + ## NOTE: You may need to set this to '/*' in order to use this with ALB ingress controllers + ## + path: / + ## @param ingest.ingress.annotations Additional annotations for the Ingress resource. To enable certificate autogeneration, place here your cert-manager annotations. + ## Use this parameter to set the required annotations for cert-manager, see + ## ref: https://cert-manager.io/docs/usage/ingress/#supported-annotations + ## e.g: + ## annotations: + ## kubernetes.io/ingress.class: nginx + ## cert-manager.io/cluster-issuer: cluster-issuer-name + ## + annotations: {} + ## @param ingest.ingress.tls Enable TLS configuration for the host defined at `ingress.hostname` parameter + ## TLS certificates will be retrieved from a TLS secret with name: `{{- printf "%s-tls" .Values.ingress.hostname }}` + ## You can: + ## - Use the `ingress.secrets` parameter to create this TLS secret + ## - Rely on cert-manager to create it by setting the corresponding annotations + ## - Rely on Helm to create self-signed certificates by setting `ingress.selfSigned=true` + ## + tls: false + ## @param ingest.ingress.selfSigned Create a TLS secret for this ingress record using self-signed certificates generated by Helm + ## + selfSigned: false + ## @param ingest.ingress.ingressClassName IngressClass that will be be used to implement the Ingress (Kubernetes 1.18+) + ## This is supported in Kubernetes 1.18+ and required if you have more than one IngressClass marked as the default for your cluster . + ## ref: https://kubernetes.io/blog/2020/04/02/improvements-to-the-ingress-api-in-kubernetes-1.18/ + ## + ingressClassName: "" + ## @param ingest.ingress.extraHosts An array with additional hostname(s) to be covered with the ingress record + ## e.g: + ## extraHosts: + ## - name: elasticsearch.local + ## path: / + ## + extraHosts: [] + ## @param ingest.ingress.extraPaths An array with additional arbitrary paths that may need to be added to the ingress under the main host + ## e.g: + ## extraPaths: + ## - path: /* + ## backend: + ## serviceName: ssl-redirect + ## servicePort: use-annotation + ## + extraPaths: [] + ## @param ingest.ingress.extraTls TLS configuration for additional hostname(s) to be covered with this ingress record + ## ref: https://kubernetes.io/docs/concepts/services-networking/ingress/#tls + ## e.g: + ## extraTls: + ## - hosts: + ## - elasticsearch.local + ## secretName: elasticsearch.local-tls + ## + extraTls: [] + ## @param ingest.ingress.secrets Custom TLS certificates as secrets + ## NOTE: 'key' and 'certificate' are expected in PEM format + ## NOTE: 'name' should line up with a 'secretName' set further up + ## If it is not set and you're using cert-manager, this is unneeded, as it will create a secret for you with valid certificates + ## If it is not set and you're NOT using cert-manager either, self-signed certificates will be created valid for 365 days + ## It is also possible to create and manage the certificates outside of this helm chart + ## Please see README.md for more information + ## e.g: + ## secrets: + ## - name: elasticsearch.local-tls + ## key: |- + ## -----BEGIN RSA PRIVATE KEY----- + ## ... + ## -----END RSA PRIVATE KEY----- + ## certificate: |- + ## -----BEGIN CERTIFICATE----- + ## ... + ## -----END CERTIFICATE----- + ## + secrets: [] + ## @param ingest.ingress.extraRules Additional rules to be covered with this ingress record + ## ref: https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-rules + ## e.g: + ## extraRules: + ## - host: example.local + ## http: + ## path: / + ## backend: + ## service: + ## name: example-svc + ## port: + ## name: http + ## + extraRules: [] + + +## @section Metrics parameters + +## Elasticsearch Prometheus exporter configuration +## ref: https://hub.docker.com/r/bitnami/elasticsearch-exporter/tags/ +## +metrics: + ## @param metrics.enabled Enable prometheus exporter + ## + enabled: false + ## @param metrics.nameOverride Metrics pod name + ## + nameOverride: "" + ## @param metrics.fullnameOverride String to fully override common.names.fullname + ## + fullnameOverride: "" + ## @param metrics.image.registry Metrics exporter image registry + ## @param metrics.image.repository Metrics exporter image repository + ## @param metrics.image.tag Metrics exporter image tag + ## @param metrics.image.digest Metrics exporter image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag + ## @param metrics.image.pullPolicy Metrics exporter image pull policy + ## @param metrics.image.pullSecrets Metrics exporter image pull secrets + ## + image: + registry: docker.io + repository: bitnami/elasticsearch-exporter + tag: 1.5.0-debian-11-r80 + digest: "" + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## e.g: + ## pullSecrets: + ## - myRegistryKeySecretName + ## + pullSecrets: [] + ## @param metrics.annotations [object] Annotations for metrics + ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ + ## + annotations: + helm.sh/hook: "post-install,post-upgrade" + helm.sh/hook-weight: "5" + ## @param metrics.extraArgs Extra arguments to add to the default exporter command + ## ref: https://github.com/justwatchcom/elasticsearch_exporter + ## e.g + ## extraArgs: + ## - --es.snapshots + ## - --es.indices + ## + extraArgs: [] + ## @param metrics.hostAliases Add deployment host aliases + ## https://kubernetes.io/docs/concepts/services-networking/add-entries-to-pod-etc-hosts-with-host-aliases/ + ## + hostAliases: [] + ## @param metrics.schedulerName Name of the k8s scheduler (other than default) + ## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ + ## + schedulerName: "" + ## @param metrics.priorityClassName Elasticsearch metrics exporter pods' priorityClassName + ## + priorityClassName: "" + ## Elasticsearch Prometheus exporter service type + ## + service: + ## @param metrics.service.type Metrics exporter endpoint service type + ## + type: ClusterIP + ## @param metrics.service.port Metrics exporter endpoint service port + ## + port: 9114 + ## @param metrics.service.annotations [object] Provide any additional annotations which may be required. + ## This can be used to set the LoadBalancer service type to internal only. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer + ## + annotations: + prometheus.io/scrape: "true" + prometheus.io/port: "9114" + ## @param metrics.podAffinityPreset Metrics Pod affinity preset. Ignored if `affinity` is set. Allowed values: `soft` or `hard` + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity + ## + podAffinityPreset: "" + ## @param metrics.podAntiAffinityPreset Metrics Pod anti-affinity preset. Ignored if `affinity` is set. Allowed values: `soft` or `hard` + ## Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity + ## + podAntiAffinityPreset: "" + ## Node affinity preset + ## Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity + ## @param metrics.nodeAffinityPreset.type Metrics Node affinity preset type. Ignored if `affinity` is set. Allowed values: `soft` or `hard` + ## @param metrics.nodeAffinityPreset.key Metrics Node label key to match Ignored if `affinity` is set. + ## @param metrics.nodeAffinityPreset.values Metrics Node label values to match. Ignored if `affinity` is set. + ## + nodeAffinityPreset: + type: "" + ## E.g. + ## key: "kubernetes.io/e2e-az-name" + ## + key: "" + ## E.g. + ## values: + ## - e2e-az1 + ## - e2e-az2 + ## + values: [] + ## @param metrics.affinity Metrics Affinity for pod assignment + ## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity + ## Note: podAffinityPreset, podAntiAffinityPreset, and nodeAffinityPreset will be ignored when it's set + ## + affinity: {} + ## @param metrics.nodeSelector Metrics Node labels for pod assignment + ## Ref: https://kubernetes.io/docs/user-guide/node-selection/ + ## + nodeSelector: {} + ## @param metrics.tolerations Metrics Tolerations for pod assignment + ## Ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ + ## + tolerations: [] + ## @param metrics.topologySpreadConstraints Topology Spread Constraints for pod assignment spread across your cluster among failure-domains. Evaluated as a template + ## Ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/#spread-constraints-for-pods + ## + topologySpreadConstraints: [] + ## Elasticsearch Prometheus exporter resource requests and limits + ## ref: https://kubernetes.io/docs/user-guide/compute-resources/ + ## We usually recommend not to specify default resources and to leave this as a conscious + ## choice for the user. This also increases chances charts run on environments with little + ## resources, such as Minikube. If you do want to specify resources, uncomment the following + ## lines, adjust them as necessary, and remove the curly braces after 'resources:'. + ## @param metrics.resources.limits The resources limits for the container + ## @param metrics.resources.requests The requested resources for the container + ## + resources: + ## Example: + ## limits: + ## cpu: 100m + ## memory: 128Mi + limits: {} + ## Examples: + ## requests: + ## cpu: 100m + ## memory: 128Mi + requests: {} + ## Elasticsearch metrics container's liveness probe + ## ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes + ## @param metrics.livenessProbe.enabled Enable/disable the liveness probe (metrics pod) + ## @param metrics.livenessProbe.initialDelaySeconds Delay before liveness probe is initiated (metrics pod) + ## @param metrics.livenessProbe.periodSeconds How often to perform the probe (metrics pod) + ## @param metrics.livenessProbe.timeoutSeconds When the probe times out (metrics pod) + ## @param metrics.livenessProbe.failureThreshold Minimum consecutive failures for the probe to be considered failed after having succeeded + ## @param metrics.livenessProbe.successThreshold Minimum consecutive successes for the probe to be considered successful after having failed (metrics pod) + ## + livenessProbe: + enabled: true + initialDelaySeconds: 60 + periodSeconds: 10 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 5 + ## Elasticsearch metrics container's readiness probe + ## ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes + ## @param metrics.readinessProbe.enabled Enable/disable the readiness probe (metrics pod) + ## @param metrics.readinessProbe.initialDelaySeconds Delay before readiness probe is initiated (metrics pod) + ## @param metrics.readinessProbe.periodSeconds How often to perform the probe (metrics pod) + ## @param metrics.readinessProbe.timeoutSeconds When the probe times out (metrics pod) + ## @param metrics.readinessProbe.failureThreshold Minimum consecutive failures for the probe to be considered failed after having succeeded + ## @param metrics.readinessProbe.successThreshold Minimum consecutive successes for the probe to be considered successful after having failed (metrics pod) + ## + readinessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 5 + ## Elasticsearch metrics container's startup probe + ## ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes + ## @param metrics.startupProbe.enabled Enable/disable the startup probe (metrics pod) + ## @param metrics.startupProbe.initialDelaySeconds Delay before startup probe is initiated (metrics pod) + ## @param metrics.startupProbe.periodSeconds How often to perform the probe (metrics pod) + ## @param metrics.startupProbe.timeoutSeconds When the probe times out (metrics pod) + ## @param metrics.startupProbe.failureThreshold Minimum consecutive failures for the probe to be considered failed after having succeeded + ## @param metrics.startupProbe.successThreshold Minimum consecutive successes for the probe to be considered successful after having failed (metrics pod) + ## + startupProbe: + enabled: false + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 5 + ## @param metrics.customStartupProbe Custom liveness probe for the Web component + ## + customStartupProbe: {} + ## @param metrics.customLivenessProbe Custom liveness probe for the Web component + ## + customLivenessProbe: {} + ## @param metrics.customReadinessProbe Custom readiness probe for the Web component + ## + customReadinessProbe: {} + ## @param metrics.podAnnotations [object] Metrics exporter pod Annotation and Labels + ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ + ## + podAnnotations: + prometheus.io/scrape: "true" + prometheus.io/port: "9114" + ## @param metrics.podLabels Extra labels to add to Pod + ## + podLabels: {} + ## Configure Pods Security Context + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod + ## @param metrics.podSecurityContext.enabled Enabled Elasticsearch metrics exporter pods' Security Context + ## @param metrics.podSecurityContext.fsGroup Set Elasticsearch metrics exporter pod's Security Context fsGroup + ## + podSecurityContext: + enabled: true + fsGroup: 1001 + ## Configure Container Security Context + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod + ## @param metrics.containerSecurityContext.enabled Enabled Elasticsearch metrics exporter containers' Security Context + ## @param metrics.containerSecurityContext.runAsUser Set Elasticsearch metrics exporter containers' Security Context runAsUser + ## @param metrics.containerSecurityContext.runAsNonRoot Set Elasticsearch metrics exporter container's Security Context runAsNonRoot + ## + containerSecurityContext: + enabled: true + runAsUser: 1001 + runAsNonRoot: true + ## @param metrics.command Override default container command (useful when using custom images) + ## + command: [] + ## @param metrics.args Override default container args (useful when using custom images) + ## + args: [] + ## @param metrics.extraEnvVars Array with extra environment variables to add to Elasticsearch metrics exporter nodes + ## e.g: + ## extraEnvVars: + ## - name: FOO + ## value: "bar" + ## + extraEnvVars: [] + ## @param metrics.extraEnvVarsCM Name of existing ConfigMap containing extra env vars for Elasticsearch metrics exporter nodes + ## + extraEnvVarsCM: "" + ## @param metrics.extraEnvVarsSecret Name of existing Secret containing extra env vars for Elasticsearch metrics exporter nodes + ## + extraEnvVarsSecret: "" + ## @param metrics.extraVolumes Optionally specify extra list of additional volumes for the Elasticsearch metrics exporter pod(s) + ## + extraVolumes: [] + ## @param metrics.extraVolumeMounts Optionally specify extra list of additional volumeMounts for the Elasticsearch metrics exporter container(s) + ## + extraVolumeMounts: [] + ## @param metrics.sidecars Add additional sidecar containers to the Elasticsearch metrics exporter pod(s) + ## e.g: + ## sidecars: + ## - name: your-image-name + ## image: your-image + ## imagePullPolicy: Always + ## ports: + ## - name: portname + ## containerPort: 1234 + ## + sidecars: [] + ## @param metrics.initContainers Add additional init containers to the Elasticsearch metrics exporter pod(s) + ## ref: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ + ## e.g: + ## initContainers: + ## - name: your-image-name + ## image: your-image + ## imagePullPolicy: Always + ## command: ['sh', '-c', 'echo "hello world"'] + ## + initContainers: [] + ## Prometheus Operator ServiceMonitor configuration + ## + serviceMonitor: + ## @param metrics.serviceMonitor.enabled Create ServiceMonitor Resource for scraping metrics using PrometheusOperator + ## + enabled: false + ## @param metrics.serviceMonitor.namespace Namespace which Prometheus is running in + ## e.g: + ## namespace: monitoring + ## + namespace: knowlg-db + ## @param metrics.serviceMonitor.jobLabel The name of the label on the target service to use as the job name in prometheus. + ## + jobLabel: "" + ## @param metrics.serviceMonitor.interval Interval at which metrics should be scraped + ## ref: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#endpoint + ## + interval: "" + ## @param metrics.serviceMonitor.scrapeTimeout Timeout after which the scrape is ended + ## ref: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#endpoint + ## + scrapeTimeout: "" + ## @param metrics.serviceMonitor.relabelings RelabelConfigs to apply to samples before scraping + ## ref: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#relabelconfig + ## + relabelings: [] + ## @param metrics.serviceMonitor.metricRelabelings MetricRelabelConfigs to apply to samples before ingestion + ## ref: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#relabelconfig + ## + metricRelabelings: [] + ## @param metrics.serviceMonitor.selector ServiceMonitor selector labels + ## ref: https://github.com/bitnami/charts/tree/main/bitnami/prometheus-operator#prometheus-configuration + ## + ## selector: + ## prometheus: my-prometheus + ## + selector: {} + ## @param metrics.serviceMonitor.labels Extra labels for the ServiceMonitor + ## + labels: {} + ## @param metrics.serviceMonitor.honorLabels honorLabels chooses the metric's labels on collisions with target labels + ## + honorLabels: false + ## Prometheus Operator PrometheusRule configuration + ## + prometheusRule: + ## @param metrics.prometheusRule.enabled Creates a Prometheus Operator PrometheusRule (also requires `metrics.enabled` to be `true` and `metrics.prometheusRule.rules`) + ## + enabled: false + ## @param metrics.prometheusRule.namespace Namespace for the PrometheusRule Resource (defaults to the Release Namespace) + ## + namespace: knowlg-db + ## @param metrics.prometheusRule.additionalLabels Additional labels that can be used so PrometheusRule will be discovered by Prometheus + ## + additionalLabels: {} + ## @param metrics.prometheusRule.rules Prometheus Rule definitions + # - alert: es cluster error + # annotations: + # summary: "es cluster error" + # description: "es cluster error, cluster state {{`{{`}} $labels.color {{`}}`}}" + # expr: elasticsearch_cluster_health_status{color="red"} ==1 or elasticsearch_cluster_health_status{color="yellow"} ==1 + # for: 1m + # labels: + # severity: critical + # group: PaaS + ## + rules: [] + +## @section Init Container Parameters + +## 'volumePermissions' init container parameters +## Changes the owner and group of the persistent volume mount point to runAsUser:fsGroup values +## based on the *podSecurityContext/*containerSecurityContext parameters +## +volumePermissions: + ## @param volumePermissions.enabled Enable init container that changes volume permissions in the data directory (for cases where the default k8s `runAsUser` and `fsUser` values do not work) + ## + enabled: false + ## @param volumePermissions.image.registry Init container volume-permissions image registry + ## @param volumePermissions.image.repository Init container volume-permissions image name + ## @param volumePermissions.image.tag Init container volume-permissions image tag + ## @param volumePermissions.image.digest Init container volume-permissions image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag + ## @param volumePermissions.image.pullPolicy Init container volume-permissions image pull policy + ## @param volumePermissions.image.pullSecrets Init container volume-permissions image pull secrets + ## + image: + registry: docker.io + repository: bitnami/bitnami-shell + tag: 11-debian-11-r97 + digest: "" + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## e.g: + ## pullSecrets: + ## - myRegistryKeySecretName + ## + pullSecrets: [] + ## Init container' resource requests and limits + ## ref: https://kubernetes.io/docs/user-guide/compute-resources/ + ## We usually recommend not to specify default resources and to leave this as a conscious + ## choice for the user. This also increases chances charts run on environments with little + ## resources, such as Minikube. If you do want to specify resources, uncomment the following + ## lines, adjust them as necessary, and remove the curly braces after 'resources:'. + ## @param volumePermissions.resources.limits The resources limits for the container + ## @param volumePermissions.resources.requests The requested resources for the container + ## + resources: + limits: {} + requests: {} +sysctlImage: + enabled: true + registry: docker.io + repository: bitnami/bitnami-shell + tag: 11-debian-11-r97 + digest: "" + pullPolicy: IfNotPresent + pullSecrets: [] + resources: + limits: {} + requests: {} +kibana: + elasticsearch: + hosts: + - '{{ include "elasticsearch.service.name" . }}' + port: '{{ include "elasticsearch.service.ports.restAPI" . }}' diff --git a/knowlg-automation/helm_charts/flink/.helmignore b/knowlg-automation/helm_charts/flink/.helmignore new file mode 100644 index 000000000..50af03172 --- /dev/null +++ b/knowlg-automation/helm_charts/flink/.helmignore @@ -0,0 +1,22 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/knowlg-automation/helm_charts/flink/Chart.yaml b/knowlg-automation/helm_charts/flink/Chart.yaml new file mode 100644 index 000000000..31dcba4d2 --- /dev/null +++ b/knowlg-automation/helm_charts/flink/Chart.yaml @@ -0,0 +1,21 @@ +apiVersion: v2 +name: flink +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. +appVersion: 1.16.0 diff --git a/knowlg-automation/helm_charts/flink/templates/_helpers.tpl b/knowlg-automation/helm_charts/flink/templates/_helpers.tpl new file mode 100644 index 000000000..18df42085 --- /dev/null +++ b/knowlg-automation/helm_charts/flink/templates/_helpers.tpl @@ -0,0 +1,67 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "datapipeline_jobs.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{- define "name" -}} +{{- default .Release.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "datapipeline_jobs.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "datapipeline_jobs.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Common labels +*/}} +{{- define "datapipeline_jobs.labels" -}} +helm.sh/chart: {{ include "datapipeline_jobs.chart" . }} +{{ include "datapipeline_jobs.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end -}} + +{{/* +Selector labels +*/}} +{{- define "datapipeline_jobs.selectorLabels" -}} +app.kubernetes.io/name: {{ include "datapipeline_jobs.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} + +{{/* +Create the name of the service account to use +*/}} +{{- define "datapipeline_jobs.serviceAccountName" -}} +{{- if .Values.serviceAccount.create -}} + {{ default (include "datapipeline_jobs.fullname" .) .Values.serviceAccount.name }} +{{- else -}} + {{ default "default" .Values.serviceAccount.name }} +{{- end -}} +{{- end -}} diff --git a/knowlg-automation/helm_charts/flink/templates/flink_job_configmap.yaml b/knowlg-automation/helm_charts/flink/templates/flink_job_configmap.yaml new file mode 100644 index 000000000..6f3356860 --- /dev/null +++ b/knowlg-automation/helm_charts/flink/templates/flink_job_configmap.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ .Release.Name }}-config + namespace: {{ .Values.namespace }} + labels: + app: flink +data: + base-config: |+ +{{ .Values.base_config | indent 4 }} +{{- $name := .Release.Name }} +{{ index .Values $name | toYaml | indent 2 }} + log4j_console_properties: |+ +{{ .Values.log4j_console_properties | indent 4 }} \ No newline at end of file diff --git a/knowlg-automation/helm_charts/flink/templates/flink_job_deployment.yaml b/knowlg-automation/helm_charts/flink/templates/flink_job_deployment.yaml new file mode 100644 index 000000000..6f4b3aaa8 --- /dev/null +++ b/knowlg-automation/helm_charts/flink/templates/flink_job_deployment.yaml @@ -0,0 +1,202 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ .Release.Name }}-jobmanager + namespace: {{ .Values.namespace }} + labels: + app: flink + component: {{ .Release.Name }}-jobmanager +spec: + type: ClusterIP + ports: + - name: rpc + port: {{ .Values.jobmanager.rpc_port }} + - name: blob + port: {{ .Values.jobmanager.blob_port }} + - name: query + port: {{ .Values.jobmanager.query_port }} + - name: ui + port: {{ .Values.jobmanager.ui_port }} + - name: prom + port: {{ .Values.jobmanager.prom_port }} + selector: + app: flink + component: {{ .Release.Name }}-jobmanager +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ .Release.Name }}-jobmanager-webui + namespace: {{ .Values.namespace }} + {{- if .Values.service.annotations }} + {{- with .Values.service.annotations }} +annotations: + {{ toYaml . | indent 4 }} + {{- end }} + {{- end }} +spec: + {{- if eq .Values.service.type "ClusterIP" }} + type: ClusterIP + {{- end }} + {{- if eq .Values.service.type "LoadBalancer" }} + type: LoadBalancer + {{- end }} + ports: + - name: rest + port: {{ .Values.rest_port }} + protocol: TCP + targetPort: {{ .Values.resttcp_port }} + selector: + app: flink + component: {{ .Release.Name }}-jobmanager +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ .Release.Name }}-jobmanager + namespace: {{ .Values.namespace }} +spec: + template: + metadata: + labels: + app: flink + component: {{ .Release.Name }}-jobmanager + annotations: + prometheus.io/scrape: 'true' + prometheus.io/port: "{{ .Values.jobmanager.prom_port }}" + spec: + volumes: + - name: flink-config-volume + configMap: + name: {{ .Release.Name }}-config + items: + - key: flink-conf + path: flink-conf.yaml + - key: base-config + path: base-config.conf + - key: {{ .Release.Name }} + path: {{ .Release.Name }}.conf + - key: log4j_console_properties + path: log4j-console.properties + restartPolicy: OnFailure +# imagePullSecrets: +# - name: {{ .Values.imagepullsecrets }} + containers: + - name: {{ .Release.Name }}-jobmanager + image: "{{ .Values.repository }}:{{ .Values.image_tag }}" + imagePullPolicy: Always + workingDir: /opt/flink + command: ["/opt/flink/bin/standalone-job.sh"] + args: ["start-foreground", +{{- if eq .Values.checkpoint_store_type "azure" }} + "-Dfs.azure.account.key.{{ .Values.azure_account }}.blob.core.windows.net={{ .Values.azure_secret }}", +{{- end }} +{{- if eq .Values.checkpoint_store_type "s3" }} + "-Ds3.access-key={{ .Values.s3_access_key }}", + "-Ds3.secret-key={{ .Values.s3_secret_key }}", + "-Ds3.endpoint={{ .Values.s3_endpoint }}", + "-Ds3.path.style.access={{ .Values.s3_path_style_access }}", +{{- end }} + "--job-classname={{ .Values.job_classname }}", + "-Dweb.submit.enable=false", + "-Dmetrics.reporter.prom.class=org.apache.flink.metrics.prometheus.PrometheusReporter", + "-Dmetrics.reporter.prom.port={{ .Values.jobmanager.prom_port }}", + "-Djobmanager.rpc.address={{ .Release.Name }}-jobmanager", + "-Djobmanager.rpc.port={{ .Values.jobmanager.rpc_port }}", + "-Dparallelism.default=1", + "-Dblob.server.port={{ .Values.jobmanager.blob_port }}", + "-Dqueryable-state.server.ports={{ .Values.jobmanager.query_port }}", + "--config.file.path", + "/data/flink/conf/{{ .Release.Name }}.conf"] + ports: + - containerPort: {{ .Values.jobmanager.rpc_port }} + name: rpc + - containerPort: {{ .Values.jobmanager.blob_port }} + name: blob + - containerPort: {{ .Values.jobmanager.query_port }} + name: query + - containerPort: {{ .Values.jobmanager.ui_port }} + name: ui + volumeMounts: + - name: flink-config-volume + mountPath: /opt/flink/conf/flink-conf.yaml + subPath: flink-conf.yaml + - name: flink-config-volume + mountPath: /data/flink/conf/base-config.conf + subPath: base-config.conf + - name: flink-config-volume + mountPath: /data/flink/conf/{{ .Release.Name }}.conf + subPath: {{ .Release.Name }}.conf + - name: flink-config-volume + mountPath: /opt/flink/conf/log4j-console.properties + subPath: log4j-console.properties + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Release.Name }}-taskmanager + namespace: {{ .Values.namespace }} +spec: + replicas: {{ .Values.taskmanager.replicas }} + selector: + matchLabels: + app: flink + component: {{ .Release.Name }}-taskmanager + template: + metadata: + labels: + app: flink + component: {{ .Release.Name }}-taskmanager + spec: + volumes: + - name: flink-config-volume + configMap: + name: {{ .Release.Name }}-config + items: + - key: flink-conf + path: flink-conf.yaml + - key: log4j_console_properties + path: log4j-console.properties +# imagePullSecrets: +# - name: {{ .Values.imagepullsecrets }} + containers: + - name: {{ .Release.Name }}-taskmanager + image: "{{ .Values.repository }}:{{ .Values.image_tag }}" + imagePullPolicy: Always + resources: + requests: + cpu: "{{ .Values.taskmanager.cpu_requests }}" + workingDir: {{ .Values.taskmanager.flink_work_dir }} + command: ["/opt/flink/bin/taskmanager.sh"] + args: ["start-foreground", +{{- if eq .Values.checkpoint_store_type "azure" }} + "-Dfs.azure.account.key.{{ .Values.azure_account }}.blob.core.windows.net={{ .Values.azure_secret }}", +{{- end }} +{{- if eq .Values.checkpoint_store_type "s3" }} + "-Ds3.access-key={{ .Values.s3_access_key }}", + "-Ds3.secret-key={{ .Values.s3_secret_key }}", + "-Ds3.endpoint={{ .Values.s3_endpoint }}", + "-Ds3.path.style.access={{ .Values.s3_path_style_access }}", +{{- end }} + "-Dweb.submit.enable=false", + "-Dmetrics.reporter.prom.class=org.apache.flink.metrics.prometheus.PrometheusReporter", + "-Dmetrics.reporter.prom.host={{ .Release.Name }}-taskmanager", + "-Dmetrics.reporter.prom.port=9251-9260", + "-Djobmanager.rpc.address={{ .Release.Name }}-jobmanager", + "-Dtaskmanager.rpc.port={{ .Values.taskmanager.rpc_port }}"] + ports: + - containerPort: {{ .Values.taskmanager.rpc_port }} + name: rpc + {{- if .Values.healthcheck }} + livenessProbe: +{{ toYaml .Values.livenessProbe | indent 10 }} + {{- end }} + volumeMounts: + - name: flink-config-volume + mountPath: /opt/flink/conf/flink-conf.yaml + subPath: flink-conf.yaml + - name: flink-config-volume + mountPath: /opt/flink/conf/log4j-console.properties + subPath: log4j-console.properties \ No newline at end of file diff --git a/knowlg-automation/helm_charts/flink/values.yaml b/knowlg-automation/helm_charts/flink/values.yaml new file mode 100644 index 000000000..1878f07dc --- /dev/null +++ b/knowlg-automation/helm_charts/flink/values.yaml @@ -0,0 +1,183 @@ +namespace: "flink-dev" +imagepullsecrets: "" +dockerhub: "" +repository: "docker.io/anandp504/sunbird-oneclick" +image_tag: "1.0.0" +serviceMonitor: + enabled: false +replicaCount: 1 + +jobmanager: + rpc_port: 6123 + blob_port: 6124 + query_port: 6125 + ui_port: 8081 + prom_port: 9250 + heap_memory: 1024 + +rest_port: 80 +resttcp_port: 8081 +service: + type: ClusterIP + + +taskmanager: + prom_port: 9251 + rpc_port: 6122 + heap_memory: 1024 + replicas: 1 + cpu_requests: 0.3 + +checkpoint_store_type: "" + +# AWS S3 Details +s3_access_key: "" +s3_secret_key: "" +s3_endpoint: "" +s3_path_style_access: "" + +# Azure Container Details +azure_account: "" +azure_secret: "" + +log4j_console_properties: | + # This affects logging for both user code and Flink + rootLogger.level = INFO + rootLogger.appenderRef.console.ref = ConsoleAppender + + # Uncomment this if you want to _only_ change Flink's logging + #logger.flink.name = org.apache.flink + #logger.flink.level = INFO + + # The following lines keep the log level of common libraries/connectors on + # log level INFO. The root logger does not override this. You have to manually + # change the log levels here. + logger.akka.name = akka + logger.akka.level = ERROR + logger.kafka.name= org.apache.kafka + logger.kafka.level = ERROR + logger.hadoop.name = org.apache.hadoop + logger.hadoop.level = ERROR + logger.zookeeper.name = org.apache.zookeeper + logger.zookeeper.level = ERROR + + # Log all infos to the console + appender.console.name = ConsoleAppender + appender.console.type = CONSOLE + appender.console.layout.type = PatternLayout + appender.console.layout.pattern = %d{yyyy-MM-dd HH:mm:ss,SSS} %-5p %-60c %x - %m%n + + # Suppress the irrelevant (wrong) warnings from the Netty channel handler + logger.netty.name = org.apache.flink.shaded.akka.org.jboss.netty.channel.DefaultChannelPipeline + logger.netty.level = OFF + +base_config: | + kafka { + broker-servers = "kafka-headless.kafka.svc.cluster.local:9092" + producer.broker-servers = "kafka-headless.kafka.svc.cluster.local:9092" + consumer.broker-servers = "kafka-headless.kafka.svc.cluster.local:9092" + zookeeper = "kafka-zookeeper-headless.svc.cluster.local:2181" + producer { + max-request-size = 1572864 + batch.size = 98304 + linger.ms = 10 + compression = "snappy" + } + } + job { + env = "dev" + enable.distributed.checkpointing = false + statebackend { + blob { + storage { + account = "obsrvacc.blob.core.windows.net" + container = "obsrv-blob" + checkpointing.dir = "checkpoint" + } + } + base.url = "" + } + } + task { + parallelism = 1 + consumer.parallelism = 1 + checkpointing.compressed = true + checkpointing.interval = 10 + checkpointing.pause.between.seconds = 3000 + restart-strategy.attempts = 3 + restart-strategy.delay = 30000 # in milli-seconds + } + + redisdb.connection.timeout = 100 + redis { + host = localhost + port = 6379 + } + + redis-meta { + host = localhost + port = 6379 + } + + postgres { + host = localhost + port = 5432 + maxConnections = 2 + user = "postgres" + password = "postgres" + } + + lms-cassandra { + host = "localhost" + port = "9042" + } + +druid-validator: + druid-validator: |+ + include file("/data/flink/conf/base-config.conf") + kafka { + input.topic = dev.telemetry.denorm + output.telemetry.route.topic = dev.druid.events.telemetry + output.summary.route.topic = dev.druid.events.summary + output.failed.topic = dev.telemetry.failed + output.duplicate.topic = dev.telemetry.duplicate + groupId = dev-druid-validator-group + } + task { + consumer.parallelism = 1 + downstream.operators.parallelism = 1 + druid.validation.enabled = true + druid.deduplication.enabled = false + } + schema { + path { + telemetry = "schemas/telemetry" + summary = "schemas/summary" + } + file { + default = envelope.json + summary = me_workflow_summary.json + search = search.json + } + } + redis { + database { + duplicationstore.id = 8 + key.expiry.seconds = 3600 + } + } + + flink-conf: |+ + jobmanager.memory.flink.size: 1024m + taskmanager.memory.flink.size: 1024m + taskmanager.numberOfTaskSlots: 1 + parallelism.default: 1 + jobmanager.execution.failover-strategy: region + taskmanager.memory.network.fraction: 0.1 + scheduler-mode: reactive + heartbeat.timeout: 8000 + heartbeat.interval: 5000 + taskmanager.memory.process.size: 1700m + jobmanager.memory.process.size: 1600m + +job_classname: org.sunbird.dp.validator.task.DruidValidatorStreamTask \ No newline at end of file diff --git a/knowlg-automation/helm_charts/kafka/.helmignore b/knowlg-automation/helm_charts/kafka/.helmignore new file mode 100644 index 000000000..f0c131944 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/.helmignore @@ -0,0 +1,21 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj diff --git a/knowlg-automation/helm_charts/kafka/Chart.lock b/knowlg-automation/helm_charts/kafka/Chart.lock new file mode 100644 index 000000000..4f0ea0177 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/Chart.lock @@ -0,0 +1,9 @@ +dependencies: +- name: zookeeper + repository: https://charts.bitnami.com/bitnami + version: 11.0.2 +- name: common + repository: https://charts.bitnami.com/bitnami + version: 2.2.2 +digest: sha256:406b7c170751d9aeab272ff010ee5affc4a8b2a4487f0157a68a0f726ff155b6 +generated: "2022-12-18T23:00:36.711399047Z" diff --git a/knowlg-automation/helm_charts/kafka/Chart.yaml b/knowlg-automation/helm_charts/kafka/Chart.yaml new file mode 100644 index 000000000..cee687d00 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/Chart.yaml @@ -0,0 +1,33 @@ +annotations: + category: Infrastructure +apiVersion: v2 +appVersion: 3.3.1 +dependencies: +- condition: zookeeper.enabled + name: zookeeper + repository: https://charts.bitnami.com/bitnami + version: 11.x.x +- name: common + repository: https://charts.bitnami.com/bitnami + tags: + - bitnami-common + version: 2.x.x +description: Apache Kafka is a distributed streaming platform designed to build real-time + pipelines and can be used as a message broker or as a replacement for a log aggregation + solution for big data applications. +home: https://github.com/bitnami/charts/tree/main/bitnami/kafka +icon: https://bitnami.com/assets/stacks/kafka/img/kafka-stack-220x234.png +keywords: +- kafka +- zookeeper +- streaming +- producer +- consumer +maintainers: +- name: Bitnami + url: https://github.com/bitnami/charts +name: kafka +sources: +- https://github.com/bitnami/containers/tree/main/bitnami/kafka +- https://kafka.apache.org/ +version: 20.0.2 diff --git a/knowlg-automation/helm_charts/kafka/README.md b/knowlg-automation/helm_charts/kafka/README.md new file mode 100644 index 000000000..5e922e050 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/README.md @@ -0,0 +1,1050 @@ + + +# Apache Kafka packaged by Bitnami + +Apache Kafka is a distributed streaming platform designed to build real-time pipelines and can be used as a message broker or as a replacement for a log aggregation solution for big data applications. + +[Overview of Apache Kafka](http://kafka.apache.org/) + +Trademarks: This software listing is packaged by Bitnami. The respective trademarks mentioned in the offering are owned by the respective companies, and use of them does not imply any affiliation or endorsement. + +## TL;DR + +```console +helm repo add my-repo https://charts.bitnami.com/bitnami +helm install my-release my-repo/kafka +``` + +## Introduction + +This chart bootstraps a [Kafka](https://github.com/bitnami/containers/tree/main/bitnami/kafka) deployment on a [Kubernetes](https://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager. + +Bitnami charts can be used with [Kubeapps](https://kubeapps.dev/) for deployment and management of Helm Charts in clusters. + +## Prerequisites + +- Kubernetes 1.19+ +- Helm 3.2.0+ +- PV provisioner support in the underlying infrastructure + +## Installing the Chart + +To install the chart with the release name `my-release`: + +```console +helm repo add my-repo https://charts.bitnami.com/bitnami +helm install my-release my-repo/kafka +``` + +These commands deploy Kafka on the Kubernetes cluster in the default configuration. The [Parameters](#parameters) section lists the parameters that can be configured during installation. + +> **Tip**: List all releases using `helm list` + +## Uninstalling the Chart + +To uninstall/delete the `my-release` deployment: + +```console +helm delete my-release +``` + +The command removes all the Kubernetes components associated with the chart and deletes the release. + +## Parameters + +### Global parameters + +| Name | Description | Value | +| ------------------------- | ----------------------------------------------- | ----- | +| `global.imageRegistry` | Global Docker image registry | `""` | +| `global.imagePullSecrets` | Global Docker registry secret names as an array | `[]` | +| `global.storageClass` | Global StorageClass for Persistent Volume(s) | `""` | + + +### Common parameters + +| Name | Description | Value | +| ------------------------ | --------------------------------------------------------------------------------------- | --------------- | +| `kubeVersion` | Override Kubernetes version | `""` | +| `nameOverride` | String to partially override common.names.fullname | `""` | +| `fullnameOverride` | String to fully override common.names.fullname | `""` | +| `clusterDomain` | Default Kubernetes cluster domain | `cluster.local` | +| `commonLabels` | Labels to add to all deployed objects | `{}` | +| `commonAnnotations` | Annotations to add to all deployed objects | `{}` | +| `extraDeploy` | Array of extra objects to deploy with the release | `[]` | +| `diagnosticMode.enabled` | Enable diagnostic mode (all probes will be disabled and the command will be overridden) | `false` | +| `diagnosticMode.command` | Command to override all containers in the statefulset | `["sleep"]` | +| `diagnosticMode.args` | Args to override all containers in the statefulset | `["infinity"]` | + + +### Kafka parameters + +| Name | Description | Value | +| ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- | +| `image.registry` | Kafka image registry | `docker.io` | +| `image.repository` | Kafka image repository | `bitnami/kafka` | +| `image.tag` | Kafka image tag (immutable tags are recommended) | `3.3.1-debian-11-r25` | +| `image.digest` | Kafka image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag | `""` | +| `image.pullPolicy` | Kafka image pull policy | `IfNotPresent` | +| `image.pullSecrets` | Specify docker-registry secret names as an array | `[]` | +| `image.debug` | Specify if debug values should be set | `false` | +| `config` | Configuration file for Kafka. Auto-generated based on other parameters when not specified | `""` | +| `existingConfigmap` | ConfigMap with Kafka Configuration | `""` | +| `log4j` | An optional log4j.properties file to overwrite the default of the Kafka brokers | `""` | +| `existingLog4jConfigMap` | The name of an existing ConfigMap containing a log4j.properties file | `""` | +| `heapOpts` | Kafka Java Heap size | `-Xmx1024m -Xms1024m` | +| `deleteTopicEnable` | Switch to enable topic deletion or not | `false` | +| `autoCreateTopicsEnable` | Switch to enable auto creation of topics. Enabling auto creation of topics not recommended for production or similar environments | `true` | +| `logFlushIntervalMessages` | The number of messages to accept before forcing a flush of data to disk | `_10000` | +| `logFlushIntervalMs` | The maximum amount of time a message can sit in a log before we force a flush | `1000` | +| `logRetentionBytes` | A size-based retention policy for logs | `_1073741824` | +| `logRetentionCheckIntervalMs` | The interval at which log segments are checked to see if they can be deleted | `300000` | +| `logRetentionHours` | The minimum age of a log file to be eligible for deletion due to age | `168` | +| `logSegmentBytes` | The maximum size of a log segment file. When this size is reached a new log segment will be created | `_1073741824` | +| `logsDirs` | A comma separated list of directories in which kafka's log data is kept | `/bitnami/kafka/data` | +| `maxMessageBytes` | The largest record batch size allowed by Kafka | `_1000012` | +| `defaultReplicationFactor` | Default replication factors for automatically created topics | `1` | +| `offsetsTopicReplicationFactor` | The replication factor for the offsets topic | `1` | +| `transactionStateLogReplicationFactor` | The replication factor for the transaction topic | `1` | +| `transactionStateLogMinIsr` | Overridden min.insync.replicas config for the transaction topic | `1` | +| `numIoThreads` | The number of threads doing disk I/O | `8` | +| `numNetworkThreads` | The number of threads handling network requests | `3` | +| `numPartitions` | The default number of log partitions per topic | `1` | +| `numRecoveryThreadsPerDataDir` | The number of threads per data directory to be used for log recovery at startup and flushing at shutdown | `1` | +| `socketReceiveBufferBytes` | The receive buffer (SO_RCVBUF) used by the socket server | `102400` | +| `socketRequestMaxBytes` | The maximum size of a request that the socket server will accept (protection against OOM) | `_104857600` | +| `socketSendBufferBytes` | The send buffer (SO_SNDBUF) used by the socket server | `102400` | +| `zookeeperConnectionTimeoutMs` | Timeout in ms for connecting to ZooKeeper | `6000` | +| `zookeeperChrootPath` | Path which puts data under some path in the global ZooKeeper namespace | `""` | +| `authorizerClassName` | The Authorizer is configured by setting authorizer.class.name=kafka.security.authorizer.AclAuthorizer in server.properties | `""` | +| `allowEveryoneIfNoAclFound` | By default, if a resource has no associated ACLs, then no one is allowed to access that resource except super users | `true` | +| `superUsers` | You can add super users in server.properties | `User:admin` | +| `auth.clientProtocol` | Authentication protocol for communications with clients. Allowed protocols: `plaintext`, `tls`, `mtls`, `sasl` and `sasl_tls` | `plaintext` | +| `auth.externalClientProtocol` | Authentication protocol for communications with external clients. Defaults to value of `auth.clientProtocol`. Allowed protocols: `plaintext`, `tls`, `mtls`, `sasl` and `sasl_tls` | `""` | +| `auth.interBrokerProtocol` | Authentication protocol for inter-broker communications. Allowed protocols: `plaintext`, `tls`, `mtls`, `sasl` and `sasl_tls` | `plaintext` | +| `auth.sasl.mechanisms` | SASL mechanisms when either `auth.interBrokerProtocol`, `auth.clientProtocol` or `auth.externalClientProtocol` are `sasl`. Allowed types: `plain`, `scram-sha-256`, `scram-sha-512` | `plain,scram-sha-256,scram-sha-512` | +| `auth.sasl.interBrokerMechanism` | SASL mechanism for inter broker communication. | `plain` | +| `auth.sasl.jaas.clientUsers` | Kafka client user list | `["user"]` | +| `auth.sasl.jaas.clientPasswords` | Kafka client passwords. This is mandatory if more than one user is specified in clientUsers | `[]` | +| `auth.sasl.jaas.interBrokerUser` | Kafka inter broker communication user for SASL authentication | `admin` | +| `auth.sasl.jaas.interBrokerPassword` | Kafka inter broker communication password for SASL authentication | `""` | +| `auth.sasl.jaas.zookeeperUser` | Kafka ZooKeeper user for SASL authentication | `""` | +| `auth.sasl.jaas.zookeeperPassword` | Kafka ZooKeeper password for SASL authentication | `""` | +| `auth.sasl.jaas.existingSecret` | Name of the existing secret containing credentials for clientUsers, interBrokerUser and zookeeperUser | `""` | +| `auth.tls.type` | Format to use for TLS certificates. Allowed types: `jks` and `pem` | `jks` | +| `auth.tls.pemChainIncluded` | Flag to denote that the Certificate Authority (CA) certificates are bundled with the endpoint cert. | `false` | +| `auth.tls.existingSecrets` | Array existing secrets containing the TLS certificates for the Kafka brokers | `[]` | +| `auth.tls.autoGenerated` | Generate automatically self-signed TLS certificates for Kafka brokers. Currently only supported if `auth.tls.type` is `pem` | `false` | +| `auth.tls.password` | Password to access the JKS files or PEM key when they are password-protected. | `""` | +| `auth.tls.existingSecret` | Name of the secret containing the password to access the JKS files or PEM key when they are password-protected. (`key`: `password`) | `""` | +| `auth.tls.jksTruststoreSecret` | Name of the existing secret containing your truststore if truststore not existing or different from the ones in the `auth.tls.existingSecrets` | `""` | +| `auth.tls.jksKeystoreSAN` | The secret key from the `auth.tls.existingSecrets` containing the keystore with a SAN certificate | `""` | +| `auth.tls.jksTruststore` | The secret key from the `auth.tls.existingSecrets` or `auth.tls.jksTruststoreSecret` containing the truststore | `""` | +| `auth.tls.endpointIdentificationAlgorithm` | The endpoint identification algorithm to validate server hostname using server certificate | `https` | +| `auth.zookeeper.tls.enabled` | Enable TLS for Zookeeper client connections. | `false` | +| `auth.zookeeper.tls.type` | Format to use for TLS certificates. Allowed types: `jks` and `pem`. | `jks` | +| `auth.zookeeper.tls.verifyHostname` | Hostname validation. | `true` | +| `auth.zookeeper.tls.existingSecret` | Name of the existing secret containing the TLS certificates for ZooKeeper client communications. | `""` | +| `auth.zookeeper.tls.existingSecretKeystoreKey` | The secret key from the auth.zookeeper.tls.existingSecret containing the Keystore. | `zookeeper.keystore.jks` | +| `auth.zookeeper.tls.existingSecretTruststoreKey` | The secret key from the auth.zookeeper.tls.existingSecret containing the Truststore. | `zookeeper.truststore.jks` | +| `auth.zookeeper.tls.passwordsSecret` | Existing secret containing Keystore and Truststore passwords. | `""` | +| `auth.zookeeper.tls.passwordsSecretKeystoreKey` | The secret key from the auth.zookeeper.tls.passwordsSecret containing the password for the Keystore. | `keystore-password` | +| `auth.zookeeper.tls.passwordsSecretTruststoreKey` | The secret key from the auth.zookeeper.tls.passwordsSecret containing the password for the Truststore. | `truststore-password` | +| `listeners` | The address(es) the socket server listens on. Auto-calculated it's set to an empty array | `[]` | +| `advertisedListeners` | The address(es) (hostname:port) the broker will advertise to producers and consumers. Auto-calculated it's set to an empty array | `[]` | +| `listenerSecurityProtocolMap` | The protocol->listener mapping. Auto-calculated it's set to nil | `""` | +| `allowPlaintextListener` | Allow to use the PLAINTEXT listener | `true` | +| `interBrokerListenerName` | The listener that the brokers should communicate on | `INTERNAL` | +| `command` | Override Kafka container command | `["/scripts/setup.sh"]` | +| `args` | Override Kafka container arguments | `[]` | +| `extraEnvVars` | Extra environment variables to add to Kafka pods | `[]` | +| `extraEnvVarsCM` | ConfigMap with extra environment variables | `""` | +| `extraEnvVarsSecret` | Secret with extra environment variables | `""` | + + +### Statefulset parameters + +| Name | Description | Value | +| --------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- | +| `replicaCount` | Number of Kafka nodes | `1` | +| `minBrokerId` | Minimal broker.id value, nodes increment their `broker.id` respectively | `0` | +| `brokerRackAssignment` | Set Broker Assignment for multi tenant environment Allowed values: `aws-az` | `""` | +| `containerPorts.client` | Kafka client container port | `9092` | +| `containerPorts.internal` | Kafka inter-broker container port | `9093` | +| `containerPorts.external` | Kafka external container port | `9094` | +| `livenessProbe.enabled` | Enable livenessProbe on Kafka containers | `true` | +| `livenessProbe.initialDelaySeconds` | Initial delay seconds for livenessProbe | `10` | +| `livenessProbe.periodSeconds` | Period seconds for livenessProbe | `10` | +| `livenessProbe.timeoutSeconds` | Timeout seconds for livenessProbe | `5` | +| `livenessProbe.failureThreshold` | Failure threshold for livenessProbe | `3` | +| `livenessProbe.successThreshold` | Success threshold for livenessProbe | `1` | +| `readinessProbe.enabled` | Enable readinessProbe on Kafka containers | `true` | +| `readinessProbe.initialDelaySeconds` | Initial delay seconds for readinessProbe | `5` | +| `readinessProbe.periodSeconds` | Period seconds for readinessProbe | `10` | +| `readinessProbe.timeoutSeconds` | Timeout seconds for readinessProbe | `5` | +| `readinessProbe.failureThreshold` | Failure threshold for readinessProbe | `6` | +| `readinessProbe.successThreshold` | Success threshold for readinessProbe | `1` | +| `startupProbe.enabled` | Enable startupProbe on Kafka containers | `false` | +| `startupProbe.initialDelaySeconds` | Initial delay seconds for startupProbe | `30` | +| `startupProbe.periodSeconds` | Period seconds for startupProbe | `10` | +| `startupProbe.timeoutSeconds` | Timeout seconds for startupProbe | `1` | +| `startupProbe.failureThreshold` | Failure threshold for startupProbe | `15` | +| `startupProbe.successThreshold` | Success threshold for startupProbe | `1` | +| `customLivenessProbe` | Custom livenessProbe that overrides the default one | `{}` | +| `customReadinessProbe` | Custom readinessProbe that overrides the default one | `{}` | +| `customStartupProbe` | Custom startupProbe that overrides the default one | `{}` | +| `lifecycleHooks` | lifecycleHooks for the Kafka container to automate configuration before or after startup | `{}` | +| `resources.limits` | The resources limits for the container | `{}` | +| `resources.requests` | The requested resources for the container | `{}` | +| `podSecurityContext.enabled` | Enable security context for the pods | `true` | +| `podSecurityContext.fsGroup` | Set Kafka pod's Security Context fsGroup | `1001` | +| `containerSecurityContext.enabled` | Enable Kafka containers' Security Context | `true` | +| `containerSecurityContext.runAsUser` | Set Kafka containers' Security Context runAsUser | `1001` | +| `containerSecurityContext.runAsNonRoot` | Set Kafka containers' Security Context runAsNonRoot | `true` | +| `containerSecurityContext.allowPrivilegeEscalation` | Force the child process to be run as nonprivilege | `false` | +| `hostAliases` | Kafka pods host aliases | `[]` | +| `hostNetwork` | Specify if host network should be enabled for Kafka pods | `false` | +| `hostIPC` | Specify if host IPC should be enabled for Kafka pods | `false` | +| `podLabels` | Extra labels for Kafka pods | `{}` | +| `podAnnotations` | Extra annotations for Kafka pods | `{}` | +| `podAffinityPreset` | Pod affinity preset. Ignored if `affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `podAntiAffinityPreset` | Pod anti-affinity preset. Ignored if `affinity` is set. Allowed values: `soft` or `hard` | `soft` | +| `nodeAffinityPreset.type` | Node affinity preset type. Ignored if `affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `nodeAffinityPreset.key` | Node label key to match Ignored if `affinity` is set. | `""` | +| `nodeAffinityPreset.values` | Node label values to match. Ignored if `affinity` is set. | `[]` | +| `affinity` | Affinity for pod assignment | `{}` | +| `nodeSelector` | Node labels for pod assignment | `{}` | +| `tolerations` | Tolerations for pod assignment | `[]` | +| `topologySpreadConstraints` | Topology Spread Constraints for pod assignment spread across your cluster among failure-domains. Evaluated as a template | `[]` | +| `terminationGracePeriodSeconds` | Seconds the pod needs to gracefully terminate | `""` | +| `podManagementPolicy` | StatefulSet controller supports relax its ordering guarantees while preserving its uniqueness and identity guarantees. There are two valid pod management policies: OrderedReady and Parallel | `Parallel` | +| `priorityClassName` | Name of the existing priority class to be used by kafka pods | `""` | +| `schedulerName` | Name of the k8s scheduler (other than default) | `""` | +| `updateStrategy.type` | Kafka statefulset strategy type | `RollingUpdate` | +| `updateStrategy.rollingUpdate` | Kafka statefulset rolling update configuration parameters | `{}` | +| `extraVolumes` | Optionally specify extra list of additional volumes for the Kafka pod(s) | `[]` | +| `extraVolumeMounts` | Optionally specify extra list of additional volumeMounts for the Kafka container(s) | `[]` | +| `sidecars` | Add additional sidecar containers to the Kafka pod(s) | `[]` | +| `initContainers` | Add additional Add init containers to the Kafka pod(s) | `[]` | +| `pdb.create` | Deploy a pdb object for the Kafka pod | `false` | +| `pdb.minAvailable` | Maximum number/percentage of unavailable Kafka replicas | `""` | +| `pdb.maxUnavailable` | Maximum number/percentage of unavailable Kafka replicas | `1` | + + +### Traffic Exposure parameters + +| Name | Description | Value | +| ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | --------------------- | +| `service.type` | Kubernetes Service type | `ClusterIP` | +| `service.ports.client` | Kafka svc port for client connections | `9092` | +| `service.ports.internal` | Kafka svc port for inter-broker connections | `9093` | +| `service.ports.external` | Kafka svc port for external connections | `9094` | +| `service.nodePorts.client` | Node port for the Kafka client connections | `""` | +| `service.nodePorts.external` | Node port for the Kafka external connections | `""` | +| `service.sessionAffinity` | Control where client requests go, to the same pod or round-robin | `None` | +| `service.sessionAffinityConfig` | Additional settings for the sessionAffinity | `{}` | +| `service.clusterIP` | Kafka service Cluster IP | `""` | +| `service.loadBalancerIP` | Kafka service Load Balancer IP | `""` | +| `service.loadBalancerSourceRanges` | Kafka service Load Balancer sources | `[]` | +| `service.externalTrafficPolicy` | Kafka service external traffic policy | `Cluster` | +| `service.annotations` | Additional custom annotations for Kafka service | `{}` | +| `service.headless.publishNotReadyAddresses` | Indicates that any agent which deals with endpoints for this Service should disregard any indications of ready/not-ready | `false` | +| `service.headless.annotations` | Annotations for the headless service. | `{}` | +| `service.headless.labels` | Labels for the headless service. | `{}` | +| `service.extraPorts` | Extra ports to expose in the Kafka service (normally used with the `sidecar` value) | `[]` | +| `externalAccess.enabled` | Enable Kubernetes external cluster access to Kafka brokers | `false` | +| `externalAccess.autoDiscovery.enabled` | Enable using an init container to auto-detect external IPs/ports by querying the K8s API | `false` | +| `externalAccess.autoDiscovery.image.registry` | Init container auto-discovery image registry | `docker.io` | +| `externalAccess.autoDiscovery.image.repository` | Init container auto-discovery image repository | `bitnami/kubectl` | +| `externalAccess.autoDiscovery.image.tag` | Init container auto-discovery image tag (immutable tags are recommended) | `1.25.5-debian-11-r2` | +| `externalAccess.autoDiscovery.image.digest` | Petete image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag | `""` | +| `externalAccess.autoDiscovery.image.pullPolicy` | Init container auto-discovery image pull policy | `IfNotPresent` | +| `externalAccess.autoDiscovery.image.pullSecrets` | Init container auto-discovery image pull secrets | `[]` | +| `externalAccess.autoDiscovery.resources.limits` | The resources limits for the auto-discovery init container | `{}` | +| `externalAccess.autoDiscovery.resources.requests` | The requested resources for the auto-discovery init container | `{}` | +| `externalAccess.service.type` | Kubernetes Service type for external access. It can be NodePort, LoadBalancer or ClusterIP | `LoadBalancer` | +| `externalAccess.service.ports.external` | Kafka port used for external access when service type is LoadBalancer | `9094` | +| `externalAccess.service.loadBalancerIPs` | Array of load balancer IPs for each Kafka broker. Length must be the same as replicaCount | `[]` | +| `externalAccess.service.loadBalancerNames` | Array of load balancer Names for each Kafka broker. Length must be the same as replicaCount | `[]` | +| `externalAccess.service.loadBalancerAnnotations` | Array of load balancer annotations for each Kafka broker. Length must be the same as replicaCount | `[]` | +| `externalAccess.service.loadBalancerSourceRanges` | Address(es) that are allowed when service is LoadBalancer | `[]` | +| `externalAccess.service.nodePorts` | Array of node ports used for each Kafka broker. Length must be the same as replicaCount | `[]` | +| `externalAccess.service.useHostIPs` | Use service host IPs to configure Kafka external listener when service type is NodePort | `false` | +| `externalAccess.service.usePodIPs` | using the MY_POD_IP address for external access. | `false` | +| `externalAccess.service.domain` | Domain or external ip used to configure Kafka external listener when service type is NodePort or ClusterIP | `""` | +| `externalAccess.service.publishNotReadyAddresses` | Indicates that any agent which deals with endpoints for this Service should disregard any indications of ready/not-ready | `false` | +| `externalAccess.service.labels` | Service labels for external access | `{}` | +| `externalAccess.service.annotations` | Service annotations for external access | `{}` | +| `externalAccess.service.extraPorts` | Extra ports to expose in the Kafka external service | `[]` | +| `networkPolicy.enabled` | Specifies whether a NetworkPolicy should be created | `false` | +| `networkPolicy.allowExternal` | Don't require client label for connections | `true` | +| `networkPolicy.explicitNamespacesSelector` | A Kubernetes LabelSelector to explicitly select namespaces from which traffic could be allowed | `{}` | +| `networkPolicy.externalAccess.from` | customize the from section for External Access on tcp-external port | `[]` | +| `networkPolicy.egressRules.customRules` | Custom network policy rule | `{}` | + + +### Persistence parameters + +| Name | Description | Value | +| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | +| `persistence.enabled` | Enable Kafka data persistence using PVC, note that ZooKeeper persistence is unaffected | `true` | +| `persistence.existingClaim` | A manually managed Persistent Volume and Claim | `""` | +| `persistence.storageClass` | PVC Storage Class for Kafka data volume | `""` | +| `persistence.accessModes` | Persistent Volume Access Modes | `["ReadWriteOnce"]` | +| `persistence.size` | PVC Storage Request for Kafka data volume | `8Gi` | +| `persistence.annotations` | Annotations for the PVC | `{}` | +| `persistence.labels` | Labels for the PVC | `{}` | +| `persistence.selector` | Selector to match an existing Persistent Volume for Kafka data PVC. If set, the PVC can't have a PV dynamically provisioned for it | `{}` | +| `persistence.mountPath` | Mount path of the Kafka data volume | `/bitnami/kafka` | +| `logPersistence.enabled` | Enable Kafka logs persistence using PVC, note that ZooKeeper persistence is unaffected | `false` | +| `logPersistence.existingClaim` | A manually managed Persistent Volume and Claim | `""` | +| `logPersistence.storageClass` | PVC Storage Class for Kafka logs volume | `""` | +| `logPersistence.accessModes` | Persistent Volume Access Modes | `["ReadWriteOnce"]` | +| `logPersistence.size` | PVC Storage Request for Kafka logs volume | `8Gi` | +| `logPersistence.annotations` | Annotations for the PVC | `{}` | +| `logPersistence.selector` | Selector to match an existing Persistent Volume for Kafka log data PVC. If set, the PVC can't have a PV dynamically provisioned for it | `{}` | +| `logPersistence.mountPath` | Mount path of the Kafka logs volume | `/opt/bitnami/kafka/logs` | + + +### Volume Permissions parameters + +| Name | Description | Value | +| ------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | +| `volumePermissions.enabled` | Enable init container that changes the owner and group of the persistent volume | `false` | +| `volumePermissions.image.registry` | Init container volume-permissions image registry | `docker.io` | +| `volumePermissions.image.repository` | Init container volume-permissions image repository | `bitnami/bitnami-shell` | +| `volumePermissions.image.tag` | Init container volume-permissions image tag (immutable tags are recommended) | `11-debian-11-r63` | +| `volumePermissions.image.digest` | Init container volume-permissions image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag | `""` | +| `volumePermissions.image.pullPolicy` | Init container volume-permissions image pull policy | `IfNotPresent` | +| `volumePermissions.image.pullSecrets` | Init container volume-permissions image pull secrets | `[]` | +| `volumePermissions.resources.limits` | Init container volume-permissions resource limits | `{}` | +| `volumePermissions.resources.requests` | Init container volume-permissions resource requests | `{}` | +| `volumePermissions.containerSecurityContext.runAsUser` | User ID for the init container | `0` | + + +### Other Parameters + +| Name | Description | Value | +| --------------------------------------------- | ---------------------------------------------------------------------------------------------- | ------- | +| `serviceAccount.create` | Enable creation of ServiceAccount for Kafka pods | `true` | +| `serviceAccount.name` | The name of the service account to use. If not set and `create` is `true`, a name is generated | `""` | +| `serviceAccount.automountServiceAccountToken` | Allows auto mount of ServiceAccountToken on the serviceAccount created | `true` | +| `serviceAccount.annotations` | Additional custom annotations for the ServiceAccount | `{}` | +| `rbac.create` | Whether to create & use RBAC resources or not | `false` | + + +### Metrics parameters + +| Name | Description | Value | +| ----------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | +| `metrics.kafka.enabled` | Whether or not to create a standalone Kafka exporter to expose Kafka metrics | `false` | +| `metrics.kafka.image.registry` | Kafka exporter image registry | `docker.io` | +| `metrics.kafka.image.repository` | Kafka exporter image repository | `bitnami/kafka-exporter` | +| `metrics.kafka.image.tag` | Kafka exporter image tag (immutable tags are recommended) | `1.6.0-debian-11-r40` | +| `metrics.kafka.image.digest` | Kafka exporter image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag | `""` | +| `metrics.kafka.image.pullPolicy` | Kafka exporter image pull policy | `IfNotPresent` | +| `metrics.kafka.image.pullSecrets` | Specify docker-registry secret names as an array | `[]` | +| `metrics.kafka.certificatesSecret` | Name of the existing secret containing the optional certificate and key files | `""` | +| `metrics.kafka.tlsCert` | The secret key from the certificatesSecret if 'client-cert' key different from the default (cert-file) | `cert-file` | +| `metrics.kafka.tlsKey` | The secret key from the certificatesSecret if 'client-key' key different from the default (key-file) | `key-file` | +| `metrics.kafka.tlsCaSecret` | Name of the existing secret containing the optional ca certificate for Kafka exporter client authentication | `""` | +| `metrics.kafka.tlsCaCert` | The secret key from the certificatesSecret or tlsCaSecret if 'ca-cert' key different from the default (ca-file) | `ca-file` | +| `metrics.kafka.extraFlags` | Extra flags to be passed to Kafka exporter | `{}` | +| `metrics.kafka.command` | Override Kafka exporter container command | `[]` | +| `metrics.kafka.args` | Override Kafka exporter container arguments | `[]` | +| `metrics.kafka.containerPorts.metrics` | Kafka exporter metrics container port | `9308` | +| `metrics.kafka.resources.limits` | The resources limits for the container | `{}` | +| `metrics.kafka.resources.requests` | The requested resources for the container | `{}` | +| `metrics.kafka.podSecurityContext.enabled` | Enable security context for the pods | `true` | +| `metrics.kafka.podSecurityContext.fsGroup` | Set Kafka exporter pod's Security Context fsGroup | `1001` | +| `metrics.kafka.containerSecurityContext.enabled` | Enable Kafka exporter containers' Security Context | `true` | +| `metrics.kafka.containerSecurityContext.runAsUser` | Set Kafka exporter containers' Security Context runAsUser | `1001` | +| `metrics.kafka.containerSecurityContext.runAsNonRoot` | Set Kafka exporter containers' Security Context runAsNonRoot | `true` | +| `metrics.kafka.hostAliases` | Kafka exporter pods host aliases | `[]` | +| `metrics.kafka.podLabels` | Extra labels for Kafka exporter pods | `{}` | +| `metrics.kafka.podAnnotations` | Extra annotations for Kafka exporter pods | `{}` | +| `metrics.kafka.podAffinityPreset` | Pod affinity preset. Ignored if `metrics.kafka.affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `metrics.kafka.podAntiAffinityPreset` | Pod anti-affinity preset. Ignored if `metrics.kafka.affinity` is set. Allowed values: `soft` or `hard` | `soft` | +| `metrics.kafka.nodeAffinityPreset.type` | Node affinity preset type. Ignored if `metrics.kafka.affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `metrics.kafka.nodeAffinityPreset.key` | Node label key to match Ignored if `metrics.kafka.affinity` is set. | `""` | +| `metrics.kafka.nodeAffinityPreset.values` | Node label values to match. Ignored if `metrics.kafka.affinity` is set. | `[]` | +| `metrics.kafka.affinity` | Affinity for pod assignment | `{}` | +| `metrics.kafka.nodeSelector` | Node labels for pod assignment | `{}` | +| `metrics.kafka.tolerations` | Tolerations for pod assignment | `[]` | +| `metrics.kafka.schedulerName` | Name of the k8s scheduler (other than default) for Kafka exporter | `""` | +| `metrics.kafka.priorityClassName` | Kafka exporter pods' priorityClassName | `""` | +| `metrics.kafka.topologySpreadConstraints` | Topology Spread Constraints for pod assignment | `[]` | +| `metrics.kafka.extraVolumes` | Optionally specify extra list of additional volumes for the Kafka exporter pod(s) | `[]` | +| `metrics.kafka.extraVolumeMounts` | Optionally specify extra list of additional volumeMounts for the Kafka exporter container(s) | `[]` | +| `metrics.kafka.sidecars` | Add additional sidecar containers to the Kafka exporter pod(s) | `[]` | +| `metrics.kafka.initContainers` | Add init containers to the Kafka exporter pods | `[]` | +| `metrics.kafka.service.ports.metrics` | Kafka exporter metrics service port | `9308` | +| `metrics.kafka.service.clusterIP` | Static clusterIP or None for headless services | `""` | +| `metrics.kafka.service.sessionAffinity` | Control where client requests go, to the same pod or round-robin | `None` | +| `metrics.kafka.service.annotations` | Annotations for the Kafka exporter service | `{}` | +| `metrics.kafka.serviceAccount.create` | Enable creation of ServiceAccount for Kafka exporter pods | `true` | +| `metrics.kafka.serviceAccount.name` | The name of the service account to use. If not set and `create` is `true`, a name is generated | `""` | +| `metrics.kafka.serviceAccount.automountServiceAccountToken` | Allows auto mount of ServiceAccountToken on the serviceAccount created | `true` | +| `metrics.jmx.enabled` | Whether or not to expose JMX metrics to Prometheus | `false` | +| `metrics.jmx.image.registry` | JMX exporter image registry | `docker.io` | +| `metrics.jmx.image.repository` | JMX exporter image repository | `bitnami/jmx-exporter` | +| `metrics.jmx.image.tag` | JMX exporter image tag (immutable tags are recommended) | `0.17.2-debian-11-r29` | +| `metrics.jmx.image.digest` | JMX exporter image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag | `""` | +| `metrics.jmx.image.pullPolicy` | JMX exporter image pull policy | `IfNotPresent` | +| `metrics.jmx.image.pullSecrets` | Specify docker-registry secret names as an array | `[]` | +| `metrics.jmx.containerSecurityContext.enabled` | Enable Prometheus JMX exporter containers' Security Context | `true` | +| `metrics.jmx.containerSecurityContext.runAsUser` | Set Prometheus JMX exporter containers' Security Context runAsUser | `1001` | +| `metrics.jmx.containerSecurityContext.runAsNonRoot` | Set Prometheus JMX exporter containers' Security Context runAsNonRoot | `true` | +| `metrics.jmx.containerPorts.metrics` | Prometheus JMX exporter metrics container port | `5556` | +| `metrics.jmx.resources.limits` | The resources limits for the JMX exporter container | `{}` | +| `metrics.jmx.resources.requests` | The requested resources for the JMX exporter container | `{}` | +| `metrics.jmx.service.ports.metrics` | Prometheus JMX exporter metrics service port | `5556` | +| `metrics.jmx.service.clusterIP` | Static clusterIP or None for headless services | `""` | +| `metrics.jmx.service.sessionAffinity` | Control where client requests go, to the same pod or round-robin | `None` | +| `metrics.jmx.service.annotations` | Annotations for the Prometheus JMX exporter service | `{}` | +| `metrics.jmx.whitelistObjectNames` | Allows setting which JMX objects you want to expose to via JMX stats to JMX exporter | `["kafka.controller:*","kafka.server:*","java.lang:*","kafka.network:*","kafka.log:*"]` | +| `metrics.jmx.config` | Configuration file for JMX exporter | `""` | +| `metrics.jmx.existingConfigmap` | Name of existing ConfigMap with JMX exporter configuration | `""` | +| `metrics.jmx.extraRules` | Add extra rules to JMX exporter configuration | `""` | +| `metrics.serviceMonitor.enabled` | if `true`, creates a Prometheus Operator ServiceMonitor (requires `metrics.kafka.enabled` or `metrics.jmx.enabled` to be `true`) | `false` | +| `metrics.serviceMonitor.namespace` | Namespace in which Prometheus is running | `""` | +| `metrics.serviceMonitor.interval` | Interval at which metrics should be scraped | `""` | +| `metrics.serviceMonitor.scrapeTimeout` | Timeout after which the scrape is ended | `""` | +| `metrics.serviceMonitor.labels` | Additional labels that can be used so ServiceMonitor will be discovered by Prometheus | `{}` | +| `metrics.serviceMonitor.selector` | Prometheus instance selector labels | `{}` | +| `metrics.serviceMonitor.relabelings` | RelabelConfigs to apply to samples before scraping | `[]` | +| `metrics.serviceMonitor.metricRelabelings` | MetricRelabelConfigs to apply to samples before ingestion | `[]` | +| `metrics.serviceMonitor.honorLabels` | Specify honorLabels parameter to add the scrape endpoint | `false` | +| `metrics.serviceMonitor.jobLabel` | The name of the label on the target service to use as the job name in prometheus. | `""` | +| `metrics.prometheusRule.enabled` | if `true`, creates a Prometheus Operator PrometheusRule (requires `metrics.kafka.enabled` or `metrics.jmx.enabled` to be `true`) | `false` | +| `metrics.prometheusRule.namespace` | Namespace in which Prometheus is running | `""` | +| `metrics.prometheusRule.labels` | Additional labels that can be used so PrometheusRule will be discovered by Prometheus | `{}` | +| `metrics.prometheusRule.groups` | Prometheus Rule Groups for Kafka | `[]` | + + +### Kafka provisioning parameters + +| Name | Description | Value | +| ---------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | --------------------- | +| `provisioning.enabled` | Enable kafka provisioning Job | `false` | +| `provisioning.numPartitions` | Default number of partitions for topics when unspecified | `1` | +| `provisioning.replicationFactor` | Default replication factor for topics when unspecified | `1` | +| `provisioning.topics` | Kafka topics to provision | `[]` | +| `provisioning.nodeSelector` | Node labels for pod assignment | `{}` | +| `provisioning.tolerations` | Tolerations for pod assignment | `[]` | +| `provisioning.extraProvisioningCommands` | Extra commands to run to provision cluster resources | `[]` | +| `provisioning.parallel` | Number of provisioning commands to run at the same time | `1` | +| `provisioning.preScript` | Extra bash script to run before topic provisioning. $CLIENT_CONF is path to properties file with most needed configurations | `""` | +| `provisioning.postScript` | Extra bash script to run after topic provisioning. $CLIENT_CONF is path to properties file with most needed configurations | `""` | +| `provisioning.auth.tls.type` | Format to use for TLS certificates. Allowed types: `jks` and `pem`. | `jks` | +| `provisioning.auth.tls.certificatesSecret` | Existing secret containing the TLS certificates for the Kafka provisioning Job. | `""` | +| `provisioning.auth.tls.cert` | The secret key from the certificatesSecret if 'cert' key different from the default (tls.crt) | `tls.crt` | +| `provisioning.auth.tls.key` | The secret key from the certificatesSecret if 'key' key different from the default (tls.key) | `tls.key` | +| `provisioning.auth.tls.caCert` | The secret key from the certificatesSecret if 'caCert' key different from the default (ca.crt) | `ca.crt` | +| `provisioning.auth.tls.keystore` | The secret key from the certificatesSecret if 'keystore' key different from the default (keystore.jks) | `keystore.jks` | +| `provisioning.auth.tls.truststore` | The secret key from the certificatesSecret if 'truststore' key different from the default (truststore.jks) | `truststore.jks` | +| `provisioning.auth.tls.passwordsSecret` | Name of the secret containing passwords to access the JKS files or PEM key when they are password-protected. | `""` | +| `provisioning.auth.tls.keyPasswordSecretKey` | The secret key from the passwordsSecret if 'keyPasswordSecretKey' key different from the default (key-password) | `key-password` | +| `provisioning.auth.tls.keystorePasswordSecretKey` | The secret key from the passwordsSecret if 'keystorePasswordSecretKey' key different from the default (keystore-password) | `keystore-password` | +| `provisioning.auth.tls.truststorePasswordSecretKey` | The secret key from the passwordsSecret if 'truststorePasswordSecretKey' key different from the default (truststore-password) | `truststore-password` | +| `provisioning.auth.tls.keyPassword` | Password to access the password-protected PEM key if necessary. Ignored if 'passwordsSecret' is provided. | `""` | +| `provisioning.auth.tls.keystorePassword` | Password to access the JKS keystore. Ignored if 'passwordsSecret' is provided. | `""` | +| `provisioning.auth.tls.truststorePassword` | Password to access the JKS truststore. Ignored if 'passwordsSecret' is provided. | `""` | +| `provisioning.command` | Override provisioning container command | `[]` | +| `provisioning.args` | Override provisioning container arguments | `[]` | +| `provisioning.extraEnvVars` | Extra environment variables to add to the provisioning pod | `[]` | +| `provisioning.extraEnvVarsCM` | ConfigMap with extra environment variables | `""` | +| `provisioning.extraEnvVarsSecret` | Secret with extra environment variables | `""` | +| `provisioning.podAnnotations` | Extra annotations for Kafka provisioning pods | `{}` | +| `provisioning.podLabels` | Extra labels for Kafka provisioning pods | `{}` | +| `provisioning.serviceAccount.create` | Enable creation of ServiceAccount for Kafka provisioning pods | `false` | +| `provisioning.serviceAccount.name` | The name of the service account to use. If not set and `create` is `true`, a name is generated | `""` | +| `provisioning.serviceAccount.automountServiceAccountToken` | Allows auto mount of ServiceAccountToken on the serviceAccount created | `true` | +| `provisioning.resources.limits` | The resources limits for the Kafka provisioning container | `{}` | +| `provisioning.resources.requests` | The requested resources for the Kafka provisioning container | `{}` | +| `provisioning.podSecurityContext.enabled` | Enable security context for the pods | `true` | +| `provisioning.podSecurityContext.fsGroup` | Set Kafka provisioning pod's Security Context fsGroup | `1001` | +| `provisioning.containerSecurityContext.enabled` | Enable Kafka provisioning containers' Security Context | `true` | +| `provisioning.containerSecurityContext.runAsUser` | Set Kafka provisioning containers' Security Context runAsUser | `1001` | +| `provisioning.containerSecurityContext.runAsNonRoot` | Set Kafka provisioning containers' Security Context runAsNonRoot | `true` | +| `provisioning.schedulerName` | Name of the k8s scheduler (other than default) for kafka provisioning | `""` | +| `provisioning.extraVolumes` | Optionally specify extra list of additional volumes for the Kafka provisioning pod(s) | `[]` | +| `provisioning.extraVolumeMounts` | Optionally specify extra list of additional volumeMounts for the Kafka provisioning container(s) | `[]` | +| `provisioning.sidecars` | Add additional sidecar containers to the Kafka provisioning pod(s) | `[]` | +| `provisioning.initContainers` | Add additional Add init containers to the Kafka provisioning pod(s) | `[]` | +| `provisioning.waitForKafka` | If true use an init container to wait until kafka is ready before starting provisioning | `true` | + + +### ZooKeeper chart parameters + +| Name | Description | Value | +| --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | +| `zookeeper.enabled` | Switch to enable or disable the ZooKeeper helm chart | `true` | +| `zookeeper.replicaCount` | Number of ZooKeeper nodes | `1` | +| `zookeeper.auth.client.enabled` | Enable ZooKeeper auth | `false` | +| `zookeeper.auth.client.clientUser` | User that will use ZooKeeper clients to auth | `""` | +| `zookeeper.auth.client.clientPassword` | Password that will use ZooKeeper clients to auth | `""` | +| `zookeeper.auth.client.serverUsers` | Comma, semicolon or whitespace separated list of user to be created. Specify them as a string, for example: "user1,user2,admin" | `""` | +| `zookeeper.auth.client.serverPasswords` | Comma, semicolon or whitespace separated list of passwords to assign to users when created. Specify them as a string, for example: "pass4user1, pass4user2, pass4admin" | `""` | +| `zookeeper.persistence.enabled` | Enable persistence on ZooKeeper using PVC(s) | `true` | +| `zookeeper.persistence.storageClass` | Persistent Volume storage class | `""` | +| `zookeeper.persistence.accessModes` | Persistent Volume access modes | `["ReadWriteOnce"]` | +| `zookeeper.persistence.size` | Persistent Volume size | `8Gi` | +| `externalZookeeper.servers` | List of external zookeeper servers to use. Typically used in combination with 'zookeeperChrootPath'. | `[]` | + + +Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example, + +```console +helm install my-release \ + --set replicaCount=3 \ + my-repo/kafka +``` + +The above command deploys Kafka with 3 brokers (replicas). + +Alternatively, a YAML file that specifies the values for the parameters can be provided while installing the chart. For example, + +```console +helm install my-release -f values.yaml my-repo/kafka +``` + +> **Tip**: You can use the default [values.yaml](values.yaml) + +## Configuration and installation details + +### [Rolling VS Immutable tags](https://docs.bitnami.com/containers/how-to/understand-rolling-tags-containers/) + +It is strongly recommended to use immutable tags in a production environment. This ensures your deployment does not change automatically if the same tag is updated with a different image. + +Bitnami will release a new chart updating its containers if a new version of the main container, significant changes, or critical vulnerabilities exist. + +### Setting custom parameters + +Any environment variable beginning with `KAFKA_CFG_` will be mapped to its corresponding Kafka key. For example, use `KAFKA_CFG_BACKGROUND_THREADS` in order to set `background.threads`. In order to pass custom environment variables use the `extraEnvVars` property. + +Using `extraEnvVars` with `KAFKA_CFG_` is the preferred and simplest way to add custom Kafka parameters not otherwise specified in this chart. Alternatively, you can provide a *full* Kafka configuration using `config` or `existingConfigmap`. +Setting either `config` or `existingConfigmap` will cause the chart to disregard `KAFKA_CFG_` settings, which are used by many other Kafka-related chart values described above, as well as dynamically generated parameters such as `zookeeper.connect`. This can cause unexpected behavior. + +### Listeners configuration + +This chart allows you to automatically configure Kafka with 3 listeners: + +- One for inter-broker communications. +- A second one for communications with clients within the K8s cluster. +- (optional) a third listener for communications with clients outside the K8s cluster. Check [this section](#accessing-kafka-brokers-from-outside-the-cluster) for more information. + +For more complex configurations, set the `listeners`, `advertisedListeners` and `listenerSecurityProtocolMap` parameters as needed. + +### Enable security for Kafka and Zookeeper + +You can configure different authentication protocols for each listener you configure in Kafka. For instance, you can use `sasl_tls` authentication for client communications, while using `tls` for inter-broker communications. This table shows the available protocols and the security they provide: + +| Method | Authentication | Encryption via TLS | +|-----------|------------------------------|--------------------| +| plaintext | None | No | +| tls | None | Yes | +| mtls | Yes (two-way authentication) | Yes | +| sasl | Yes (via SASL) | No | +| sasl_tls | Yes (via SASL) | Yes | + +Learn more about how to configure Kafka to use the different authentication protocols in the [chart documentation](https://docs.bitnami.com/kubernetes/infrastructure/kafka/administration/enable-security/). + +If you enabled SASL authentication on any listener, you can set the SASL credentials using the parameters below: + +- `auth.sasl.jaas.clientUsers`/`auth.sasl.jaas.clientPasswords`: when enabling SASL authentication for communications with clients. +- `auth.sasl.jaas.interBrokerUser`/`auth.sasl.jaas.interBrokerPassword`: when enabling SASL authentication for inter-broker communications. +- `auth.jaas.zookeeperUser`/`auth.jaas.zookeeperPassword`: In the case that the Zookeeper chart is deployed with SASL authentication enabled. + +In order to configure TLS authentication/encryption, you **can** create a secret per Kafka broker you have in the cluster containing the Java Key Stores (JKS) files: the truststore (`kafka.truststore.jks`) and the keystore (`kafka.keystore.jks`). Then, you need pass the secret names with the `auth.tls.existingSecrets` parameter when deploying the chart. + +> **Note**: If the JKS files are password protected (recommended), you will need to provide the password to get access to the keystores. To do so, use the `auth.tls.password` parameter to provide your password. + +For instance, to configure TLS authentication on a Kafka cluster with 2 Kafka brokers use the commands below to create the secrets: + +```console +kubectl create secret generic kafka-jks-0 --from-file=kafka.truststore.jks=./kafka.truststore.jks --from-file=kafka.keystore.jks=./kafka-0.keystore.jks +kubectl create secret generic kafka-jks-1 --from-file=kafka.truststore.jks=./kafka.truststore.jks --from-file=kafka.keystore.jks=./kafka-1.keystore.jks +``` + +> **Note**: the command above assumes you already created the truststore and keystores files. This [script](https://raw.githubusercontent.com/confluentinc/confluent-platform-security-tools/master/kafka-generate-ssl.sh) can help you with the JKS files generation. + +If, for some reason (like using Cert-Manager) you can not use the default JKS secret scheme, you can use the additional parameters: + +- `auth.tls.jksTruststoreSecret` to define additional secret, where the `kafka.truststore.jks` is being kept. The truststore password **must** be the same as in `auth.tls.password` +- `auth.tls.jksTruststore` to overwrite the default value of the truststore key (`kafka.truststore.jks`). +- `auth.tls.jksKeystoreSAN` if you want to use a SAN certificate for your brokers. Setting this parameter would mean that the chart expects a existing key in the `auth.tls.jksTruststoreSecret` with the `auth.tls.jksKeystoreSAN` value and use this as a keystore for **all** brokers +> **Note**: If you are using cert-manager, particularly when an ACME issuer is used, the `ca.crt` field is not put in the `Secret` that cert-manager creates. To handle this, the `auth.tls.pemChainIncluded` property can be set to `true` and the initContainer created by this Chart will attempt to extract the intermediate certs from the `tls.crt` field of the secret (which is a PEM chain) + +> **Note**: The truststore/keystore from above **must** be protected with the same password as in `auth.tls.password` + +You can deploy the chart with authentication using the following parameters: + +```console +replicaCount=2 +auth.clientProtocol=sasl +auth.interBrokerProtocol=tls +auth.tls.existingSecrets[0]=kafka-jks-0 +auth.tls.existingSecrets[1]=kafka-jks-1 +auth.tls.password=jksPassword +auth.sasl.jaas.clientUsers[0]=brokerUser +auth.sasl.jaas.clientPasswords[0]=brokerPassword +auth.sasl.jaas.zookeeperUser=zookeeperUser +auth.sasl.jaas.zookeeperPassword=zookeeperPassword +zookeeper.auth.enabled=true +zookeeper.auth.serverUsers=zookeeperUser +zookeeper.auth.serverPasswords=zookeeperPassword +zookeeper.auth.clientUser=zookeeperUser +zookeeper.auth.clientPassword=zookeeperPassword +``` + +You can deploy the chart with AclAuthorizer using the following parameters: + +```console +replicaCount=2 +auth.clientProtocol=sasl +auth.interBrokerProtocol=sasl_tls +auth.tls.existingSecrets[0]=kafka-jks-0 +auth.tls.existingSecrets[1]=kafka-jks-1 +auth.tls.password=jksPassword +auth.sasl.jaas.clientUsers[0]=brokerUser +auth.sasl.jaas.clientPasswords[0]=brokerPassword +auth.sasl.jaas.zookeeperUser=zookeeperUser +auth.sasl.jaas.zookeeperPassword=zookeeperPassword +zookeeper.auth.enabled=true +zookeeper.auth.serverUsers=zookeeperUser +zookeeper.auth.serverPasswords=zookeeperPassword +zookeeper.auth.clientUser=zookeeperUser +zookeeper.auth.clientPassword=zookeeperPassword +authorizerClassName=kafka.security.authorizer.AclAuthorizer +allowEveryoneIfNoAclFound=false +superUsers=User:admin +``` + +If you are using Kafka ACLs, you might encounter in kafka-authorizer.log the following event: `[...] Principal = User:ANONYMOUS is Allowed Operation [...]`. + +By setting the following parameter: `auth.clientProtocol=mtls`, it will set the configuration in Kafka to `ssl.client.auth=required`. This option will require the clients to authenticate to Kafka brokers. + +As result, we will be able to see in kafka-authorizer.log the events specific Subject: `[...] Principal = User:CN=kafka,OU=...,O=...,L=...,C=..,ST=... is [...]`. + +If you also enable exposing metrics using the Kafka exporter, and you are using `sasl_tls`, `tls`, or `mtls` authentication protocols, you need to mount the CA certificated used to sign the brokers certificates in the exporter so it can validate the Kafka brokers. To do so, create a secret containing the CA, and set the `metrics.certificatesSecret` parameter. As an alternative, you can skip TLS validation using extra flags: + +```console +metrics.kafka.extraFlags={tls.insecure-skip-tls-verify: ""} +``` + +### Accessing Kafka brokers from outside the cluster + +In order to access Kafka Brokers from outside the cluster, an additional listener and advertised listener must be configured. Additionally, a specific service per kafka pod will be created. + +There are three ways of configuring external access. Using LoadBalancer services, using NodePort services or using ClusterIP services. + +#### Using LoadBalancer services + +You have two alternatives to use LoadBalancer services: + +- Option A) Use random load balancer IPs using an **initContainer** that waits for the IPs to be ready and discover them automatically. + +```console +externalAccess.enabled=true +externalAccess.service.type=LoadBalancer +externalAccess.service.ports.external=9094 +externalAccess.autoDiscovery.enabled=true +serviceAccount.create=true +rbac.create=true +``` + +Note: This option requires creating RBAC rules on clusters where RBAC policies are enabled. + +- Option B) Manually specify the load balancer IPs: + +```console +externalAccess.enabled=true +externalAccess.service.type=LoadBalancer +externalAccess.service.ports.external=9094 +externalAccess.service.loadBalancerIPs[0]='external-ip-1' +externalAccess.service.loadBalancerIPs[1]='external-ip-2'} +``` + +Note: You need to know in advance the load balancer IPs so each Kafka broker advertised listener is configured with it. + +Following the aforementioned steps will also allow to connect the brokers from the outside using the cluster's default service (when `service.type` is `LoadBalancer` or `NodePort`). Use the property `service.externalPort` to specify the port used for external connections. + +#### Using NodePort services + +You have two alternatives to use NodePort services: + +- Option A) Use random node ports using an **initContainer** that discover them automatically. + +```console +externalAccess.enabled=true +externalAccess.service.type=NodePort +externalAccess.autoDiscovery.enabled=true +serviceAccount.create=true +rbac.create=true +``` + +Note: This option requires creating RBAC rules on clusters where RBAC policies are enabled. + +- Option B) Manually specify the node ports: + +```console +externalAccess.enabled=true +externalAccess.service.type=NodePort +externalAccess.service.nodePorts[0]='node-port-1' +externalAccess.service.nodePorts[1]='node-port-2' +``` + +Note: You need to know in advance the node ports that will be exposed so each Kafka broker advertised listener is configured with it. + +The pod will try to get the external ip of the node using `curl -s https://ipinfo.io/ip` unless `externalAccess.service.domain` or `externalAccess.service.useHostIPs` is provided. + +#### Using ClusterIP services + +Note: This option requires that an ingress is deployed within your cluster + +```console +externalAccess.enabled=true +externalAccess.service.type=ClusterIP +externalAccess.service.ports.external=9094 +externalAccess.service.domain='ingress-ip' +``` + +Note: the deployed ingress must contain the following block: + +```console +tcp: + 9094: "{{ .Release.Namespace }}/{{ include "kafka.fullname" . }}-0-external:9094" + 9095: "{{ .Release.Namespace }}/{{ include "kafka.fullname" . }}-1-external:9094" + 9096: "{{ .Release.Namespace }}/{{ include "kafka.fullname" . }}-2-external:9094" +``` + +#### Name resolution with External-DNS + +You can use the following values to generate External-DNS annotations which automatically creates DNS records for each ReplicaSet pod: + +```yaml +externalAccess: + service: + annotations: + external-dns.alpha.kubernetes.io/hostname: "{{ .targetPod }}.example.com" +``` + +### Sidecars + +If you have a need for additional containers to run within the same pod as Kafka (e.g. an additional metrics or logging exporter), you can do so via the `sidecars` config parameter. Simply define your container according to the Kubernetes container spec. + +```yaml +sidecars: + - name: your-image-name + image: your-image + imagePullPolicy: Always + ports: + - name: portname + containerPort: 1234 +``` + +### Setting Pod's affinity + +This chart allows you to set your custom affinity using the `affinity` parameter. Find more information about Pod's affinity in the [kubernetes documentation](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity). + +As an alternative, you can use of the preset configurations for pod affinity, pod anti-affinity, and node affinity available at the [bitnami/common](https://github.com/bitnami/charts/tree/main/bitnami/common#affinities) chart. To do so, set the `podAffinityPreset`, `podAntiAffinityPreset`, or `nodeAffinityPreset` parameters. + +### Deploying extra resources + +There are cases where you may want to deploy extra objects, such as Kafka Connect. For covering this case, the chart allows adding the full specification of other objects using the `extraDeploy` parameter. The following example would create a deployment including a Kafka Connect deployment so you can connect Kafka with MongoDB®: + +```yaml +## Extra objects to deploy (value evaluated as a template) +## +extraDeploy: + - | + apiVersion: apps/v1 + kind: Deployment + metadata: + name: {{ include "kafka.fullname" . }}-connect + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: connector + spec: + replicas: 1 + selector: + matchLabels: {{- include "common.labels.matchLabels" . | nindent 6 }} + app.kubernetes.io/component: connector + template: + metadata: + labels: {{- include "common.labels.standard" . | nindent 8 }} + app.kubernetes.io/component: connector + spec: + containers: + - name: connect + image: KAFKA-CONNECT-IMAGE + imagePullPolicy: IfNotPresent + ports: + - name: connector + containerPort: 8083 + volumeMounts: + - name: configuration + mountPath: /bitnami/kafka/config + volumes: + - name: configuration + configMap: + name: {{ include "kafka.fullname" . }}-connect + - | + apiVersion: v1 + kind: ConfigMap + metadata: + name: {{ include "kafka.fullname" . }}-connect + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: connector + data: + connect-standalone.properties: |- + bootstrap.servers = {{ include "kafka.fullname" . }}-0.{{ include "kafka.fullname" . }}-headless.{{ .Release.Namespace }}.svc.{{ .Values.clusterDomain }}:{{ .Values.service.port }} + ... + mongodb.properties: |- + connection.uri=mongodb://root:password@mongodb-hostname:27017 + ... + - | + apiVersion: v1 + kind: Service + metadata: + name: {{ include "kafka.fullname" . }}-connect + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: connector + spec: + ports: + - protocol: TCP + port: 8083 + targetPort: connector + selector: {{- include "common.labels.matchLabels" . | nindent 4 }} + app.kubernetes.io/component: connector +``` + +You can create the Kafka Connect image using the Dockerfile below: + +```Dockerfile +FROM bitnami/kafka:latest +# Download MongoDB® Connector for Apache Kafka https://www.confluent.io/hub/mongodb/kafka-connect-mongodb +RUN mkdir -p /opt/bitnami/kafka/plugins && \ + cd /opt/bitnami/kafka/plugins && \ + curl --remote-name --location --silent https://search.maven.org/remotecontent?filepath=org/mongodb/kafka/mongo-kafka-connect/1.2.0/mongo-kafka-connect-1.2.0-all.jar +CMD /opt/bitnami/kafka/bin/connect-standalone.sh /opt/bitnami/kafka/config/connect-standalone.properties /opt/bitnami/kafka/config/mongo.properties +``` + +## Persistence + +The [Bitnami Kafka](https://github.com/bitnami/containers/tree/main/bitnami/kafka) image stores the Kafka data at the `/bitnami/kafka` path of the container. + +Persistent Volume Claims are used to keep the data across deployments. This is known to work in GCE, AWS, and minikube. See the [Parameters](#persistence-parameters) section to configure the PVC or to disable persistence. + +### Adjust permissions of persistent volume mountpoint + +As the image run as non-root by default, it is necessary to adjust the ownership of the persistent volume so that the container can write data into it. + +By default, the chart is configured to use Kubernetes Security Context to automatically change the ownership of the volume. However, this feature does not work in all Kubernetes distributions. +As an alternative, this chart supports using an initContainer to change the ownership of the volume before mounting it in the final destination. + +You can enable this initContainer by setting `volumePermissions.enabled` to `true`. + +## Troubleshooting + +Find more information about how to deal with common errors related to Bitnami's Helm charts in [this troubleshooting guide](https://docs.bitnami.com/general/how-to/troubleshoot-helm-chart-issues). + +## Upgrading + +### To 20.0.0 + +This major updates the Zookeeper subchart to it newest major, 11.0.0. For more information on this subchart's major, please refer to [zookeeper upgrade notes](https://github.com/bitnami/charts/tree/main/bitnami/zookeeper#to-1100). + +### To 19.0.0 + +This major updates Kafka to its newest version, 3.3.x. For more information, please refer to [kafka upgrade notes](https://kafka.apache.org/33/documentation.html#upgrade). + +### To 18.0.0 + +This major updates the Zookeeper subchart to it newest major, 10.0.0. For more information on this subchart's major, please refer to [zookeeper upgrade notes](https://github.com/bitnami/charts/tree/main/bitnami/zookeeper#to-1000). + +### To 16.0.0 + +This major updates the Zookeeper subchart to it newest major, 9.0.0. For more information on this subchart's major, please refer to [zookeeper upgrade notes](https://github.com/bitnami/charts/tree/main/bitnami/zookeeper#to-900). + +### To 15.0.0 + +This major release bumps Kafka major version to `3.x` series. +It also renames several values in this chart and adds missing features, in order to be inline with the rest of assets in the Bitnami charts repository. Some affected values are: + +- `service.port`, `service.internalPort` and `service.externalPort` have been regrouped under the `service.ports` map. +- `metrics.kafka.service.port` has been regrouped under the `metrics.kafka.service.ports` map. +- `metrics.jmx.service.port` has been regrouped under the `metrics.jmx.service.ports` map. +- `updateStrategy` (string) and `rollingUpdatePartition` are regrouped under the `updateStrategy` map. +- Several parameters marked as deprecated `14.x.x` are not supported anymore. + +Additionally updates the ZooKeeper subchart to it newest major, `8.0.0`, which contains similar changes. + +### To 14.0.0 + +In this version, the `image` block is defined once and is used in the different templates, while in the previous version, the `image` block was duplicated for the main container and the provisioning one + +```yaml +image: + registry: docker.io + repository: bitnami/kafka + tag: 2.8.0 +``` + +VS + +```yaml +image: + registry: docker.io + repository: bitnami/kafka + tag: 2.8.0 +... +provisioning: + image: + registry: docker.io + repository: bitnami/kafka + tag: 2.8.0 +``` + +See [PR#7114](https://github.com/bitnami/charts/pull/7114) for more info about the implemented changes + +### To 13.0.0 + +This major updates the Zookeeper subchart to it newest major, 7.0.0, which renames all TLS-related settings. For more information on this subchart's major, please refer to [zookeeper upgrade notes](https://github.com/bitnami/charts/tree/main/bitnami/zookeeper#to-700). + +### To 12.2.0 + +This version also introduces `bitnami/common`, a [library chart](https://helm.sh/docs/topics/library_charts/#helm) as a dependency. More documentation about this new utility could be found [here](https://github.com/bitnami/charts/tree/main/bitnami/common#bitnami-common-library-chart). Please, make sure that you have updated the chart dependencies before executing any upgrade. + +### To 12.0.0 + +[On November 13, 2020, Helm v2 support was formally finished](https://github.com/helm/charts#status-of-the-project), this major version is the result of the required changes applied to the Helm Chart to be able to incorporate the different features added in Helm v3 and to be consistent with the Helm project itself regarding the Helm v2 EOL. + +**What changes were introduced in this major version?** + +- Previous versions of this Helm Chart use `apiVersion: v1` (installable by both Helm 2 and 3), this Helm Chart was updated to `apiVersion: v2` (installable by Helm 3 only). [Here](https://helm.sh/docs/topics/charts/#the-apiversion-field) you can find more information about the `apiVersion` field. +- Move dependency information from the *requirements.yaml* to the *Chart.yaml* +- After running `helm dependency update`, a *Chart.lock* file is generated containing the same structure used in the previous *requirements.lock* +- The different fields present in the *Chart.yaml* file has been ordered alphabetically in a homogeneous way for all the Bitnami Helm Charts + +**Considerations when upgrading to this version** + +- If you want to upgrade to this version from a previous one installed with Helm v3, you shouldn't face any issues +- If you want to upgrade to this version using Helm v2, this scenario is not supported as this version doesn't support Helm v2 anymore +- If you installed the previous version with Helm v2 and wants to upgrade to this version with Helm v3, please refer to the [official Helm documentation](https://helm.sh/docs/topics/v2_v3_migration/#migration-use-cases) about migrating from Helm v2 to v3 + +**Useful links** + +- https://docs.bitnami.com/tutorials/resolve-helm2-helm3-post-migration-issues/ +- https://helm.sh/docs/topics/v2_v3_migration/ +- https://helm.sh/blog/migrate-from-helm-v2-to-helm-v3/ + +### To 11.8.0 + +External access to brokers can now be achieved through the cluster's Kafka service. + +- `service.nodePort` -> deprecated in favor of `service.nodePorts.client` and `service.nodePorts.external` + +### To 11.7.0 + +The way to configure the users and passwords changed. Now it is allowed to create multiple users during the installation by providing the list of users and passwords. + +- `auth.jaas.clientUser` (string) -> deprecated in favor of `auth.jaas.clientUsers` (array). +- `auth.jaas.clientPassword` (string) -> deprecated in favor of `auth.jaas.clientPasswords` (array). + +### To 11.0.0 + +The way to configure listeners and athentication on Kafka is totally refactored allowing users to configure different authentication protocols on different listeners. Please check the [Listeners Configuration](#listeners-configuration) section for more information. + +Backwards compatibility is not guaranteed you adapt your values.yaml to the new format. Here you can find some parameters that were renamed or disappeared in favor of new ones on this major version: + +- `auth.enabled` -> deprecated in favor of `auth.clientProtocol` and `auth.interBrokerProtocol` parameters. +- `auth.ssl` -> deprecated in favor of `auth.clientProtocol` and `auth.interBrokerProtocol` parameters. +- `auth.certificatesSecret` -> renamed to `auth.jksSecret`. +- `auth.certificatesPassword` -> renamed to `auth.jksPassword`. +- `sslEndpointIdentificationAlgorithm` -> renamedo to `auth.tlsEndpointIdentificationAlgorithm`. +- `auth.interBrokerUser` -> renamed to `auth.jaas.interBrokerUser` +- `auth.interBrokerPassword` -> renamed to `auth.jaas.interBrokerPassword` +- `auth.zookeeperUser` -> renamed to `auth.jaas.zookeeperUser` +- `auth.zookeeperPassword` -> renamed to `auth.jaas.zookeeperPassword` +- `auth.existingSecret` -> renamed to `auth.jaas.existingSecret` +- `service.sslPort` -> deprecated in favor of `service.internalPort` +- `service.nodePorts.kafka` and `service.nodePorts.ssl` -> deprecated in favor of `service.nodePort` +- `metrics.kafka.extraFlag` -> new parameter +- `metrics.kafka.certificatesSecret` -> new parameter + +### To 10.0.0 + +If you are setting the `config` or `log4j` parameter, backwards compatibility is not guaranteed, because the `KAFKA_MOUNTED_CONFDIR` has moved from `/opt/bitnami/kafka/conf` to `/bitnami/kafka/config`. In order to continue using these parameters, you must also upgrade your image to `docker.io/bitnami/kafka:2.4.1-debian-10-r38` or later. + +### To 9.0.0 + +Backwards compatibility is not guaranteed you adapt your values.yaml to the new format. Here you can find some parameters that were renamed on this major version: + +```diff +- securityContext.enabled +- securityContext.fsGroup +- securityContext.fsGroup ++ podSecurityContext +- externalAccess.service.loadBalancerIP ++ externalAccess.service.loadBalancerIPs +- externalAccess.service.nodePort ++ externalAccess.service.nodePorts +- metrics.jmx.configMap.enabled +- metrics.jmx.configMap.overrideConfig ++ metrics.jmx.config +- metrics.jmx.configMap.overrideName ++ metrics.jmx.existingConfigmap +``` + +Ports names were prefixed with the protocol to comply with Istio (see https://istio.io/docs/ops/deployment/requirements/). + +### To 8.0.0 + +There is not backwards compatibility since the brokerID changes to the POD_NAME. For more information see [this PR](https://github.com/bitnami/charts/pull/2028). + +### To 7.0.0 + +Backwards compatibility is not guaranteed when Kafka metrics are enabled, unless you modify the labels used on the exporter deployments. +Use the workaround below to upgrade from versions previous to 7.0.0. The following example assumes that the release name is kafka: + +```console +helm upgrade kafka my-repo/kafka --version 6.1.8 --set metrics.kafka.enabled=false +helm upgrade kafka my-repo/kafka --version 7.0.0 --set metrics.kafka.enabled=true +``` + +### To 2.0.0 + +Backwards compatibility is not guaranteed unless you modify the labels used on the chart's deployments. +Use the workaround below to upgrade from versions previous to 2.0.0. The following example assumes that the release name is kafka: + +```console +kubectl delete statefulset kafka-kafka --cascade=false +kubectl delete statefulset kafka-zookeeper --cascade=false +``` + +### To 1.0.0 + +Backwards compatibility is not guaranteed unless you modify the labels used on the chart's deployments. +Use the workaround below to upgrade from versions previous to 1.0.0. The following example assumes that the release name is kafka: + +```console +kubectl delete statefulset kafka-kafka --cascade=false +kubectl delete statefulset kafka-zookeeper --cascade=false +``` + +## License + +Copyright © 2022 Bitnami + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/knowlg-automation/helm_charts/kafka/charts/common/.helmignore b/knowlg-automation/helm_charts/kafka/charts/common/.helmignore new file mode 100644 index 000000000..50af03172 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/common/.helmignore @@ -0,0 +1,22 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/knowlg-automation/helm_charts/kafka/charts/common/Chart.yaml b/knowlg-automation/helm_charts/kafka/charts/common/Chart.yaml new file mode 100644 index 000000000..f9ba944c8 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/common/Chart.yaml @@ -0,0 +1,23 @@ +annotations: + category: Infrastructure +apiVersion: v2 +appVersion: 2.2.2 +description: A Library Helm Chart for grouping common logic between bitnami charts. + This chart is not deployable by itself. +home: https://github.com/bitnami/charts/tree/main/bitnami/common +icon: https://bitnami.com/downloads/logos/bitnami-mark.png +keywords: +- common +- helper +- template +- function +- bitnami +maintainers: +- name: Bitnami + url: https://github.com/bitnami/charts +name: common +sources: +- https://github.com/bitnami/charts +- https://www.bitnami.com/ +type: library +version: 2.2.2 diff --git a/knowlg-automation/helm_charts/kafka/charts/common/README.md b/knowlg-automation/helm_charts/kafka/charts/common/README.md new file mode 100644 index 000000000..ec43a5fab --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/common/README.md @@ -0,0 +1,351 @@ +# Bitnami Common Library Chart + +A [Helm Library Chart](https://helm.sh/docs/topics/library_charts/#helm) for grouping common logic between bitnami charts. + +## TL;DR + +```yaml +dependencies: + - name: common + version: 1.x.x + repository: https://charts.bitnami.com/bitnami +``` + +```bash +$ helm dependency update +``` + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "common.names.fullname" . }} +data: + myvalue: "Hello World" +``` + +## Introduction + +This chart provides a common template helpers which can be used to develop new charts using [Helm](https://helm.sh) package manager. + +Bitnami charts can be used with [Kubeapps](https://kubeapps.dev/) for deployment and management of Helm Charts in clusters. + +## Prerequisites + +- Kubernetes 1.19+ +- Helm 3.2.0+ + +## Parameters + +The following table lists the helpers available in the library which are scoped in different sections. + +### Affinities + +| Helper identifier | Description | Expected Input | +|-------------------------------|------------------------------------------------------|------------------------------------------------| +| `common.affinities.nodes.soft` | Return a soft nodeAffinity definition | `dict "key" "FOO" "values" (list "BAR" "BAZ")` | +| `common.affinities.nodes.hard` | Return a hard nodeAffinity definition | `dict "key" "FOO" "values" (list "BAR" "BAZ")` | +| `common.affinities.pods.soft` | Return a soft podAffinity/podAntiAffinity definition | `dict "component" "FOO" "context" $` | +| `common.affinities.pods.hard` | Return a hard podAffinity/podAntiAffinity definition | `dict "component" "FOO" "context" $` | +| `common.affinities.topologyKey` | Return a topologyKey definition | `dict "topologyKey" "FOO"` | + +### Capabilities + +| Helper identifier | Description | Expected Input | +|------------------------------------------------|------------------------------------------------------------------------------------------------|-------------------| +| `common.capabilities.kubeVersion` | Return the target Kubernetes version (using client default if .Values.kubeVersion is not set). | `.` Chart context | +| `common.capabilities.cronjob.apiVersion` | Return the appropriate apiVersion for cronjob. | `.` Chart context | +| `common.capabilities.deployment.apiVersion` | Return the appropriate apiVersion for deployment. | `.` Chart context | +| `common.capabilities.statefulset.apiVersion` | Return the appropriate apiVersion for statefulset. | `.` Chart context | +| `common.capabilities.ingress.apiVersion` | Return the appropriate apiVersion for ingress. | `.` Chart context | +| `common.capabilities.rbac.apiVersion` | Return the appropriate apiVersion for RBAC resources. | `.` Chart context | +| `common.capabilities.crd.apiVersion` | Return the appropriate apiVersion for CRDs. | `.` Chart context | +| `common.capabilities.policy.apiVersion` | Return the appropriate apiVersion for podsecuritypolicy. | `.` Chart context | +| `common.capabilities.networkPolicy.apiVersion` | Return the appropriate apiVersion for networkpolicy. | `.` Chart context | +| `common.capabilities.apiService.apiVersion` | Return the appropriate apiVersion for APIService. | `.` Chart context | +| `common.capabilities.hpa.apiVersion` | Return the appropriate apiVersion for Horizontal Pod Autoscaler | `.` Chart context | +| `common.capabilities.supportsHelmVersion` | Returns true if the used Helm version is 3.3+ | `.` Chart context | + +### Errors + +| Helper identifier | Description | Expected Input | +|-----------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------| +| `common.errors.upgrade.passwords.empty` | It will ensure required passwords are given when we are upgrading a chart. If `validationErrors` is not empty it will throw an error and will stop the upgrade action. | `dict "validationErrors" (list $validationError00 $validationError01) "context" $` | + +### Images + +| Helper identifier | Description | Expected Input | +|-----------------------------|------------------------------------------------------|---------------------------------------------------------------------------------------------------------| +| `common.images.image` | Return the proper and full image name | `dict "imageRoot" .Values.path.to.the.image "global" $`, see [ImageRoot](#imageroot) for the structure. | +| `common.images.pullSecrets` | Return the proper Docker Image Registry Secret Names (deprecated: use common.images.renderPullSecrets instead) | `dict "images" (list .Values.path.to.the.image1, .Values.path.to.the.image2) "global" .Values.global` | +| `common.images.renderPullSecrets` | Return the proper Docker Image Registry Secret Names (evaluates values as templates) | `dict "images" (list .Values.path.to.the.image1, .Values.path.to.the.image2) "context" $` | + +### Ingress + +| Helper identifier | Description | Expected Input | +|-------------------------------------------|-------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `common.ingress.backend` | Generate a proper Ingress backend entry depending on the API version | `dict "serviceName" "foo" "servicePort" "bar"`, see the [Ingress deprecation notice](https://kubernetes.io/blog/2019/07/18/api-deprecations-in-1-16/) for the syntax differences | +| `common.ingress.supportsPathType` | Prints "true" if the pathType field is supported | `.` Chart context | +| `common.ingress.supportsIngressClassname` | Prints "true" if the ingressClassname field is supported | `.` Chart context | +| `common.ingress.certManagerRequest` | Prints "true" if required cert-manager annotations for TLS signed certificates are set in the Ingress annotations | `dict "annotations" .Values.path.to.the.ingress.annotations` | + +### Labels + +| Helper identifier | Description | Expected Input | +|-----------------------------|-----------------------------------------------------------------------------|-------------------| +| `common.labels.standard` | Return Kubernetes standard labels | `.` Chart context | +| `common.labels.matchLabels` | Labels to use on `deploy.spec.selector.matchLabels` and `svc.spec.selector` | `.` Chart context | + +### Names + +| Helper identifier | Description | Expected Input | +|-----------------------------------|-----------------------------------------------------------------------|-------------------| +| `common.names.name` | Expand the name of the chart or use `.Values.nameOverride` | `.` Chart context | +| `common.names.fullname` | Create a default fully qualified app name. | `.` Chart context | +| `common.names.namespace` | Allow the release namespace to be overridden | `.` Chart context | +| `common.names.fullname.namespace` | Create a fully qualified app name adding the installation's namespace | `.` Chart context | +| `common.names.chart` | Chart name plus version | `.` Chart context | + +### Secrets + +| Helper identifier | Description | Expected Input | +|-----------------------------------|--------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `common.secrets.name` | Generate the name of the secret. | `dict "existingSecret" .Values.path.to.the.existingSecret "defaultNameSuffix" "mySuffix" "context" $` see [ExistingSecret](#existingsecret) for the structure. | +| `common.secrets.key` | Generate secret key. | `dict "existingSecret" .Values.path.to.the.existingSecret "key" "keyName"` see [ExistingSecret](#existingsecret) for the structure. | +| `common.secrets.passwords.manage` | Generate secret password or retrieve one if already created. | `dict "secret" "secret-name" "key" "keyName" "providedValues" (list "path.to.password1" "path.to.password2") "length" 10 "strong" false "chartName" "chartName" "context" $`, length, strong and chartNAme fields are optional. | +| `common.secrets.exists` | Returns whether a previous generated secret already exists. | `dict "secret" "secret-name" "context" $` | + +### Storage + +| Helper identifier | Description | Expected Input | +|-------------------------------|---------------------------------------|---------------------------------------------------------------------------------------------------------------------| +| `common.storage.class` | Return the proper Storage Class | `dict "persistence" .Values.path.to.the.persistence "global" $`, see [Persistence](#persistence) for the structure. | + +### TplValues + +| Helper identifier | Description | Expected Input | +|---------------------------|----------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------| +| `common.tplvalues.render` | Renders a value that contains template | `dict "value" .Values.path.to.the.Value "context" $`, value is the value should rendered as template, context frequently is the chart context `$` or `.` | + +### Utils + +| Helper identifier | Description | Expected Input | +|--------------------------------|------------------------------------------------------------------------------------------|------------------------------------------------------------------------| +| `common.utils.fieldToEnvVar` | Build environment variable name given a field. | `dict "field" "my-password"` | +| `common.utils.secret.getvalue` | Print instructions to get a secret value. | `dict "secret" "secret-name" "field" "secret-value-field" "context" $` | +| `common.utils.getValueFromKey` | Gets a value from `.Values` object given its key path | `dict "key" "path.to.key" "context" $` | +| `common.utils.getKeyFromList` | Returns first `.Values` key with a defined value or first of the list if all non-defined | `dict "keys" (list "path.to.key1" "path.to.key2") "context" $` | + +### Validations + +| Helper identifier | Description | Expected Input | +|--------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `common.validations.values.single.empty` | Validate a value must not be empty. | `dict "valueKey" "path.to.value" "secret" "secret.name" "field" "my-password" "subchart" "subchart" "context" $` secret, field and subchart are optional. In case they are given, the helper will generate a how to get instruction. See [ValidateValue](#validatevalue) | +| `common.validations.values.multiple.empty` | Validate a multiple values must not be empty. It returns a shared error for all the values. | `dict "required" (list $validateValueConf00 $validateValueConf01) "context" $`. See [ValidateValue](#validatevalue) | +| `common.validations.values.mariadb.passwords` | This helper will ensure required password for MariaDB are not empty. It returns a shared error for all the values. | `dict "secret" "mariadb-secret" "subchart" "true" "context" $` subchart field is optional and could be true or false it depends on where you will use mariadb chart and the helper. | +| `common.validations.values.mysql.passwords` | This helper will ensure required password for MySQL are not empty. It returns a shared error for all the values. | `dict "secret" "mysql-secret" "subchart" "true" "context" $` subchart field is optional and could be true or false it depends on where you will use mysql chart and the helper. | +| `common.validations.values.postgresql.passwords` | This helper will ensure required password for PostgreSQL are not empty. It returns a shared error for all the values. | `dict "secret" "postgresql-secret" "subchart" "true" "context" $` subchart field is optional and could be true or false it depends on where you will use postgresql chart and the helper. | +| `common.validations.values.redis.passwords` | This helper will ensure required password for Redis® are not empty. It returns a shared error for all the values. | `dict "secret" "redis-secret" "subchart" "true" "context" $` subchart field is optional and could be true or false it depends on where you will use redis chart and the helper. | +| `common.validations.values.cassandra.passwords` | This helper will ensure required password for Cassandra are not empty. It returns a shared error for all the values. | `dict "secret" "cassandra-secret" "subchart" "true" "context" $` subchart field is optional and could be true or false it depends on where you will use cassandra chart and the helper. | +| `common.validations.values.mongodb.passwords` | This helper will ensure required password for MongoDB® are not empty. It returns a shared error for all the values. | `dict "secret" "mongodb-secret" "subchart" "true" "context" $` subchart field is optional and could be true or false it depends on where you will use mongodb chart and the helper. | + +### Warnings + +| Helper identifier | Description | Expected Input | +|------------------------------|----------------------------------|------------------------------------------------------------| +| `common.warnings.rollingTag` | Warning about using rolling tag. | `ImageRoot` see [ImageRoot](#imageroot) for the structure. | + +## Special input schemas + +### ImageRoot + +```yaml +registry: + type: string + description: Docker registry where the image is located + example: docker.io + +repository: + type: string + description: Repository and image name + example: bitnami/nginx + +tag: + type: string + description: image tag + example: 1.16.1-debian-10-r63 + +pullPolicy: + type: string + description: Specify a imagePullPolicy. Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' + +pullSecrets: + type: array + items: + type: string + description: Optionally specify an array of imagePullSecrets (evaluated as templates). + +debug: + type: boolean + description: Set to true if you would like to see extra information on logs + example: false + +## An instance would be: +# registry: docker.io +# repository: bitnami/nginx +# tag: 1.16.1-debian-10-r63 +# pullPolicy: IfNotPresent +# debug: false +``` + +### Persistence + +```yaml +enabled: + type: boolean + description: Whether enable persistence. + example: true + +storageClass: + type: string + description: Ghost data Persistent Volume Storage Class, If set to "-", storageClassName: "" which disables dynamic provisioning. + example: "-" + +accessMode: + type: string + description: Access mode for the Persistent Volume Storage. + example: ReadWriteOnce + +size: + type: string + description: Size the Persistent Volume Storage. + example: 8Gi + +path: + type: string + description: Path to be persisted. + example: /bitnami + +## An instance would be: +# enabled: true +# storageClass: "-" +# accessMode: ReadWriteOnce +# size: 8Gi +# path: /bitnami +``` + +### ExistingSecret + +```yaml +name: + type: string + description: Name of the existing secret. + example: mySecret +keyMapping: + description: Mapping between the expected key name and the name of the key in the existing secret. + type: object + +## An instance would be: +# name: mySecret +# keyMapping: +# password: myPasswordKey +``` + +#### Example of use + +When we store sensitive data for a deployment in a secret, some times we want to give to users the possibility of using theirs existing secrets. + +```yaml +# templates/secret.yaml +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "common.names.fullname" . }} + labels: + app: {{ include "common.names.fullname" . }} +type: Opaque +data: + password: {{ .Values.password | b64enc | quote }} + +# templates/dpl.yaml +--- +... + env: + - name: PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "common.secrets.name" (dict "existingSecret" .Values.existingSecret "context" $) }} + key: {{ include "common.secrets.key" (dict "existingSecret" .Values.existingSecret "key" "password") }} +... + +# values.yaml +--- +name: mySecret +keyMapping: + password: myPasswordKey +``` + +### ValidateValue + +#### NOTES.txt + +```console +{{- $validateValueConf00 := (dict "valueKey" "path.to.value00" "secret" "secretName" "field" "password-00") -}} +{{- $validateValueConf01 := (dict "valueKey" "path.to.value01" "secret" "secretName" "field" "password-01") -}} + +{{ include "common.validations.values.multiple.empty" (dict "required" (list $validateValueConf00 $validateValueConf01) "context" $) }} +``` + +If we force those values to be empty we will see some alerts + +```console +$ helm install test mychart --set path.to.value00="",path.to.value01="" + 'path.to.value00' must not be empty, please add '--set path.to.value00=$PASSWORD_00' to the command. To get the current value: + + export PASSWORD_00=$(kubectl get secret --namespace default secretName -o jsonpath="{.data.password-00}" | base64 -d) + + 'path.to.value01' must not be empty, please add '--set path.to.value01=$PASSWORD_01' to the command. To get the current value: + + export PASSWORD_01=$(kubectl get secret --namespace default secretName -o jsonpath="{.data.password-01}" | base64 -d) +``` + +## Upgrading + +### To 1.0.0 + +[On November 13, 2020, Helm v2 support was formally finished](https://github.com/helm/charts#status-of-the-project), this major version is the result of the required changes applied to the Helm Chart to be able to incorporate the different features added in Helm v3 and to be consistent with the Helm project itself regarding the Helm v2 EOL. + +**What changes were introduced in this major version?** + +- Previous versions of this Helm Chart use `apiVersion: v1` (installable by both Helm 2 and 3), this Helm Chart was updated to `apiVersion: v2` (installable by Helm 3 only). [Here](https://helm.sh/docs/topics/charts/#the-apiversion-field) you can find more information about the `apiVersion` field. +- Use `type: library`. [Here](https://v3.helm.sh/docs/faq/#library-chart-support) you can find more information. +- The different fields present in the *Chart.yaml* file has been ordered alphabetically in a homogeneous way for all the Bitnami Helm Charts + +**Considerations when upgrading to this version** + +- If you want to upgrade to this version from a previous one installed with Helm v3, you shouldn't face any issues +- If you want to upgrade to this version using Helm v2, this scenario is not supported as this version doesn't support Helm v2 anymore +- If you installed the previous version with Helm v2 and wants to upgrade to this version with Helm v3, please refer to the [official Helm documentation](https://helm.sh/docs/topics/v2_v3_migration/#migration-use-cases) about migrating from Helm v2 to v3 + +**Useful links** + +- https://docs.bitnami.com/tutorials/resolve-helm2-helm3-post-migration-issues/ +- https://helm.sh/docs/topics/v2_v3_migration/ +- https://helm.sh/blog/migrate-from-helm-v2-to-helm-v3/ + +## License + +Copyright © 2022 Bitnami + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/knowlg-automation/helm_charts/kafka/charts/common/templates/_affinities.tpl b/knowlg-automation/helm_charts/kafka/charts/common/templates/_affinities.tpl new file mode 100644 index 000000000..81902a681 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/common/templates/_affinities.tpl @@ -0,0 +1,106 @@ +{{/* vim: set filetype=mustache: */}} + +{{/* +Return a soft nodeAffinity definition +{{ include "common.affinities.nodes.soft" (dict "key" "FOO" "values" (list "BAR" "BAZ")) -}} +*/}} +{{- define "common.affinities.nodes.soft" -}} +preferredDuringSchedulingIgnoredDuringExecution: + - preference: + matchExpressions: + - key: {{ .key }} + operator: In + values: + {{- range .values }} + - {{ . | quote }} + {{- end }} + weight: 1 +{{- end -}} + +{{/* +Return a hard nodeAffinity definition +{{ include "common.affinities.nodes.hard" (dict "key" "FOO" "values" (list "BAR" "BAZ")) -}} +*/}} +{{- define "common.affinities.nodes.hard" -}} +requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: {{ .key }} + operator: In + values: + {{- range .values }} + - {{ . | quote }} + {{- end }} +{{- end -}} + +{{/* +Return a nodeAffinity definition +{{ include "common.affinities.nodes" (dict "type" "soft" "key" "FOO" "values" (list "BAR" "BAZ")) -}} +*/}} +{{- define "common.affinities.nodes" -}} + {{- if eq .type "soft" }} + {{- include "common.affinities.nodes.soft" . -}} + {{- else if eq .type "hard" }} + {{- include "common.affinities.nodes.hard" . -}} + {{- end -}} +{{- end -}} + +{{/* +Return a topologyKey definition +{{ include "common.affinities.topologyKey" (dict "topologyKey" "BAR") -}} +*/}} +{{- define "common.affinities.topologyKey" -}} +{{ .topologyKey | default "kubernetes.io/hostname" -}} +{{- end -}} + +{{/* +Return a soft podAffinity/podAntiAffinity definition +{{ include "common.affinities.pods.soft" (dict "component" "FOO" "extraMatchLabels" .Values.extraMatchLabels "topologyKey" "BAR" "context" $) -}} +*/}} +{{- define "common.affinities.pods.soft" -}} +{{- $component := default "" .component -}} +{{- $extraMatchLabels := default (dict) .extraMatchLabels -}} +preferredDuringSchedulingIgnoredDuringExecution: + - podAffinityTerm: + labelSelector: + matchLabels: {{- (include "common.labels.matchLabels" .context) | nindent 10 }} + {{- if not (empty $component) }} + {{ printf "app.kubernetes.io/component: %s" $component }} + {{- end }} + {{- range $key, $value := $extraMatchLabels }} + {{ $key }}: {{ $value | quote }} + {{- end }} + topologyKey: {{ include "common.affinities.topologyKey" (dict "topologyKey" .topologyKey) }} + weight: 1 +{{- end -}} + +{{/* +Return a hard podAffinity/podAntiAffinity definition +{{ include "common.affinities.pods.hard" (dict "component" "FOO" "extraMatchLabels" .Values.extraMatchLabels "topologyKey" "BAR" "context" $) -}} +*/}} +{{- define "common.affinities.pods.hard" -}} +{{- $component := default "" .component -}} +{{- $extraMatchLabels := default (dict) .extraMatchLabels -}} +requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchLabels: {{- (include "common.labels.matchLabels" .context) | nindent 8 }} + {{- if not (empty $component) }} + {{ printf "app.kubernetes.io/component: %s" $component }} + {{- end }} + {{- range $key, $value := $extraMatchLabels }} + {{ $key }}: {{ $value | quote }} + {{- end }} + topologyKey: {{ include "common.affinities.topologyKey" (dict "topologyKey" .topologyKey) }} +{{- end -}} + +{{/* +Return a podAffinity/podAntiAffinity definition +{{ include "common.affinities.pods" (dict "type" "soft" "key" "FOO" "values" (list "BAR" "BAZ")) -}} +*/}} +{{- define "common.affinities.pods" -}} + {{- if eq .type "soft" }} + {{- include "common.affinities.pods.soft" . -}} + {{- else if eq .type "hard" }} + {{- include "common.affinities.pods.hard" . -}} + {{- end -}} +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/charts/common/templates/_capabilities.tpl b/knowlg-automation/helm_charts/kafka/charts/common/templates/_capabilities.tpl new file mode 100644 index 000000000..9d9b76004 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/common/templates/_capabilities.tpl @@ -0,0 +1,154 @@ +{{/* vim: set filetype=mustache: */}} + +{{/* +Return the target Kubernetes version +*/}} +{{- define "common.capabilities.kubeVersion" -}} +{{- if .Values.global }} + {{- if .Values.global.kubeVersion }} + {{- .Values.global.kubeVersion -}} + {{- else }} + {{- default .Capabilities.KubeVersion.Version .Values.kubeVersion -}} + {{- end -}} +{{- else }} +{{- default .Capabilities.KubeVersion.Version .Values.kubeVersion -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for poddisruptionbudget. +*/}} +{{- define "common.capabilities.policy.apiVersion" -}} +{{- if semverCompare "<1.21-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "policy/v1beta1" -}} +{{- else -}} +{{- print "policy/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for networkpolicy. +*/}} +{{- define "common.capabilities.networkPolicy.apiVersion" -}} +{{- if semverCompare "<1.7-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "extensions/v1beta1" -}} +{{- else -}} +{{- print "networking.k8s.io/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for cronjob. +*/}} +{{- define "common.capabilities.cronjob.apiVersion" -}} +{{- if semverCompare "<1.21-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "batch/v1beta1" -}} +{{- else -}} +{{- print "batch/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for deployment. +*/}} +{{- define "common.capabilities.deployment.apiVersion" -}} +{{- if semverCompare "<1.14-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "extensions/v1beta1" -}} +{{- else -}} +{{- print "apps/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for statefulset. +*/}} +{{- define "common.capabilities.statefulset.apiVersion" -}} +{{- if semverCompare "<1.14-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "apps/v1beta1" -}} +{{- else -}} +{{- print "apps/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for ingress. +*/}} +{{- define "common.capabilities.ingress.apiVersion" -}} +{{- if .Values.ingress -}} +{{- if .Values.ingress.apiVersion -}} +{{- .Values.ingress.apiVersion -}} +{{- else if semverCompare "<1.14-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "extensions/v1beta1" -}} +{{- else if semverCompare "<1.19-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "networking.k8s.io/v1beta1" -}} +{{- else -}} +{{- print "networking.k8s.io/v1" -}} +{{- end }} +{{- else if semverCompare "<1.14-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "extensions/v1beta1" -}} +{{- else if semverCompare "<1.19-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "networking.k8s.io/v1beta1" -}} +{{- else -}} +{{- print "networking.k8s.io/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for RBAC resources. +*/}} +{{- define "common.capabilities.rbac.apiVersion" -}} +{{- if semverCompare "<1.17-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "rbac.authorization.k8s.io/v1beta1" -}} +{{- else -}} +{{- print "rbac.authorization.k8s.io/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for CRDs. +*/}} +{{- define "common.capabilities.crd.apiVersion" -}} +{{- if semverCompare "<1.19-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "apiextensions.k8s.io/v1beta1" -}} +{{- else -}} +{{- print "apiextensions.k8s.io/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for APIService. +*/}} +{{- define "common.capabilities.apiService.apiVersion" -}} +{{- if semverCompare "<1.10-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "apiregistration.k8s.io/v1beta1" -}} +{{- else -}} +{{- print "apiregistration.k8s.io/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for Horizontal Pod Autoscaler. +*/}} +{{- define "common.capabilities.hpa.apiVersion" -}} +{{- if semverCompare "<1.23-0" (include "common.capabilities.kubeVersion" .context) -}} +{{- if .beta2 -}} +{{- print "autoscaling/v2beta2" -}} +{{- else -}} +{{- print "autoscaling/v2beta1" -}} +{{- end -}} +{{- else -}} +{{- print "autoscaling/v2" -}} +{{- end -}} +{{- end -}} + +{{/* +Returns true if the used Helm version is 3.3+. +A way to check the used Helm version was not introduced until version 3.3.0 with .Capabilities.HelmVersion, which contains an additional "{}}" structure. +This check is introduced as a regexMatch instead of {{ if .Capabilities.HelmVersion }} because checking for the key HelmVersion in <3.3 results in a "interface not found" error. +**To be removed when the catalog's minimun Helm version is 3.3** +*/}} +{{- define "common.capabilities.supportsHelmVersion" -}} +{{- if regexMatch "{(v[0-9])*[^}]*}}$" (.Capabilities | toString ) }} + {{- true -}} +{{- end -}} +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/charts/common/templates/_errors.tpl b/knowlg-automation/helm_charts/kafka/charts/common/templates/_errors.tpl new file mode 100644 index 000000000..a79cc2e32 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/common/templates/_errors.tpl @@ -0,0 +1,23 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Through error when upgrading using empty passwords values that must not be empty. + +Usage: +{{- $validationError00 := include "common.validations.values.single.empty" (dict "valueKey" "path.to.password00" "secret" "secretName" "field" "password-00") -}} +{{- $validationError01 := include "common.validations.values.single.empty" (dict "valueKey" "path.to.password01" "secret" "secretName" "field" "password-01") -}} +{{ include "common.errors.upgrade.passwords.empty" (dict "validationErrors" (list $validationError00 $validationError01) "context" $) }} + +Required password params: + - validationErrors - String - Required. List of validation strings to be return, if it is empty it won't throw error. + - context - Context - Required. Parent context. +*/}} +{{- define "common.errors.upgrade.passwords.empty" -}} + {{- $validationErrors := join "" .validationErrors -}} + {{- if and $validationErrors .context.Release.IsUpgrade -}} + {{- $errorString := "\nPASSWORDS ERROR: You must provide your current passwords when upgrading the release." -}} + {{- $errorString = print $errorString "\n Note that even after reinstallation, old credentials may be needed as they may be kept in persistent volume claims." -}} + {{- $errorString = print $errorString "\n Further information can be obtained at https://docs.bitnami.com/general/how-to/troubleshoot-helm-chart-issues/#credential-errors-while-upgrading-chart-releases" -}} + {{- $errorString = print $errorString "\n%s" -}} + {{- printf $errorString $validationErrors | fail -}} + {{- end -}} +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/charts/common/templates/_images.tpl b/knowlg-automation/helm_charts/kafka/charts/common/templates/_images.tpl new file mode 100644 index 000000000..46c659e79 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/common/templates/_images.tpl @@ -0,0 +1,76 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Return the proper image name +{{ include "common.images.image" ( dict "imageRoot" .Values.path.to.the.image "global" $) }} +*/}} +{{- define "common.images.image" -}} +{{- $registryName := .imageRoot.registry -}} +{{- $repositoryName := .imageRoot.repository -}} +{{- $separator := ":" -}} +{{- $termination := .imageRoot.tag | toString -}} +{{- if .global }} + {{- if .global.imageRegistry }} + {{- $registryName = .global.imageRegistry -}} + {{- end -}} +{{- end -}} +{{- if .imageRoot.digest }} + {{- $separator = "@" -}} + {{- $termination = .imageRoot.digest | toString -}} +{{- end -}} +{{- printf "%s/%s%s%s" $registryName $repositoryName $separator $termination -}} +{{- end -}} + +{{/* +Return the proper Docker Image Registry Secret Names (deprecated: use common.images.renderPullSecrets instead) +{{ include "common.images.pullSecrets" ( dict "images" (list .Values.path.to.the.image1, .Values.path.to.the.image2) "global" .Values.global) }} +*/}} +{{- define "common.images.pullSecrets" -}} + {{- $pullSecrets := list }} + + {{- if .global }} + {{- range .global.imagePullSecrets -}} + {{- $pullSecrets = append $pullSecrets . -}} + {{- end -}} + {{- end -}} + + {{- range .images -}} + {{- range .pullSecrets -}} + {{- $pullSecrets = append $pullSecrets . -}} + {{- end -}} + {{- end -}} + + {{- if (not (empty $pullSecrets)) }} +imagePullSecrets: + {{- range $pullSecrets }} + - name: {{ . }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Return the proper Docker Image Registry Secret Names evaluating values as templates +{{ include "common.images.renderPullSecrets" ( dict "images" (list .Values.path.to.the.image1, .Values.path.to.the.image2) "context" $) }} +*/}} +{{- define "common.images.renderPullSecrets" -}} + {{- $pullSecrets := list }} + {{- $context := .context }} + + {{- if $context.Values.global }} + {{- range $context.Values.global.imagePullSecrets -}} + {{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" . "context" $context)) -}} + {{- end -}} + {{- end -}} + + {{- range .images -}} + {{- range .pullSecrets -}} + {{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" . "context" $context)) -}} + {{- end -}} + {{- end -}} + + {{- if (not (empty $pullSecrets)) }} +imagePullSecrets: + {{- range $pullSecrets }} + - name: {{ . }} + {{- end }} + {{- end }} +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/charts/common/templates/_ingress.tpl b/knowlg-automation/helm_charts/kafka/charts/common/templates/_ingress.tpl new file mode 100644 index 000000000..831da9caa --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/common/templates/_ingress.tpl @@ -0,0 +1,68 @@ +{{/* vim: set filetype=mustache: */}} + +{{/* +Generate backend entry that is compatible with all Kubernetes API versions. + +Usage: +{{ include "common.ingress.backend" (dict "serviceName" "backendName" "servicePort" "backendPort" "context" $) }} + +Params: + - serviceName - String. Name of an existing service backend + - servicePort - String/Int. Port name (or number) of the service. It will be translated to different yaml depending if it is a string or an integer. + - context - Dict - Required. The context for the template evaluation. +*/}} +{{- define "common.ingress.backend" -}} +{{- $apiVersion := (include "common.capabilities.ingress.apiVersion" .context) -}} +{{- if or (eq $apiVersion "extensions/v1beta1") (eq $apiVersion "networking.k8s.io/v1beta1") -}} +serviceName: {{ .serviceName }} +servicePort: {{ .servicePort }} +{{- else -}} +service: + name: {{ .serviceName }} + port: + {{- if typeIs "string" .servicePort }} + name: {{ .servicePort }} + {{- else if or (typeIs "int" .servicePort) (typeIs "float64" .servicePort) }} + number: {{ .servicePort | int }} + {{- end }} +{{- end -}} +{{- end -}} + +{{/* +Print "true" if the API pathType field is supported +Usage: +{{ include "common.ingress.supportsPathType" . }} +*/}} +{{- define "common.ingress.supportsPathType" -}} +{{- if (semverCompare "<1.18-0" (include "common.capabilities.kubeVersion" .)) -}} +{{- print "false" -}} +{{- else -}} +{{- print "true" -}} +{{- end -}} +{{- end -}} + +{{/* +Returns true if the ingressClassname field is supported +Usage: +{{ include "common.ingress.supportsIngressClassname" . }} +*/}} +{{- define "common.ingress.supportsIngressClassname" -}} +{{- if semverCompare "<1.18-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "false" -}} +{{- else -}} +{{- print "true" -}} +{{- end -}} +{{- end -}} + +{{/* +Return true if cert-manager required annotations for TLS signed +certificates are set in the Ingress annotations +Ref: https://cert-manager.io/docs/usage/ingress/#supported-annotations +Usage: +{{ include "common.ingress.certManagerRequest" ( dict "annotations" .Values.path.to.the.ingress.annotations ) }} +*/}} +{{- define "common.ingress.certManagerRequest" -}} +{{ if or (hasKey .annotations "cert-manager.io/cluster-issuer") (hasKey .annotations "cert-manager.io/issuer") (hasKey .annotations "kubernetes.io/tls-acme") }} + {{- true -}} +{{- end -}} +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/charts/common/templates/_labels.tpl b/knowlg-automation/helm_charts/kafka/charts/common/templates/_labels.tpl new file mode 100644 index 000000000..252066c7e --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/common/templates/_labels.tpl @@ -0,0 +1,18 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Kubernetes standard labels +*/}} +{{- define "common.labels.standard" -}} +app.kubernetes.io/name: {{ include "common.names.name" . }} +helm.sh/chart: {{ include "common.names.chart" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end -}} + +{{/* +Labels to use on deploy.spec.selector.matchLabels and svc.spec.selector +*/}} +{{- define "common.labels.matchLabels" -}} +app.kubernetes.io/name: {{ include "common.names.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/charts/common/templates/_names.tpl b/knowlg-automation/helm_charts/kafka/charts/common/templates/_names.tpl new file mode 100644 index 000000000..617a23489 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/common/templates/_names.tpl @@ -0,0 +1,66 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "common.names.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "common.names.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "common.names.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create a default fully qualified dependency name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +Usage: +{{ include "common.names.dependency.fullname" (dict "chartName" "dependency-chart-name" "chartValues" .Values.dependency-chart "context" $) }} +*/}} +{{- define "common.names.dependency.fullname" -}} +{{- if .chartValues.fullnameOverride -}} +{{- .chartValues.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .chartName .chartValues.nameOverride -}} +{{- if contains $name .context.Release.Name -}} +{{- .context.Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .context.Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Allow the release namespace to be overridden for multi-namespace deployments in combined charts. +*/}} +{{- define "common.names.namespace" -}} +{{- default .Release.Namespace .Values.namespaceOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a fully qualified app name adding the installation's namespace. +*/}} +{{- define "common.names.fullname.namespace" -}} +{{- printf "%s-%s" (include "common.names.fullname" .) (include "common.names.namespace" .) | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/charts/common/templates/_secrets.tpl b/knowlg-automation/helm_charts/kafka/charts/common/templates/_secrets.tpl new file mode 100644 index 000000000..a1708b2e8 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/common/templates/_secrets.tpl @@ -0,0 +1,165 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Generate secret name. + +Usage: +{{ include "common.secrets.name" (dict "existingSecret" .Values.path.to.the.existingSecret "defaultNameSuffix" "mySuffix" "context" $) }} + +Params: + - existingSecret - ExistingSecret/String - Optional. The path to the existing secrets in the values.yaml given by the user + to be used instead of the default one. Allows for it to be of type String (just the secret name) for backwards compatibility. + +info: https://github.com/bitnami/charts/tree/main/bitnami/common#existingsecret + - defaultNameSuffix - String - Optional. It is used only if we have several secrets in the same deployment. + - context - Dict - Required. The context for the template evaluation. +*/}} +{{- define "common.secrets.name" -}} +{{- $name := (include "common.names.fullname" .context) -}} + +{{- if .defaultNameSuffix -}} +{{- $name = printf "%s-%s" $name .defaultNameSuffix | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{- with .existingSecret -}} +{{- if not (typeIs "string" .) -}} +{{- with .name -}} +{{- $name = . -}} +{{- end -}} +{{- else -}} +{{- $name = . -}} +{{- end -}} +{{- end -}} + +{{- printf "%s" $name -}} +{{- end -}} + +{{/* +Generate secret key. + +Usage: +{{ include "common.secrets.key" (dict "existingSecret" .Values.path.to.the.existingSecret "key" "keyName") }} + +Params: + - existingSecret - ExistingSecret/String - Optional. The path to the existing secrets in the values.yaml given by the user + to be used instead of the default one. Allows for it to be of type String (just the secret name) for backwards compatibility. + +info: https://github.com/bitnami/charts/tree/main/bitnami/common#existingsecret + - key - String - Required. Name of the key in the secret. +*/}} +{{- define "common.secrets.key" -}} +{{- $key := .key -}} + +{{- if .existingSecret -}} + {{- if not (typeIs "string" .existingSecret) -}} + {{- if .existingSecret.keyMapping -}} + {{- $key = index .existingSecret.keyMapping $.key -}} + {{- end -}} + {{- end }} +{{- end -}} + +{{- printf "%s" $key -}} +{{- end -}} + +{{/* +Generate secret password or retrieve one if already created. + +Usage: +{{ include "common.secrets.passwords.manage" (dict "secret" "secret-name" "key" "keyName" "providedValues" (list "path.to.password1" "path.to.password2") "length" 10 "strong" false "chartName" "chartName" "context" $) }} + +Params: + - secret - String - Required - Name of the 'Secret' resource where the password is stored. + - key - String - Required - Name of the key in the secret. + - providedValues - List - Required - The path to the validating value in the values.yaml, e.g: "mysql.password". Will pick first parameter with a defined value. + - length - int - Optional - Length of the generated random password. + - strong - Boolean - Optional - Whether to add symbols to the generated random password. + - chartName - String - Optional - Name of the chart used when said chart is deployed as a subchart. + - context - Context - Required - Parent context. + +The order in which this function returns a secret password: + 1. Already existing 'Secret' resource + (If a 'Secret' resource is found under the name provided to the 'secret' parameter to this function and that 'Secret' resource contains a key with the name passed as the 'key' parameter to this function then the value of this existing secret password will be returned) + 2. Password provided via the values.yaml + (If one of the keys passed to the 'providedValues' parameter to this function is a valid path to a key in the values.yaml and has a value, the value of the first key with a value will be returned) + 3. Randomly generated secret password + (A new random secret password with the length specified in the 'length' parameter will be generated and returned) + +*/}} +{{- define "common.secrets.passwords.manage" -}} + +{{- $password := "" }} +{{- $subchart := "" }} +{{- $chartName := default "" .chartName }} +{{- $passwordLength := default 10 .length }} +{{- $providedPasswordKey := include "common.utils.getKeyFromList" (dict "keys" .providedValues "context" $.context) }} +{{- $providedPasswordValue := include "common.utils.getValueFromKey" (dict "key" $providedPasswordKey "context" $.context) }} +{{- $secretData := (lookup "v1" "Secret" (include "common.names.namespace" .context) .secret).data }} +{{- if $secretData }} + {{- if hasKey $secretData .key }} + {{- $password = index $secretData .key | quote }} + {{- else }} + {{- printf "\nPASSWORDS ERROR: The secret \"%s\" does not contain the key \"%s\"\n" .secret .key | fail -}} + {{- end -}} +{{- else if $providedPasswordValue }} + {{- $password = $providedPasswordValue | toString | b64enc | quote }} +{{- else }} + + {{- if .context.Values.enabled }} + {{- $subchart = $chartName }} + {{- end -}} + + {{- $requiredPassword := dict "valueKey" $providedPasswordKey "secret" .secret "field" .key "subchart" $subchart "context" $.context -}} + {{- $requiredPasswordError := include "common.validations.values.single.empty" $requiredPassword -}} + {{- $passwordValidationErrors := list $requiredPasswordError -}} + {{- include "common.errors.upgrade.passwords.empty" (dict "validationErrors" $passwordValidationErrors "context" $.context) -}} + + {{- if .strong }} + {{- $subStr := list (lower (randAlpha 1)) (randNumeric 1) (upper (randAlpha 1)) | join "_" }} + {{- $password = randAscii $passwordLength }} + {{- $password = regexReplaceAllLiteral "\\W" $password "@" | substr 5 $passwordLength }} + {{- $password = printf "%s%s" $subStr $password | toString | shuffle | b64enc | quote }} + {{- else }} + {{- $password = randAlphaNum $passwordLength | b64enc | quote }} + {{- end }} +{{- end -}} +{{- printf "%s" $password -}} +{{- end -}} + +{{/* +Reuses the value from an existing secret, otherwise sets its value to a default value. + +Usage: +{{ include "common.secrets.lookup" (dict "secret" "secret-name" "key" "keyName" "defaultValue" .Values.myValue "context" $) }} + +Params: + - secret - String - Required - Name of the 'Secret' resource where the password is stored. + - key - String - Required - Name of the key in the secret. + - defaultValue - String - Required - The path to the validating value in the values.yaml, e.g: "mysql.password". Will pick first parameter with a defined value. + - context - Context - Required - Parent context. + +*/}} +{{- define "common.secrets.lookup" -}} +{{- $value := "" -}} +{{- $defaultValue := required "\n'common.secrets.lookup': Argument 'defaultValue' missing or empty" .defaultValue -}} +{{- $secretData := (lookup "v1" "Secret" (include "common.names.namespace" .context) .secret).data -}} +{{- if and $secretData (hasKey $secretData .key) -}} + {{- $value = index $secretData .key -}} +{{- else -}} + {{- $value = $defaultValue | toString | b64enc -}} +{{- end -}} +{{- printf "%s" $value -}} +{{- end -}} + +{{/* +Returns whether a previous generated secret already exists + +Usage: +{{ include "common.secrets.exists" (dict "secret" "secret-name" "context" $) }} + +Params: + - secret - String - Required - Name of the 'Secret' resource where the password is stored. + - context - Context - Required - Parent context. +*/}} +{{- define "common.secrets.exists" -}} +{{- $secret := (lookup "v1" "Secret" (include "common.names.namespace" .context) .secret) }} +{{- if $secret }} + {{- true -}} +{{- end -}} +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/charts/common/templates/_storage.tpl b/knowlg-automation/helm_charts/kafka/charts/common/templates/_storage.tpl new file mode 100644 index 000000000..60e2a844f --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/common/templates/_storage.tpl @@ -0,0 +1,23 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Return the proper Storage Class +{{ include "common.storage.class" ( dict "persistence" .Values.path.to.the.persistence "global" $) }} +*/}} +{{- define "common.storage.class" -}} + +{{- $storageClass := .persistence.storageClass -}} +{{- if .global -}} + {{- if .global.storageClass -}} + {{- $storageClass = .global.storageClass -}} + {{- end -}} +{{- end -}} + +{{- if $storageClass -}} + {{- if (eq "-" $storageClass) -}} + {{- printf "storageClassName: \"\"" -}} + {{- else }} + {{- printf "storageClassName: %s" $storageClass -}} + {{- end -}} +{{- end -}} + +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/charts/common/templates/_tplvalues.tpl b/knowlg-automation/helm_charts/kafka/charts/common/templates/_tplvalues.tpl new file mode 100644 index 000000000..2db166851 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/common/templates/_tplvalues.tpl @@ -0,0 +1,13 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Renders a value that contains template. +Usage: +{{ include "common.tplvalues.render" ( dict "value" .Values.path.to.the.Value "context" $) }} +*/}} +{{- define "common.tplvalues.render" -}} + {{- if typeIs "string" .value }} + {{- tpl .value .context }} + {{- else }} + {{- tpl (.value | toYaml) .context }} + {{- end }} +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/charts/common/templates/_utils.tpl b/knowlg-automation/helm_charts/kafka/charts/common/templates/_utils.tpl new file mode 100644 index 000000000..b1ead50cf --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/common/templates/_utils.tpl @@ -0,0 +1,62 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Print instructions to get a secret value. +Usage: +{{ include "common.utils.secret.getvalue" (dict "secret" "secret-name" "field" "secret-value-field" "context" $) }} +*/}} +{{- define "common.utils.secret.getvalue" -}} +{{- $varname := include "common.utils.fieldToEnvVar" . -}} +export {{ $varname }}=$(kubectl get secret --namespace {{ include "common.names.namespace" .context | quote }} {{ .secret }} -o jsonpath="{.data.{{ .field }}}" | base64 -d) +{{- end -}} + +{{/* +Build env var name given a field +Usage: +{{ include "common.utils.fieldToEnvVar" dict "field" "my-password" }} +*/}} +{{- define "common.utils.fieldToEnvVar" -}} + {{- $fieldNameSplit := splitList "-" .field -}} + {{- $upperCaseFieldNameSplit := list -}} + + {{- range $fieldNameSplit -}} + {{- $upperCaseFieldNameSplit = append $upperCaseFieldNameSplit ( upper . ) -}} + {{- end -}} + + {{ join "_" $upperCaseFieldNameSplit }} +{{- end -}} + +{{/* +Gets a value from .Values given +Usage: +{{ include "common.utils.getValueFromKey" (dict "key" "path.to.key" "context" $) }} +*/}} +{{- define "common.utils.getValueFromKey" -}} +{{- $splitKey := splitList "." .key -}} +{{- $value := "" -}} +{{- $latestObj := $.context.Values -}} +{{- range $splitKey -}} + {{- if not $latestObj -}} + {{- printf "please review the entire path of '%s' exists in values" $.key | fail -}} + {{- end -}} + {{- $value = ( index $latestObj . ) -}} + {{- $latestObj = $value -}} +{{- end -}} +{{- printf "%v" (default "" $value) -}} +{{- end -}} + +{{/* +Returns first .Values key with a defined value or first of the list if all non-defined +Usage: +{{ include "common.utils.getKeyFromList" (dict "keys" (list "path.to.key1" "path.to.key2") "context" $) }} +*/}} +{{- define "common.utils.getKeyFromList" -}} +{{- $key := first .keys -}} +{{- $reverseKeys := reverse .keys }} +{{- range $reverseKeys }} + {{- $value := include "common.utils.getValueFromKey" (dict "key" . "context" $.context ) }} + {{- if $value -}} + {{- $key = . }} + {{- end -}} +{{- end -}} +{{- printf "%s" $key -}} +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/charts/common/templates/_warnings.tpl b/knowlg-automation/helm_charts/kafka/charts/common/templates/_warnings.tpl new file mode 100644 index 000000000..ae10fa41e --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/common/templates/_warnings.tpl @@ -0,0 +1,14 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Warning about using rolling tag. +Usage: +{{ include "common.warnings.rollingTag" .Values.path.to.the.imageRoot }} +*/}} +{{- define "common.warnings.rollingTag" -}} + +{{- if and (contains "bitnami/" .repository) (not (.tag | toString | regexFind "-r\\d+$|sha256:")) }} +WARNING: Rolling tag detected ({{ .repository }}:{{ .tag }}), please note that it is strongly recommended to avoid using rolling tags in a production environment. ++info https://docs.bitnami.com/containers/how-to/understand-rolling-tags-containers/ +{{- end }} + +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/charts/common/templates/validations/_cassandra.tpl b/knowlg-automation/helm_charts/kafka/charts/common/templates/validations/_cassandra.tpl new file mode 100644 index 000000000..ded1ae3bc --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/common/templates/validations/_cassandra.tpl @@ -0,0 +1,72 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Validate Cassandra required passwords are not empty. + +Usage: +{{ include "common.validations.values.cassandra.passwords" (dict "secret" "secretName" "subchart" false "context" $) }} +Params: + - secret - String - Required. Name of the secret where Cassandra values are stored, e.g: "cassandra-passwords-secret" + - subchart - Boolean - Optional. Whether Cassandra is used as subchart or not. Default: false +*/}} +{{- define "common.validations.values.cassandra.passwords" -}} + {{- $existingSecret := include "common.cassandra.values.existingSecret" . -}} + {{- $enabled := include "common.cassandra.values.enabled" . -}} + {{- $dbUserPrefix := include "common.cassandra.values.key.dbUser" . -}} + {{- $valueKeyPassword := printf "%s.password" $dbUserPrefix -}} + + {{- if and (or (not $existingSecret) (eq $existingSecret "\"\"")) (eq $enabled "true") -}} + {{- $requiredPasswords := list -}} + + {{- $requiredPassword := dict "valueKey" $valueKeyPassword "secret" .secret "field" "cassandra-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredPassword -}} + + {{- include "common.validations.values.multiple.empty" (dict "required" $requiredPasswords "context" .context) -}} + + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for existingSecret. + +Usage: +{{ include "common.cassandra.values.existingSecret" (dict "context" $) }} +Params: + - subchart - Boolean - Optional. Whether Cassandra is used as subchart or not. Default: false +*/}} +{{- define "common.cassandra.values.existingSecret" -}} + {{- if .subchart -}} + {{- .context.Values.cassandra.dbUser.existingSecret | quote -}} + {{- else -}} + {{- .context.Values.dbUser.existingSecret | quote -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for enabled cassandra. + +Usage: +{{ include "common.cassandra.values.enabled" (dict "context" $) }} +*/}} +{{- define "common.cassandra.values.enabled" -}} + {{- if .subchart -}} + {{- printf "%v" .context.Values.cassandra.enabled -}} + {{- else -}} + {{- printf "%v" (not .context.Values.enabled) -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for the key dbUser + +Usage: +{{ include "common.cassandra.values.key.dbUser" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether Cassandra is used as subchart or not. Default: false +*/}} +{{- define "common.cassandra.values.key.dbUser" -}} + {{- if .subchart -}} + cassandra.dbUser + {{- else -}} + dbUser + {{- end -}} +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/charts/common/templates/validations/_mariadb.tpl b/knowlg-automation/helm_charts/kafka/charts/common/templates/validations/_mariadb.tpl new file mode 100644 index 000000000..b6906ff77 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/common/templates/validations/_mariadb.tpl @@ -0,0 +1,103 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Validate MariaDB required passwords are not empty. + +Usage: +{{ include "common.validations.values.mariadb.passwords" (dict "secret" "secretName" "subchart" false "context" $) }} +Params: + - secret - String - Required. Name of the secret where MariaDB values are stored, e.g: "mysql-passwords-secret" + - subchart - Boolean - Optional. Whether MariaDB is used as subchart or not. Default: false +*/}} +{{- define "common.validations.values.mariadb.passwords" -}} + {{- $existingSecret := include "common.mariadb.values.auth.existingSecret" . -}} + {{- $enabled := include "common.mariadb.values.enabled" . -}} + {{- $architecture := include "common.mariadb.values.architecture" . -}} + {{- $authPrefix := include "common.mariadb.values.key.auth" . -}} + {{- $valueKeyRootPassword := printf "%s.rootPassword" $authPrefix -}} + {{- $valueKeyUsername := printf "%s.username" $authPrefix -}} + {{- $valueKeyPassword := printf "%s.password" $authPrefix -}} + {{- $valueKeyReplicationPassword := printf "%s.replicationPassword" $authPrefix -}} + + {{- if and (or (not $existingSecret) (eq $existingSecret "\"\"")) (eq $enabled "true") -}} + {{- $requiredPasswords := list -}} + + {{- $requiredRootPassword := dict "valueKey" $valueKeyRootPassword "secret" .secret "field" "mariadb-root-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredRootPassword -}} + + {{- $valueUsername := include "common.utils.getValueFromKey" (dict "key" $valueKeyUsername "context" .context) }} + {{- if not (empty $valueUsername) -}} + {{- $requiredPassword := dict "valueKey" $valueKeyPassword "secret" .secret "field" "mariadb-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredPassword -}} + {{- end -}} + + {{- if (eq $architecture "replication") -}} + {{- $requiredReplicationPassword := dict "valueKey" $valueKeyReplicationPassword "secret" .secret "field" "mariadb-replication-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredReplicationPassword -}} + {{- end -}} + + {{- include "common.validations.values.multiple.empty" (dict "required" $requiredPasswords "context" .context) -}} + + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for existingSecret. + +Usage: +{{ include "common.mariadb.values.auth.existingSecret" (dict "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MariaDB is used as subchart or not. Default: false +*/}} +{{- define "common.mariadb.values.auth.existingSecret" -}} + {{- if .subchart -}} + {{- .context.Values.mariadb.auth.existingSecret | quote -}} + {{- else -}} + {{- .context.Values.auth.existingSecret | quote -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for enabled mariadb. + +Usage: +{{ include "common.mariadb.values.enabled" (dict "context" $) }} +*/}} +{{- define "common.mariadb.values.enabled" -}} + {{- if .subchart -}} + {{- printf "%v" .context.Values.mariadb.enabled -}} + {{- else -}} + {{- printf "%v" (not .context.Values.enabled) -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for architecture + +Usage: +{{ include "common.mariadb.values.architecture" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MariaDB is used as subchart or not. Default: false +*/}} +{{- define "common.mariadb.values.architecture" -}} + {{- if .subchart -}} + {{- .context.Values.mariadb.architecture -}} + {{- else -}} + {{- .context.Values.architecture -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for the key auth + +Usage: +{{ include "common.mariadb.values.key.auth" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MariaDB is used as subchart or not. Default: false +*/}} +{{- define "common.mariadb.values.key.auth" -}} + {{- if .subchart -}} + mariadb.auth + {{- else -}} + auth + {{- end -}} +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/charts/common/templates/validations/_mongodb.tpl b/knowlg-automation/helm_charts/kafka/charts/common/templates/validations/_mongodb.tpl new file mode 100644 index 000000000..f820ec107 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/common/templates/validations/_mongodb.tpl @@ -0,0 +1,108 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Validate MongoDB® required passwords are not empty. + +Usage: +{{ include "common.validations.values.mongodb.passwords" (dict "secret" "secretName" "subchart" false "context" $) }} +Params: + - secret - String - Required. Name of the secret where MongoDB® values are stored, e.g: "mongodb-passwords-secret" + - subchart - Boolean - Optional. Whether MongoDB® is used as subchart or not. Default: false +*/}} +{{- define "common.validations.values.mongodb.passwords" -}} + {{- $existingSecret := include "common.mongodb.values.auth.existingSecret" . -}} + {{- $enabled := include "common.mongodb.values.enabled" . -}} + {{- $authPrefix := include "common.mongodb.values.key.auth" . -}} + {{- $architecture := include "common.mongodb.values.architecture" . -}} + {{- $valueKeyRootPassword := printf "%s.rootPassword" $authPrefix -}} + {{- $valueKeyUsername := printf "%s.username" $authPrefix -}} + {{- $valueKeyDatabase := printf "%s.database" $authPrefix -}} + {{- $valueKeyPassword := printf "%s.password" $authPrefix -}} + {{- $valueKeyReplicaSetKey := printf "%s.replicaSetKey" $authPrefix -}} + {{- $valueKeyAuthEnabled := printf "%s.enabled" $authPrefix -}} + + {{- $authEnabled := include "common.utils.getValueFromKey" (dict "key" $valueKeyAuthEnabled "context" .context) -}} + + {{- if and (or (not $existingSecret) (eq $existingSecret "\"\"")) (eq $enabled "true") (eq $authEnabled "true") -}} + {{- $requiredPasswords := list -}} + + {{- $requiredRootPassword := dict "valueKey" $valueKeyRootPassword "secret" .secret "field" "mongodb-root-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredRootPassword -}} + + {{- $valueUsername := include "common.utils.getValueFromKey" (dict "key" $valueKeyUsername "context" .context) }} + {{- $valueDatabase := include "common.utils.getValueFromKey" (dict "key" $valueKeyDatabase "context" .context) }} + {{- if and $valueUsername $valueDatabase -}} + {{- $requiredPassword := dict "valueKey" $valueKeyPassword "secret" .secret "field" "mongodb-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredPassword -}} + {{- end -}} + + {{- if (eq $architecture "replicaset") -}} + {{- $requiredReplicaSetKey := dict "valueKey" $valueKeyReplicaSetKey "secret" .secret "field" "mongodb-replica-set-key" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredReplicaSetKey -}} + {{- end -}} + + {{- include "common.validations.values.multiple.empty" (dict "required" $requiredPasswords "context" .context) -}} + + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for existingSecret. + +Usage: +{{ include "common.mongodb.values.auth.existingSecret" (dict "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MongoDb is used as subchart or not. Default: false +*/}} +{{- define "common.mongodb.values.auth.existingSecret" -}} + {{- if .subchart -}} + {{- .context.Values.mongodb.auth.existingSecret | quote -}} + {{- else -}} + {{- .context.Values.auth.existingSecret | quote -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for enabled mongodb. + +Usage: +{{ include "common.mongodb.values.enabled" (dict "context" $) }} +*/}} +{{- define "common.mongodb.values.enabled" -}} + {{- if .subchart -}} + {{- printf "%v" .context.Values.mongodb.enabled -}} + {{- else -}} + {{- printf "%v" (not .context.Values.enabled) -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for the key auth + +Usage: +{{ include "common.mongodb.values.key.auth" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MongoDB® is used as subchart or not. Default: false +*/}} +{{- define "common.mongodb.values.key.auth" -}} + {{- if .subchart -}} + mongodb.auth + {{- else -}} + auth + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for architecture + +Usage: +{{ include "common.mongodb.values.architecture" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MongoDB® is used as subchart or not. Default: false +*/}} +{{- define "common.mongodb.values.architecture" -}} + {{- if .subchart -}} + {{- .context.Values.mongodb.architecture -}} + {{- else -}} + {{- .context.Values.architecture -}} + {{- end -}} +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/charts/common/templates/validations/_mysql.tpl b/knowlg-automation/helm_charts/kafka/charts/common/templates/validations/_mysql.tpl new file mode 100644 index 000000000..74472a061 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/common/templates/validations/_mysql.tpl @@ -0,0 +1,103 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Validate MySQL required passwords are not empty. + +Usage: +{{ include "common.validations.values.mysql.passwords" (dict "secret" "secretName" "subchart" false "context" $) }} +Params: + - secret - String - Required. Name of the secret where MySQL values are stored, e.g: "mysql-passwords-secret" + - subchart - Boolean - Optional. Whether MySQL is used as subchart or not. Default: false +*/}} +{{- define "common.validations.values.mysql.passwords" -}} + {{- $existingSecret := include "common.mysql.values.auth.existingSecret" . -}} + {{- $enabled := include "common.mysql.values.enabled" . -}} + {{- $architecture := include "common.mysql.values.architecture" . -}} + {{- $authPrefix := include "common.mysql.values.key.auth" . -}} + {{- $valueKeyRootPassword := printf "%s.rootPassword" $authPrefix -}} + {{- $valueKeyUsername := printf "%s.username" $authPrefix -}} + {{- $valueKeyPassword := printf "%s.password" $authPrefix -}} + {{- $valueKeyReplicationPassword := printf "%s.replicationPassword" $authPrefix -}} + + {{- if and (or (not $existingSecret) (eq $existingSecret "\"\"")) (eq $enabled "true") -}} + {{- $requiredPasswords := list -}} + + {{- $requiredRootPassword := dict "valueKey" $valueKeyRootPassword "secret" .secret "field" "mysql-root-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredRootPassword -}} + + {{- $valueUsername := include "common.utils.getValueFromKey" (dict "key" $valueKeyUsername "context" .context) }} + {{- if not (empty $valueUsername) -}} + {{- $requiredPassword := dict "valueKey" $valueKeyPassword "secret" .secret "field" "mysql-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredPassword -}} + {{- end -}} + + {{- if (eq $architecture "replication") -}} + {{- $requiredReplicationPassword := dict "valueKey" $valueKeyReplicationPassword "secret" .secret "field" "mysql-replication-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredReplicationPassword -}} + {{- end -}} + + {{- include "common.validations.values.multiple.empty" (dict "required" $requiredPasswords "context" .context) -}} + + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for existingSecret. + +Usage: +{{ include "common.mysql.values.auth.existingSecret" (dict "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MySQL is used as subchart or not. Default: false +*/}} +{{- define "common.mysql.values.auth.existingSecret" -}} + {{- if .subchart -}} + {{- .context.Values.mysql.auth.existingSecret | quote -}} + {{- else -}} + {{- .context.Values.auth.existingSecret | quote -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for enabled mysql. + +Usage: +{{ include "common.mysql.values.enabled" (dict "context" $) }} +*/}} +{{- define "common.mysql.values.enabled" -}} + {{- if .subchart -}} + {{- printf "%v" .context.Values.mysql.enabled -}} + {{- else -}} + {{- printf "%v" (not .context.Values.enabled) -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for architecture + +Usage: +{{ include "common.mysql.values.architecture" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MySQL is used as subchart or not. Default: false +*/}} +{{- define "common.mysql.values.architecture" -}} + {{- if .subchart -}} + {{- .context.Values.mysql.architecture -}} + {{- else -}} + {{- .context.Values.architecture -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for the key auth + +Usage: +{{ include "common.mysql.values.key.auth" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MySQL is used as subchart or not. Default: false +*/}} +{{- define "common.mysql.values.key.auth" -}} + {{- if .subchart -}} + mysql.auth + {{- else -}} + auth + {{- end -}} +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/charts/common/templates/validations/_postgresql.tpl b/knowlg-automation/helm_charts/kafka/charts/common/templates/validations/_postgresql.tpl new file mode 100644 index 000000000..164ec0d01 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/common/templates/validations/_postgresql.tpl @@ -0,0 +1,129 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Validate PostgreSQL required passwords are not empty. + +Usage: +{{ include "common.validations.values.postgresql.passwords" (dict "secret" "secretName" "subchart" false "context" $) }} +Params: + - secret - String - Required. Name of the secret where postgresql values are stored, e.g: "postgresql-passwords-secret" + - subchart - Boolean - Optional. Whether postgresql is used as subchart or not. Default: false +*/}} +{{- define "common.validations.values.postgresql.passwords" -}} + {{- $existingSecret := include "common.postgresql.values.existingSecret" . -}} + {{- $enabled := include "common.postgresql.values.enabled" . -}} + {{- $valueKeyPostgresqlPassword := include "common.postgresql.values.key.postgressPassword" . -}} + {{- $valueKeyPostgresqlReplicationEnabled := include "common.postgresql.values.key.replicationPassword" . -}} + {{- if and (or (not $existingSecret) (eq $existingSecret "\"\"")) (eq $enabled "true") -}} + {{- $requiredPasswords := list -}} + {{- $requiredPostgresqlPassword := dict "valueKey" $valueKeyPostgresqlPassword "secret" .secret "field" "postgresql-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredPostgresqlPassword -}} + + {{- $enabledReplication := include "common.postgresql.values.enabled.replication" . -}} + {{- if (eq $enabledReplication "true") -}} + {{- $requiredPostgresqlReplicationPassword := dict "valueKey" $valueKeyPostgresqlReplicationEnabled "secret" .secret "field" "postgresql-replication-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredPostgresqlReplicationPassword -}} + {{- end -}} + + {{- include "common.validations.values.multiple.empty" (dict "required" $requiredPasswords "context" .context) -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to decide whether evaluate global values. + +Usage: +{{ include "common.postgresql.values.use.global" (dict "key" "key-of-global" "context" $) }} +Params: + - key - String - Required. Field to be evaluated within global, e.g: "existingSecret" +*/}} +{{- define "common.postgresql.values.use.global" -}} + {{- if .context.Values.global -}} + {{- if .context.Values.global.postgresql -}} + {{- index .context.Values.global.postgresql .key | quote -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for existingSecret. + +Usage: +{{ include "common.postgresql.values.existingSecret" (dict "context" $) }} +*/}} +{{- define "common.postgresql.values.existingSecret" -}} + {{- $globalValue := include "common.postgresql.values.use.global" (dict "key" "existingSecret" "context" .context) -}} + + {{- if .subchart -}} + {{- default (.context.Values.postgresql.existingSecret | quote) $globalValue -}} + {{- else -}} + {{- default (.context.Values.existingSecret | quote) $globalValue -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for enabled postgresql. + +Usage: +{{ include "common.postgresql.values.enabled" (dict "context" $) }} +*/}} +{{- define "common.postgresql.values.enabled" -}} + {{- if .subchart -}} + {{- printf "%v" .context.Values.postgresql.enabled -}} + {{- else -}} + {{- printf "%v" (not .context.Values.enabled) -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for the key postgressPassword. + +Usage: +{{ include "common.postgresql.values.key.postgressPassword" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether postgresql is used as subchart or not. Default: false +*/}} +{{- define "common.postgresql.values.key.postgressPassword" -}} + {{- $globalValue := include "common.postgresql.values.use.global" (dict "key" "postgresqlUsername" "context" .context) -}} + + {{- if not $globalValue -}} + {{- if .subchart -}} + postgresql.postgresqlPassword + {{- else -}} + postgresqlPassword + {{- end -}} + {{- else -}} + global.postgresql.postgresqlPassword + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for enabled.replication. + +Usage: +{{ include "common.postgresql.values.enabled.replication" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether postgresql is used as subchart or not. Default: false +*/}} +{{- define "common.postgresql.values.enabled.replication" -}} + {{- if .subchart -}} + {{- printf "%v" .context.Values.postgresql.replication.enabled -}} + {{- else -}} + {{- printf "%v" .context.Values.replication.enabled -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for the key replication.password. + +Usage: +{{ include "common.postgresql.values.key.replicationPassword" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether postgresql is used as subchart or not. Default: false +*/}} +{{- define "common.postgresql.values.key.replicationPassword" -}} + {{- if .subchart -}} + postgresql.replication.password + {{- else -}} + replication.password + {{- end -}} +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/charts/common/templates/validations/_redis.tpl b/knowlg-automation/helm_charts/kafka/charts/common/templates/validations/_redis.tpl new file mode 100644 index 000000000..dcccfc1ae --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/common/templates/validations/_redis.tpl @@ -0,0 +1,76 @@ + +{{/* vim: set filetype=mustache: */}} +{{/* +Validate Redis® required passwords are not empty. + +Usage: +{{ include "common.validations.values.redis.passwords" (dict "secret" "secretName" "subchart" false "context" $) }} +Params: + - secret - String - Required. Name of the secret where redis values are stored, e.g: "redis-passwords-secret" + - subchart - Boolean - Optional. Whether redis is used as subchart or not. Default: false +*/}} +{{- define "common.validations.values.redis.passwords" -}} + {{- $enabled := include "common.redis.values.enabled" . -}} + {{- $valueKeyPrefix := include "common.redis.values.keys.prefix" . -}} + {{- $standarizedVersion := include "common.redis.values.standarized.version" . }} + + {{- $existingSecret := ternary (printf "%s%s" $valueKeyPrefix "auth.existingSecret") (printf "%s%s" $valueKeyPrefix "existingSecret") (eq $standarizedVersion "true") }} + {{- $existingSecretValue := include "common.utils.getValueFromKey" (dict "key" $existingSecret "context" .context) }} + + {{- $valueKeyRedisPassword := ternary (printf "%s%s" $valueKeyPrefix "auth.password") (printf "%s%s" $valueKeyPrefix "password") (eq $standarizedVersion "true") }} + {{- $valueKeyRedisUseAuth := ternary (printf "%s%s" $valueKeyPrefix "auth.enabled") (printf "%s%s" $valueKeyPrefix "usePassword") (eq $standarizedVersion "true") }} + + {{- if and (or (not $existingSecret) (eq $existingSecret "\"\"")) (eq $enabled "true") -}} + {{- $requiredPasswords := list -}} + + {{- $useAuth := include "common.utils.getValueFromKey" (dict "key" $valueKeyRedisUseAuth "context" .context) -}} + {{- if eq $useAuth "true" -}} + {{- $requiredRedisPassword := dict "valueKey" $valueKeyRedisPassword "secret" .secret "field" "redis-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredRedisPassword -}} + {{- end -}} + + {{- include "common.validations.values.multiple.empty" (dict "required" $requiredPasswords "context" .context) -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for enabled redis. + +Usage: +{{ include "common.redis.values.enabled" (dict "context" $) }} +*/}} +{{- define "common.redis.values.enabled" -}} + {{- if .subchart -}} + {{- printf "%v" .context.Values.redis.enabled -}} + {{- else -}} + {{- printf "%v" (not .context.Values.enabled) -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right prefix path for the values + +Usage: +{{ include "common.redis.values.key.prefix" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether redis is used as subchart or not. Default: false +*/}} +{{- define "common.redis.values.keys.prefix" -}} + {{- if .subchart -}}redis.{{- else -}}{{- end -}} +{{- end -}} + +{{/* +Checks whether the redis chart's includes the standarizations (version >= 14) + +Usage: +{{ include "common.redis.values.standarized.version" (dict "context" $) }} +*/}} +{{- define "common.redis.values.standarized.version" -}} + + {{- $standarizedAuth := printf "%s%s" (include "common.redis.values.keys.prefix" .) "auth" -}} + {{- $standarizedAuthValues := include "common.utils.getValueFromKey" (dict "key" $standarizedAuth "context" .context) }} + + {{- if $standarizedAuthValues -}} + {{- true -}} + {{- end -}} +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/charts/common/templates/validations/_validations.tpl b/knowlg-automation/helm_charts/kafka/charts/common/templates/validations/_validations.tpl new file mode 100644 index 000000000..9a814cf40 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/common/templates/validations/_validations.tpl @@ -0,0 +1,46 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Validate values must not be empty. + +Usage: +{{- $validateValueConf00 := (dict "valueKey" "path.to.value" "secret" "secretName" "field" "password-00") -}} +{{- $validateValueConf01 := (dict "valueKey" "path.to.value" "secret" "secretName" "field" "password-01") -}} +{{ include "common.validations.values.empty" (dict "required" (list $validateValueConf00 $validateValueConf01) "context" $) }} + +Validate value params: + - valueKey - String - Required. The path to the validating value in the values.yaml, e.g: "mysql.password" + - secret - String - Optional. Name of the secret where the validating value is generated/stored, e.g: "mysql-passwords-secret" + - field - String - Optional. Name of the field in the secret data, e.g: "mysql-password" +*/}} +{{- define "common.validations.values.multiple.empty" -}} + {{- range .required -}} + {{- include "common.validations.values.single.empty" (dict "valueKey" .valueKey "secret" .secret "field" .field "context" $.context) -}} + {{- end -}} +{{- end -}} + +{{/* +Validate a value must not be empty. + +Usage: +{{ include "common.validations.value.empty" (dict "valueKey" "mariadb.password" "secret" "secretName" "field" "my-password" "subchart" "subchart" "context" $) }} + +Validate value params: + - valueKey - String - Required. The path to the validating value in the values.yaml, e.g: "mysql.password" + - secret - String - Optional. Name of the secret where the validating value is generated/stored, e.g: "mysql-passwords-secret" + - field - String - Optional. Name of the field in the secret data, e.g: "mysql-password" + - subchart - String - Optional - Name of the subchart that the validated password is part of. +*/}} +{{- define "common.validations.values.single.empty" -}} + {{- $value := include "common.utils.getValueFromKey" (dict "key" .valueKey "context" .context) }} + {{- $subchart := ternary "" (printf "%s." .subchart) (empty .subchart) }} + + {{- if not $value -}} + {{- $varname := "my-value" -}} + {{- $getCurrentValue := "" -}} + {{- if and .secret .field -}} + {{- $varname = include "common.utils.fieldToEnvVar" . -}} + {{- $getCurrentValue = printf " To get the current value:\n\n %s\n" (include "common.utils.secret.getvalue" .) -}} + {{- end -}} + {{- printf "\n '%s' must not be empty, please add '--set %s%s=$%s' to the command.%s" .valueKey $subchart .valueKey $varname $getCurrentValue -}} + {{- end -}} +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/charts/common/values.yaml b/knowlg-automation/helm_charts/kafka/charts/common/values.yaml new file mode 100644 index 000000000..f2df68e5e --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/common/values.yaml @@ -0,0 +1,5 @@ +## bitnami/common +## It is required by CI/CD tools and processes. +## @skip exampleValue +## +exampleValue: common-chart diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/.helmignore b/knowlg-automation/helm_charts/kafka/charts/zookeeper/.helmignore new file mode 100644 index 000000000..f0c131944 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/.helmignore @@ -0,0 +1,21 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/Chart.lock b/knowlg-automation/helm_charts/kafka/charts/zookeeper/Chart.lock new file mode 100644 index 000000000..e6b30f2fb --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: common + repository: https://charts.bitnami.com/bitnami + version: 2.2.1 +digest: sha256:6c67cfa9945bf608209d4e2ca8f17079fca4770907c7902d984187ab5b21811e +generated: "2022-12-09T23:46:15.313038016Z" diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/Chart.yaml b/knowlg-automation/helm_charts/kafka/charts/zookeeper/Chart.yaml new file mode 100644 index 000000000..26e072a05 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/Chart.yaml @@ -0,0 +1,24 @@ +annotations: + category: Infrastructure +apiVersion: v2 +appVersion: 3.8.0 +dependencies: +- name: common + repository: https://charts.bitnami.com/bitnami + tags: + - bitnami-common + version: 2.x.x +description: Apache ZooKeeper provides a reliable, centralized register of configuration + data and services for distributed applications. +home: https://github.com/bitnami/charts/tree/main/bitnami/zookeeper +icon: https://bitnami.com/assets/stacks/zookeeper/img/zookeeper-stack-220x234.png +keywords: +- zookeeper +maintainers: +- name: Bitnami + url: https://github.com/bitnami/charts +name: zookeeper +sources: +- https://github.com/bitnami/containers/tree/main/bitnami/zookeeper +- https://zookeeper.apache.org/ +version: 11.0.2 diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/README.md b/knowlg-automation/helm_charts/kafka/charts/zookeeper/README.md new file mode 100644 index 000000000..7dfdf5433 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/README.md @@ -0,0 +1,532 @@ + + +# Apache ZooKeeper packaged by Bitnami + +Apache ZooKeeper provides a reliable, centralized register of configuration data and services for distributed applications. + +[Overview of Apache ZooKeeper](https://zookeeper.apache.org) + +Trademarks: This software listing is packaged by Bitnami. The respective trademarks mentioned in the offering are owned by the respective companies, and use of them does not imply any affiliation or endorsement. + +## TL;DR + +```console +$ helm repo add my-repo https://charts.bitnami.com/bitnami +$ helm install my-release my-repo/zookeeper +``` + +## Introduction + +This chart bootstraps a [ZooKeeper](https://github.com/bitnami/containers/tree/main/bitnami/zookeeper) deployment on a [Kubernetes](https://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager. + +Bitnami charts can be used with [Kubeapps](https://kubeapps.dev/) for deployment and management of Helm Charts in clusters. + +## Prerequisites + +- Kubernetes 1.19+ +- Helm 3.2.0+ +- PV provisioner support in the underlying infrastructure + +## Installing the Chart + +To install the chart with the release name `my-release`: + +```console +$ helm repo add my-repo https://charts.bitnami.com/bitnami +$ helm install my-release my-repo/zookeeper +``` + +These commands deploy ZooKeeper on the Kubernetes cluster in the default configuration. The [Parameters](#parameters) section lists the parameters that can be configured during installation. + +> **Tip**: List all releases using `helm list` + +## Uninstalling the Chart + +To uninstall/delete the `my-release` deployment: + +```console +$ helm delete my-release +``` + +The command removes all the Kubernetes components associated with the chart and deletes the release. + +## Parameters + +### Global parameters + +| Name | Description | Value | +| ------------------------- | ----------------------------------------------- | ----- | +| `global.imageRegistry` | Global Docker image registry | `""` | +| `global.imagePullSecrets` | Global Docker registry secret names as an array | `[]` | +| `global.storageClass` | Global StorageClass for Persistent Volume(s) | `""` | + + +### Common parameters + +| Name | Description | Value | +| ------------------------ | -------------------------------------------------------------------------------------------- | --------------- | +| `kubeVersion` | Override Kubernetes version | `""` | +| `nameOverride` | String to partially override common.names.fullname template (will maintain the release name) | `""` | +| `fullnameOverride` | String to fully override common.names.fullname template | `""` | +| `clusterDomain` | Kubernetes Cluster Domain | `cluster.local` | +| `extraDeploy` | Extra objects to deploy (evaluated as a template) | `[]` | +| `commonLabels` | Add labels to all the deployed resources | `{}` | +| `commonAnnotations` | Add annotations to all the deployed resources | `{}` | +| `namespaceOverride` | Override namespace for ZooKeeper resources | `""` | +| `diagnosticMode.enabled` | Enable diagnostic mode (all probes will be disabled and the command will be overridden) | `false` | +| `diagnosticMode.command` | Command to override all containers in the statefulset | `["sleep"]` | +| `diagnosticMode.args` | Args to override all containers in the statefulset | `["infinity"]` | + + +### ZooKeeper chart parameters + +| Name | Description | Value | +| ----------------------------- | -------------------------------------------------------------------------------------------------------------------------- | ----------------------- | +| `image.registry` | ZooKeeper image registry | `docker.io` | +| `image.repository` | ZooKeeper image repository | `bitnami/zookeeper` | +| `image.tag` | ZooKeeper image tag (immutable tags are recommended) | `3.8.0-debian-11-r65` | +| `image.digest` | ZooKeeper image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag | `""` | +| `image.pullPolicy` | ZooKeeper image pull policy | `IfNotPresent` | +| `image.pullSecrets` | Specify docker-registry secret names as an array | `[]` | +| `image.debug` | Specify if debug values should be set | `false` | +| `auth.client.enabled` | Enable ZooKeeper client-server authentication. It uses SASL/Digest-MD5 | `false` | +| `auth.client.clientUser` | User that will use ZooKeeper clients to auth | `""` | +| `auth.client.clientPassword` | Password that will use ZooKeeper clients to auth | `""` | +| `auth.client.serverUsers` | Comma, semicolon or whitespace separated list of user to be created | `""` | +| `auth.client.serverPasswords` | Comma, semicolon or whitespace separated list of passwords to assign to users when created | `""` | +| `auth.client.existingSecret` | Use existing secret (ignores previous passwords) | `""` | +| `auth.quorum.enabled` | Enable ZooKeeper server-server authentication. It uses SASL/Digest-MD5 | `false` | +| `auth.quorum.learnerUser` | User that the ZooKeeper quorumLearner will use to authenticate to quorumServers. | `""` | +| `auth.quorum.learnerPassword` | Password that the ZooKeeper quorumLearner will use to authenticate to quorumServers. | `""` | +| `auth.quorum.serverUsers` | Comma, semicolon or whitespace separated list of users for the quorumServers. | `""` | +| `auth.quorum.serverPasswords` | Comma, semicolon or whitespace separated list of passwords to assign to users when created | `""` | +| `auth.quorum.existingSecret` | Use existing secret (ignores previous passwords) | `""` | +| `tickTime` | Basic time unit (in milliseconds) used by ZooKeeper for heartbeats | `2000` | +| `initLimit` | ZooKeeper uses to limit the length of time the ZooKeeper servers in quorum have to connect to a leader | `10` | +| `syncLimit` | How far out of date a server can be from a leader | `5` | +| `preAllocSize` | Block size for transaction log file | `65536` | +| `snapCount` | The number of transactions recorded in the transaction log before a snapshot can be taken (and the transaction log rolled) | `100000` | +| `maxClientCnxns` | Limits the number of concurrent connections that a single client may make to a single member of the ZooKeeper ensemble | `60` | +| `maxSessionTimeout` | Maximum session timeout (in milliseconds) that the server will allow the client to negotiate | `40000` | +| `heapSize` | Size (in MB) for the Java Heap options (Xmx and Xms) | `1024` | +| `fourlwCommandsWhitelist` | A list of comma separated Four Letter Words commands that can be executed | `srvr, mntr, ruok` | +| `minServerId` | Minimal SERVER_ID value, nodes increment their IDs respectively | `1` | +| `listenOnAllIPs` | Allow ZooKeeper to listen for connections from its peers on all available IP addresses | `false` | +| `autopurge.snapRetainCount` | The most recent snapshots amount (and corresponding transaction logs) to retain | `3` | +| `autopurge.purgeInterval` | The time interval (in hours) for which the purge task has to be triggered | `0` | +| `logLevel` | Log level for the ZooKeeper server. ERROR by default | `ERROR` | +| `jvmFlags` | Default JVM flags for the ZooKeeper process | `""` | +| `dataLogDir` | Dedicated data log directory | `""` | +| `configuration` | Configure ZooKeeper with a custom zoo.cfg file | `""` | +| `existingConfigmap` | The name of an existing ConfigMap with your custom configuration for ZooKeeper | `""` | +| `extraEnvVars` | Array with extra environment variables to add to ZooKeeper nodes | `[]` | +| `extraEnvVarsCM` | Name of existing ConfigMap containing extra env vars for ZooKeeper nodes | `""` | +| `extraEnvVarsSecret` | Name of existing Secret containing extra env vars for ZooKeeper nodes | `""` | +| `command` | Override default container command (useful when using custom images) | `["/scripts/setup.sh"]` | +| `args` | Override default container args (useful when using custom images) | `[]` | + + +### Statefulset parameters + +| Name | Description | Value | +| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- | +| `replicaCount` | Number of ZooKeeper nodes | `1` | +| `containerPorts.client` | ZooKeeper client container port | `2181` | +| `containerPorts.tls` | ZooKeeper TLS container port | `3181` | +| `containerPorts.follower` | ZooKeeper follower container port | `2888` | +| `containerPorts.election` | ZooKeeper election container port | `3888` | +| `livenessProbe.enabled` | Enable livenessProbe on ZooKeeper containers | `true` | +| `livenessProbe.initialDelaySeconds` | Initial delay seconds for livenessProbe | `30` | +| `livenessProbe.periodSeconds` | Period seconds for livenessProbe | `10` | +| `livenessProbe.timeoutSeconds` | Timeout seconds for livenessProbe | `5` | +| `livenessProbe.failureThreshold` | Failure threshold for livenessProbe | `6` | +| `livenessProbe.successThreshold` | Success threshold for livenessProbe | `1` | +| `livenessProbe.probeCommandTimeout` | Probe command timeout for livenessProbe | `2` | +| `readinessProbe.enabled` | Enable readinessProbe on ZooKeeper containers | `true` | +| `readinessProbe.initialDelaySeconds` | Initial delay seconds for readinessProbe | `5` | +| `readinessProbe.periodSeconds` | Period seconds for readinessProbe | `10` | +| `readinessProbe.timeoutSeconds` | Timeout seconds for readinessProbe | `5` | +| `readinessProbe.failureThreshold` | Failure threshold for readinessProbe | `6` | +| `readinessProbe.successThreshold` | Success threshold for readinessProbe | `1` | +| `readinessProbe.probeCommandTimeout` | Probe command timeout for readinessProbe | `2` | +| `startupProbe.enabled` | Enable startupProbe on ZooKeeper containers | `false` | +| `startupProbe.initialDelaySeconds` | Initial delay seconds for startupProbe | `30` | +| `startupProbe.periodSeconds` | Period seconds for startupProbe | `10` | +| `startupProbe.timeoutSeconds` | Timeout seconds for startupProbe | `1` | +| `startupProbe.failureThreshold` | Failure threshold for startupProbe | `15` | +| `startupProbe.successThreshold` | Success threshold for startupProbe | `1` | +| `customLivenessProbe` | Custom livenessProbe that overrides the default one | `{}` | +| `customReadinessProbe` | Custom readinessProbe that overrides the default one | `{}` | +| `customStartupProbe` | Custom startupProbe that overrides the default one | `{}` | +| `lifecycleHooks` | for the ZooKeeper container(s) to automate configuration before or after startup | `{}` | +| `resources.limits` | The resources limits for the ZooKeeper containers | `{}` | +| `resources.requests.memory` | The requested memory for the ZooKeeper containers | `256Mi` | +| `resources.requests.cpu` | The requested cpu for the ZooKeeper containers | `250m` | +| `podSecurityContext.enabled` | Enabled ZooKeeper pods' Security Context | `true` | +| `podSecurityContext.fsGroup` | Set ZooKeeper pod's Security Context fsGroup | `1001` | +| `containerSecurityContext.enabled` | Enabled ZooKeeper containers' Security Context | `true` | +| `containerSecurityContext.runAsUser` | Set ZooKeeper containers' Security Context runAsUser | `1001` | +| `containerSecurityContext.runAsNonRoot` | Set ZooKeeper containers' Security Context runAsNonRoot | `true` | +| `containerSecurityContext.allowPrivilegeEscalation` | Force the child process to be run as nonprivilege | `false` | +| `hostAliases` | ZooKeeper pods host aliases | `[]` | +| `podLabels` | Extra labels for ZooKeeper pods | `{}` | +| `podAnnotations` | Annotations for ZooKeeper pods | `{}` | +| `podAffinityPreset` | Pod affinity preset. Ignored if `affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `podAntiAffinityPreset` | Pod anti-affinity preset. Ignored if `affinity` is set. Allowed values: `soft` or `hard` | `soft` | +| `nodeAffinityPreset.type` | Node affinity preset type. Ignored if `affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `nodeAffinityPreset.key` | Node label key to match Ignored if `affinity` is set. | `""` | +| `nodeAffinityPreset.values` | Node label values to match. Ignored if `affinity` is set. | `[]` | +| `affinity` | Affinity for pod assignment | `{}` | +| `nodeSelector` | Node labels for pod assignment | `{}` | +| `tolerations` | Tolerations for pod assignment | `[]` | +| `topologySpreadConstraints` | Topology Spread Constraints for pod assignment spread across your cluster among failure-domains. Evaluated as a template | `[]` | +| `podManagementPolicy` | StatefulSet controller supports relax its ordering guarantees while preserving its uniqueness and identity guarantees. There are two valid pod management policies: `OrderedReady` and `Parallel` | `Parallel` | +| `priorityClassName` | Name of the existing priority class to be used by ZooKeeper pods, priority class needs to be created beforehand | `""` | +| `schedulerName` | Kubernetes pod scheduler registry | `""` | +| `updateStrategy.type` | ZooKeeper statefulset strategy type | `RollingUpdate` | +| `updateStrategy.rollingUpdate` | ZooKeeper statefulset rolling update configuration parameters | `{}` | +| `extraVolumes` | Optionally specify extra list of additional volumes for the ZooKeeper pod(s) | `[]` | +| `extraVolumeMounts` | Optionally specify extra list of additional volumeMounts for the ZooKeeper container(s) | `[]` | +| `sidecars` | Add additional sidecar containers to the ZooKeeper pod(s) | `[]` | +| `initContainers` | Add additional init containers to the ZooKeeper pod(s) | `[]` | +| `pdb.create` | Deploy a pdb object for the ZooKeeper pod | `false` | +| `pdb.minAvailable` | Minimum available ZooKeeper replicas | `""` | +| `pdb.maxUnavailable` | Maximum unavailable ZooKeeper replicas | `1` | + + +### Traffic Exposure parameters + +| Name | Description | Value | +| ------------------------------------------- | --------------------------------------------------------------------------------------- | ----------- | +| `service.type` | Kubernetes Service type | `ClusterIP` | +| `service.ports.client` | ZooKeeper client service port | `2181` | +| `service.ports.tls` | ZooKeeper TLS service port | `3181` | +| `service.ports.follower` | ZooKeeper follower service port | `2888` | +| `service.ports.election` | ZooKeeper election service port | `3888` | +| `service.nodePorts.client` | Node port for clients | `""` | +| `service.nodePorts.tls` | Node port for TLS | `""` | +| `service.disableBaseClientPort` | Remove client port from service definitions. | `false` | +| `service.sessionAffinity` | Control where client requests go, to the same pod or round-robin | `None` | +| `service.sessionAffinityConfig` | Additional settings for the sessionAffinity | `{}` | +| `service.clusterIP` | ZooKeeper service Cluster IP | `""` | +| `service.loadBalancerIP` | ZooKeeper service Load Balancer IP | `""` | +| `service.loadBalancerSourceRanges` | ZooKeeper service Load Balancer sources | `[]` | +| `service.externalTrafficPolicy` | ZooKeeper service external traffic policy | `Cluster` | +| `service.annotations` | Additional custom annotations for ZooKeeper service | `{}` | +| `service.extraPorts` | Extra ports to expose in the ZooKeeper service (normally used with the `sidecar` value) | `[]` | +| `service.headless.annotations` | Annotations for the Headless Service | `{}` | +| `service.headless.publishNotReadyAddresses` | If the ZooKeeper headless service should publish DNS records for not ready pods | `true` | +| `networkPolicy.enabled` | Specifies whether a NetworkPolicy should be created | `false` | +| `networkPolicy.allowExternal` | Don't require client label for connections | `true` | + + +### Other Parameters + +| Name | Description | Value | +| --------------------------------------------- | ---------------------------------------------------------------------- | ------- | +| `serviceAccount.create` | Enable creation of ServiceAccount for ZooKeeper pod | `false` | +| `serviceAccount.name` | The name of the ServiceAccount to use. | `""` | +| `serviceAccount.automountServiceAccountToken` | Allows auto mount of ServiceAccountToken on the serviceAccount created | `true` | +| `serviceAccount.annotations` | Additional custom annotations for the ServiceAccount | `{}` | + + +### Persistence parameters + +| Name | Description | Value | +| -------------------------------------- | ------------------------------------------------------------------------------ | ------------------- | +| `persistence.enabled` | Enable ZooKeeper data persistence using PVC. If false, use emptyDir | `true` | +| `persistence.existingClaim` | Name of an existing PVC to use (only when deploying a single replica) | `""` | +| `persistence.storageClass` | PVC Storage Class for ZooKeeper data volume | `""` | +| `persistence.accessModes` | PVC Access modes | `["ReadWriteOnce"]` | +| `persistence.size` | PVC Storage Request for ZooKeeper data volume | `8Gi` | +| `persistence.annotations` | Annotations for the PVC | `{}` | +| `persistence.labels` | Labels for the PVC | `{}` | +| `persistence.selector` | Selector to match an existing Persistent Volume for ZooKeeper's data PVC | `{}` | +| `persistence.dataLogDir.size` | PVC Storage Request for ZooKeeper's dedicated data log directory | `8Gi` | +| `persistence.dataLogDir.existingClaim` | Provide an existing `PersistentVolumeClaim` for ZooKeeper's data log directory | `""` | +| `persistence.dataLogDir.selector` | Selector to match an existing Persistent Volume for ZooKeeper's data log PVC | `{}` | + + +### Volume Permissions parameters + +| Name | Description | Value | +| ------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | +| `volumePermissions.enabled` | Enable init container that changes the owner and group of the persistent volume | `false` | +| `volumePermissions.image.registry` | Init container volume-permissions image registry | `docker.io` | +| `volumePermissions.image.repository` | Init container volume-permissions image repository | `bitnami/bitnami-shell` | +| `volumePermissions.image.tag` | Init container volume-permissions image tag (immutable tags are recommended) | `11-debian-11-r60` | +| `volumePermissions.image.digest` | Init container volume-permissions image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag | `""` | +| `volumePermissions.image.pullPolicy` | Init container volume-permissions image pull policy | `IfNotPresent` | +| `volumePermissions.image.pullSecrets` | Init container volume-permissions image pull secrets | `[]` | +| `volumePermissions.resources.limits` | Init container volume-permissions resource limits | `{}` | +| `volumePermissions.resources.requests` | Init container volume-permissions resource requests | `{}` | +| `volumePermissions.containerSecurityContext.enabled` | Enabled init container Security Context | `true` | +| `volumePermissions.containerSecurityContext.runAsUser` | User ID for the init container | `0` | + + +### Metrics parameters + +| Name | Description | Value | +| ------------------------------------------ | ------------------------------------------------------------------------------------- | ----------- | +| `metrics.enabled` | Enable Prometheus to access ZooKeeper metrics endpoint | `false` | +| `metrics.containerPort` | ZooKeeper Prometheus Exporter container port | `9141` | +| `metrics.service.type` | ZooKeeper Prometheus Exporter service type | `ClusterIP` | +| `metrics.service.port` | ZooKeeper Prometheus Exporter service port | `9141` | +| `metrics.service.annotations` | Annotations for Prometheus to auto-discover the metrics endpoint | `{}` | +| `metrics.serviceMonitor.enabled` | Create ServiceMonitor Resource for scraping metrics using Prometheus Operator | `false` | +| `metrics.serviceMonitor.namespace` | Namespace for the ServiceMonitor Resource (defaults to the Release Namespace) | `""` | +| `metrics.serviceMonitor.interval` | Interval at which metrics should be scraped. | `""` | +| `metrics.serviceMonitor.scrapeTimeout` | Timeout after which the scrape is ended | `""` | +| `metrics.serviceMonitor.additionalLabels` | Additional labels that can be used so ServiceMonitor will be discovered by Prometheus | `{}` | +| `metrics.serviceMonitor.selector` | Prometheus instance selector labels | `{}` | +| `metrics.serviceMonitor.relabelings` | RelabelConfigs to apply to samples before scraping | `[]` | +| `metrics.serviceMonitor.metricRelabelings` | MetricRelabelConfigs to apply to samples before ingestion | `[]` | +| `metrics.serviceMonitor.honorLabels` | Specify honorLabels parameter to add the scrape endpoint | `false` | +| `metrics.serviceMonitor.jobLabel` | The name of the label on the target service to use as the job name in prometheus. | `""` | +| `metrics.prometheusRule.enabled` | Create a PrometheusRule for Prometheus Operator | `false` | +| `metrics.prometheusRule.namespace` | Namespace for the PrometheusRule Resource (defaults to the Release Namespace) | `""` | +| `metrics.prometheusRule.additionalLabels` | Additional labels that can be used so PrometheusRule will be discovered by Prometheus | `{}` | +| `metrics.prometheusRule.rules` | PrometheusRule definitions | `[]` | + + +### TLS/SSL parameters + +| Name | Description | Value | +| ----------------------------------------- | -------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- | +| `tls.client.enabled` | Enable TLS for client connections | `false` | +| `tls.client.auth` | SSL Client auth. Can be "none", "want" or "need". | `none` | +| `tls.client.autoGenerated` | Generate automatically self-signed TLS certificates for ZooKeeper client communications | `false` | +| `tls.client.existingSecret` | Name of the existing secret containing the TLS certificates for ZooKeeper client communications | `""` | +| `tls.client.existingSecretKeystoreKey` | The secret key from the tls.client.existingSecret containing the Keystore. | `""` | +| `tls.client.existingSecretTruststoreKey` | The secret key from the tls.client.existingSecret containing the Truststore. | `""` | +| `tls.client.keystorePath` | Location of the KeyStore file used for Client connections | `/opt/bitnami/zookeeper/config/certs/client/zookeeper.keystore.jks` | +| `tls.client.truststorePath` | Location of the TrustStore file used for Client connections | `/opt/bitnami/zookeeper/config/certs/client/zookeeper.truststore.jks` | +| `tls.client.passwordsSecretName` | Existing secret containing Keystore and truststore passwords | `""` | +| `tls.client.passwordsSecretKeystoreKey` | The secret key from the tls.client.passwordsSecretName containing the password for the Keystore. | `""` | +| `tls.client.passwordsSecretTruststoreKey` | The secret key from the tls.client.passwordsSecretName containing the password for the Truststore. | `""` | +| `tls.client.keystorePassword` | Password to access KeyStore if needed | `""` | +| `tls.client.truststorePassword` | Password to access TrustStore if needed | `""` | +| `tls.quorum.enabled` | Enable TLS for quorum protocol | `false` | +| `tls.quorum.auth` | SSL Quorum Client auth. Can be "none", "want" or "need". | `none` | +| `tls.quorum.autoGenerated` | Create self-signed TLS certificates. Currently only supports PEM certificates. | `false` | +| `tls.quorum.existingSecret` | Name of the existing secret containing the TLS certificates for ZooKeeper quorum protocol | `""` | +| `tls.quorum.existingSecretKeystoreKey` | The secret key from the tls.quorum.existingSecret containing the Keystore. | `""` | +| `tls.quorum.existingSecretTruststoreKey` | The secret key from the tls.quorum.existingSecret containing the Truststore. | `""` | +| `tls.quorum.keystorePath` | Location of the KeyStore file used for Quorum protocol | `/opt/bitnami/zookeeper/config/certs/quorum/zookeeper.keystore.jks` | +| `tls.quorum.truststorePath` | Location of the TrustStore file used for Quorum protocol | `/opt/bitnami/zookeeper/config/certs/quorum/zookeeper.truststore.jks` | +| `tls.quorum.passwordsSecretName` | Existing secret containing Keystore and truststore passwords | `""` | +| `tls.quorum.passwordsSecretKeystoreKey` | The secret key from the tls.quorum.passwordsSecretName containing the password for the Keystore. | `""` | +| `tls.quorum.passwordsSecretTruststoreKey` | The secret key from the tls.quorum.passwordsSecretName containing the password for the Truststore. | `""` | +| `tls.quorum.keystorePassword` | Password to access KeyStore if needed | `""` | +| `tls.quorum.truststorePassword` | Password to access TrustStore if needed | `""` | +| `tls.resources.limits` | The resources limits for the TLS init container | `{}` | +| `tls.resources.requests` | The requested resources for the TLS init container | `{}` | + + +Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example, + +```console +$ helm install my-release \ + --set auth.clientUser=newUser \ + my-repo/zookeeper +``` + +The above command sets the ZooKeeper user to `newUser`. + +> NOTE: Once this chart is deployed, it is not possible to change the application's access credentials, such as usernames or passwords, using Helm. To change these application credentials after deployment, delete any persistent volumes (PVs) used by the chart and re-deploy it, or use the application's built-in administrative tools if available. + +Alternatively, a YAML file that specifies the values for the parameters can be provided while installing the chart. For example, + +```console +$ helm install my-release -f values.yaml my-repo/zookeeper +``` + +> **Tip**: You can use the default [values.yaml](values.yaml) + +## Configuration and installation details + +### [Rolling vs Immutable tags](https://docs.bitnami.com/containers/how-to/understand-rolling-tags-containers/) + +It is strongly recommended to use immutable tags in a production environment. This ensures your deployment does not change automatically if the same tag is updated with a different image. + +Bitnami will release a new chart updating its containers if a new version of the main container, significant changes, or critical vulnerabilities exist. + +### Configure log level + +You can configure the ZooKeeper log level using the `ZOO_LOG_LEVEL` environment variable or the parameter `logLevel`. By default, it is set to `ERROR` because each use of the liveness probe and the readiness probe produces an `INFO` message on connection and a `WARN` message on disconnection, generating a high volume of noise in your logs. + +In order to remove that log noise so levels can be set to 'INFO', two changes must be made. + +First, ensure that you are not getting metrics via the deprecated pattern of polling 'mntr' on the ZooKeeper client port. The preferred method of polling for Apache ZooKeeper metrics is the ZooKeeper metrics server. This is supported in this chart when setting `metrics.enabled` to `true`. + +Second, to avoid the connection/disconnection messages from the probes, you can set custom values for these checks which direct them to the ZooKeeper Admin Server instead of the client port. By default, an Admin Server will be started that listens on `localhost` at port `8080`. The following is an example of this use of the Admin Server for probes: + +``` +livenessProbe: + enabled: false +readinessProbe: + enabled: false +customLivenessProbe: + exec: + command: ['/bin/bash', '-c', 'curl -s -m 2 http://localhost:8080/commands/ruok | grep ruok'] + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 6 +customReadinessProbe: + exec: + command: ['/bin/bash', '-c', 'curl -s -m 2 http://localhost:8080/commands/ruok | grep error | grep null'] + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 6 +``` + +You can also set the log4j logging level and what log appenders are turned on, by using `ZOO_LOG4J_PROP` set inside of conf/log4j.properties as zookeeper.root.logger by default to + +```console +zookeeper.root.logger=INFO, CONSOLE +``` +the available appender is + +- CONSOLE +- ROLLINGFILE +- RFAAUDIT +- TRACEFILE + +## Persistence + +The [Bitnami ZooKeeper](https://github.com/bitnami/containers/tree/main/bitnami/zookeeper) image stores the ZooKeeper data and configurations at the `/bitnami/zookeeper` path of the container. + +Persistent Volume Claims are used to keep the data across deployments. This is known to work in GCE, AWS, and minikube. See the [Parameters](#parameters) section to configure the PVC or to disable persistence. + +If you encounter errors when working with persistent volumes, refer to our [troubleshooting guide for persistent volumes](https://docs.bitnami.com/kubernetes/faq/troubleshooting/troubleshooting-persistence-volumes/). + +### Adjust permissions of persistent volume mountpoint + +As the image run as non-root by default, it is necessary to adjust the ownership of the persistent volume so that the container can write data into it. + +By default, the chart is configured to use Kubernetes Security Context to automatically change the ownership of the volume. However, this feature does not work in all Kubernetes distributions. +As an alternative, this chart supports using an initContainer to change the ownership of the volume before mounting it in the final destination. + +You can enable this initContainer by setting `volumePermissions.enabled` to `true`. + +### Configure the data log directory + +You can use a dedicated device for logs (instead of using the data directory) to help avoiding competition between logging and snaphots. To do so, set the `dataLogDir` parameter with the path to be used for writing transaction logs. Alternatively, set this parameter with an empty string and it will result in the log being written to the data directory (Zookeeper's default behavior). + +When using a dedicated device for logs, you can use a PVC to persist the logs. To do so, set `persistence.enabled` to `true`. See the [Persistence Parameters](#persistence-parameters) section for more information. + +### Set pod affinity + +This chart allows you to set custom pod affinity using the `affinity` parameter. Find more information about pod affinity in the [Kubernetes documentation](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity). + +As an alternative, you can use any of the preset configurations for pod affinity, pod anti-affinity, and node affinity available at the [bitnami/common](https://github.com/bitnami/charts/tree/main/bitnami/common#affinities) chart. To do so, set the `podAffinityPreset`, `podAntiAffinityPreset`, or `nodeAffinityPreset` parameters. + +## Troubleshooting + +Find more information about how to deal with common errors related to Bitnami's Helm charts in [this troubleshooting guide](https://docs.bitnami.com/general/how-to/troubleshoot-helm-chart-issues). + +## Upgrading + +### To 11.0.0 + +This major version removes `commonAnnotations` and `commonLabels` from `volumeClaimTemplates`. Now annotations and labels can be set in volume claims using `persistence.annotations` and `persistence.labels` values. If the previous deployment has already set `commonAnnotations` and/or `commonLabels` values, to ensure a clean upgrade from previous version without loosing data, please set `persistence.annotations` and/or `persistence.labels` values with the same content as the common values. + +### To 10.0.0 + +This new version of the chart adds support for server-server authentication. +The chart previously supported client-server authentication, to avoid confusion, the previous parameters have been renamed from `auth.*` to `auth.client.*`. + +### To 9.0.0 + +This new version of the chart includes the new ZooKeeper major version 3.8.0. Upgrade compatibility is not guaranteed. + +### To 8.0.0 + +This major release renames several values in this chart and adds missing features, in order to be inline with the rest of assets in the Bitnami charts repository. + +Affected values: + +- `allowAnonymousLogin` is deprecated. +- `containerPort`, `tlsContainerPort`, `followerContainerPort` and `electionContainerPort` have been regrouped under the `containerPorts` map. +- `service.port`, `service.tlsClientPort`, `service.followerPort`, and `service.electionPort` have been regrouped under the `service.ports` map. +- `updateStrategy` (string) and `rollingUpdatePartition` are regrouped under the `updateStrategy` map. +- `podDisruptionBudget.*` parameters are renamed to `pdb.*`. + +### To 7.0.0 + +This new version renames the parameters used to configure TLS for both client and quorum. + +- `service.tls.disable_base_client_port` is renamed to `service.disableBaseClientPort` +- `service.tls.client_port` is renamed to `service.tlsClientPort` +- `service.tls.client_enable` is renamed to `tls.client.enabled` +- `service.tls.client_keystore_path` is renamed to `tls.client.keystorePath` +- `service.tls.client_truststore_path` is renamed to `tls.client.truststorePath` +- `service.tls.client_keystore_password` is renamed to `tls.client.keystorePassword` +- `service.tls.client_truststore_password` is renamed to `tls.client.truststorePassword` +- `service.tls.quorum_enable` is renamed to `tls.quorum.enabled` +- `service.tls.quorum_keystore_path` is renamed to `tls.quorum.keystorePath` +- `service.tls.quorum_truststore_path` is renamed to `tls.quorum.truststorePath` +- `service.tls.quorum_keystore_password` is renamed to `tls.quorum.keystorePassword` +- `service.tls.quorum_truststore_password` is renamed to `tls.quorum.truststorePassword` + +### To 6.1.0 + +This version introduces `bitnami/common`, a [library chart](https://helm.sh/docs/topics/library_charts/#helm) as a dependency. More documentation about this new utility could be found [here](https://github.com/bitnami/charts/tree/main/bitnami/common#bitnami-common-library-chart). Please, make sure that you have updated the chart dependencies before executing any upgrade. + +### To 6.0.0 + +[On November 13, 2020, Helm v2 support was formally finished](https://github.com/helm/charts#status-of-the-project), this major version is the result of the required changes applied to the Helm Chart to be able to incorporate the different features added in Helm v3 and to be consistent with the Helm project itself regarding the Helm v2 EOL. + +[Learn more about this change and related upgrade considerations](https://docs.bitnami.com/kubernetes/infrastructure/zookeeper/administration/upgrade-helm3/). + +### To 5.21.0 + +A couple of parameters related to Zookeeper metrics were renamed or disappeared in favor of new ones: + +- `metrics.port` is renamed to `metrics.containerPort`. +- `metrics.annotations` is deprecated in favor of `metrics.service.annotations`. + +### To 3.0.0 + +This new version of the chart includes the new ZooKeeper major version 3.5.5. Note that to perform an automatic upgrade +of the application, each node will need to have at least one snapshot file created in the data directory. If not, the +new version of the application won't be able to start the service. Please refer to [ZOOKEEPER-3056](https://issues.apache.org/jira/browse/ZOOKEEPER-3056) +in order to find ways to workaround this issue in case you are facing it. + +### To 2.0.0 + +Backwards compatibility is not guaranteed unless you modify the labels used on the chart's statefulsets. +Use the workaround below to upgrade from versions previous to 2.0.0. The following example assumes that the release name is `zookeeper`: + +```console +$ kubectl delete statefulset zookeeper-zookeeper --cascade=false +``` + +### To 1.0.0 + +Backwards compatibility is not guaranteed unless you modify the labels used on the chart's deployments. +Use the workaround below to upgrade from versions previous to 1.0.0. The following example assumes that the release name is zookeeper: + +```console +$ kubectl delete statefulset zookeeper-zookeeper --cascade=false +``` + +## License + +Copyright © 2022 Bitnami + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/.helmignore b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/.helmignore new file mode 100644 index 000000000..50af03172 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/.helmignore @@ -0,0 +1,22 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/Chart.yaml b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/Chart.yaml new file mode 100644 index 000000000..653c063f2 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/Chart.yaml @@ -0,0 +1,23 @@ +annotations: + category: Infrastructure +apiVersion: v2 +appVersion: 2.2.1 +description: A Library Helm Chart for grouping common logic between bitnami charts. + This chart is not deployable by itself. +home: https://github.com/bitnami/charts/tree/main/bitnami/common +icon: https://bitnami.com/downloads/logos/bitnami-mark.png +keywords: +- common +- helper +- template +- function +- bitnami +maintainers: +- name: Bitnami + url: https://github.com/bitnami/charts +name: common +sources: +- https://github.com/bitnami/charts +- https://www.bitnami.com/ +type: library +version: 2.2.1 diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/README.md b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/README.md new file mode 100644 index 000000000..ec43a5fab --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/README.md @@ -0,0 +1,351 @@ +# Bitnami Common Library Chart + +A [Helm Library Chart](https://helm.sh/docs/topics/library_charts/#helm) for grouping common logic between bitnami charts. + +## TL;DR + +```yaml +dependencies: + - name: common + version: 1.x.x + repository: https://charts.bitnami.com/bitnami +``` + +```bash +$ helm dependency update +``` + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "common.names.fullname" . }} +data: + myvalue: "Hello World" +``` + +## Introduction + +This chart provides a common template helpers which can be used to develop new charts using [Helm](https://helm.sh) package manager. + +Bitnami charts can be used with [Kubeapps](https://kubeapps.dev/) for deployment and management of Helm Charts in clusters. + +## Prerequisites + +- Kubernetes 1.19+ +- Helm 3.2.0+ + +## Parameters + +The following table lists the helpers available in the library which are scoped in different sections. + +### Affinities + +| Helper identifier | Description | Expected Input | +|-------------------------------|------------------------------------------------------|------------------------------------------------| +| `common.affinities.nodes.soft` | Return a soft nodeAffinity definition | `dict "key" "FOO" "values" (list "BAR" "BAZ")` | +| `common.affinities.nodes.hard` | Return a hard nodeAffinity definition | `dict "key" "FOO" "values" (list "BAR" "BAZ")` | +| `common.affinities.pods.soft` | Return a soft podAffinity/podAntiAffinity definition | `dict "component" "FOO" "context" $` | +| `common.affinities.pods.hard` | Return a hard podAffinity/podAntiAffinity definition | `dict "component" "FOO" "context" $` | +| `common.affinities.topologyKey` | Return a topologyKey definition | `dict "topologyKey" "FOO"` | + +### Capabilities + +| Helper identifier | Description | Expected Input | +|------------------------------------------------|------------------------------------------------------------------------------------------------|-------------------| +| `common.capabilities.kubeVersion` | Return the target Kubernetes version (using client default if .Values.kubeVersion is not set). | `.` Chart context | +| `common.capabilities.cronjob.apiVersion` | Return the appropriate apiVersion for cronjob. | `.` Chart context | +| `common.capabilities.deployment.apiVersion` | Return the appropriate apiVersion for deployment. | `.` Chart context | +| `common.capabilities.statefulset.apiVersion` | Return the appropriate apiVersion for statefulset. | `.` Chart context | +| `common.capabilities.ingress.apiVersion` | Return the appropriate apiVersion for ingress. | `.` Chart context | +| `common.capabilities.rbac.apiVersion` | Return the appropriate apiVersion for RBAC resources. | `.` Chart context | +| `common.capabilities.crd.apiVersion` | Return the appropriate apiVersion for CRDs. | `.` Chart context | +| `common.capabilities.policy.apiVersion` | Return the appropriate apiVersion for podsecuritypolicy. | `.` Chart context | +| `common.capabilities.networkPolicy.apiVersion` | Return the appropriate apiVersion for networkpolicy. | `.` Chart context | +| `common.capabilities.apiService.apiVersion` | Return the appropriate apiVersion for APIService. | `.` Chart context | +| `common.capabilities.hpa.apiVersion` | Return the appropriate apiVersion for Horizontal Pod Autoscaler | `.` Chart context | +| `common.capabilities.supportsHelmVersion` | Returns true if the used Helm version is 3.3+ | `.` Chart context | + +### Errors + +| Helper identifier | Description | Expected Input | +|-----------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------| +| `common.errors.upgrade.passwords.empty` | It will ensure required passwords are given when we are upgrading a chart. If `validationErrors` is not empty it will throw an error and will stop the upgrade action. | `dict "validationErrors" (list $validationError00 $validationError01) "context" $` | + +### Images + +| Helper identifier | Description | Expected Input | +|-----------------------------|------------------------------------------------------|---------------------------------------------------------------------------------------------------------| +| `common.images.image` | Return the proper and full image name | `dict "imageRoot" .Values.path.to.the.image "global" $`, see [ImageRoot](#imageroot) for the structure. | +| `common.images.pullSecrets` | Return the proper Docker Image Registry Secret Names (deprecated: use common.images.renderPullSecrets instead) | `dict "images" (list .Values.path.to.the.image1, .Values.path.to.the.image2) "global" .Values.global` | +| `common.images.renderPullSecrets` | Return the proper Docker Image Registry Secret Names (evaluates values as templates) | `dict "images" (list .Values.path.to.the.image1, .Values.path.to.the.image2) "context" $` | + +### Ingress + +| Helper identifier | Description | Expected Input | +|-------------------------------------------|-------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `common.ingress.backend` | Generate a proper Ingress backend entry depending on the API version | `dict "serviceName" "foo" "servicePort" "bar"`, see the [Ingress deprecation notice](https://kubernetes.io/blog/2019/07/18/api-deprecations-in-1-16/) for the syntax differences | +| `common.ingress.supportsPathType` | Prints "true" if the pathType field is supported | `.` Chart context | +| `common.ingress.supportsIngressClassname` | Prints "true" if the ingressClassname field is supported | `.` Chart context | +| `common.ingress.certManagerRequest` | Prints "true" if required cert-manager annotations for TLS signed certificates are set in the Ingress annotations | `dict "annotations" .Values.path.to.the.ingress.annotations` | + +### Labels + +| Helper identifier | Description | Expected Input | +|-----------------------------|-----------------------------------------------------------------------------|-------------------| +| `common.labels.standard` | Return Kubernetes standard labels | `.` Chart context | +| `common.labels.matchLabels` | Labels to use on `deploy.spec.selector.matchLabels` and `svc.spec.selector` | `.` Chart context | + +### Names + +| Helper identifier | Description | Expected Input | +|-----------------------------------|-----------------------------------------------------------------------|-------------------| +| `common.names.name` | Expand the name of the chart or use `.Values.nameOverride` | `.` Chart context | +| `common.names.fullname` | Create a default fully qualified app name. | `.` Chart context | +| `common.names.namespace` | Allow the release namespace to be overridden | `.` Chart context | +| `common.names.fullname.namespace` | Create a fully qualified app name adding the installation's namespace | `.` Chart context | +| `common.names.chart` | Chart name plus version | `.` Chart context | + +### Secrets + +| Helper identifier | Description | Expected Input | +|-----------------------------------|--------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `common.secrets.name` | Generate the name of the secret. | `dict "existingSecret" .Values.path.to.the.existingSecret "defaultNameSuffix" "mySuffix" "context" $` see [ExistingSecret](#existingsecret) for the structure. | +| `common.secrets.key` | Generate secret key. | `dict "existingSecret" .Values.path.to.the.existingSecret "key" "keyName"` see [ExistingSecret](#existingsecret) for the structure. | +| `common.secrets.passwords.manage` | Generate secret password or retrieve one if already created. | `dict "secret" "secret-name" "key" "keyName" "providedValues" (list "path.to.password1" "path.to.password2") "length" 10 "strong" false "chartName" "chartName" "context" $`, length, strong and chartNAme fields are optional. | +| `common.secrets.exists` | Returns whether a previous generated secret already exists. | `dict "secret" "secret-name" "context" $` | + +### Storage + +| Helper identifier | Description | Expected Input | +|-------------------------------|---------------------------------------|---------------------------------------------------------------------------------------------------------------------| +| `common.storage.class` | Return the proper Storage Class | `dict "persistence" .Values.path.to.the.persistence "global" $`, see [Persistence](#persistence) for the structure. | + +### TplValues + +| Helper identifier | Description | Expected Input | +|---------------------------|----------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------| +| `common.tplvalues.render` | Renders a value that contains template | `dict "value" .Values.path.to.the.Value "context" $`, value is the value should rendered as template, context frequently is the chart context `$` or `.` | + +### Utils + +| Helper identifier | Description | Expected Input | +|--------------------------------|------------------------------------------------------------------------------------------|------------------------------------------------------------------------| +| `common.utils.fieldToEnvVar` | Build environment variable name given a field. | `dict "field" "my-password"` | +| `common.utils.secret.getvalue` | Print instructions to get a secret value. | `dict "secret" "secret-name" "field" "secret-value-field" "context" $` | +| `common.utils.getValueFromKey` | Gets a value from `.Values` object given its key path | `dict "key" "path.to.key" "context" $` | +| `common.utils.getKeyFromList` | Returns first `.Values` key with a defined value or first of the list if all non-defined | `dict "keys" (list "path.to.key1" "path.to.key2") "context" $` | + +### Validations + +| Helper identifier | Description | Expected Input | +|--------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `common.validations.values.single.empty` | Validate a value must not be empty. | `dict "valueKey" "path.to.value" "secret" "secret.name" "field" "my-password" "subchart" "subchart" "context" $` secret, field and subchart are optional. In case they are given, the helper will generate a how to get instruction. See [ValidateValue](#validatevalue) | +| `common.validations.values.multiple.empty` | Validate a multiple values must not be empty. It returns a shared error for all the values. | `dict "required" (list $validateValueConf00 $validateValueConf01) "context" $`. See [ValidateValue](#validatevalue) | +| `common.validations.values.mariadb.passwords` | This helper will ensure required password for MariaDB are not empty. It returns a shared error for all the values. | `dict "secret" "mariadb-secret" "subchart" "true" "context" $` subchart field is optional and could be true or false it depends on where you will use mariadb chart and the helper. | +| `common.validations.values.mysql.passwords` | This helper will ensure required password for MySQL are not empty. It returns a shared error for all the values. | `dict "secret" "mysql-secret" "subchart" "true" "context" $` subchart field is optional and could be true or false it depends on where you will use mysql chart and the helper. | +| `common.validations.values.postgresql.passwords` | This helper will ensure required password for PostgreSQL are not empty. It returns a shared error for all the values. | `dict "secret" "postgresql-secret" "subchart" "true" "context" $` subchart field is optional and could be true or false it depends on where you will use postgresql chart and the helper. | +| `common.validations.values.redis.passwords` | This helper will ensure required password for Redis® are not empty. It returns a shared error for all the values. | `dict "secret" "redis-secret" "subchart" "true" "context" $` subchart field is optional and could be true or false it depends on where you will use redis chart and the helper. | +| `common.validations.values.cassandra.passwords` | This helper will ensure required password for Cassandra are not empty. It returns a shared error for all the values. | `dict "secret" "cassandra-secret" "subchart" "true" "context" $` subchart field is optional and could be true or false it depends on where you will use cassandra chart and the helper. | +| `common.validations.values.mongodb.passwords` | This helper will ensure required password for MongoDB® are not empty. It returns a shared error for all the values. | `dict "secret" "mongodb-secret" "subchart" "true" "context" $` subchart field is optional and could be true or false it depends on where you will use mongodb chart and the helper. | + +### Warnings + +| Helper identifier | Description | Expected Input | +|------------------------------|----------------------------------|------------------------------------------------------------| +| `common.warnings.rollingTag` | Warning about using rolling tag. | `ImageRoot` see [ImageRoot](#imageroot) for the structure. | + +## Special input schemas + +### ImageRoot + +```yaml +registry: + type: string + description: Docker registry where the image is located + example: docker.io + +repository: + type: string + description: Repository and image name + example: bitnami/nginx + +tag: + type: string + description: image tag + example: 1.16.1-debian-10-r63 + +pullPolicy: + type: string + description: Specify a imagePullPolicy. Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' + +pullSecrets: + type: array + items: + type: string + description: Optionally specify an array of imagePullSecrets (evaluated as templates). + +debug: + type: boolean + description: Set to true if you would like to see extra information on logs + example: false + +## An instance would be: +# registry: docker.io +# repository: bitnami/nginx +# tag: 1.16.1-debian-10-r63 +# pullPolicy: IfNotPresent +# debug: false +``` + +### Persistence + +```yaml +enabled: + type: boolean + description: Whether enable persistence. + example: true + +storageClass: + type: string + description: Ghost data Persistent Volume Storage Class, If set to "-", storageClassName: "" which disables dynamic provisioning. + example: "-" + +accessMode: + type: string + description: Access mode for the Persistent Volume Storage. + example: ReadWriteOnce + +size: + type: string + description: Size the Persistent Volume Storage. + example: 8Gi + +path: + type: string + description: Path to be persisted. + example: /bitnami + +## An instance would be: +# enabled: true +# storageClass: "-" +# accessMode: ReadWriteOnce +# size: 8Gi +# path: /bitnami +``` + +### ExistingSecret + +```yaml +name: + type: string + description: Name of the existing secret. + example: mySecret +keyMapping: + description: Mapping between the expected key name and the name of the key in the existing secret. + type: object + +## An instance would be: +# name: mySecret +# keyMapping: +# password: myPasswordKey +``` + +#### Example of use + +When we store sensitive data for a deployment in a secret, some times we want to give to users the possibility of using theirs existing secrets. + +```yaml +# templates/secret.yaml +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "common.names.fullname" . }} + labels: + app: {{ include "common.names.fullname" . }} +type: Opaque +data: + password: {{ .Values.password | b64enc | quote }} + +# templates/dpl.yaml +--- +... + env: + - name: PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "common.secrets.name" (dict "existingSecret" .Values.existingSecret "context" $) }} + key: {{ include "common.secrets.key" (dict "existingSecret" .Values.existingSecret "key" "password") }} +... + +# values.yaml +--- +name: mySecret +keyMapping: + password: myPasswordKey +``` + +### ValidateValue + +#### NOTES.txt + +```console +{{- $validateValueConf00 := (dict "valueKey" "path.to.value00" "secret" "secretName" "field" "password-00") -}} +{{- $validateValueConf01 := (dict "valueKey" "path.to.value01" "secret" "secretName" "field" "password-01") -}} + +{{ include "common.validations.values.multiple.empty" (dict "required" (list $validateValueConf00 $validateValueConf01) "context" $) }} +``` + +If we force those values to be empty we will see some alerts + +```console +$ helm install test mychart --set path.to.value00="",path.to.value01="" + 'path.to.value00' must not be empty, please add '--set path.to.value00=$PASSWORD_00' to the command. To get the current value: + + export PASSWORD_00=$(kubectl get secret --namespace default secretName -o jsonpath="{.data.password-00}" | base64 -d) + + 'path.to.value01' must not be empty, please add '--set path.to.value01=$PASSWORD_01' to the command. To get the current value: + + export PASSWORD_01=$(kubectl get secret --namespace default secretName -o jsonpath="{.data.password-01}" | base64 -d) +``` + +## Upgrading + +### To 1.0.0 + +[On November 13, 2020, Helm v2 support was formally finished](https://github.com/helm/charts#status-of-the-project), this major version is the result of the required changes applied to the Helm Chart to be able to incorporate the different features added in Helm v3 and to be consistent with the Helm project itself regarding the Helm v2 EOL. + +**What changes were introduced in this major version?** + +- Previous versions of this Helm Chart use `apiVersion: v1` (installable by both Helm 2 and 3), this Helm Chart was updated to `apiVersion: v2` (installable by Helm 3 only). [Here](https://helm.sh/docs/topics/charts/#the-apiversion-field) you can find more information about the `apiVersion` field. +- Use `type: library`. [Here](https://v3.helm.sh/docs/faq/#library-chart-support) you can find more information. +- The different fields present in the *Chart.yaml* file has been ordered alphabetically in a homogeneous way for all the Bitnami Helm Charts + +**Considerations when upgrading to this version** + +- If you want to upgrade to this version from a previous one installed with Helm v3, you shouldn't face any issues +- If you want to upgrade to this version using Helm v2, this scenario is not supported as this version doesn't support Helm v2 anymore +- If you installed the previous version with Helm v2 and wants to upgrade to this version with Helm v3, please refer to the [official Helm documentation](https://helm.sh/docs/topics/v2_v3_migration/#migration-use-cases) about migrating from Helm v2 to v3 + +**Useful links** + +- https://docs.bitnami.com/tutorials/resolve-helm2-helm3-post-migration-issues/ +- https://helm.sh/docs/topics/v2_v3_migration/ +- https://helm.sh/blog/migrate-from-helm-v2-to-helm-v3/ + +## License + +Copyright © 2022 Bitnami + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_affinities.tpl b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_affinities.tpl new file mode 100644 index 000000000..81902a681 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_affinities.tpl @@ -0,0 +1,106 @@ +{{/* vim: set filetype=mustache: */}} + +{{/* +Return a soft nodeAffinity definition +{{ include "common.affinities.nodes.soft" (dict "key" "FOO" "values" (list "BAR" "BAZ")) -}} +*/}} +{{- define "common.affinities.nodes.soft" -}} +preferredDuringSchedulingIgnoredDuringExecution: + - preference: + matchExpressions: + - key: {{ .key }} + operator: In + values: + {{- range .values }} + - {{ . | quote }} + {{- end }} + weight: 1 +{{- end -}} + +{{/* +Return a hard nodeAffinity definition +{{ include "common.affinities.nodes.hard" (dict "key" "FOO" "values" (list "BAR" "BAZ")) -}} +*/}} +{{- define "common.affinities.nodes.hard" -}} +requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: {{ .key }} + operator: In + values: + {{- range .values }} + - {{ . | quote }} + {{- end }} +{{- end -}} + +{{/* +Return a nodeAffinity definition +{{ include "common.affinities.nodes" (dict "type" "soft" "key" "FOO" "values" (list "BAR" "BAZ")) -}} +*/}} +{{- define "common.affinities.nodes" -}} + {{- if eq .type "soft" }} + {{- include "common.affinities.nodes.soft" . -}} + {{- else if eq .type "hard" }} + {{- include "common.affinities.nodes.hard" . -}} + {{- end -}} +{{- end -}} + +{{/* +Return a topologyKey definition +{{ include "common.affinities.topologyKey" (dict "topologyKey" "BAR") -}} +*/}} +{{- define "common.affinities.topologyKey" -}} +{{ .topologyKey | default "kubernetes.io/hostname" -}} +{{- end -}} + +{{/* +Return a soft podAffinity/podAntiAffinity definition +{{ include "common.affinities.pods.soft" (dict "component" "FOO" "extraMatchLabels" .Values.extraMatchLabels "topologyKey" "BAR" "context" $) -}} +*/}} +{{- define "common.affinities.pods.soft" -}} +{{- $component := default "" .component -}} +{{- $extraMatchLabels := default (dict) .extraMatchLabels -}} +preferredDuringSchedulingIgnoredDuringExecution: + - podAffinityTerm: + labelSelector: + matchLabels: {{- (include "common.labels.matchLabels" .context) | nindent 10 }} + {{- if not (empty $component) }} + {{ printf "app.kubernetes.io/component: %s" $component }} + {{- end }} + {{- range $key, $value := $extraMatchLabels }} + {{ $key }}: {{ $value | quote }} + {{- end }} + topologyKey: {{ include "common.affinities.topologyKey" (dict "topologyKey" .topologyKey) }} + weight: 1 +{{- end -}} + +{{/* +Return a hard podAffinity/podAntiAffinity definition +{{ include "common.affinities.pods.hard" (dict "component" "FOO" "extraMatchLabels" .Values.extraMatchLabels "topologyKey" "BAR" "context" $) -}} +*/}} +{{- define "common.affinities.pods.hard" -}} +{{- $component := default "" .component -}} +{{- $extraMatchLabels := default (dict) .extraMatchLabels -}} +requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchLabels: {{- (include "common.labels.matchLabels" .context) | nindent 8 }} + {{- if not (empty $component) }} + {{ printf "app.kubernetes.io/component: %s" $component }} + {{- end }} + {{- range $key, $value := $extraMatchLabels }} + {{ $key }}: {{ $value | quote }} + {{- end }} + topologyKey: {{ include "common.affinities.topologyKey" (dict "topologyKey" .topologyKey) }} +{{- end -}} + +{{/* +Return a podAffinity/podAntiAffinity definition +{{ include "common.affinities.pods" (dict "type" "soft" "key" "FOO" "values" (list "BAR" "BAZ")) -}} +*/}} +{{- define "common.affinities.pods" -}} + {{- if eq .type "soft" }} + {{- include "common.affinities.pods.soft" . -}} + {{- else if eq .type "hard" }} + {{- include "common.affinities.pods.hard" . -}} + {{- end -}} +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_capabilities.tpl b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_capabilities.tpl new file mode 100644 index 000000000..9d9b76004 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_capabilities.tpl @@ -0,0 +1,154 @@ +{{/* vim: set filetype=mustache: */}} + +{{/* +Return the target Kubernetes version +*/}} +{{- define "common.capabilities.kubeVersion" -}} +{{- if .Values.global }} + {{- if .Values.global.kubeVersion }} + {{- .Values.global.kubeVersion -}} + {{- else }} + {{- default .Capabilities.KubeVersion.Version .Values.kubeVersion -}} + {{- end -}} +{{- else }} +{{- default .Capabilities.KubeVersion.Version .Values.kubeVersion -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for poddisruptionbudget. +*/}} +{{- define "common.capabilities.policy.apiVersion" -}} +{{- if semverCompare "<1.21-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "policy/v1beta1" -}} +{{- else -}} +{{- print "policy/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for networkpolicy. +*/}} +{{- define "common.capabilities.networkPolicy.apiVersion" -}} +{{- if semverCompare "<1.7-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "extensions/v1beta1" -}} +{{- else -}} +{{- print "networking.k8s.io/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for cronjob. +*/}} +{{- define "common.capabilities.cronjob.apiVersion" -}} +{{- if semverCompare "<1.21-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "batch/v1beta1" -}} +{{- else -}} +{{- print "batch/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for deployment. +*/}} +{{- define "common.capabilities.deployment.apiVersion" -}} +{{- if semverCompare "<1.14-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "extensions/v1beta1" -}} +{{- else -}} +{{- print "apps/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for statefulset. +*/}} +{{- define "common.capabilities.statefulset.apiVersion" -}} +{{- if semverCompare "<1.14-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "apps/v1beta1" -}} +{{- else -}} +{{- print "apps/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for ingress. +*/}} +{{- define "common.capabilities.ingress.apiVersion" -}} +{{- if .Values.ingress -}} +{{- if .Values.ingress.apiVersion -}} +{{- .Values.ingress.apiVersion -}} +{{- else if semverCompare "<1.14-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "extensions/v1beta1" -}} +{{- else if semverCompare "<1.19-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "networking.k8s.io/v1beta1" -}} +{{- else -}} +{{- print "networking.k8s.io/v1" -}} +{{- end }} +{{- else if semverCompare "<1.14-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "extensions/v1beta1" -}} +{{- else if semverCompare "<1.19-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "networking.k8s.io/v1beta1" -}} +{{- else -}} +{{- print "networking.k8s.io/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for RBAC resources. +*/}} +{{- define "common.capabilities.rbac.apiVersion" -}} +{{- if semverCompare "<1.17-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "rbac.authorization.k8s.io/v1beta1" -}} +{{- else -}} +{{- print "rbac.authorization.k8s.io/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for CRDs. +*/}} +{{- define "common.capabilities.crd.apiVersion" -}} +{{- if semverCompare "<1.19-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "apiextensions.k8s.io/v1beta1" -}} +{{- else -}} +{{- print "apiextensions.k8s.io/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for APIService. +*/}} +{{- define "common.capabilities.apiService.apiVersion" -}} +{{- if semverCompare "<1.10-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "apiregistration.k8s.io/v1beta1" -}} +{{- else -}} +{{- print "apiregistration.k8s.io/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for Horizontal Pod Autoscaler. +*/}} +{{- define "common.capabilities.hpa.apiVersion" -}} +{{- if semverCompare "<1.23-0" (include "common.capabilities.kubeVersion" .context) -}} +{{- if .beta2 -}} +{{- print "autoscaling/v2beta2" -}} +{{- else -}} +{{- print "autoscaling/v2beta1" -}} +{{- end -}} +{{- else -}} +{{- print "autoscaling/v2" -}} +{{- end -}} +{{- end -}} + +{{/* +Returns true if the used Helm version is 3.3+. +A way to check the used Helm version was not introduced until version 3.3.0 with .Capabilities.HelmVersion, which contains an additional "{}}" structure. +This check is introduced as a regexMatch instead of {{ if .Capabilities.HelmVersion }} because checking for the key HelmVersion in <3.3 results in a "interface not found" error. +**To be removed when the catalog's minimun Helm version is 3.3** +*/}} +{{- define "common.capabilities.supportsHelmVersion" -}} +{{- if regexMatch "{(v[0-9])*[^}]*}}$" (.Capabilities | toString ) }} + {{- true -}} +{{- end -}} +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_errors.tpl b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_errors.tpl new file mode 100644 index 000000000..a79cc2e32 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_errors.tpl @@ -0,0 +1,23 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Through error when upgrading using empty passwords values that must not be empty. + +Usage: +{{- $validationError00 := include "common.validations.values.single.empty" (dict "valueKey" "path.to.password00" "secret" "secretName" "field" "password-00") -}} +{{- $validationError01 := include "common.validations.values.single.empty" (dict "valueKey" "path.to.password01" "secret" "secretName" "field" "password-01") -}} +{{ include "common.errors.upgrade.passwords.empty" (dict "validationErrors" (list $validationError00 $validationError01) "context" $) }} + +Required password params: + - validationErrors - String - Required. List of validation strings to be return, if it is empty it won't throw error. + - context - Context - Required. Parent context. +*/}} +{{- define "common.errors.upgrade.passwords.empty" -}} + {{- $validationErrors := join "" .validationErrors -}} + {{- if and $validationErrors .context.Release.IsUpgrade -}} + {{- $errorString := "\nPASSWORDS ERROR: You must provide your current passwords when upgrading the release." -}} + {{- $errorString = print $errorString "\n Note that even after reinstallation, old credentials may be needed as they may be kept in persistent volume claims." -}} + {{- $errorString = print $errorString "\n Further information can be obtained at https://docs.bitnami.com/general/how-to/troubleshoot-helm-chart-issues/#credential-errors-while-upgrading-chart-releases" -}} + {{- $errorString = print $errorString "\n%s" -}} + {{- printf $errorString $validationErrors | fail -}} + {{- end -}} +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_images.tpl b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_images.tpl new file mode 100644 index 000000000..46c659e79 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_images.tpl @@ -0,0 +1,76 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Return the proper image name +{{ include "common.images.image" ( dict "imageRoot" .Values.path.to.the.image "global" $) }} +*/}} +{{- define "common.images.image" -}} +{{- $registryName := .imageRoot.registry -}} +{{- $repositoryName := .imageRoot.repository -}} +{{- $separator := ":" -}} +{{- $termination := .imageRoot.tag | toString -}} +{{- if .global }} + {{- if .global.imageRegistry }} + {{- $registryName = .global.imageRegistry -}} + {{- end -}} +{{- end -}} +{{- if .imageRoot.digest }} + {{- $separator = "@" -}} + {{- $termination = .imageRoot.digest | toString -}} +{{- end -}} +{{- printf "%s/%s%s%s" $registryName $repositoryName $separator $termination -}} +{{- end -}} + +{{/* +Return the proper Docker Image Registry Secret Names (deprecated: use common.images.renderPullSecrets instead) +{{ include "common.images.pullSecrets" ( dict "images" (list .Values.path.to.the.image1, .Values.path.to.the.image2) "global" .Values.global) }} +*/}} +{{- define "common.images.pullSecrets" -}} + {{- $pullSecrets := list }} + + {{- if .global }} + {{- range .global.imagePullSecrets -}} + {{- $pullSecrets = append $pullSecrets . -}} + {{- end -}} + {{- end -}} + + {{- range .images -}} + {{- range .pullSecrets -}} + {{- $pullSecrets = append $pullSecrets . -}} + {{- end -}} + {{- end -}} + + {{- if (not (empty $pullSecrets)) }} +imagePullSecrets: + {{- range $pullSecrets }} + - name: {{ . }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Return the proper Docker Image Registry Secret Names evaluating values as templates +{{ include "common.images.renderPullSecrets" ( dict "images" (list .Values.path.to.the.image1, .Values.path.to.the.image2) "context" $) }} +*/}} +{{- define "common.images.renderPullSecrets" -}} + {{- $pullSecrets := list }} + {{- $context := .context }} + + {{- if $context.Values.global }} + {{- range $context.Values.global.imagePullSecrets -}} + {{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" . "context" $context)) -}} + {{- end -}} + {{- end -}} + + {{- range .images -}} + {{- range .pullSecrets -}} + {{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" . "context" $context)) -}} + {{- end -}} + {{- end -}} + + {{- if (not (empty $pullSecrets)) }} +imagePullSecrets: + {{- range $pullSecrets }} + - name: {{ . }} + {{- end }} + {{- end }} +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_ingress.tpl b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_ingress.tpl new file mode 100644 index 000000000..831da9caa --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_ingress.tpl @@ -0,0 +1,68 @@ +{{/* vim: set filetype=mustache: */}} + +{{/* +Generate backend entry that is compatible with all Kubernetes API versions. + +Usage: +{{ include "common.ingress.backend" (dict "serviceName" "backendName" "servicePort" "backendPort" "context" $) }} + +Params: + - serviceName - String. Name of an existing service backend + - servicePort - String/Int. Port name (or number) of the service. It will be translated to different yaml depending if it is a string or an integer. + - context - Dict - Required. The context for the template evaluation. +*/}} +{{- define "common.ingress.backend" -}} +{{- $apiVersion := (include "common.capabilities.ingress.apiVersion" .context) -}} +{{- if or (eq $apiVersion "extensions/v1beta1") (eq $apiVersion "networking.k8s.io/v1beta1") -}} +serviceName: {{ .serviceName }} +servicePort: {{ .servicePort }} +{{- else -}} +service: + name: {{ .serviceName }} + port: + {{- if typeIs "string" .servicePort }} + name: {{ .servicePort }} + {{- else if or (typeIs "int" .servicePort) (typeIs "float64" .servicePort) }} + number: {{ .servicePort | int }} + {{- end }} +{{- end -}} +{{- end -}} + +{{/* +Print "true" if the API pathType field is supported +Usage: +{{ include "common.ingress.supportsPathType" . }} +*/}} +{{- define "common.ingress.supportsPathType" -}} +{{- if (semverCompare "<1.18-0" (include "common.capabilities.kubeVersion" .)) -}} +{{- print "false" -}} +{{- else -}} +{{- print "true" -}} +{{- end -}} +{{- end -}} + +{{/* +Returns true if the ingressClassname field is supported +Usage: +{{ include "common.ingress.supportsIngressClassname" . }} +*/}} +{{- define "common.ingress.supportsIngressClassname" -}} +{{- if semverCompare "<1.18-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "false" -}} +{{- else -}} +{{- print "true" -}} +{{- end -}} +{{- end -}} + +{{/* +Return true if cert-manager required annotations for TLS signed +certificates are set in the Ingress annotations +Ref: https://cert-manager.io/docs/usage/ingress/#supported-annotations +Usage: +{{ include "common.ingress.certManagerRequest" ( dict "annotations" .Values.path.to.the.ingress.annotations ) }} +*/}} +{{- define "common.ingress.certManagerRequest" -}} +{{ if or (hasKey .annotations "cert-manager.io/cluster-issuer") (hasKey .annotations "cert-manager.io/issuer") (hasKey .annotations "kubernetes.io/tls-acme") }} + {{- true -}} +{{- end -}} +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_labels.tpl b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_labels.tpl new file mode 100644 index 000000000..252066c7e --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_labels.tpl @@ -0,0 +1,18 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Kubernetes standard labels +*/}} +{{- define "common.labels.standard" -}} +app.kubernetes.io/name: {{ include "common.names.name" . }} +helm.sh/chart: {{ include "common.names.chart" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end -}} + +{{/* +Labels to use on deploy.spec.selector.matchLabels and svc.spec.selector +*/}} +{{- define "common.labels.matchLabels" -}} +app.kubernetes.io/name: {{ include "common.names.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_names.tpl b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_names.tpl new file mode 100644 index 000000000..1bdac8b77 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_names.tpl @@ -0,0 +1,70 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "common.names.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "common.names.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "common.names.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create a default fully qualified dependency name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +Usage: +{{ include "common.names.dependency.fullname" (dict "chartName" "dependency-chart-name" "chartValues" .Values.dependency-chart "context" $) }} +*/}} +{{- define "common.names.dependency.fullname" -}} +{{- if .chartValues.fullnameOverride -}} +{{- .chartValues.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .chartName .chartValues.nameOverride -}} +{{- if contains $name .context.Release.Name -}} +{{- .context.Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .context.Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Allow the release namespace to be overridden for multi-namespace deployments in combined charts. +*/}} +{{- define "common.names.namespace" -}} +{{- if .Values.namespaceOverride -}} +{{- .Values.namespaceOverride -}} +{{- else -}} +{{- .Release.Namespace -}} +{{- end -}} +{{- end -}} + +{{/* +Create a fully qualified app name adding the installation's namespace. +*/}} +{{- define "common.names.fullname.namespace" -}} +{{- printf "%s-%s" (include "common.names.fullname" .) (include "common.names.namespace" .) | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_secrets.tpl b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_secrets.tpl new file mode 100644 index 000000000..4267d4204 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_secrets.tpl @@ -0,0 +1,165 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Generate secret name. + +Usage: +{{ include "common.secrets.name" (dict "existingSecret" .Values.path.to.the.existingSecret "defaultNameSuffix" "mySuffix" "context" $) }} + +Params: + - existingSecret - ExistingSecret/String - Optional. The path to the existing secrets in the values.yaml given by the user + to be used instead of the default one. Allows for it to be of type String (just the secret name) for backwards compatibility. + +info: https://github.com/bitnami/charts/tree/main/bitnami/common#existingsecret + - defaultNameSuffix - String - Optional. It is used only if we have several secrets in the same deployment. + - context - Dict - Required. The context for the template evaluation. +*/}} +{{- define "common.secrets.name" -}} +{{- $name := (include "common.names.fullname" .context) -}} + +{{- if .defaultNameSuffix -}} +{{- $name = printf "%s-%s" $name .defaultNameSuffix | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{- with .existingSecret -}} +{{- if not (typeIs "string" .) -}} +{{- with .name -}} +{{- $name = . -}} +{{- end -}} +{{- else -}} +{{- $name = . -}} +{{- end -}} +{{- end -}} + +{{- printf "%s" $name -}} +{{- end -}} + +{{/* +Generate secret key. + +Usage: +{{ include "common.secrets.key" (dict "existingSecret" .Values.path.to.the.existingSecret "key" "keyName") }} + +Params: + - existingSecret - ExistingSecret/String - Optional. The path to the existing secrets in the values.yaml given by the user + to be used instead of the default one. Allows for it to be of type String (just the secret name) for backwards compatibility. + +info: https://github.com/bitnami/charts/tree/main/bitnami/common#existingsecret + - key - String - Required. Name of the key in the secret. +*/}} +{{- define "common.secrets.key" -}} +{{- $key := .key -}} + +{{- if .existingSecret -}} + {{- if not (typeIs "string" .existingSecret) -}} + {{- if .existingSecret.keyMapping -}} + {{- $key = index .existingSecret.keyMapping $.key -}} + {{- end -}} + {{- end }} +{{- end -}} + +{{- printf "%s" $key -}} +{{- end -}} + +{{/* +Generate secret password or retrieve one if already created. + +Usage: +{{ include "common.secrets.passwords.manage" (dict "secret" "secret-name" "key" "keyName" "providedValues" (list "path.to.password1" "path.to.password2") "length" 10 "strong" false "chartName" "chartName" "context" $) }} + +Params: + - secret - String - Required - Name of the 'Secret' resource where the password is stored. + - key - String - Required - Name of the key in the secret. + - providedValues - List - Required - The path to the validating value in the values.yaml, e.g: "mysql.password". Will pick first parameter with a defined value. + - length - int - Optional - Length of the generated random password. + - strong - Boolean - Optional - Whether to add symbols to the generated random password. + - chartName - String - Optional - Name of the chart used when said chart is deployed as a subchart. + - context - Context - Required - Parent context. + +The order in which this function returns a secret password: + 1. Already existing 'Secret' resource + (If a 'Secret' resource is found under the name provided to the 'secret' parameter to this function and that 'Secret' resource contains a key with the name passed as the 'key' parameter to this function then the value of this existing secret password will be returned) + 2. Password provided via the values.yaml + (If one of the keys passed to the 'providedValues' parameter to this function is a valid path to a key in the values.yaml and has a value, the value of the first key with a value will be returned) + 3. Randomly generated secret password + (A new random secret password with the length specified in the 'length' parameter will be generated and returned) + +*/}} +{{- define "common.secrets.passwords.manage" -}} + +{{- $password := "" }} +{{- $subchart := "" }} +{{- $chartName := default "" .chartName }} +{{- $passwordLength := default 10 .length }} +{{- $providedPasswordKey := include "common.utils.getKeyFromList" (dict "keys" .providedValues "context" $.context) }} +{{- $providedPasswordValue := include "common.utils.getValueFromKey" (dict "key" $providedPasswordKey "context" $.context) }} +{{- $secretData := (lookup "v1" "Secret" $.context.Release.Namespace .secret).data }} +{{- if $secretData }} + {{- if hasKey $secretData .key }} + {{- $password = index $secretData .key | quote }} + {{- else }} + {{- printf "\nPASSWORDS ERROR: The secret \"%s\" does not contain the key \"%s\"\n" .secret .key | fail -}} + {{- end -}} +{{- else if $providedPasswordValue }} + {{- $password = $providedPasswordValue | toString | b64enc | quote }} +{{- else }} + + {{- if .context.Values.enabled }} + {{- $subchart = $chartName }} + {{- end -}} + + {{- $requiredPassword := dict "valueKey" $providedPasswordKey "secret" .secret "field" .key "subchart" $subchart "context" $.context -}} + {{- $requiredPasswordError := include "common.validations.values.single.empty" $requiredPassword -}} + {{- $passwordValidationErrors := list $requiredPasswordError -}} + {{- include "common.errors.upgrade.passwords.empty" (dict "validationErrors" $passwordValidationErrors "context" $.context) -}} + + {{- if .strong }} + {{- $subStr := list (lower (randAlpha 1)) (randNumeric 1) (upper (randAlpha 1)) | join "_" }} + {{- $password = randAscii $passwordLength }} + {{- $password = regexReplaceAllLiteral "\\W" $password "@" | substr 5 $passwordLength }} + {{- $password = printf "%s%s" $subStr $password | toString | shuffle | b64enc | quote }} + {{- else }} + {{- $password = randAlphaNum $passwordLength | b64enc | quote }} + {{- end }} +{{- end -}} +{{- printf "%s" $password -}} +{{- end -}} + +{{/* +Reuses the value from an existing secret, otherwise sets its value to a default value. + +Usage: +{{ include "common.secrets.lookup" (dict "secret" "secret-name" "key" "keyName" "defaultValue" .Values.myValue "context" $) }} + +Params: + - secret - String - Required - Name of the 'Secret' resource where the password is stored. + - key - String - Required - Name of the key in the secret. + - defaultValue - String - Required - The path to the validating value in the values.yaml, e.g: "mysql.password". Will pick first parameter with a defined value. + - context - Context - Required - Parent context. + +*/}} +{{- define "common.secrets.lookup" -}} +{{- $value := "" -}} +{{- $defaultValue := required "\n'common.secrets.lookup': Argument 'defaultValue' missing or empty" .defaultValue -}} +{{- $secretData := (lookup "v1" "Secret" $.context.Release.Namespace .secret).data -}} +{{- if and $secretData (hasKey $secretData .key) -}} + {{- $value = index $secretData .key -}} +{{- else -}} + {{- $value = $defaultValue | toString | b64enc -}} +{{- end -}} +{{- printf "%s" $value -}} +{{- end -}} + +{{/* +Returns whether a previous generated secret already exists + +Usage: +{{ include "common.secrets.exists" (dict "secret" "secret-name" "context" $) }} + +Params: + - secret - String - Required - Name of the 'Secret' resource where the password is stored. + - context - Context - Required - Parent context. +*/}} +{{- define "common.secrets.exists" -}} +{{- $secret := (lookup "v1" "Secret" $.context.Release.Namespace .secret) }} +{{- if $secret }} + {{- true -}} +{{- end -}} +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_storage.tpl b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_storage.tpl new file mode 100644 index 000000000..60e2a844f --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_storage.tpl @@ -0,0 +1,23 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Return the proper Storage Class +{{ include "common.storage.class" ( dict "persistence" .Values.path.to.the.persistence "global" $) }} +*/}} +{{- define "common.storage.class" -}} + +{{- $storageClass := .persistence.storageClass -}} +{{- if .global -}} + {{- if .global.storageClass -}} + {{- $storageClass = .global.storageClass -}} + {{- end -}} +{{- end -}} + +{{- if $storageClass -}} + {{- if (eq "-" $storageClass) -}} + {{- printf "storageClassName: \"\"" -}} + {{- else }} + {{- printf "storageClassName: %s" $storageClass -}} + {{- end -}} +{{- end -}} + +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_tplvalues.tpl b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_tplvalues.tpl new file mode 100644 index 000000000..2db166851 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_tplvalues.tpl @@ -0,0 +1,13 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Renders a value that contains template. +Usage: +{{ include "common.tplvalues.render" ( dict "value" .Values.path.to.the.Value "context" $) }} +*/}} +{{- define "common.tplvalues.render" -}} + {{- if typeIs "string" .value }} + {{- tpl .value .context }} + {{- else }} + {{- tpl (.value | toYaml) .context }} + {{- end }} +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_utils.tpl b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_utils.tpl new file mode 100644 index 000000000..8c22b2a38 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_utils.tpl @@ -0,0 +1,62 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Print instructions to get a secret value. +Usage: +{{ include "common.utils.secret.getvalue" (dict "secret" "secret-name" "field" "secret-value-field" "context" $) }} +*/}} +{{- define "common.utils.secret.getvalue" -}} +{{- $varname := include "common.utils.fieldToEnvVar" . -}} +export {{ $varname }}=$(kubectl get secret --namespace {{ .context.Release.Namespace | quote }} {{ .secret }} -o jsonpath="{.data.{{ .field }}}" | base64 -d) +{{- end -}} + +{{/* +Build env var name given a field +Usage: +{{ include "common.utils.fieldToEnvVar" dict "field" "my-password" }} +*/}} +{{- define "common.utils.fieldToEnvVar" -}} + {{- $fieldNameSplit := splitList "-" .field -}} + {{- $upperCaseFieldNameSplit := list -}} + + {{- range $fieldNameSplit -}} + {{- $upperCaseFieldNameSplit = append $upperCaseFieldNameSplit ( upper . ) -}} + {{- end -}} + + {{ join "_" $upperCaseFieldNameSplit }} +{{- end -}} + +{{/* +Gets a value from .Values given +Usage: +{{ include "common.utils.getValueFromKey" (dict "key" "path.to.key" "context" $) }} +*/}} +{{- define "common.utils.getValueFromKey" -}} +{{- $splitKey := splitList "." .key -}} +{{- $value := "" -}} +{{- $latestObj := $.context.Values -}} +{{- range $splitKey -}} + {{- if not $latestObj -}} + {{- printf "please review the entire path of '%s' exists in values" $.key | fail -}} + {{- end -}} + {{- $value = ( index $latestObj . ) -}} + {{- $latestObj = $value -}} +{{- end -}} +{{- printf "%v" (default "" $value) -}} +{{- end -}} + +{{/* +Returns first .Values key with a defined value or first of the list if all non-defined +Usage: +{{ include "common.utils.getKeyFromList" (dict "keys" (list "path.to.key1" "path.to.key2") "context" $) }} +*/}} +{{- define "common.utils.getKeyFromList" -}} +{{- $key := first .keys -}} +{{- $reverseKeys := reverse .keys }} +{{- range $reverseKeys }} + {{- $value := include "common.utils.getValueFromKey" (dict "key" . "context" $.context ) }} + {{- if $value -}} + {{- $key = . }} + {{- end -}} +{{- end -}} +{{- printf "%s" $key -}} +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_warnings.tpl b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_warnings.tpl new file mode 100644 index 000000000..ae10fa41e --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/_warnings.tpl @@ -0,0 +1,14 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Warning about using rolling tag. +Usage: +{{ include "common.warnings.rollingTag" .Values.path.to.the.imageRoot }} +*/}} +{{- define "common.warnings.rollingTag" -}} + +{{- if and (contains "bitnami/" .repository) (not (.tag | toString | regexFind "-r\\d+$|sha256:")) }} +WARNING: Rolling tag detected ({{ .repository }}:{{ .tag }}), please note that it is strongly recommended to avoid using rolling tags in a production environment. ++info https://docs.bitnami.com/containers/how-to/understand-rolling-tags-containers/ +{{- end }} + +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/validations/_cassandra.tpl b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/validations/_cassandra.tpl new file mode 100644 index 000000000..ded1ae3bc --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/validations/_cassandra.tpl @@ -0,0 +1,72 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Validate Cassandra required passwords are not empty. + +Usage: +{{ include "common.validations.values.cassandra.passwords" (dict "secret" "secretName" "subchart" false "context" $) }} +Params: + - secret - String - Required. Name of the secret where Cassandra values are stored, e.g: "cassandra-passwords-secret" + - subchart - Boolean - Optional. Whether Cassandra is used as subchart or not. Default: false +*/}} +{{- define "common.validations.values.cassandra.passwords" -}} + {{- $existingSecret := include "common.cassandra.values.existingSecret" . -}} + {{- $enabled := include "common.cassandra.values.enabled" . -}} + {{- $dbUserPrefix := include "common.cassandra.values.key.dbUser" . -}} + {{- $valueKeyPassword := printf "%s.password" $dbUserPrefix -}} + + {{- if and (or (not $existingSecret) (eq $existingSecret "\"\"")) (eq $enabled "true") -}} + {{- $requiredPasswords := list -}} + + {{- $requiredPassword := dict "valueKey" $valueKeyPassword "secret" .secret "field" "cassandra-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredPassword -}} + + {{- include "common.validations.values.multiple.empty" (dict "required" $requiredPasswords "context" .context) -}} + + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for existingSecret. + +Usage: +{{ include "common.cassandra.values.existingSecret" (dict "context" $) }} +Params: + - subchart - Boolean - Optional. Whether Cassandra is used as subchart or not. Default: false +*/}} +{{- define "common.cassandra.values.existingSecret" -}} + {{- if .subchart -}} + {{- .context.Values.cassandra.dbUser.existingSecret | quote -}} + {{- else -}} + {{- .context.Values.dbUser.existingSecret | quote -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for enabled cassandra. + +Usage: +{{ include "common.cassandra.values.enabled" (dict "context" $) }} +*/}} +{{- define "common.cassandra.values.enabled" -}} + {{- if .subchart -}} + {{- printf "%v" .context.Values.cassandra.enabled -}} + {{- else -}} + {{- printf "%v" (not .context.Values.enabled) -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for the key dbUser + +Usage: +{{ include "common.cassandra.values.key.dbUser" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether Cassandra is used as subchart or not. Default: false +*/}} +{{- define "common.cassandra.values.key.dbUser" -}} + {{- if .subchart -}} + cassandra.dbUser + {{- else -}} + dbUser + {{- end -}} +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/validations/_mariadb.tpl b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/validations/_mariadb.tpl new file mode 100644 index 000000000..b6906ff77 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/validations/_mariadb.tpl @@ -0,0 +1,103 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Validate MariaDB required passwords are not empty. + +Usage: +{{ include "common.validations.values.mariadb.passwords" (dict "secret" "secretName" "subchart" false "context" $) }} +Params: + - secret - String - Required. Name of the secret where MariaDB values are stored, e.g: "mysql-passwords-secret" + - subchart - Boolean - Optional. Whether MariaDB is used as subchart or not. Default: false +*/}} +{{- define "common.validations.values.mariadb.passwords" -}} + {{- $existingSecret := include "common.mariadb.values.auth.existingSecret" . -}} + {{- $enabled := include "common.mariadb.values.enabled" . -}} + {{- $architecture := include "common.mariadb.values.architecture" . -}} + {{- $authPrefix := include "common.mariadb.values.key.auth" . -}} + {{- $valueKeyRootPassword := printf "%s.rootPassword" $authPrefix -}} + {{- $valueKeyUsername := printf "%s.username" $authPrefix -}} + {{- $valueKeyPassword := printf "%s.password" $authPrefix -}} + {{- $valueKeyReplicationPassword := printf "%s.replicationPassword" $authPrefix -}} + + {{- if and (or (not $existingSecret) (eq $existingSecret "\"\"")) (eq $enabled "true") -}} + {{- $requiredPasswords := list -}} + + {{- $requiredRootPassword := dict "valueKey" $valueKeyRootPassword "secret" .secret "field" "mariadb-root-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredRootPassword -}} + + {{- $valueUsername := include "common.utils.getValueFromKey" (dict "key" $valueKeyUsername "context" .context) }} + {{- if not (empty $valueUsername) -}} + {{- $requiredPassword := dict "valueKey" $valueKeyPassword "secret" .secret "field" "mariadb-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredPassword -}} + {{- end -}} + + {{- if (eq $architecture "replication") -}} + {{- $requiredReplicationPassword := dict "valueKey" $valueKeyReplicationPassword "secret" .secret "field" "mariadb-replication-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredReplicationPassword -}} + {{- end -}} + + {{- include "common.validations.values.multiple.empty" (dict "required" $requiredPasswords "context" .context) -}} + + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for existingSecret. + +Usage: +{{ include "common.mariadb.values.auth.existingSecret" (dict "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MariaDB is used as subchart or not. Default: false +*/}} +{{- define "common.mariadb.values.auth.existingSecret" -}} + {{- if .subchart -}} + {{- .context.Values.mariadb.auth.existingSecret | quote -}} + {{- else -}} + {{- .context.Values.auth.existingSecret | quote -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for enabled mariadb. + +Usage: +{{ include "common.mariadb.values.enabled" (dict "context" $) }} +*/}} +{{- define "common.mariadb.values.enabled" -}} + {{- if .subchart -}} + {{- printf "%v" .context.Values.mariadb.enabled -}} + {{- else -}} + {{- printf "%v" (not .context.Values.enabled) -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for architecture + +Usage: +{{ include "common.mariadb.values.architecture" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MariaDB is used as subchart or not. Default: false +*/}} +{{- define "common.mariadb.values.architecture" -}} + {{- if .subchart -}} + {{- .context.Values.mariadb.architecture -}} + {{- else -}} + {{- .context.Values.architecture -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for the key auth + +Usage: +{{ include "common.mariadb.values.key.auth" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MariaDB is used as subchart or not. Default: false +*/}} +{{- define "common.mariadb.values.key.auth" -}} + {{- if .subchart -}} + mariadb.auth + {{- else -}} + auth + {{- end -}} +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/validations/_mongodb.tpl b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/validations/_mongodb.tpl new file mode 100644 index 000000000..f820ec107 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/validations/_mongodb.tpl @@ -0,0 +1,108 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Validate MongoDB® required passwords are not empty. + +Usage: +{{ include "common.validations.values.mongodb.passwords" (dict "secret" "secretName" "subchart" false "context" $) }} +Params: + - secret - String - Required. Name of the secret where MongoDB® values are stored, e.g: "mongodb-passwords-secret" + - subchart - Boolean - Optional. Whether MongoDB® is used as subchart or not. Default: false +*/}} +{{- define "common.validations.values.mongodb.passwords" -}} + {{- $existingSecret := include "common.mongodb.values.auth.existingSecret" . -}} + {{- $enabled := include "common.mongodb.values.enabled" . -}} + {{- $authPrefix := include "common.mongodb.values.key.auth" . -}} + {{- $architecture := include "common.mongodb.values.architecture" . -}} + {{- $valueKeyRootPassword := printf "%s.rootPassword" $authPrefix -}} + {{- $valueKeyUsername := printf "%s.username" $authPrefix -}} + {{- $valueKeyDatabase := printf "%s.database" $authPrefix -}} + {{- $valueKeyPassword := printf "%s.password" $authPrefix -}} + {{- $valueKeyReplicaSetKey := printf "%s.replicaSetKey" $authPrefix -}} + {{- $valueKeyAuthEnabled := printf "%s.enabled" $authPrefix -}} + + {{- $authEnabled := include "common.utils.getValueFromKey" (dict "key" $valueKeyAuthEnabled "context" .context) -}} + + {{- if and (or (not $existingSecret) (eq $existingSecret "\"\"")) (eq $enabled "true") (eq $authEnabled "true") -}} + {{- $requiredPasswords := list -}} + + {{- $requiredRootPassword := dict "valueKey" $valueKeyRootPassword "secret" .secret "field" "mongodb-root-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredRootPassword -}} + + {{- $valueUsername := include "common.utils.getValueFromKey" (dict "key" $valueKeyUsername "context" .context) }} + {{- $valueDatabase := include "common.utils.getValueFromKey" (dict "key" $valueKeyDatabase "context" .context) }} + {{- if and $valueUsername $valueDatabase -}} + {{- $requiredPassword := dict "valueKey" $valueKeyPassword "secret" .secret "field" "mongodb-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredPassword -}} + {{- end -}} + + {{- if (eq $architecture "replicaset") -}} + {{- $requiredReplicaSetKey := dict "valueKey" $valueKeyReplicaSetKey "secret" .secret "field" "mongodb-replica-set-key" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredReplicaSetKey -}} + {{- end -}} + + {{- include "common.validations.values.multiple.empty" (dict "required" $requiredPasswords "context" .context) -}} + + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for existingSecret. + +Usage: +{{ include "common.mongodb.values.auth.existingSecret" (dict "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MongoDb is used as subchart or not. Default: false +*/}} +{{- define "common.mongodb.values.auth.existingSecret" -}} + {{- if .subchart -}} + {{- .context.Values.mongodb.auth.existingSecret | quote -}} + {{- else -}} + {{- .context.Values.auth.existingSecret | quote -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for enabled mongodb. + +Usage: +{{ include "common.mongodb.values.enabled" (dict "context" $) }} +*/}} +{{- define "common.mongodb.values.enabled" -}} + {{- if .subchart -}} + {{- printf "%v" .context.Values.mongodb.enabled -}} + {{- else -}} + {{- printf "%v" (not .context.Values.enabled) -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for the key auth + +Usage: +{{ include "common.mongodb.values.key.auth" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MongoDB® is used as subchart or not. Default: false +*/}} +{{- define "common.mongodb.values.key.auth" -}} + {{- if .subchart -}} + mongodb.auth + {{- else -}} + auth + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for architecture + +Usage: +{{ include "common.mongodb.values.architecture" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MongoDB® is used as subchart or not. Default: false +*/}} +{{- define "common.mongodb.values.architecture" -}} + {{- if .subchart -}} + {{- .context.Values.mongodb.architecture -}} + {{- else -}} + {{- .context.Values.architecture -}} + {{- end -}} +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/validations/_mysql.tpl b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/validations/_mysql.tpl new file mode 100644 index 000000000..74472a061 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/validations/_mysql.tpl @@ -0,0 +1,103 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Validate MySQL required passwords are not empty. + +Usage: +{{ include "common.validations.values.mysql.passwords" (dict "secret" "secretName" "subchart" false "context" $) }} +Params: + - secret - String - Required. Name of the secret where MySQL values are stored, e.g: "mysql-passwords-secret" + - subchart - Boolean - Optional. Whether MySQL is used as subchart or not. Default: false +*/}} +{{- define "common.validations.values.mysql.passwords" -}} + {{- $existingSecret := include "common.mysql.values.auth.existingSecret" . -}} + {{- $enabled := include "common.mysql.values.enabled" . -}} + {{- $architecture := include "common.mysql.values.architecture" . -}} + {{- $authPrefix := include "common.mysql.values.key.auth" . -}} + {{- $valueKeyRootPassword := printf "%s.rootPassword" $authPrefix -}} + {{- $valueKeyUsername := printf "%s.username" $authPrefix -}} + {{- $valueKeyPassword := printf "%s.password" $authPrefix -}} + {{- $valueKeyReplicationPassword := printf "%s.replicationPassword" $authPrefix -}} + + {{- if and (or (not $existingSecret) (eq $existingSecret "\"\"")) (eq $enabled "true") -}} + {{- $requiredPasswords := list -}} + + {{- $requiredRootPassword := dict "valueKey" $valueKeyRootPassword "secret" .secret "field" "mysql-root-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredRootPassword -}} + + {{- $valueUsername := include "common.utils.getValueFromKey" (dict "key" $valueKeyUsername "context" .context) }} + {{- if not (empty $valueUsername) -}} + {{- $requiredPassword := dict "valueKey" $valueKeyPassword "secret" .secret "field" "mysql-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredPassword -}} + {{- end -}} + + {{- if (eq $architecture "replication") -}} + {{- $requiredReplicationPassword := dict "valueKey" $valueKeyReplicationPassword "secret" .secret "field" "mysql-replication-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredReplicationPassword -}} + {{- end -}} + + {{- include "common.validations.values.multiple.empty" (dict "required" $requiredPasswords "context" .context) -}} + + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for existingSecret. + +Usage: +{{ include "common.mysql.values.auth.existingSecret" (dict "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MySQL is used as subchart or not. Default: false +*/}} +{{- define "common.mysql.values.auth.existingSecret" -}} + {{- if .subchart -}} + {{- .context.Values.mysql.auth.existingSecret | quote -}} + {{- else -}} + {{- .context.Values.auth.existingSecret | quote -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for enabled mysql. + +Usage: +{{ include "common.mysql.values.enabled" (dict "context" $) }} +*/}} +{{- define "common.mysql.values.enabled" -}} + {{- if .subchart -}} + {{- printf "%v" .context.Values.mysql.enabled -}} + {{- else -}} + {{- printf "%v" (not .context.Values.enabled) -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for architecture + +Usage: +{{ include "common.mysql.values.architecture" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MySQL is used as subchart or not. Default: false +*/}} +{{- define "common.mysql.values.architecture" -}} + {{- if .subchart -}} + {{- .context.Values.mysql.architecture -}} + {{- else -}} + {{- .context.Values.architecture -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for the key auth + +Usage: +{{ include "common.mysql.values.key.auth" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MySQL is used as subchart or not. Default: false +*/}} +{{- define "common.mysql.values.key.auth" -}} + {{- if .subchart -}} + mysql.auth + {{- else -}} + auth + {{- end -}} +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/validations/_postgresql.tpl b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/validations/_postgresql.tpl new file mode 100644 index 000000000..164ec0d01 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/validations/_postgresql.tpl @@ -0,0 +1,129 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Validate PostgreSQL required passwords are not empty. + +Usage: +{{ include "common.validations.values.postgresql.passwords" (dict "secret" "secretName" "subchart" false "context" $) }} +Params: + - secret - String - Required. Name of the secret where postgresql values are stored, e.g: "postgresql-passwords-secret" + - subchart - Boolean - Optional. Whether postgresql is used as subchart or not. Default: false +*/}} +{{- define "common.validations.values.postgresql.passwords" -}} + {{- $existingSecret := include "common.postgresql.values.existingSecret" . -}} + {{- $enabled := include "common.postgresql.values.enabled" . -}} + {{- $valueKeyPostgresqlPassword := include "common.postgresql.values.key.postgressPassword" . -}} + {{- $valueKeyPostgresqlReplicationEnabled := include "common.postgresql.values.key.replicationPassword" . -}} + {{- if and (or (not $existingSecret) (eq $existingSecret "\"\"")) (eq $enabled "true") -}} + {{- $requiredPasswords := list -}} + {{- $requiredPostgresqlPassword := dict "valueKey" $valueKeyPostgresqlPassword "secret" .secret "field" "postgresql-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredPostgresqlPassword -}} + + {{- $enabledReplication := include "common.postgresql.values.enabled.replication" . -}} + {{- if (eq $enabledReplication "true") -}} + {{- $requiredPostgresqlReplicationPassword := dict "valueKey" $valueKeyPostgresqlReplicationEnabled "secret" .secret "field" "postgresql-replication-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredPostgresqlReplicationPassword -}} + {{- end -}} + + {{- include "common.validations.values.multiple.empty" (dict "required" $requiredPasswords "context" .context) -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to decide whether evaluate global values. + +Usage: +{{ include "common.postgresql.values.use.global" (dict "key" "key-of-global" "context" $) }} +Params: + - key - String - Required. Field to be evaluated within global, e.g: "existingSecret" +*/}} +{{- define "common.postgresql.values.use.global" -}} + {{- if .context.Values.global -}} + {{- if .context.Values.global.postgresql -}} + {{- index .context.Values.global.postgresql .key | quote -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for existingSecret. + +Usage: +{{ include "common.postgresql.values.existingSecret" (dict "context" $) }} +*/}} +{{- define "common.postgresql.values.existingSecret" -}} + {{- $globalValue := include "common.postgresql.values.use.global" (dict "key" "existingSecret" "context" .context) -}} + + {{- if .subchart -}} + {{- default (.context.Values.postgresql.existingSecret | quote) $globalValue -}} + {{- else -}} + {{- default (.context.Values.existingSecret | quote) $globalValue -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for enabled postgresql. + +Usage: +{{ include "common.postgresql.values.enabled" (dict "context" $) }} +*/}} +{{- define "common.postgresql.values.enabled" -}} + {{- if .subchart -}} + {{- printf "%v" .context.Values.postgresql.enabled -}} + {{- else -}} + {{- printf "%v" (not .context.Values.enabled) -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for the key postgressPassword. + +Usage: +{{ include "common.postgresql.values.key.postgressPassword" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether postgresql is used as subchart or not. Default: false +*/}} +{{- define "common.postgresql.values.key.postgressPassword" -}} + {{- $globalValue := include "common.postgresql.values.use.global" (dict "key" "postgresqlUsername" "context" .context) -}} + + {{- if not $globalValue -}} + {{- if .subchart -}} + postgresql.postgresqlPassword + {{- else -}} + postgresqlPassword + {{- end -}} + {{- else -}} + global.postgresql.postgresqlPassword + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for enabled.replication. + +Usage: +{{ include "common.postgresql.values.enabled.replication" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether postgresql is used as subchart or not. Default: false +*/}} +{{- define "common.postgresql.values.enabled.replication" -}} + {{- if .subchart -}} + {{- printf "%v" .context.Values.postgresql.replication.enabled -}} + {{- else -}} + {{- printf "%v" .context.Values.replication.enabled -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for the key replication.password. + +Usage: +{{ include "common.postgresql.values.key.replicationPassword" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether postgresql is used as subchart or not. Default: false +*/}} +{{- define "common.postgresql.values.key.replicationPassword" -}} + {{- if .subchart -}} + postgresql.replication.password + {{- else -}} + replication.password + {{- end -}} +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/validations/_redis.tpl b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/validations/_redis.tpl new file mode 100644 index 000000000..dcccfc1ae --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/validations/_redis.tpl @@ -0,0 +1,76 @@ + +{{/* vim: set filetype=mustache: */}} +{{/* +Validate Redis® required passwords are not empty. + +Usage: +{{ include "common.validations.values.redis.passwords" (dict "secret" "secretName" "subchart" false "context" $) }} +Params: + - secret - String - Required. Name of the secret where redis values are stored, e.g: "redis-passwords-secret" + - subchart - Boolean - Optional. Whether redis is used as subchart or not. Default: false +*/}} +{{- define "common.validations.values.redis.passwords" -}} + {{- $enabled := include "common.redis.values.enabled" . -}} + {{- $valueKeyPrefix := include "common.redis.values.keys.prefix" . -}} + {{- $standarizedVersion := include "common.redis.values.standarized.version" . }} + + {{- $existingSecret := ternary (printf "%s%s" $valueKeyPrefix "auth.existingSecret") (printf "%s%s" $valueKeyPrefix "existingSecret") (eq $standarizedVersion "true") }} + {{- $existingSecretValue := include "common.utils.getValueFromKey" (dict "key" $existingSecret "context" .context) }} + + {{- $valueKeyRedisPassword := ternary (printf "%s%s" $valueKeyPrefix "auth.password") (printf "%s%s" $valueKeyPrefix "password") (eq $standarizedVersion "true") }} + {{- $valueKeyRedisUseAuth := ternary (printf "%s%s" $valueKeyPrefix "auth.enabled") (printf "%s%s" $valueKeyPrefix "usePassword") (eq $standarizedVersion "true") }} + + {{- if and (or (not $existingSecret) (eq $existingSecret "\"\"")) (eq $enabled "true") -}} + {{- $requiredPasswords := list -}} + + {{- $useAuth := include "common.utils.getValueFromKey" (dict "key" $valueKeyRedisUseAuth "context" .context) -}} + {{- if eq $useAuth "true" -}} + {{- $requiredRedisPassword := dict "valueKey" $valueKeyRedisPassword "secret" .secret "field" "redis-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredRedisPassword -}} + {{- end -}} + + {{- include "common.validations.values.multiple.empty" (dict "required" $requiredPasswords "context" .context) -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for enabled redis. + +Usage: +{{ include "common.redis.values.enabled" (dict "context" $) }} +*/}} +{{- define "common.redis.values.enabled" -}} + {{- if .subchart -}} + {{- printf "%v" .context.Values.redis.enabled -}} + {{- else -}} + {{- printf "%v" (not .context.Values.enabled) -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right prefix path for the values + +Usage: +{{ include "common.redis.values.key.prefix" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether redis is used as subchart or not. Default: false +*/}} +{{- define "common.redis.values.keys.prefix" -}} + {{- if .subchart -}}redis.{{- else -}}{{- end -}} +{{- end -}} + +{{/* +Checks whether the redis chart's includes the standarizations (version >= 14) + +Usage: +{{ include "common.redis.values.standarized.version" (dict "context" $) }} +*/}} +{{- define "common.redis.values.standarized.version" -}} + + {{- $standarizedAuth := printf "%s%s" (include "common.redis.values.keys.prefix" .) "auth" -}} + {{- $standarizedAuthValues := include "common.utils.getValueFromKey" (dict "key" $standarizedAuth "context" .context) }} + + {{- if $standarizedAuthValues -}} + {{- true -}} + {{- end -}} +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/validations/_validations.tpl b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/validations/_validations.tpl new file mode 100644 index 000000000..9a814cf40 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/templates/validations/_validations.tpl @@ -0,0 +1,46 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Validate values must not be empty. + +Usage: +{{- $validateValueConf00 := (dict "valueKey" "path.to.value" "secret" "secretName" "field" "password-00") -}} +{{- $validateValueConf01 := (dict "valueKey" "path.to.value" "secret" "secretName" "field" "password-01") -}} +{{ include "common.validations.values.empty" (dict "required" (list $validateValueConf00 $validateValueConf01) "context" $) }} + +Validate value params: + - valueKey - String - Required. The path to the validating value in the values.yaml, e.g: "mysql.password" + - secret - String - Optional. Name of the secret where the validating value is generated/stored, e.g: "mysql-passwords-secret" + - field - String - Optional. Name of the field in the secret data, e.g: "mysql-password" +*/}} +{{- define "common.validations.values.multiple.empty" -}} + {{- range .required -}} + {{- include "common.validations.values.single.empty" (dict "valueKey" .valueKey "secret" .secret "field" .field "context" $.context) -}} + {{- end -}} +{{- end -}} + +{{/* +Validate a value must not be empty. + +Usage: +{{ include "common.validations.value.empty" (dict "valueKey" "mariadb.password" "secret" "secretName" "field" "my-password" "subchart" "subchart" "context" $) }} + +Validate value params: + - valueKey - String - Required. The path to the validating value in the values.yaml, e.g: "mysql.password" + - secret - String - Optional. Name of the secret where the validating value is generated/stored, e.g: "mysql-passwords-secret" + - field - String - Optional. Name of the field in the secret data, e.g: "mysql-password" + - subchart - String - Optional - Name of the subchart that the validated password is part of. +*/}} +{{- define "common.validations.values.single.empty" -}} + {{- $value := include "common.utils.getValueFromKey" (dict "key" .valueKey "context" .context) }} + {{- $subchart := ternary "" (printf "%s." .subchart) (empty .subchart) }} + + {{- if not $value -}} + {{- $varname := "my-value" -}} + {{- $getCurrentValue := "" -}} + {{- if and .secret .field -}} + {{- $varname = include "common.utils.fieldToEnvVar" . -}} + {{- $getCurrentValue = printf " To get the current value:\n\n %s\n" (include "common.utils.secret.getvalue" .) -}} + {{- end -}} + {{- printf "\n '%s' must not be empty, please add '--set %s%s=$%s' to the command.%s" .valueKey $subchart .valueKey $varname $getCurrentValue -}} + {{- end -}} +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/values.yaml b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/values.yaml new file mode 100644 index 000000000..f2df68e5e --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/charts/common/values.yaml @@ -0,0 +1,5 @@ +## bitnami/common +## It is required by CI/CD tools and processes. +## @skip exampleValue +## +exampleValue: common-chart diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/NOTES.txt b/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/NOTES.txt new file mode 100644 index 000000000..c287e1e56 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/NOTES.txt @@ -0,0 +1,76 @@ +CHART NAME: {{ .Chart.Name }} +CHART VERSION: {{ .Chart.Version }} +APP VERSION: {{ .Chart.AppVersion }} + +{{- if and (not .Values.auth.client.enabled) (eq .Values.service.type "LoadBalancer") }} +------------------------------------------------------------------------------- + WARNING + + By specifying "serviceType=LoadBalancer" and not specifying "auth.enabled=true" + you have most likely exposed the ZooKeeper service externally without any + authentication mechanism. + + For security reasons, we strongly suggest that you switch to "ClusterIP" or + "NodePort". As alternative, you can also specify a valid password on the + "auth.clientPassword" parameter. + +------------------------------------------------------------------------------- +{{- end }} + +** Please be patient while the chart is being deployed ** + +{{- if .Values.diagnosticMode.enabled }} +The chart has been deployed in diagnostic mode. All probes have been disabled and the command has been overwritten with: + + command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 4 }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 4 }} + +Get the list of pods by executing: + + kubectl get pods --namespace {{ .Release.Namespace }} -l app.kubernetes.io/instance={{ .Release.Name }} + +Access the pod you want to debug by executing + + kubectl exec --namespace {{ .Release.Namespace }} -ti -- bash + +In order to replicate the container startup scripts execute this command: + + /opt/bitnami/scripts/zookeeper/entrypoint.sh /opt/bitnami/scripts/zookeeper/run.sh + +{{- else }} + +ZooKeeper can be accessed via port {{ .Values.service.ports.client }} on the following DNS name from within your cluster: + + {{ template "common.names.fullname" . }}.{{ template "zookeeper.namespace" . }}.svc.{{ .Values.clusterDomain }} + +To connect to your ZooKeeper server run the following commands: + + export POD_NAME=$(kubectl get pods --namespace {{ template "zookeeper.namespace" . }} -l "app.kubernetes.io/name={{ template "common.names.name" . }},app.kubernetes.io/instance={{ .Release.Name }},app.kubernetes.io/component=zookeeper" -o jsonpath="{.items[0].metadata.name}") + kubectl exec -it $POD_NAME -- zkCli.sh + +To connect to your ZooKeeper server from outside the cluster execute the following commands: + +{{- if eq .Values.service.type "NodePort" }} + + export NODE_IP=$(kubectl get nodes --namespace {{ template "zookeeper.namespace" . }} -o jsonpath="{.items[0].status.addresses[0].address}") + export NODE_PORT=$(kubectl get --namespace {{ template "zookeeper.namespace" . }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "common.names.fullname" . }}) + zkCli.sh $NODE_IP:$NODE_PORT + +{{- else if eq .Values.service.type "LoadBalancer" }} + + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + Watch the status with: 'kubectl get svc --namespace {{ template "zookeeper.namespace" . }} -w {{ template "common.names.fullname" . }}' + + export SERVICE_IP=$(kubectl get svc --namespace {{ template "zookeeper.namespace" . }} {{ template "common.names.fullname" . }} --template "{{ "{{ range (index .status.loadBalancer.ingress 0) }}{{ . }}{{ end }}" }}") + zkCli.sh $SERVICE_IP:{{ .Values.service.ports.client }} + +{{- else if eq .Values.service.type "ClusterIP" }} + + kubectl port-forward --namespace {{ template "zookeeper.namespace" . }} svc/{{ template "common.names.fullname" . }} {{ .Values.service.ports.client }}:{{ .Values.containerPorts.client }} & + zkCli.sh 127.0.0.1:{{ .Values.service.ports.client }} + +{{- end }} +{{- end }} + +{{- include "zookeeper.validateValues" . }} +{{- include "zookeeper.checkRollingTags" . }} diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/_helpers.tpl b/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/_helpers.tpl new file mode 100644 index 000000000..d855bada0 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/_helpers.tpl @@ -0,0 +1,361 @@ +{{/* vim: set filetype=mustache: */}} + +{{/* +Return the proper ZooKeeper image name +*/}} +{{- define "zookeeper.image" -}} +{{ include "common.images.image" (dict "imageRoot" .Values.image "global" .Values.global) }} +{{- end -}} + +{{/* +Return the proper image name (for the init container volume-permissions image) +*/}} +{{- define "zookeeper.volumePermissions.image" -}} +{{ include "common.images.image" (dict "imageRoot" .Values.volumePermissions.image "global" .Values.global) }} +{{- end -}} + +{{/* +Return the proper Docker Image Registry Secret Names +*/}} +{{- define "zookeeper.imagePullSecrets" -}} +{{- include "common.images.pullSecrets" (dict "images" (list .Values.image .Values.volumePermissions.image) "global" .Values.global) -}} +{{- end -}} + +{{/* +Check if there are rolling tags in the images +*/}} +{{- define "zookeeper.checkRollingTags" -}} +{{- include "common.warnings.rollingTag" .Values.image }} +{{- include "common.warnings.rollingTag" .Values.volumePermissions.image }} +{{- end -}} + +{{/* +Return ZooKeeper Namespace to use +*/}} +{{- define "zookeeper.namespace" -}} +{{- if .Values.namespaceOverride -}} + {{- .Values.namespaceOverride -}} +{{- else -}} + {{- .Release.Namespace -}} +{{- end -}} +{{- end -}} + +{{/* + Create the name of the service account to use + */}} +{{- define "zookeeper.serviceAccountName" -}} +{{- if .Values.serviceAccount.create -}} + {{ default (include "common.names.fullname" .) .Values.serviceAccount.name }} +{{- else -}} + {{ default "default" .Values.serviceAccount.name }} +{{- end -}} +{{- end -}} + +{{/* +Return the ZooKeeper client-server authentication credentials secret +*/}} +{{- define "zookeeper.client.secretName" -}} +{{- if .Values.auth.client.existingSecret -}} + {{- printf "%s" (tpl .Values.auth.client.existingSecret $) -}} +{{- else -}} + {{- printf "%s-client-auth" (include "common.names.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{/* +Return the ZooKeeper server-server authentication credentials secret +*/}} +{{- define "zookeeper.quorum.secretName" -}} +{{- if .Values.auth.quorum.existingSecret -}} + {{- printf "%s" (tpl .Values.auth.quorum.existingSecret $) -}} +{{- else -}} + {{- printf "%s-quorum-auth" (include "common.names.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{/* +Return true if a ZooKeeper client-server authentication credentials secret object should be created +*/}} +{{- define "zookeeper.client.createSecret" -}} +{{- if and .Values.auth.client.enabled (empty .Values.auth.client.existingSecret) -}} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Return true if a ZooKeeper server-server authentication credentials secret object should be created +*/}} +{{- define "zookeeper.quorum.createSecret" -}} +{{- if and .Values.auth.quorum.enabled (empty .Values.auth.quorum.existingSecret) -}} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Returns the available value for certain key in an existing secret (if it exists), +otherwise it generates a random value. +*/}} +{{- define "getValueFromSecret" }} + {{- $len := (default 16 .Length) | int -}} + {{- $obj := (lookup "v1" "Secret" .Namespace .Name).data -}} + {{- if $obj }} + {{- index $obj .Key | b64dec -}} + {{- else -}} + {{- randAlphaNum $len -}} + {{- end -}} +{{- end }} + +{{/* +Return the ZooKeeper configuration ConfigMap name +*/}} +{{- define "zookeeper.configmapName" -}} +{{- if .Values.existingConfigmap -}} + {{- printf "%s" (tpl .Values.existingConfigmap $) -}} +{{- else -}} + {{- printf "%s" (include "common.names.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{/* +Return true if a ConfigMap object should be created for ZooKeeper configuration +*/}} +{{- define "zookeeper.createConfigmap" -}} +{{- if and .Values.configuration (not .Values.existingConfigmap) }} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Return true if a TLS secret should be created for ZooKeeper quorum +*/}} +{{- define "zookeeper.quorum.createTlsSecret" -}} +{{- if and .Values.tls.quorum.enabled .Values.tls.quorum.autoGenerated (not .Values.tls.quorum.existingSecret) }} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Return the secret containing ZooKeeper quorum TLS certificates +*/}} +{{- define "zookeeper.quorum.tlsSecretName" -}} +{{- $secretName := .Values.tls.quorum.existingSecret -}} +{{- if $secretName -}} + {{- printf "%s" (tpl $secretName $) -}} +{{- else -}} + {{- printf "%s-quorum-crt" (include "common.names.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{/* +Return true if a secret containing the Keystore and Truststore password should be created for ZooKeeper quorum +*/}} +{{- define "zookeeper.quorum.createTlsPasswordsSecret" -}} +{{- if and .Values.tls.quorum.enabled (not .Values.tls.quorum.passwordsSecretName) }} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Return the name of the secret containing the Keystore and Truststore password +*/}} +{{- define "zookeeper.quorum.tlsPasswordsSecret" -}} +{{- $secretName := .Values.tls.quorum.passwordsSecretName -}} +{{- if $secretName -}} + {{- printf "%s" (tpl $secretName $) -}} +{{- else -}} + {{- printf "%s-quorum-tls-pass" (include "common.names.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{/* +Return true if a TLS secret should be created for ZooKeeper client +*/}} +{{- define "zookeeper.client.createTlsSecret" -}} +{{- if and .Values.tls.client.enabled .Values.tls.client.autoGenerated (not .Values.tls.client.existingSecret) }} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Return the secret containing ZooKeeper client TLS certificates +*/}} +{{- define "zookeeper.client.tlsSecretName" -}} +{{- $secretName := .Values.tls.client.existingSecret -}} +{{- if $secretName -}} + {{- printf "%s" (tpl $secretName $) -}} +{{- else -}} + {{- printf "%s-client-crt" (include "common.names.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{/* +Get the quorum keystore key to be retrieved from tls.quorum.existingSecret. +*/}} +{{- define "zookeeper.quorum.tlsKeystoreKey" -}} +{{- if and .Values.tls.quorum.existingSecret .Values.tls.quorum.existingSecretKeystoreKey -}} + {{- printf "%s" .Values.tls.quorum.existingSecretKeystoreKey -}} +{{- else -}} + {{- printf "zookeeper.keystore.jks" -}} +{{- end -}} +{{- end -}} + +{{/* +Get the quorum truststore key to be retrieved from tls.quorum.existingSecret. +*/}} +{{- define "zookeeper.quorum.tlsTruststoreKey" -}} +{{- if and .Values.tls.quorum.existingSecret .Values.tls.quorum.existingSecretTruststoreKey -}} + {{- printf "%s" .Values.tls.quorum.existingSecretTruststoreKey -}} +{{- else -}} + {{- printf "zookeeper.truststore.jks" -}} +{{- end -}} +{{- end -}} + +{{/* +Get the client keystore key to be retrieved from tls.client.existingSecret. +*/}} +{{- define "zookeeper.client.tlsKeystoreKey" -}} +{{- if and .Values.tls.client.existingSecret .Values.tls.client.existingSecretKeystoreKey -}} + {{- printf "%s" .Values.tls.client.existingSecretKeystoreKey -}} +{{- else -}} + {{- printf "zookeeper.keystore.jks" -}} +{{- end -}} +{{- end -}} + +{{/* +Get the client truststore key to be retrieved from tls.client.existingSecret. +*/}} +{{- define "zookeeper.client.tlsTruststoreKey" -}} +{{- if and .Values.tls.client.existingSecret .Values.tls.client.existingSecretTruststoreKey -}} + {{- printf "%s" .Values.tls.client.existingSecretTruststoreKey -}} +{{- else -}} + {{- printf "zookeeper.truststore.jks" -}} +{{- end -}} +{{- end -}} + +{{/* +Return true if a secret containing the Keystore and Truststore password should be created for ZooKeeper client +*/}} +{{- define "zookeeper.client.createTlsPasswordsSecret" -}} +{{- if and .Values.tls.client.enabled (not .Values.tls.client.passwordsSecretName) }} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Return the name of the secret containing the Keystore and Truststore password +*/}} +{{- define "zookeeper.client.tlsPasswordsSecret" -}} +{{- $secretName := .Values.tls.client.passwordsSecretName -}} +{{- if $secretName -}} + {{- printf "%s" (tpl $secretName $) -}} +{{- else -}} + {{- printf "%s-client-tls-pass" (include "common.names.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{/* +Get the quorum keystore password key to be retrieved from tls.quorum.passwordSecretName. +*/}} +{{- define "zookeeper.quorum.tlsPasswordKeystoreKey" -}} +{{- if and .Values.tls.quorum.passwordsSecretName .Values.tls.quorum.passwordsSecretKeystoreKey -}} + {{- printf "%s" .Values.tls.quorum.passwordsSecretKeystoreKey -}} +{{- else -}} + {{- printf "keystore-password" -}} +{{- end -}} +{{- end -}} + +{{/* +Get the quorum truststore password key to be retrieved from tls.quorum.passwordSecretName. +*/}} +{{- define "zookeeper.quorum.tlsPasswordTruststoreKey" -}} +{{- if and .Values.tls.quorum.passwordsSecretName .Values.tls.quorum.passwordsSecretTruststoreKey -}} + {{- printf "%s" .Values.tls.quorum.passwordsSecretTruststoreKey -}} +{{- else -}} + {{- printf "truststore-password" -}} +{{- end -}} +{{- end -}} + +{{/* +Get the client keystore password key to be retrieved from tls.client.passwordSecretName. +*/}} +{{- define "zookeeper.client.tlsPasswordKeystoreKey" -}} +{{- if and .Values.tls.client.passwordsSecretName .Values.tls.client.passwordsSecretKeystoreKey -}} + {{- printf "%s" .Values.tls.client.passwordsSecretKeystoreKey -}} +{{- else -}} + {{- printf "keystore-password" -}} +{{- end -}} +{{- end -}} + +{{/* +Get the client truststore password key to be retrieved from tls.client.passwordSecretName. +*/}} +{{- define "zookeeper.client.tlsPasswordTruststoreKey" -}} +{{- if and .Values.tls.client.passwordsSecretName .Values.tls.client.passwordsSecretTruststoreKey -}} + {{- printf "%s" .Values.tls.client.passwordsSecretTruststoreKey -}} +{{- else -}} + {{- printf "truststore-password" -}} +{{- end -}} +{{- end -}} + +{{/* +Compile all warnings into a single message. +*/}} +{{- define "zookeeper.validateValues" -}} +{{- $messages := list -}} +{{- $messages := append $messages (include "zookeeper.validateValues.client.auth" .) -}} +{{- $messages := append $messages (include "zookeeper.validateValues.quorum.auth" .) -}} +{{- $messages := append $messages (include "zookeeper.validateValues.client.tls" .) -}} +{{- $messages := append $messages (include "zookeeper.validateValues.quorum.tls" .) -}} +{{- $messages := without $messages "" -}} +{{- $message := join "\n" $messages -}} + +{{- if $message -}} +{{- printf "\nVALUES VALIDATION:\n%s" $message | fail -}} +{{- end -}} +{{- end -}} + +{{/* +Validate values of ZooKeeper - Authentication enabled +*/}} +{{- define "zookeeper.validateValues.client.auth" -}} +{{- if and .Values.auth.client.enabled (not .Values.auth.client.existingSecret) (or (not .Values.auth.client.clientUser) (not .Values.auth.client.serverUsers)) }} +zookeeper: auth.client.enabled + In order to enable client-server authentication, you need to provide the list + of users to be created and the user to use for clients authentication. +{{- end -}} +{{- end -}} + +{{/* +Validate values of ZooKeeper - Authentication enabled +*/}} +{{- define "zookeeper.validateValues.quorum.auth" -}} +{{- if and .Values.auth.quorum.enabled (not .Values.auth.quorum.existingSecret) (or (not .Values.auth.quorum.learnerUser) (not .Values.auth.quorum.serverUsers)) }} +zookeeper: auth.quorum.enabled + In order to enable server-server authentication, you need to provide the list + of users to be created and the user to use for quorum authentication. +{{- end -}} +{{- end -}} + +{{/* +Validate values of ZooKeeper - Client TLS enabled +*/}} +{{- define "zookeeper.validateValues.client.tls" -}} +{{- if and .Values.tls.client.enabled (not .Values.tls.client.autoGenerated) (not .Values.tls.client.existingSecret) }} +zookeeper: tls.client.enabled + In order to enable Client TLS encryption, you also need to provide + an existing secret containing the Keystore and Truststore or + enable auto-generated certificates. +{{- end -}} +{{- end -}} + +{{/* +Validate values of ZooKeeper - Quorum TLS enabled +*/}} +{{- define "zookeeper.validateValues.quorum.tls" -}} +{{- if and .Values.tls.quorum.enabled (not .Values.tls.quorum.autoGenerated) (not .Values.tls.quorum.existingSecret) }} +zookeeper: tls.quorum.enabled + In order to enable Quorum TLS, you also need to provide + an existing secret containing the Keystore and Truststore or + enable auto-generated certificates. +{{- end -}} +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/configmap.yaml b/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/configmap.yaml new file mode 100644 index 000000000..12b4f489f --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/configmap.yaml @@ -0,0 +1,17 @@ +{{- if (include "zookeeper.createConfigmap" .) }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "common.names.fullname" . }} + namespace: {{ template "zookeeper.namespace" . }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +data: + zoo.cfg: |- + {{- include "common.tplvalues.render" ( dict "value" .Values.configuration "context" $ ) | nindent 4 }} +{{- end }} diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/extra-list.yaml b/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/extra-list.yaml new file mode 100644 index 000000000..9ac65f9e1 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/extra-list.yaml @@ -0,0 +1,4 @@ +{{- range .Values.extraDeploy }} +--- +{{ include "common.tplvalues.render" (dict "value" . "context" $) }} +{{- end }} diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/metrics-svc.yaml b/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/metrics-svc.yaml new file mode 100644 index 000000000..5afc4b3e5 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/metrics-svc.yaml @@ -0,0 +1,29 @@ +{{- if .Values.metrics.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ template "common.names.fullname" . }}-metrics + namespace: {{ template "zookeeper.namespace" . }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: metrics + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if or .Values.metrics.service.annotations .Values.commonAnnotations }} + annotations: + {{- if .Values.metrics.service.annotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.service.annotations "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- end }} +spec: + type: {{ .Values.metrics.service.type }} + ports: + - name: tcp-metrics + port: {{ .Values.metrics.service.port }} + targetPort: metrics + selector: {{- include "common.labels.matchLabels" . | nindent 4 }} + app.kubernetes.io/component: zookeeper +{{- end }} diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/networkpolicy.yaml b/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/networkpolicy.yaml new file mode 100644 index 000000000..63532832c --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/networkpolicy.yaml @@ -0,0 +1,41 @@ +{{- if .Values.networkPolicy.enabled }} +kind: NetworkPolicy +apiVersion: {{ include "common.capabilities.networkPolicy.apiVersion" . }} +metadata: + name: {{ include "common.names.fullname" . }} + namespace: {{ template "zookeeper.namespace" . }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + podSelector: + matchLabels: {{- include "common.labels.matchLabels" . | nindent 6 }} + policyTypes: + - Ingress + ingress: + # Allow inbound connections to ZooKeeper + - ports: + - port: {{ .Values.containerPorts.client }} + {{- if .Values.metrics.enabled }} + - port: {{ .Values.metrics.containerPort }} + {{- end }} + {{- if not .Values.networkPolicy.allowExternal }} + from: + - podSelector: + matchLabels: + {{ include "common.names.fullname" . }}-client: "true" + - podSelector: + matchLabels: {{- include "common.labels.matchLabels" . | nindent 14 }} + {{- end }} + # Allow internal communications between nodes + - ports: + - port: {{ .Values.containerPorts.follower }} + - port: {{ .Values.containerPorts.election }} + from: + - podSelector: + matchLabels: {{- include "common.labels.matchLabels" . | nindent 14 }} +{{- end }} diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/pdb.yaml b/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/pdb.yaml new file mode 100644 index 000000000..f7faf65f9 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/pdb.yaml @@ -0,0 +1,26 @@ +{{- $replicaCount := int .Values.replicaCount }} +{{- if and .Values.pdb.create (gt $replicaCount 1) }} +apiVersion: {{ include "common.capabilities.policy.apiVersion" . }} +kind: PodDisruptionBudget +metadata: + name: {{ template "common.names.fullname" . }} + namespace: {{ template "zookeeper.namespace" . }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: zookeeper + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + {{- if .Values.pdb.minAvailable }} + minAvailable: {{ .Values.pdb.minAvailable }} + {{- end }} + {{- if .Values.pdb.maxUnavailable }} + maxUnavailable: {{ .Values.pdb.maxUnavailable }} + {{- end }} + selector: + matchLabels: {{- include "common.labels.matchLabels" . | nindent 6 }} + app.kubernetes.io/component: zookeeper +{{- end }} diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/prometheusrule.yaml b/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/prometheusrule.yaml new file mode 100644 index 000000000..87dcd3565 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/prometheusrule.yaml @@ -0,0 +1,27 @@ +{{- if and .Values.metrics.enabled .Values.metrics.prometheusRule.enabled .Values.metrics.prometheusRule.rules }} +apiVersion: monitoring.coreos.com/v1 +kind: PrometheusRule +metadata: + name: {{ include "common.names.fullname" . }} + {{- if .Values.metrics.prometheusRule.namespace }} + namespace: {{ .Values.metrics.prometheusRule.namespace }} + {{- else }} + namespace: {{ .Release.Namespace }} + {{- end }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: metrics + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.metrics.prometheusRule.additionalLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.prometheusRule.additionalLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + groups: + - name: {{ include "common.names.fullname" . }} + rules: {{- toYaml .Values.metrics.prometheusRule.rules | nindent 8 }} +{{- end }} + diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/scripts-configmap.yaml b/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/scripts-configmap.yaml new file mode 100644 index 000000000..d0a7ddb49 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/scripts-configmap.yaml @@ -0,0 +1,102 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ printf "%s-scripts" (include "common.names.fullname" .) }} + namespace: {{ template "zookeeper.namespace" . }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: zookeeper + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +data: + init-certs.sh: |- + #!/bin/bash + + {{- if .Values.tls.client.enabled }} + if [[ -f "/certs/client/tls.key" ]] && [[ -f "/certs/client/tls.crt" ]] && [[ -f "/certs/client/ca.crt" ]]; then + if [[ -f "/opt/bitnami/zookeeper/config/certs/client/.initialized" ]]; then + exit 0 + fi + openssl pkcs12 -export -in "/certs/client/tls.crt" \ + -passout pass:"${ZOO_TLS_CLIENT_KEYSTORE_PASSWORD}" \ + -inkey "/certs/client/tls.key" \ + -out "/tmp/keystore.p12" + keytool -importkeystore -srckeystore "/tmp/keystore.p12" \ + -srcstoretype PKCS12 \ + -srcstorepass "${ZOO_TLS_CLIENT_KEYSTORE_PASSWORD}" \ + -deststorepass "${ZOO_TLS_CLIENT_KEYSTORE_PASSWORD}" \ + -destkeystore "/opt/bitnami/zookeeper/config/certs/client/zookeeper.keystore.jks" + rm "/tmp/keystore.p12" + keytool -import -file "/certs/client/ca.crt" \ + -keystore "/opt/bitnami/zookeeper/config/certs/client/zookeeper.truststore.jks" \ + -storepass "${ZOO_TLS_CLIENT_TRUSTSTORE_PASSWORD}" \ + -noprompt + touch /opt/bitnami/zookeeper/config/certs/client/.initialized + {{- if .Values.tls.client.autoGenerated }} + else + echo "Couldn't find the expected PEM certificates! They are mandatory when Client encryption via TLS is enabled." + exit 1 + fi + {{- else }} + elif [[ -f {{ printf "/certs/client/%s" (include "zookeeper.client.tlsTruststoreKey" .) | quote }} ]] && [[ -f {{ printf "/certs/client/%s" (include "zookeeper.client.tlsKeystoreKey" .) | quote }} ]]; then + cp {{ printf "/certs/client/%s" (include "zookeeper.client.tlsTruststoreKey" .) | quote }} "/opt/bitnami/zookeeper/config/certs/client/zookeeper.truststore.jks" + cp {{ printf "/certs/client/%s" (include "zookeeper.client.tlsKeystoreKey" .) | quote }} "/opt/bitnami/zookeeper/config/certs/client/zookeeper.keystore.jks" + else + echo "Couldn't find the expected Java Key Stores (JKS) files! They are mandatory when Client encryption via TLS is enabled." + exit 1 + fi + {{- end }} + {{- end }} + {{- if .Values.tls.quorum.enabled }} + if [[ -f "/certs/quorum/tls.key" ]] && [[ -f "/certs/quorum/tls.crt" ]] && [[ -f "/certs/quorum/ca.crt" ]]; then + openssl pkcs12 -export -in "/certs/quorum/tls.crt" \ + -passout pass:"${ZOO_TLS_QUORUM_KEYSTORE_PASSWORD}" \ + -inkey "/certs/quorum/tls.key" \ + -out "/tmp/keystore.p12" + keytool -importkeystore -srckeystore "/tmp/keystore.p12" \ + -srcstoretype PKCS12 \ + -srcstorepass "${ZOO_TLS_QUORUM_KEYSTORE_PASSWORD}" \ + -deststorepass "${ZOO_TLS_QUORUM_KEYSTORE_PASSWORD}" \ + -destkeystore "/opt/bitnami/zookeeper/config/certs/quorum/zookeeper.keystore.jks" + rm "/tmp/keystore.p12" + keytool -import -file "/certs/quorum/ca.crt" \ + -keystore "/opt/bitnami/zookeeper/config/certs/quorum/zookeeper.truststore.jks" \ + -storepass "${ZOO_TLS_QUORUM_TRUSTSTORE_PASSWORD}" \ + -noprompt + {{- if .Values.tls.quorum.autoGenerated }} + else + echo "Couldn't find the expected PEM certificates! They are mandatory when encryption Quorum via TLS is enabled." + exit 1 + fi + {{- else }} + elif [[ -f {{ printf "/certs/quorum/%s" (include "zookeeper.quorum.tlsTruststoreKey" .) | quote }} ]] && [[ -f {{ printf "/certs/quorum/%s" (include "zookeeper.quorum.tlsKeystoreKey" .) | quote }} ]]; then + cp {{ printf "/certs/quorum/%s" (include "zookeeper.quorum.tlsTruststoreKey" .) | quote }} "/opt/bitnami/zookeeper/config/certs/quorum/zookeeper.truststore.jks" + cp {{ printf "/certs/quorum/%s" (include "zookeeper.quorum.tlsKeystoreKey" .) | quote }} "/opt/bitnami/zookeeper/config/certs/quorum/zookeeper.keystore.jks" + else + echo "Couldn't find the expected Java Key Stores (JKS) files! They are mandatory when Quorum encryption via TLS is enabled." + exit 1 + fi + {{- end }} + {{- end }} + setup.sh: |- + #!/bin/bash + + # Execute entrypoint as usual after obtaining ZOO_SERVER_ID + # check ZOO_SERVER_ID in persistent volume via myid + # if not present, set based on POD hostname + if [[ -f "/bitnami/zookeeper/data/myid" ]]; then + export ZOO_SERVER_ID="$(cat /bitnami/zookeeper/data/myid)" + else + HOSTNAME="$(hostname -s)" + if [[ $HOSTNAME =~ (.*)-([0-9]+)$ ]]; then + ORD=${BASH_REMATCH[2]} + export ZOO_SERVER_ID="$((ORD + {{ .Values.minServerId }} ))" + else + echo "Failed to get index from hostname $HOST" + exit 1 + fi + fi + exec /entrypoint.sh /run.sh diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/secrets.yaml b/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/secrets.yaml new file mode 100644 index 000000000..82ebc2eed --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/secrets.yaml @@ -0,0 +1,77 @@ +{{- if (include "zookeeper.client.createSecret" .) }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ printf "%s-client-auth" (include "common.names.fullname" .) }} + namespace: {{ template "zookeeper.namespace" . }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: zookeeper + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +type: Opaque +data: + client-password: {{ include "common.secrets.passwords.manage" (dict "secret" (printf "%s-client-auth" (include "common.names.fullname" .)) "key" "client-password" "providedValues" (list "auth.client.clientPassword") "context" $) }} + server-password: {{ include "common.secrets.passwords.manage" (dict "secret" (printf "%s-client-auth" (include "common.names.fullname" .)) "key" "server-password" "providedValues" (list "auth.client.serverPasswords") "context" $) }} +{{- end }} +{{- if (include "zookeeper.quorum.createSecret" .) }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ printf "%s-quorum-auth" (include "common.names.fullname" .) }} + namespace: {{ template "zookeeper.namespace" . }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: zookeeper + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +type: Opaque +data: + quorum-learner-password: {{ include "common.secrets.passwords.manage" (dict "secret" (printf "%s-quorum-auth" (include "common.names.fullname" .)) "key" "quorum-learner-password" "providedValues" (list "auth.quorum.learnerPassword") "context" $) }} + quorum-server-password: {{ include "common.secrets.passwords.manage" (dict "secret" (printf "%s-quorum-auth" (include "common.names.fullname" .)) "key" "quorum-server-password" "providedValues" (list "auth.quorum.serverPasswords") "context" $) }} +{{- end }} +{{- if (include "zookeeper.client.createTlsPasswordsSecret" .) }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "common.names.fullname" . }}-client-tls-pass + namespace: {{ template "zookeeper.namespace" . }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +type: Opaque +data: + keystore-password: {{ default (randAlphaNum 10) .Values.tls.client.keystorePassword | b64enc | quote }} + truststore-password: {{ default (randAlphaNum 10) .Values.tls.client.truststorePassword | b64enc | quote }} +{{- end }} +{{- if (include "zookeeper.quorum.createTlsPasswordsSecret" .) }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "common.names.fullname" . }}-quorum-tls-pass + namespace: {{ template "zookeeper.namespace" . }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +type: Opaque +data: + keystore-password: {{ default (randAlphaNum 10) .Values.tls.quorum.keystorePassword | b64enc | quote }} + truststore-password: {{ default (randAlphaNum 10) .Values.tls.quorum.truststorePassword | b64enc | quote }} +{{- end }} diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/serviceaccount.yaml b/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/serviceaccount.yaml new file mode 100644 index 000000000..958a57ac2 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/serviceaccount.yaml @@ -0,0 +1,21 @@ +{{- if .Values.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "zookeeper.serviceAccountName" . }} + namespace: {{ template "zookeeper.namespace" . }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: zookeeper + role: zookeeper + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + annotations: + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.serviceAccount.annotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.serviceAccount.annotations "context" $ ) | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.serviceAccount.automountServiceAccountToken }} +{{- end }} diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/servicemonitor.yaml b/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/servicemonitor.yaml new file mode 100644 index 000000000..2c8af3350 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/servicemonitor.yaml @@ -0,0 +1,53 @@ +{{- if and .Values.metrics.enabled .Values.metrics.serviceMonitor.enabled }} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ template "common.names.fullname" . }} + {{- if .Values.metrics.serviceMonitor.namespace }} + namespace: {{ .Values.metrics.serviceMonitor.namespace }} + {{- else }} + namespace: {{ .Release.Namespace }} + {{- end }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: metrics + {{- if .Values.metrics.serviceMonitor.additionalLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.serviceMonitor.additionalLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + {{- if .Values.metrics.serviceMonitor.jobLabel }} + jobLabel: {{ .Values.metrics.serviceMonitor.jobLabel }} + {{- end }} + selector: + matchLabels: {{- include "common.labels.matchLabels" . | nindent 6 }} + {{- if .Values.metrics.serviceMonitor.selector }} + {{- include "common.tplvalues.render" (dict "value" .Values.metrics.serviceMonitor.selector "context" $) | nindent 6 }} + {{- end }} + app.kubernetes.io/component: metrics + endpoints: + - port: tcp-metrics + path: "/metrics" + {{- if .Values.metrics.serviceMonitor.interval }} + interval: {{ .Values.metrics.serviceMonitor.interval }} + {{- end }} + {{- if .Values.metrics.serviceMonitor.scrapeTimeout }} + scrapeTimeout: {{ .Values.metrics.serviceMonitor.scrapeTimeout }} + {{- end }} + {{- if .Values.metrics.serviceMonitor.relabelings }} + relabelings: {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.serviceMonitor.relabelings "context" $) | nindent 6 }} + {{- end }} + {{- if .Values.metrics.serviceMonitor.metricRelabelings }} + metricRelabelings: {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.serviceMonitor.metricRelabelings "context" $) | nindent 6 }} + {{- end }} + {{- if .Values.metrics.serviceMonitor.honorLabels }} + honorLabels: {{ .Values.metrics.serviceMonitor.honorLabels }} + {{- end }} + namespaceSelector: + matchNames: + - {{ template "zookeeper.namespace" . }} +{{- end }} diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/statefulset.yaml b/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/statefulset.yaml new file mode 100644 index 000000000..025d3630b --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/statefulset.yaml @@ -0,0 +1,532 @@ +apiVersion: {{ include "common.capabilities.statefulset.apiVersion" . }} +kind: StatefulSet +metadata: + name: {{ template "common.names.fullname" . }} + namespace: {{ template "zookeeper.namespace" . }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: zookeeper + role: zookeeper + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + replicas: {{ .Values.replicaCount }} + podManagementPolicy: {{ .Values.podManagementPolicy }} + selector: + matchLabels: {{- include "common.labels.matchLabels" . | nindent 6 }} + app.kubernetes.io/component: zookeeper + serviceName: {{ printf "%s-headless" (include "common.names.fullname" .) | trunc 63 | trimSuffix "-" }} + {{- if .Values.updateStrategy }} + updateStrategy: {{- toYaml .Values.updateStrategy | nindent 4 }} + {{- end }} + template: + metadata: + annotations: + {{- if .Values.podAnnotations }} + {{- include "common.tplvalues.render" (dict "value" .Values.podAnnotations "context" $) | nindent 8 }} + {{- end }} + {{- if (include "zookeeper.createConfigmap" .) }} + checksum/configuration: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} + {{- end }} + {{- if or (include "zookeeper.quorum.createSecret" .) (include "zookeeper.client.createSecret" .) (include "zookeeper.client.createTlsPasswordsSecret" .) (include "zookeeper.quorum.createTlsPasswordsSecret" .) }} + checksum/secrets: {{ include (print $.Template.BasePath "/secrets.yaml") . | sha256sum }} + {{- end }} + {{- if or (include "zookeeper.client.createTlsSecret" .) (include "zookeeper.quorum.createTlsSecret" .) }} + checksum/tls-secrets: {{ include (print $.Template.BasePath "/tls-secrets.yaml") . | sha256sum }} + {{- end }} + labels: {{- include "common.labels.standard" . | nindent 8 }} + app.kubernetes.io/component: zookeeper + {{- if .Values.podLabels }} + {{- include "common.tplvalues.render" (dict "value" .Values.podLabels "context" $) | nindent 8 }} + {{- end }} + spec: + serviceAccountName: {{ template "zookeeper.serviceAccountName" . }} + {{- include "zookeeper.imagePullSecrets" . | nindent 6 }} + {{- if .Values.hostAliases }} + hostAliases: {{- include "common.tplvalues.render" (dict "value" .Values.hostAliases "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.affinity }} + affinity: {{- include "common.tplvalues.render" (dict "value" .Values.affinity "context" $) | nindent 8 }} + {{- else }} + affinity: + podAffinity: {{- include "common.affinities.pods" (dict "type" .Values.podAffinityPreset "component" "zookeeper" "context" $) | nindent 10 }} + podAntiAffinity: {{- include "common.affinities.pods" (dict "type" .Values.podAntiAffinityPreset "component" "zookeeper" "context" $) | nindent 10 }} + nodeAffinity: {{- include "common.affinities.nodes" (dict "type" .Values.nodeAffinityPreset.type "key" .Values.nodeAffinityPreset.key "values" .Values.nodeAffinityPreset.values) | nindent 10 }} + {{- end }} + {{- if .Values.nodeSelector }} + nodeSelector: {{- include "common.tplvalues.render" (dict "value" .Values.nodeSelector "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.tolerations }} + tolerations: {{- include "common.tplvalues.render" (dict "value" .Values.tolerations "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.topologySpreadConstraints }} + topologySpreadConstraints: {{- include "common.tplvalues.render" (dict "value" .Values.topologySpreadConstraints "context" .) | nindent 8 }} + {{- end }} + {{- if .Values.priorityClassName }} + priorityClassName: {{ .Values.priorityClassName }} + {{- end }} + {{- if .Values.schedulerName }} + schedulerName: {{ .Values.schedulerName }} + {{- end }} + {{- if .Values.podSecurityContext.enabled }} + securityContext: {{- omit .Values.podSecurityContext "enabled" | toYaml | nindent 8 }} + {{- end }} + initContainers: + {{- if and .Values.volumePermissions.enabled .Values.persistence.enabled }} + - name: volume-permissions + image: {{ template "zookeeper.volumePermissions.image" . }} + imagePullPolicy: {{ default "" .Values.volumePermissions.image.pullPolicy | quote }} + command: + - /bin/bash + args: + - -ec + - | + mkdir -p /bitnami/zookeeper + chown -R {{ .Values.containerSecurityContext.runAsUser }}:{{ .Values.podSecurityContext.fsGroup }} /bitnami/zookeeper + find /bitnami/zookeeper -mindepth 1 -maxdepth 1 -not -name ".snapshot" -not -name "lost+found" | xargs -r chown -R {{ .Values.containerSecurityContext.runAsUser }}:{{ .Values.podSecurityContext.fsGroup }} + {{- if .Values.dataLogDir }} + mkdir -p {{ .Values.dataLogDir }} + chown -R {{ .Values.containerSecurityContext.runAsUser }}:{{ .Values.podSecurityContext.fsGroup }} {{ .Values.dataLogDir }} + find {{ .Values.dataLogDir }} -mindepth 1 -maxdepth 1 -not -name ".snapshot" -not -name "lost+found" | xargs -r chown -R {{ .Values.containerSecurityContext.runAsUser }}:{{ .Values.podSecurityContext.fsGroup }} + {{- end }} + {{- if .Values.volumePermissions.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.volumePermissions.containerSecurityContext "enabled" | toYaml | nindent 12 }} + {{- end }} + {{- if .Values.volumePermissions.resources }} + resources: {{- toYaml .Values.volumePermissions.resources | nindent 12 }} + {{- end }} + volumeMounts: + - name: data + mountPath: /bitnami/zookeeper + {{- if .Values.dataLogDir }} + - name: data-log + mountPath: {{ .Values.dataLogDir }} + {{- end }} + {{- end }} + {{- if or .Values.tls.client.enabled .Values.tls.quorum.enabled }} + - name: init-certs + image: {{ include "zookeeper.image" . }} + imagePullPolicy: {{ .Values.image.pullPolicy | quote }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 12 }} + {{- end }} + command: + - /scripts/init-certs.sh + env: + - name: MY_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + {{- if or .Values.tls.client.passwordsSecretName (include "zookeeper.client.createTlsPasswordsSecret" .) }} + - name: ZOO_TLS_CLIENT_KEYSTORE_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "zookeeper.client.tlsPasswordsSecret" . }} + key: {{ include "zookeeper.client.tlsPasswordKeystoreKey" . }} + - name: ZOO_TLS_CLIENT_TRUSTSTORE_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "zookeeper.client.tlsPasswordsSecret" . }} + key: {{ include "zookeeper.client.tlsPasswordTruststoreKey" . }} + {{- end }} + {{- if or .Values.tls.quorum.passwordsSecretName (include "zookeeper.quorum.createTlsPasswordsSecret" .) }} + - name: ZOO_TLS_QUORUM_KEYSTORE_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "zookeeper.quorum.tlsPasswordsSecret" . }} + key: {{ include "zookeeper.quorum.tlsPasswordKeystoreKey" . }} + - name: ZOO_TLS_QUORUM_TRUSTSTORE_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "zookeeper.quorum.tlsPasswordsSecret" . }} + key: {{ include "zookeeper.quorum.tlsPasswordTruststoreKey" . }} + {{- end }} + {{- if .Values.tls.resources }} + resources: {{- toYaml .Values.tls.resources | nindent 12 }} + {{- end }} + volumeMounts: + - name: scripts + mountPath: /scripts/init-certs.sh + subPath: init-certs.sh + {{- if or .Values.tls.client.enabled }} + - name: client-certificates + mountPath: /certs/client + - name: client-shared-certs + mountPath: /opt/bitnami/zookeeper/config/certs/client + {{- end }} + {{- if or .Values.tls.quorum.enabled }} + - name: quorum-certificates + mountPath: /certs/quorum + - name: quorum-shared-certs + mountPath: /opt/bitnami/zookeeper/config/certs/quorum + {{- end }} + {{- end }} + {{- if .Values.initContainers }} + {{- include "common.tplvalues.render" (dict "value" .Values.initContainers "context" $) | trim | nindent 8 }} + {{- end }} + containers: + - name: zookeeper + image: {{ template "zookeeper.image" . }} + imagePullPolicy: {{ .Values.image.pullPolicy | quote }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 12 }} + {{- end }} + {{- if .Values.diagnosticMode.enabled }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 12 }} + {{- else if .Values.command }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.command "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.diagnosticMode.enabled }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 12 }} + {{- else if .Values.args }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.args "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.resources }} + resources: {{- toYaml .Values.resources | nindent 12 }} + {{- end }} + env: + - name: BITNAMI_DEBUG + value: {{ ternary "true" "false" (or .Values.image.debug .Values.diagnosticMode.enabled) | quote }} + - name: ZOO_DATA_LOG_DIR + value: {{ .Values.dataLogDir | quote }} + - name: ZOO_PORT_NUMBER + value: {{ .Values.containerPorts.client | quote }} + - name: ZOO_TICK_TIME + value: {{ .Values.tickTime | quote }} + - name: ZOO_INIT_LIMIT + value: {{ .Values.initLimit | quote }} + - name: ZOO_SYNC_LIMIT + value: {{ .Values.syncLimit | quote }} + - name: ZOO_PRE_ALLOC_SIZE + value: {{ .Values.preAllocSize | quote }} + - name: ZOO_SNAPCOUNT + value: {{ .Values.snapCount | quote }} + - name: ZOO_MAX_CLIENT_CNXNS + value: {{ .Values.maxClientCnxns | quote }} + - name: ZOO_4LW_COMMANDS_WHITELIST + value: {{ .Values.fourlwCommandsWhitelist | quote }} + - name: ZOO_LISTEN_ALLIPS_ENABLED + value: {{ ternary "yes" "no" .Values.listenOnAllIPs | quote }} + - name: ZOO_AUTOPURGE_INTERVAL + value: {{ .Values.autopurge.purgeInterval | quote }} + - name: ZOO_AUTOPURGE_RETAIN_COUNT + value: {{ .Values.autopurge.snapRetainCount | quote }} + - name: ZOO_MAX_SESSION_TIMEOUT + value: {{ .Values.maxSessionTimeout | quote }} + - name: ZOO_SERVERS + {{- $replicaCount := int .Values.replicaCount }} + {{- $minServerId := int .Values.minServerId }} + {{- $followerPort := int .Values.containerPorts.follower }} + {{- $electionPort := int .Values.containerPorts.election }} + {{- $releaseNamespace := include "zookeeper.namespace" . }} + {{- $zookeeperFullname := include "common.names.fullname" . }} + {{- $zookeeperHeadlessServiceName := printf "%s-%s" $zookeeperFullname "headless" | trunc 63 }} + {{- $clusterDomain := .Values.clusterDomain }} + value: {{ range $i, $e := until $replicaCount }}{{ $zookeeperFullname }}-{{ $e }}.{{ $zookeeperHeadlessServiceName }}.{{ $releaseNamespace }}.svc.{{ $clusterDomain }}:{{ $followerPort }}:{{ $electionPort }}::{{ add $e $minServerId }} {{ end }} + - name: ZOO_ENABLE_AUTH + value: {{ ternary "yes" "no" .Values.auth.client.enabled | quote }} + {{- if .Values.auth.client.enabled }} + - name: ZOO_CLIENT_USER + value: {{ .Values.auth.client.clientUser | quote }} + - name: ZOO_CLIENT_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "zookeeper.client.secretName" . }} + key: client-password + - name: ZOO_SERVER_USERS + value: {{ .Values.auth.client.serverUsers | quote }} + - name: ZOO_SERVER_PASSWORDS + valueFrom: + secretKeyRef: + name: {{ include "zookeeper.client.secretName" . }} + key: server-password + {{- end }} + - name: ZOO_ENABLE_QUORUM_AUTH + value: {{ ternary "yes" "no" .Values.auth.quorum.enabled | quote }} + {{- if .Values.auth.quorum.enabled }} + - name: ZOO_QUORUM_LEARNER_USER + value: {{ .Values.auth.quorum.learnerUser | quote }} + - name: ZOO_QUORUM_LEARNER_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "zookeeper.quorum.secretName" . }} + key: quorum-learner-password + - name: ZOO_QUORUM_SERVER_USERS + value: {{ .Values.auth.quorum.serverUsers | quote }} + - name: ZOO_QUORUM_SERVER_PASSWORDS + valueFrom: + secretKeyRef: + name: {{ include "zookeeper.quorum.secretName" . }} + key: quorum-server-password + {{- end }} + - name: ZOO_HEAP_SIZE + value: {{ .Values.heapSize | quote }} + - name: ZOO_LOG_LEVEL + value: {{ .Values.logLevel | quote }} + - name: ALLOW_ANONYMOUS_LOGIN + value: {{ ternary "no" "yes" .Values.auth.client.enabled | quote }} + {{- if .Values.jvmFlags }} + - name: JVMFLAGS + value: {{ .Values.jvmFlags | quote }} + {{- end }} + {{- if .Values.metrics.enabled }} + - name: ZOO_ENABLE_PROMETHEUS_METRICS + value: "yes" + - name: ZOO_PROMETHEUS_METRICS_PORT_NUMBER + value: {{ .Values.metrics.containerPort | quote }} + {{- end }} + {{- if .Values.tls.client.enabled }} + - name: ZOO_TLS_PORT_NUMBER + value: {{ .Values.containerPorts.tls | quote }} + - name: ZOO_TLS_CLIENT_ENABLE + value: {{ .Values.tls.client.enabled | quote }} + - name: ZOO_TLS_CLIENT_AUTH + value: {{ .Values.tls.client.auth | quote }} + - name: ZOO_TLS_CLIENT_KEYSTORE_FILE + value: {{ .Values.tls.client.keystorePath | quote }} + - name: ZOO_TLS_CLIENT_TRUSTSTORE_FILE + value: {{ .Values.tls.client.truststorePath | quote }} + {{- if or .Values.tls.client.keystorePassword .Values.tls.client.passwordsSecretName .Values.tls.client.autoGenerated }} + - name: ZOO_TLS_CLIENT_KEYSTORE_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "zookeeper.client.tlsPasswordsSecret" . }} + key: {{ include "zookeeper.client.tlsPasswordKeystoreKey" . }} + {{- end }} + {{- if or .Values.tls.client.truststorePassword .Values.tls.client.passwordsSecretName .Values.tls.client.autoGenerated }} + - name: ZOO_TLS_CLIENT_TRUSTSTORE_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "zookeeper.client.tlsPasswordsSecret" . }} + key: {{ include "zookeeper.client.tlsPasswordTruststoreKey" . }} + {{- end }} + {{- end }} + {{- if .Values.tls.quorum.enabled }} + - name: ZOO_TLS_QUORUM_ENABLE + value: {{ .Values.tls.quorum.enabled | quote }} + - name: ZOO_TLS_QUORUM_CLIENT_AUTH + value: {{ .Values.tls.quorum.auth | quote }} + - name: ZOO_TLS_QUORUM_KEYSTORE_FILE + value: {{ .Values.tls.quorum.keystorePath | quote }} + - name: ZOO_TLS_QUORUM_TRUSTSTORE_FILE + value: {{ .Values.tls.quorum.truststorePath | quote }} + {{- if or .Values.tls.quorum.keystorePassword .Values.tls.quorum.passwordsSecretName .Values.tls.quorum.autoGenerated }} + - name: ZOO_TLS_QUORUM_KEYSTORE_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "zookeeper.quorum.tlsPasswordsSecret" . }} + key: {{ include "zookeeper.quorum.tlsPasswordKeystoreKey" . }} + {{- end }} + {{- if or .Values.tls.quorum.truststorePassword .Values.tls.quorum.passwordsSecretName .Values.tls.quorum.autoGenerated }} + - name: ZOO_TLS_QUORUM_TRUSTSTORE_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "zookeeper.quorum.tlsPasswordsSecret" . }} + key: {{ include "zookeeper.quorum.tlsPasswordTruststoreKey" . }} + {{- end }} + {{- end }} + - name: POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + {{- if .Values.extraEnvVars }} + {{- include "common.tplvalues.render" (dict "value" .Values.extraEnvVars "context" $) | nindent 12 }} + {{- end }} + {{- if or .Values.extraEnvVarsCM .Values.extraEnvVarsSecret }} + envFrom: + {{- if .Values.extraEnvVarsCM }} + - configMapRef: + name: {{ include "common.tplvalues.render" (dict "value" .Values.extraEnvVarsCM "context" $) }} + {{- end }} + {{- if .Values.extraEnvVarsSecret }} + - secretRef: + name: {{ include "common.tplvalues.render" (dict "value" .Values.extraEnvVarsSecret "context" $) }} + {{- end }} + {{- end }} + ports: + {{- if not .Values.service.disableBaseClientPort }} + - name: client + containerPort: {{ .Values.containerPorts.client }} + {{- end }} + {{- if .Values.tls.client.enabled }} + - name: client-tls + containerPort: {{ .Values.containerPorts.tls }} + {{- end }} + - name: follower + containerPort: {{ .Values.containerPorts.follower }} + - name: election + containerPort: {{ .Values.containerPorts.election }} + {{- if .Values.metrics.enabled }} + - name: metrics + containerPort: {{ .Values.metrics.containerPort }} + {{- end }} + {{- if not .Values.diagnosticMode.enabled }} + {{- if .Values.customLivenessProbe }} + livenessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.customLivenessProbe "context" $) | nindent 12 }} + {{- else if .Values.livenessProbe.enabled }} + livenessProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.livenessProbe "enabled" "probeCommandTimeout") "context" $) | nindent 12 }} + exec: + {{- if not .Values.service.disableBaseClientPort }} + command: ['/bin/bash', '-c', 'echo "ruok" | timeout {{ .Values.livenessProbe.probeCommandTimeout }} nc -w {{ .Values.livenessProbe.probeCommandTimeout }} localhost {{ .Values.containerPorts.client }} | grep imok'] + {{- else if not .Values.tls.client.enabled }} + command: ['/bin/bash', '-c', 'echo "ruok" | timeout {{ .Values.livenessProbe.probeCommandTimeout }} openssl s_client -quiet -crlf -connect localhost:{{ .Values.containerPorts.tls }} | grep imok'] + {{- else }} + command: ['/bin/bash', '-c', 'echo "ruok" | timeout {{ .Values.livenessProbe.probeCommandTimeout }} openssl s_client -quiet -crlf -connect localhost:{{ .Values.containerPorts.tls }} -cert {{ .Values.service.tls.client_cert_pem_path }} -key {{ .Values.service.tls.client_key_pem_path }} | grep imok'] + {{- end }} + {{- end }} + {{- if .Values.customReadinessProbe }} + readinessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.customReadinessProbe "context" $) | nindent 12 }} + {{- else if .Values.readinessProbe.enabled }} + readinessProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.readinessProbe "enabled" "probeCommandTimeout") "context" $) | nindent 12 }} + exec: + {{- if not .Values.service.disableBaseClientPort }} + command: ['/bin/bash', '-c', 'echo "ruok" | timeout {{ .Values.readinessProbe.probeCommandTimeout }} nc -w {{ .Values.readinessProbe.probeCommandTimeout }} localhost {{ .Values.containerPorts.client }} | grep imok'] + {{- else if not .Values.tls.client.enabled }} + command: ['/bin/bash', '-c', 'echo "ruok" | timeout {{ .Values.readinessProbe.probeCommandTimeout }} openssl s_client -quiet -crlf -connect localhost:{{ .Values.containerPorts.tls }} | grep imok'] + {{- else }} + command: ['/bin/bash', '-c', 'echo "ruok" | timeout {{ .Values.readinessProbe.probeCommandTimeout }} openssl s_client -quiet -crlf -connect localhost:{{ .Values.containerPorts.tls }} -cert {{ .Values.service.tls.client_cert_pem_path }} -key {{ .Values.service.tls.client_key_pem_path }} | grep imok'] + {{- end }} + {{- end }} + {{- if .Values.customStartupProbe }} + startupProbe: {{- include "common.tplvalues.render" (dict "value" .Values.customStartupProbe "context" $) | nindent 12 }} + {{- else if .Values.startupProbe.enabled }} + startupProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.startupProbe "enabled") "context" $) | nindent 12 }} + tcpSocket: + {{- if not .Values.service.disableBaseClientPort }} + port: client + {{- else }} + port: follower + {{- end }} + {{- end }} + {{- end }} + {{- if .Values.lifecycleHooks }} + lifecycle: {{- include "common.tplvalues.render" (dict "value" .Values.lifecycleHooks "context" $) | nindent 12 }} + {{- end }} + volumeMounts: + - name: scripts + mountPath: /scripts/setup.sh + subPath: setup.sh + - name: data + mountPath: /bitnami/zookeeper + {{- if .Values.dataLogDir }} + - name: data-log + mountPath: {{ .Values.dataLogDir }} + {{- end }} + {{- if or .Values.configuration .Values.existingConfigmap }} + - name: config + mountPath: /opt/bitnami/zookeeper/conf/zoo.cfg + subPath: zoo.cfg + {{- end }} + {{- if .Values.tls.client.enabled }} + - name: client-shared-certs + mountPath: /opt/bitnami/zookeeper/config/certs/client + readOnly: true + {{- end }} + {{- if .Values.tls.quorum.enabled }} + - name: quorum-shared-certs + mountPath: /opt/bitnami/zookeeper/config/certs/quorum + readOnly: true + {{- end }} + {{- if .Values.extraVolumeMounts }} + {{- include "common.tplvalues.render" ( dict "value" .Values.extraVolumeMounts "context" $ ) | nindent 12 }} + {{- end }} + {{- if .Values.sidecars }} + {{- include "common.tplvalues.render" ( dict "value" .Values.sidecars "context" $ ) | nindent 8 }} + {{- end }} + volumes: + - name: scripts + configMap: + name: {{ printf "%s-scripts" (include "common.names.fullname" .) }} + defaultMode: 0755 + {{- if or .Values.configuration .Values.existingConfigmap }} + - name: config + configMap: + name: {{ include "zookeeper.configmapName" . }} + {{- end }} + {{- if and .Values.persistence.enabled .Values.persistence.existingClaim }} + - name: data + persistentVolumeClaim: + claimName: {{ printf "%s" (tpl .Values.persistence.existingClaim .) }} + {{- else if not .Values.persistence.enabled }} + - name: data + emptyDir: {} + {{- end }} + {{- if and .Values.persistence.enabled .Values.persistence.dataLogDir.existingClaim }} + - name: data-log + persistentVolumeClaim: + claimName: {{ printf "%s" (tpl .Values.persistence.dataLogDir.existingClaim .) }} + {{- else if and ( not .Values.persistence.enabled ) .Values.dataLogDir }} + - name: data-log + emptyDir: {} + {{- end }} + {{- if .Values.tls.client.enabled }} + - name: client-certificates + secret: + secretName: {{ include "zookeeper.client.tlsSecretName" . }} + defaultMode: 256 + - name: client-shared-certs + emptyDir: {} + {{- end }} + {{- if .Values.tls.quorum.enabled }} + - name: quorum-certificates + secret: + secretName: {{ include "zookeeper.quorum.tlsSecretName" . }} + defaultMode: 256 + - name: quorum-shared-certs + emptyDir: {} + {{- end }} + {{- if .Values.extraVolumes }} + {{- include "common.tplvalues.render" (dict "value" .Values.extraVolumes "context" $) | nindent 8 }} + {{- end }} + {{- if and .Values.persistence.enabled (not (and .Values.persistence.existingClaim .Values.persistence.dataLogDir.existingClaim) ) }} + volumeClaimTemplates: + {{- if not .Values.persistence.existingClaim }} + - metadata: + name: data + {{- if .Values.persistence.annotations }} + annotations: {{- include "common.tplvalues.render" (dict "value" .Values.persistence.annotations "context" $) | nindent 10 }} + {{- end }} + {{- if .Values.persistence.labels }} + labels: {{- include "common.tplvalues.render" (dict "value" .Values.persistence.labels "context" $) | nindent 10 }} + {{- end }} + spec: + accessModes: + {{- range .Values.persistence.accessModes }} + - {{ . | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.persistence.size | quote }} + {{- include "common.storage.class" (dict "persistence" .Values.persistence "global" .Values.global) | nindent 8 }} + {{- if .Values.persistence.selector }} + selector: {{- include "common.tplvalues.render" (dict "value" .Values.persistence.selector "context" $) | nindent 10 }} + {{- end }} + {{- end }} + {{- if and (not .Values.persistence.dataLogDir.existingClaim) .Values.dataLogDir }} + - metadata: + name: data-log + {{- if .Values.persistence.annotations }} + annotations: {{- include "common.tplvalues.render" (dict "value" .Values.persistence.annotations "context" $) | nindent 10 }} + {{- end }} + {{- if .Values.persistence.labels }} + labels: {{- include "common.tplvalues.render" (dict "value" .Values.persistence.labels "context" $) | nindent 10 }} + {{- end }} + spec: + accessModes: + {{- range .Values.persistence.accessModes }} + - {{ . | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.persistence.dataLogDir.size | quote }} + {{- include "common.storage.class" (dict "persistence" .Values.persistence "global" .Values.global) | nindent 8 }} + {{- if .Values.persistence.dataLogDir.selector }} + selector: {{- include "common.tplvalues.render" (dict "value" .Values.persistence.dataLogDir.selector "context" $) | nindent 10 }} + {{- end }} + {{- end }} + {{- end }} diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/svc-headless.yaml b/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/svc-headless.yaml new file mode 100644 index 000000000..ee05e1dec --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/svc-headless.yaml @@ -0,0 +1,42 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ printf "%s-headless" (include "common.names.fullname" .) | trunc 63 | trimSuffix "-" }} + namespace: {{ template "zookeeper.namespace" . }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: zookeeper + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if or .Values.commonAnnotations .Values.service.headless.annotations }} + annotations: + {{- if .Values.service.headless.annotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.service.headless.annotations "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- end }} +spec: + type: ClusterIP + clusterIP: None + publishNotReadyAddresses: {{ .Values.service.headless.publishNotReadyAddresses }} + ports: + {{- if not .Values.service.disableBaseClientPort }} + - name: tcp-client + port: {{ .Values.service.ports.client }} + targetPort: client + {{- end }} + {{- if .Values.tls.client.enabled }} + - name: tcp-client-tls + port: {{ .Values.service.ports.tls }} + targetPort: client-tls + {{- end }} + - name: tcp-follower + port: {{ .Values.service.ports.follower }} + targetPort: follower + - name: tcp-election + port: {{ .Values.service.ports.election }} + targetPort: election + selector: {{- include "common.labels.matchLabels" . | nindent 4 }} + app.kubernetes.io/component: zookeeper diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/svc.yaml b/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/svc.yaml new file mode 100644 index 000000000..6ad0b1096 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/svc.yaml @@ -0,0 +1,71 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ template "common.names.fullname" . }} + namespace: {{ template "zookeeper.namespace" . }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: zookeeper + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if or .Values.commonAnnotations .Values.service.annotations }} + annotations: + {{- if .Values.service.annotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.service.annotations "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- end }} +spec: + type: {{ .Values.service.type }} + {{- if and .Values.service.clusterIP (eq .Values.service.type "ClusterIP") }} + clusterIP: {{ .Values.service.clusterIP }} + {{- end }} + {{- if or (eq .Values.service.type "LoadBalancer") (eq .Values.service.type "NodePort") }} + externalTrafficPolicy: {{ .Values.service.externalTrafficPolicy | quote }} + {{- end }} + {{- if and (eq .Values.service.type "LoadBalancer") (not (empty .Values.service.loadBalancerSourceRanges)) }} + loadBalancerSourceRanges: {{ .Values.service.loadBalancerSourceRanges }} + {{- end }} + {{- if and (eq .Values.service.type "LoadBalancer") (not (empty .Values.service.loadBalancerIP)) }} + loadBalancerIP: {{ .Values.service.loadBalancerIP }} + {{- end }} + {{- if .Values.service.sessionAffinity }} + sessionAffinity: {{ .Values.service.sessionAffinity }} + {{- end }} + {{- if .Values.service.sessionAffinityConfig }} + sessionAffinityConfig: {{- include "common.tplvalues.render" (dict "value" .Values.service.sessionAffinityConfig "context" $) | nindent 4 }} + {{- end }} + ports: + {{- if not .Values.service.disableBaseClientPort }} + - name: tcp-client + port: {{ .Values.service.ports.client }} + targetPort: client + {{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePorts.client)) }} + nodePort: {{ .Values.service.nodePorts.client }} + {{- else if eq .Values.service.type "ClusterIP" }} + nodePort: null + {{- end }} + {{- end }} + {{- if .Values.tls.client.enabled }} + - name: tcp-client-tls + port: {{ .Values.service.ports.tls }} + targetPort: client-tls + {{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePorts.tls)) }} + nodePort: {{ .Values.service.nodePorts.tls }} + {{- else if eq .Values.service.type "ClusterIP" }} + nodePort: null + {{- end }} + {{- end }} + - name: tcp-follower + port: {{ .Values.service.ports.follower }} + targetPort: follower + - name: tcp-election + port: {{ .Values.service.ports.election }} + targetPort: election + {{- if .Values.service.extraPorts }} + {{- include "common.tplvalues.render" (dict "value" .Values.service.extraPorts "context" $) | nindent 4 }} + {{- end }} + selector: {{- include "common.labels.matchLabels" . | nindent 4 }} + app.kubernetes.io/component: zookeeper diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/tls-secrets.yaml b/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/tls-secrets.yaml new file mode 100644 index 000000000..a07480d55 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/tls-secrets.yaml @@ -0,0 +1,55 @@ +{{- if (include "zookeeper.client.createTlsSecret" .) }} +{{- $ca := genCA "zookeeper-client-ca" 365 }} +{{- $releaseNamespace := .Release.Namespace }} +{{- $clusterDomain := .Values.clusterDomain }} +{{- $fullname := include "common.names.fullname" . }} +{{- $serviceName := include "common.names.fullname" . }} +{{- $headlessServiceName := printf "%s-headless" (include "common.names.fullname" .) }} +{{- $altNames := list (printf "*.%s.%s.svc.%s" $headlessServiceName $releaseNamespace $clusterDomain) (printf "%s.%s.svc.%s" $headlessServiceName $releaseNamespace $clusterDomain) (printf "*.%s.%s.svc.%s" $serviceName $releaseNamespace $clusterDomain) (printf "%s.%s.svc.%s" $serviceName $releaseNamespace $clusterDomain) "127.0.0.1" "localhost" $fullname }} +{{- $crt := genSignedCert $fullname nil $altNames 365 $ca }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "common.names.fullname" . }}-client-crt + namespace: {{ template "zookeeper.namespace" . }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +type: kubernetes.io/tls +data: + ca.crt: {{ $ca.Cert | b64enc | quote }} + tls.crt: {{ $crt.Cert | b64enc | quote }} + tls.key: {{ $crt.Key | b64enc | quote }} +{{- end }} +{{- if (include "zookeeper.quorum.createTlsSecret" .) }} +{{- $ca := genCA "zookeeper-quorum-ca" 365 }} +{{- $releaseNamespace := .Release.Namespace }} +{{- $clusterDomain := .Values.clusterDomain }} +{{- $fullname := include "common.names.fullname" . }} +{{- $serviceName := include "common.names.fullname" . }} +{{- $headlessServiceName := printf "%s-headless" (include "common.names.fullname" .) }} +{{- $altNames := list (printf "*.%s.%s.svc.%s" $headlessServiceName $releaseNamespace $clusterDomain) (printf "%s.%s.svc.%s" $headlessServiceName $releaseNamespace $clusterDomain) (printf "*.%s.%s.svc.%s" $serviceName $releaseNamespace $clusterDomain) (printf "%s.%s.svc.%s" $serviceName $releaseNamespace $clusterDomain) $fullname }} +{{- $crt := genSignedCert $fullname nil $altNames 365 $ca }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "common.names.fullname" . }}-quorum-crt + namespace: {{ template "zookeeper.namespace" . }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +type: kubernetes.io/tls +data: + ca.crt: {{ $ca.Cert | b64enc | quote }} + tls.crt: {{ $crt.Cert | b64enc | quote }} + tls.key: {{ $crt.Key | b64enc | quote }} +{{- end }} diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/values.yaml b/knowlg-automation/helm_charts/kafka/charts/zookeeper/values.yaml new file mode 100644 index 000000000..934383269 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/values.yaml @@ -0,0 +1,877 @@ +## @section Global parameters +## Global Docker image parameters +## Please, note that this will override the image parameters, including dependencies, configured to use the global value +## Current available global Docker image parameters: imageRegistry, imagePullSecrets and storageClass +## + +## @param global.imageRegistry Global Docker image registry +## @param global.imagePullSecrets Global Docker registry secret names as an array +## @param global.storageClass Global StorageClass for Persistent Volume(s) +## +global: + imageRegistry: "" + ## E.g. + ## imagePullSecrets: + ## - myRegistryKeySecretName + ## + imagePullSecrets: [] + storageClass: "" + +## @section Common parameters +## + +## @param kubeVersion Override Kubernetes version +## +kubeVersion: "" +## @param nameOverride String to partially override common.names.fullname template (will maintain the release name) +## +nameOverride: "" +## @param fullnameOverride String to fully override common.names.fullname template +## +fullnameOverride: "" +## @param clusterDomain Kubernetes Cluster Domain +## +clusterDomain: cluster.local +## @param extraDeploy Extra objects to deploy (evaluated as a template) +## +extraDeploy: [] +## @param commonLabels Add labels to all the deployed resources +## +commonLabels: {} +## @param commonAnnotations Add annotations to all the deployed resources +## +commonAnnotations: {} +## @param namespaceOverride Override namespace for ZooKeeper resources +## Useful when including ZooKeeper as a chart dependency, so it can be released into a different namespace than the parent +## +namespaceOverride: "" + +## Enable diagnostic mode in the statefulset +## +diagnosticMode: + ## @param diagnosticMode.enabled Enable diagnostic mode (all probes will be disabled and the command will be overridden) + ## + enabled: false + ## @param diagnosticMode.command Command to override all containers in the statefulset + ## + command: + - sleep + ## @param diagnosticMode.args Args to override all containers in the statefulset + ## + args: + - infinity + +## @section ZooKeeper chart parameters + +## Bitnami ZooKeeper image version +## ref: https://hub.docker.com/r/bitnami/zookeeper/tags/ +## @param image.registry ZooKeeper image registry +## @param image.repository ZooKeeper image repository +## @param image.tag ZooKeeper image tag (immutable tags are recommended) +## @param image.digest ZooKeeper image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag +## @param image.pullPolicy ZooKeeper image pull policy +## @param image.pullSecrets Specify docker-registry secret names as an array +## @param image.debug Specify if debug values should be set +## +image: + registry: docker.io + repository: bitnami/zookeeper + tag: 3.8.0-debian-11-r65 + digest: "" + ## Specify a imagePullPolicy + ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' + ## ref: https://kubernetes.io/docs/user-guide/images/#pre-pulling-images + ## + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## Example: + ## pullSecrets: + ## - myRegistryKeySecretName + ## + pullSecrets: [] + ## Set to true if you would like to see extra information on logs + ## + debug: false +## Authentication parameters +## +auth: + client: + ## @param auth.client.enabled Enable ZooKeeper client-server authentication. It uses SASL/Digest-MD5 + ## + enabled: false + ## @param auth.client.clientUser User that will use ZooKeeper clients to auth + ## + clientUser: "" + ## @param auth.client.clientPassword Password that will use ZooKeeper clients to auth + ## + clientPassword: "" + ## @param auth.client.serverUsers Comma, semicolon or whitespace separated list of user to be created + ## Specify them as a string, for example: "user1,user2,admin" + ## + serverUsers: "" + ## @param auth.client.serverPasswords Comma, semicolon or whitespace separated list of passwords to assign to users when created + ## Specify them as a string, for example: "pass4user1, pass4user2, pass4admin" + ## + serverPasswords: "" + ## @param auth.client.existingSecret Use existing secret (ignores previous passwords) + ## + existingSecret: "" + quorum: + ## @param auth.quorum.enabled Enable ZooKeeper server-server authentication. It uses SASL/Digest-MD5 + ## + enabled: false + ## @param auth.quorum.learnerUser User that the ZooKeeper quorumLearner will use to authenticate to quorumServers. + ## Note: Make sure the user is included in auth.quorum.serverUsers + ## + learnerUser: "" + ## @param auth.quorum.learnerPassword Password that the ZooKeeper quorumLearner will use to authenticate to quorumServers. + ## + learnerPassword: "" + ## @param auth.quorum.serverUsers Comma, semicolon or whitespace separated list of users for the quorumServers. + ## Specify them as a string, for example: "user1,user2,admin" + ## + serverUsers: "" + ## @param auth.quorum.serverPasswords Comma, semicolon or whitespace separated list of passwords to assign to users when created + ## Specify them as a string, for example: "pass4user1, pass4user2, pass4admin" + ## + serverPasswords: "" + ## @param auth.quorum.existingSecret Use existing secret (ignores previous passwords) + ## + existingSecret: "" +## @param tickTime Basic time unit (in milliseconds) used by ZooKeeper for heartbeats +## +tickTime: 2000 +## @param initLimit ZooKeeper uses to limit the length of time the ZooKeeper servers in quorum have to connect to a leader +## +initLimit: 10 +## @param syncLimit How far out of date a server can be from a leader +## +syncLimit: 5 +## @param preAllocSize Block size for transaction log file +## +preAllocSize: 65536 +## @param snapCount The number of transactions recorded in the transaction log before a snapshot can be taken (and the transaction log rolled) +## +snapCount: 100000 +## @param maxClientCnxns Limits the number of concurrent connections that a single client may make to a single member of the ZooKeeper ensemble +## +maxClientCnxns: 60 +## @param maxSessionTimeout Maximum session timeout (in milliseconds) that the server will allow the client to negotiate +## Defaults to 20 times the tickTime +## +maxSessionTimeout: 40000 +## @param heapSize Size (in MB) for the Java Heap options (Xmx and Xms) +## This env var is ignored if Xmx an Xms are configured via `jvmFlags` +## +heapSize: 1024 +## @param fourlwCommandsWhitelist A list of comma separated Four Letter Words commands that can be executed +## +fourlwCommandsWhitelist: srvr, mntr, ruok +## @param minServerId Minimal SERVER_ID value, nodes increment their IDs respectively +## Servers increment their ID starting at this minimal value. +## E.g., with `minServerId=10` and 3 replicas, server IDs will be 10, 11, 12 for z-0, z-1 and z-2 respectively. +## +minServerId: 1 +## @param listenOnAllIPs Allow ZooKeeper to listen for connections from its peers on all available IP addresses +## +listenOnAllIPs: false +## Ongoing data directory cleanup configuration +## +autopurge: + ## @param autopurge.snapRetainCount The most recent snapshots amount (and corresponding transaction logs) to retain + ## + snapRetainCount: 3 + ## @param autopurge.purgeInterval The time interval (in hours) for which the purge task has to be triggered + ## Set to a positive integer to enable the auto purging + ## + purgeInterval: 0 +## @param logLevel Log level for the ZooKeeper server. ERROR by default +## Have in mind if you set it to INFO or WARN the ReadinessProve will produce a lot of logs +## +logLevel: ERROR +## @param jvmFlags Default JVM flags for the ZooKeeper process +## +jvmFlags: "" +## @param dataLogDir Dedicated data log directory +## This allows a dedicated log device to be used, and helps avoid competition between logging and snapshots. +## E.g. +## dataLogDir: /bitnami/zookeeper/dataLog +## +dataLogDir: "" +## @param configuration Configure ZooKeeper with a custom zoo.cfg file +## e.g: +## configuration: |- +## deploy-working-dir=/bitnami/geode/data +## log-level=info +## ... +## +configuration: "" +## @param existingConfigmap The name of an existing ConfigMap with your custom configuration for ZooKeeper +## NOTE: When it's set the `configuration` parameter is ignored +## +existingConfigmap: "" +## @param extraEnvVars Array with extra environment variables to add to ZooKeeper nodes +## e.g: +## extraEnvVars: +## - name: FOO +## value: "bar" +## +extraEnvVars: [] +## @param extraEnvVarsCM Name of existing ConfigMap containing extra env vars for ZooKeeper nodes +## +extraEnvVarsCM: "" +## @param extraEnvVarsSecret Name of existing Secret containing extra env vars for ZooKeeper nodes +## +extraEnvVarsSecret: "" +## @param command Override default container command (useful when using custom images) +## +command: + - /scripts/setup.sh +## @param args Override default container args (useful when using custom images) +## +args: [] + +## @section Statefulset parameters + +## @param replicaCount Number of ZooKeeper nodes +## +replicaCount: 1 +## @param containerPorts.client ZooKeeper client container port +## @param containerPorts.tls ZooKeeper TLS container port +## @param containerPorts.follower ZooKeeper follower container port +## @param containerPorts.election ZooKeeper election container port +## +containerPorts: + client: 2181 + tls: 3181 + follower: 2888 + election: 3888 +## Configure extra options for ZooKeeper containers' liveness, readiness and startup probes +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#configure-probes +## @param livenessProbe.enabled Enable livenessProbe on ZooKeeper containers +## @param livenessProbe.initialDelaySeconds Initial delay seconds for livenessProbe +## @param livenessProbe.periodSeconds Period seconds for livenessProbe +## @param livenessProbe.timeoutSeconds Timeout seconds for livenessProbe +## @param livenessProbe.failureThreshold Failure threshold for livenessProbe +## @param livenessProbe.successThreshold Success threshold for livenessProbe +## @param livenessProbe.probeCommandTimeout Probe command timeout for livenessProbe +## +livenessProbe: + enabled: true + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 + probeCommandTimeout: 2 +## @param readinessProbe.enabled Enable readinessProbe on ZooKeeper containers +## @param readinessProbe.initialDelaySeconds Initial delay seconds for readinessProbe +## @param readinessProbe.periodSeconds Period seconds for readinessProbe +## @param readinessProbe.timeoutSeconds Timeout seconds for readinessProbe +## @param readinessProbe.failureThreshold Failure threshold for readinessProbe +## @param readinessProbe.successThreshold Success threshold for readinessProbe +## @param readinessProbe.probeCommandTimeout Probe command timeout for readinessProbe +## +readinessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 + probeCommandTimeout: 2 +## @param startupProbe.enabled Enable startupProbe on ZooKeeper containers +## @param startupProbe.initialDelaySeconds Initial delay seconds for startupProbe +## @param startupProbe.periodSeconds Period seconds for startupProbe +## @param startupProbe.timeoutSeconds Timeout seconds for startupProbe +## @param startupProbe.failureThreshold Failure threshold for startupProbe +## @param startupProbe.successThreshold Success threshold for startupProbe +## +startupProbe: + enabled: false + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 1 + failureThreshold: 15 + successThreshold: 1 +## @param customLivenessProbe Custom livenessProbe that overrides the default one +## +customLivenessProbe: {} +## @param customReadinessProbe Custom readinessProbe that overrides the default one +## +customReadinessProbe: {} +## @param customStartupProbe Custom startupProbe that overrides the default one +## +customStartupProbe: {} +## @param lifecycleHooks for the ZooKeeper container(s) to automate configuration before or after startup +## +lifecycleHooks: {} +## ZooKeeper resource requests and limits +## ref: https://kubernetes.io/docs/user-guide/compute-resources/ +## @param resources.limits The resources limits for the ZooKeeper containers +## @param resources.requests.memory The requested memory for the ZooKeeper containers +## @param resources.requests.cpu The requested cpu for the ZooKeeper containers +## +resources: + limits: {} + requests: + memory: 256Mi + cpu: 250m +## Configure Pods Security Context +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod +## @param podSecurityContext.enabled Enabled ZooKeeper pods' Security Context +## @param podSecurityContext.fsGroup Set ZooKeeper pod's Security Context fsGroup +## +podSecurityContext: + enabled: true + fsGroup: 1001 +## Configure Container Security Context +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container +## @param containerSecurityContext.enabled Enabled ZooKeeper containers' Security Context +## @param containerSecurityContext.runAsUser Set ZooKeeper containers' Security Context runAsUser +## @param containerSecurityContext.runAsNonRoot Set ZooKeeper containers' Security Context runAsNonRoot +## @param containerSecurityContext.allowPrivilegeEscalation Force the child process to be run as nonprivilege +## +containerSecurityContext: + enabled: true + runAsUser: 1001 + runAsNonRoot: true + allowPrivilegeEscalation: false +## @param hostAliases ZooKeeper pods host aliases +## https://kubernetes.io/docs/concepts/services-networking/add-entries-to-pod-etc-hosts-with-host-aliases/ +## +hostAliases: [] +## @param podLabels Extra labels for ZooKeeper pods +## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ +## +podLabels: {} +## @param podAnnotations Annotations for ZooKeeper pods +## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ +## +podAnnotations: {} +## @param podAffinityPreset Pod affinity preset. Ignored if `affinity` is set. Allowed values: `soft` or `hard` +## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity +## +podAffinityPreset: "" +## @param podAntiAffinityPreset Pod anti-affinity preset. Ignored if `affinity` is set. Allowed values: `soft` or `hard` +## Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity +## +podAntiAffinityPreset: soft +## Node affinity preset +## Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity +## +nodeAffinityPreset: + ## @param nodeAffinityPreset.type Node affinity preset type. Ignored if `affinity` is set. Allowed values: `soft` or `hard` + ## + type: "" + ## @param nodeAffinityPreset.key Node label key to match Ignored if `affinity` is set. + ## E.g. + ## key: "kubernetes.io/e2e-az-name" + ## + key: "" + ## @param nodeAffinityPreset.values Node label values to match. Ignored if `affinity` is set. + ## E.g. + ## values: + ## - e2e-az1 + ## - e2e-az2 + ## + values: [] +## @param affinity Affinity for pod assignment +## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity +## Note: podAffinityPreset, podAntiAffinityPreset, and nodeAffinityPreset will be ignored when it's set +## +affinity: {} +## @param nodeSelector Node labels for pod assignment +## Ref: https://kubernetes.io/docs/user-guide/node-selection/ +## +nodeSelector: {} +## @param tolerations Tolerations for pod assignment +## Ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ +## +tolerations: [] +## @param topologySpreadConstraints Topology Spread Constraints for pod assignment spread across your cluster among failure-domains. Evaluated as a template +## Ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/#spread-constraints-for-pods +## +topologySpreadConstraints: [] +## @param podManagementPolicy StatefulSet controller supports relax its ordering guarantees while preserving its uniqueness and identity guarantees. There are two valid pod management policies: `OrderedReady` and `Parallel` +## ref: https://kubernetes.io/docs/tutorials/stateful-application/basic-stateful-set/#pod-management-policy +## +podManagementPolicy: Parallel +## @param priorityClassName Name of the existing priority class to be used by ZooKeeper pods, priority class needs to be created beforehand +## Ref: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/ +## +priorityClassName: "" +## @param schedulerName Kubernetes pod scheduler registry +## https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ +## +schedulerName: "" +## @param updateStrategy.type ZooKeeper statefulset strategy type +## @param updateStrategy.rollingUpdate ZooKeeper statefulset rolling update configuration parameters +## ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#update-strategies +## +updateStrategy: + type: RollingUpdate + rollingUpdate: {} +## @param extraVolumes Optionally specify extra list of additional volumes for the ZooKeeper pod(s) +## Example Use Case: mount certificates to enable TLS +## e.g: +## extraVolumes: +## - name: zookeeper-keystore +## secret: +## defaultMode: 288 +## secretName: zookeeper-keystore +## - name: zookeeper-truststore +## secret: +## defaultMode: 288 +## secretName: zookeeper-truststore +## +extraVolumes: [] +## @param extraVolumeMounts Optionally specify extra list of additional volumeMounts for the ZooKeeper container(s) +## Example Use Case: mount certificates to enable TLS +## e.g: +## extraVolumeMounts: +## - name: zookeeper-keystore +## mountPath: /certs/keystore +## readOnly: true +## - name: zookeeper-truststore +## mountPath: /certs/truststore +## readOnly: true +## +extraVolumeMounts: [] +## @param sidecars Add additional sidecar containers to the ZooKeeper pod(s) +## e.g: +## sidecars: +## - name: your-image-name +## image: your-image +## imagePullPolicy: Always +## ports: +## - name: portname +## containerPort: 1234 +## +sidecars: [] +## @param initContainers Add additional init containers to the ZooKeeper pod(s) +## Example: +## initContainers: +## - name: your-image-name +## image: your-image +## imagePullPolicy: Always +## ports: +## - name: portname +## containerPort: 1234 +## +initContainers: [] +## ZooKeeper Pod Disruption Budget +## ref: https://kubernetes.io/docs/concepts/workloads/pods/disruptions/ +## @param pdb.create Deploy a pdb object for the ZooKeeper pod +## @param pdb.minAvailable Minimum available ZooKeeper replicas +## @param pdb.maxUnavailable Maximum unavailable ZooKeeper replicas +## +pdb: + create: false + minAvailable: "" + maxUnavailable: 1 + +## @section Traffic Exposure parameters + +service: + ## @param service.type Kubernetes Service type + ## + type: ClusterIP + ## @param service.ports.client ZooKeeper client service port + ## @param service.ports.tls ZooKeeper TLS service port + ## @param service.ports.follower ZooKeeper follower service port + ## @param service.ports.election ZooKeeper election service port + ## + ports: + client: 2181 + tls: 3181 + follower: 2888 + election: 3888 + ## Node ports to expose + ## NOTE: choose port between <30000-32767> + ## @param service.nodePorts.client Node port for clients + ## @param service.nodePorts.tls Node port for TLS + ## + nodePorts: + client: "" + tls: "" + ## @param service.disableBaseClientPort Remove client port from service definitions. + ## + disableBaseClientPort: false + ## @param service.sessionAffinity Control where client requests go, to the same pod or round-robin + ## Values: ClientIP or None + ## ref: https://kubernetes.io/docs/user-guide/services/ + ## + sessionAffinity: None + ## @param service.sessionAffinityConfig Additional settings for the sessionAffinity + ## sessionAffinityConfig: + ## clientIP: + ## timeoutSeconds: 300 + ## + sessionAffinityConfig: {} + ## @param service.clusterIP ZooKeeper service Cluster IP + ## e.g.: + ## clusterIP: None + ## + clusterIP: "" + ## @param service.loadBalancerIP ZooKeeper service Load Balancer IP + ## ref: https://kubernetes.io/docs/user-guide/services/#type-loadbalancer + ## + loadBalancerIP: "" + ## @param service.loadBalancerSourceRanges ZooKeeper service Load Balancer sources + ## ref: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/#restrict-access-for-loadbalancer-service + ## e.g: + ## loadBalancerSourceRanges: + ## - 10.10.10.0/24 + ## + loadBalancerSourceRanges: [] + ## @param service.externalTrafficPolicy ZooKeeper service external traffic policy + ## ref https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip + ## + externalTrafficPolicy: Cluster + ## @param service.annotations Additional custom annotations for ZooKeeper service + ## + annotations: {} + ## @param service.extraPorts Extra ports to expose in the ZooKeeper service (normally used with the `sidecar` value) + ## + extraPorts: [] + ## @param service.headless.annotations Annotations for the Headless Service + ## @param service.headless.publishNotReadyAddresses If the ZooKeeper headless service should publish DNS records for not ready pods + ## + headless: + publishNotReadyAddresses: true + annotations: {} +## Network policies +## Ref: https://kubernetes.io/docs/concepts/services-networking/network-policies/ +## +networkPolicy: + ## @param networkPolicy.enabled Specifies whether a NetworkPolicy should be created + ## + enabled: false + ## @param networkPolicy.allowExternal Don't require client label for connections + ## When set to false, only pods with the correct client label will have network access to the port Redis® is + ## listening on. When true, zookeeper accept connections from any source (with the correct destination port). + ## + allowExternal: true + +## @section Other Parameters + +## Service account for ZooKeeper to use. +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ +## +serviceAccount: + ## @param serviceAccount.create Enable creation of ServiceAccount for ZooKeeper pod + ## + create: false + ## @param serviceAccount.name The name of the ServiceAccount to use. + ## If not set and create is true, a name is generated using the common.names.fullname template + ## + name: "" + ## @param serviceAccount.automountServiceAccountToken Allows auto mount of ServiceAccountToken on the serviceAccount created + ## Can be set to false if pods using this serviceAccount do not need to use K8s API + ## + automountServiceAccountToken: true + ## @param serviceAccount.annotations Additional custom annotations for the ServiceAccount + ## + annotations: {} + +## @section Persistence parameters + +## Enable persistence using Persistent Volume Claims +## ref: https://kubernetes.io/docs/user-guide/persistent-volumes/ +## +persistence: + ## @param persistence.enabled Enable ZooKeeper data persistence using PVC. If false, use emptyDir + ## + enabled: true + ## @param persistence.existingClaim Name of an existing PVC to use (only when deploying a single replica) + ## + existingClaim: "" + ## @param persistence.storageClass PVC Storage Class for ZooKeeper data volume + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. (gp2 on AWS, standard on + ## GKE, AWS & OpenStack) + ## + storageClass: "" + ## @param persistence.accessModes PVC Access modes + ## + accessModes: + - ReadWriteOnce + ## @param persistence.size PVC Storage Request for ZooKeeper data volume + ## + size: 8Gi + ## @param persistence.annotations Annotations for the PVC + ## + annotations: {} + ## @param persistence.labels Labels for the PVC + ## + labels: {} + ## @param persistence.selector Selector to match an existing Persistent Volume for ZooKeeper's data PVC + ## If set, the PVC can't have a PV dynamically provisioned for it + ## E.g. + ## selector: + ## matchLabels: + ## app: my-app + ## + selector: {} + ## Persistence for a dedicated data log directory + ## + dataLogDir: + ## @param persistence.dataLogDir.size PVC Storage Request for ZooKeeper's dedicated data log directory + ## + size: 8Gi + ## @param persistence.dataLogDir.existingClaim Provide an existing `PersistentVolumeClaim` for ZooKeeper's data log directory + ## If defined, PVC must be created manually before volume will be bound + ## The value is evaluated as a template + ## + existingClaim: "" + ## @param persistence.dataLogDir.selector Selector to match an existing Persistent Volume for ZooKeeper's data log PVC + ## If set, the PVC can't have a PV dynamically provisioned for it + ## E.g. + ## selector: + ## matchLabels: + ## app: my-app + ## + selector: {} + +## @section Volume Permissions parameters +## + +## Init containers parameters: +## volumePermissions: Change the owner and group of the persistent volume(s) mountpoint(s) to 'runAsUser:fsGroup' on each node +## +volumePermissions: + ## @param volumePermissions.enabled Enable init container that changes the owner and group of the persistent volume + ## + enabled: false + ## @param volumePermissions.image.registry Init container volume-permissions image registry + ## @param volumePermissions.image.repository Init container volume-permissions image repository + ## @param volumePermissions.image.tag Init container volume-permissions image tag (immutable tags are recommended) + ## @param volumePermissions.image.digest Init container volume-permissions image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag + ## @param volumePermissions.image.pullPolicy Init container volume-permissions image pull policy + ## @param volumePermissions.image.pullSecrets Init container volume-permissions image pull secrets + ## + image: + registry: docker.io + repository: bitnami/bitnami-shell + tag: 11-debian-11-r60 + digest: "" + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## Example: + ## pullSecrets: + ## - myRegistryKeySecretName + ## + pullSecrets: [] + ## Init container resource requests and limits + ## ref: https://kubernetes.io/docs/user-guide/compute-resources/ + ## @param volumePermissions.resources.limits Init container volume-permissions resource limits + ## @param volumePermissions.resources.requests Init container volume-permissions resource requests + ## + resources: + limits: {} + requests: {} + ## Init container' Security Context + ## Note: the chown of the data folder is done to containerSecurityContext.runAsUser + ## and not the below volumePermissions.containerSecurityContext.runAsUser + ## @param volumePermissions.containerSecurityContext.enabled Enabled init container Security Context + ## @param volumePermissions.containerSecurityContext.runAsUser User ID for the init container + ## + containerSecurityContext: + enabled: true + runAsUser: 0 + +## @section Metrics parameters +## + +## ZooKeeper Prometheus Exporter configuration +## +metrics: + ## @param metrics.enabled Enable Prometheus to access ZooKeeper metrics endpoint + ## + enabled: false + ## @param metrics.containerPort ZooKeeper Prometheus Exporter container port + ## + containerPort: 9141 + ## Service configuration + ## + service: + ## @param metrics.service.type ZooKeeper Prometheus Exporter service type + ## + type: ClusterIP + ## @param metrics.service.port ZooKeeper Prometheus Exporter service port + ## + port: 9141 + ## @param metrics.service.annotations [object] Annotations for Prometheus to auto-discover the metrics endpoint + ## + annotations: + prometheus.io/scrape: "true" + prometheus.io/port: "{{ .Values.metrics.service.port }}" + prometheus.io/path: "/metrics" + ## Prometheus Operator ServiceMonitor configuration + ## + serviceMonitor: + ## @param metrics.serviceMonitor.enabled Create ServiceMonitor Resource for scraping metrics using Prometheus Operator + ## + enabled: false + ## @param metrics.serviceMonitor.namespace Namespace for the ServiceMonitor Resource (defaults to the Release Namespace) + ## + namespace: "" + ## @param metrics.serviceMonitor.interval Interval at which metrics should be scraped. + ## ref: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#endpoint + ## + interval: "" + ## @param metrics.serviceMonitor.scrapeTimeout Timeout after which the scrape is ended + ## ref: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#endpoint + ## + scrapeTimeout: "" + ## @param metrics.serviceMonitor.additionalLabels Additional labels that can be used so ServiceMonitor will be discovered by Prometheus + ## + additionalLabels: {} + ## @param metrics.serviceMonitor.selector Prometheus instance selector labels + ## ref: https://github.com/bitnami/charts/tree/main/bitnami/prometheus-operator#prometheus-configuration + ## + selector: {} + ## @param metrics.serviceMonitor.relabelings RelabelConfigs to apply to samples before scraping + ## + relabelings: [] + ## @param metrics.serviceMonitor.metricRelabelings MetricRelabelConfigs to apply to samples before ingestion + ## + metricRelabelings: [] + ## @param metrics.serviceMonitor.honorLabels Specify honorLabels parameter to add the scrape endpoint + ## + honorLabels: false + ## @param metrics.serviceMonitor.jobLabel The name of the label on the target service to use as the job name in prometheus. + ## + jobLabel: "" + ## Prometheus Operator PrometheusRule configuration + ## + prometheusRule: + ## @param metrics.prometheusRule.enabled Create a PrometheusRule for Prometheus Operator + ## + enabled: false + ## @param metrics.prometheusRule.namespace Namespace for the PrometheusRule Resource (defaults to the Release Namespace) + ## + namespace: "" + ## @param metrics.prometheusRule.additionalLabels Additional labels that can be used so PrometheusRule will be discovered by Prometheus + ## + additionalLabels: {} + ## @param metrics.prometheusRule.rules PrometheusRule definitions + ## - alert: ZooKeeperSyncedFollowers + ## annotations: + ## message: The number of synced followers for the leader node in ZooKeeper deployment my-release is less than 2. This usually means that some of the ZooKeeper nodes aren't communicating properly. If it doesn't resolve itself you can try killing the pods (one by one). + ## expr: max(synced_followers{service="my-release-metrics"}) < 2 + ## for: 5m + ## labels: + ## severity: critical + ## - alert: ZooKeeperOutstandingRequests + ## annotations: + ## message: The number of outstanding requests for ZooKeeper pod {{ $labels.pod }} is greater than 10. This can indicate a performance issue with the Pod or cluster a whole. + ## expr: outstanding_requests{service="my-release-metrics"} > 10 + ## for: 5m + ## labels: + ## severity: critical + ## + rules: [] + +## @section TLS/SSL parameters +## + +## Enable SSL/TLS encryption +## +tls: + client: + ## @param tls.client.enabled Enable TLS for client connections + ## + enabled: false + ## @param tls.client.auth SSL Client auth. Can be "none", "want" or "need". + ## + auth: "none" + ## @param tls.client.autoGenerated Generate automatically self-signed TLS certificates for ZooKeeper client communications + ## Currently only supports PEM certificates + ## + autoGenerated: false + ## @param tls.client.existingSecret Name of the existing secret containing the TLS certificates for ZooKeeper client communications + ## + existingSecret: "" + ## @param tls.client.existingSecretKeystoreKey The secret key from the tls.client.existingSecret containing the Keystore. + ## + existingSecretKeystoreKey: "" + ## @param tls.client.existingSecretTruststoreKey The secret key from the tls.client.existingSecret containing the Truststore. + ## + existingSecretTruststoreKey: "" + ## @param tls.client.keystorePath Location of the KeyStore file used for Client connections + ## + keystorePath: /opt/bitnami/zookeeper/config/certs/client/zookeeper.keystore.jks + ## @param tls.client.truststorePath Location of the TrustStore file used for Client connections + ## + truststorePath: /opt/bitnami/zookeeper/config/certs/client/zookeeper.truststore.jks + ## @param tls.client.passwordsSecretName Existing secret containing Keystore and truststore passwords + ## + passwordsSecretName: "" + ## @param tls.client.passwordsSecretKeystoreKey The secret key from the tls.client.passwordsSecretName containing the password for the Keystore. + ## + passwordsSecretKeystoreKey: "" + ## @param tls.client.passwordsSecretTruststoreKey The secret key from the tls.client.passwordsSecretName containing the password for the Truststore. + ## + passwordsSecretTruststoreKey: "" + ## @param tls.client.keystorePassword Password to access KeyStore if needed + ## + keystorePassword: "" + ## @param tls.client.truststorePassword Password to access TrustStore if needed + ## + truststorePassword: "" + quorum: + ## @param tls.quorum.enabled Enable TLS for quorum protocol + ## + enabled: false + ## @param tls.quorum.auth SSL Quorum Client auth. Can be "none", "want" or "need". + ## + auth: "none" + ## @param tls.quorum.autoGenerated Create self-signed TLS certificates. Currently only supports PEM certificates. + ## + autoGenerated: false + ## @param tls.quorum.existingSecret Name of the existing secret containing the TLS certificates for ZooKeeper quorum protocol + ## + existingSecret: "" + ## @param tls.quorum.existingSecretKeystoreKey The secret key from the tls.quorum.existingSecret containing the Keystore. + ## + existingSecretKeystoreKey: "" + ## @param tls.quorum.existingSecretTruststoreKey The secret key from the tls.quorum.existingSecret containing the Truststore. + ## + existingSecretTruststoreKey: "" + ## @param tls.quorum.keystorePath Location of the KeyStore file used for Quorum protocol + ## + keystorePath: /opt/bitnami/zookeeper/config/certs/quorum/zookeeper.keystore.jks + ## @param tls.quorum.truststorePath Location of the TrustStore file used for Quorum protocol + ## + truststorePath: /opt/bitnami/zookeeper/config/certs/quorum/zookeeper.truststore.jks + ## @param tls.quorum.passwordsSecretName Existing secret containing Keystore and truststore passwords + ## + passwordsSecretName: "" + ## @param tls.quorum.passwordsSecretKeystoreKey The secret key from the tls.quorum.passwordsSecretName containing the password for the Keystore. + ## + passwordsSecretKeystoreKey: "" + ## @param tls.quorum.passwordsSecretTruststoreKey The secret key from the tls.quorum.passwordsSecretName containing the password for the Truststore. + ## + passwordsSecretTruststoreKey: "" + ## @param tls.quorum.keystorePassword Password to access KeyStore if needed + ## + keystorePassword: "" + ## @param tls.quorum.truststorePassword Password to access TrustStore if needed + ## + truststorePassword: "" + ## Init container resource requests and limits + ## ref: https://kubernetes.io/docs/user-guide/compute-resources/ + ## @param tls.resources.limits The resources limits for the TLS init container + ## @param tls.resources.requests The requested resources for the TLS init container + ## + resources: + limits: {} + requests: {} diff --git a/knowlg-automation/helm_charts/kafka/templates/NOTES.txt b/knowlg-automation/helm_charts/kafka/templates/NOTES.txt new file mode 100644 index 000000000..1bdfb3315 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/templates/NOTES.txt @@ -0,0 +1,310 @@ +CHART NAME: {{ .Chart.Name }} +CHART VERSION: {{ .Chart.Version }} +APP VERSION: {{ .Chart.AppVersion }} + +{{- if .Values.diagnosticMode.enabled }} +The chart has been deployed in diagnostic mode. All probes have been disabled and the command has been overwritten with: + + command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 4 }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 4 }} + +Get the list of pods by executing: + + kubectl get pods --namespace {{ .Release.Namespace }} -l app.kubernetes.io/instance={{ .Release.Name }} + +Access the pod you want to debug by executing + + kubectl exec --namespace {{ .Release.Namespace }} -ti -- bash + +In order to replicate the container startup scripts execute this command: + + /opt/bitnami/scripts/kafka/entrypoint.sh /opt/bitnami/scripts/kafka/run.sh + +{{- else }} + +{{- $replicaCount := int .Values.replicaCount -}} +{{- $releaseNamespace := .Release.Namespace -}} +{{- $clusterDomain := .Values.clusterDomain -}} +{{- $fullname := include "common.names.fullname" . -}} +{{- $clientProtocol := include "kafka.listenerType" (dict "protocol" .Values.auth.clientProtocol) -}} +{{- $externalClientProtocol := include "kafka.listenerType" (dict "protocol" (include "kafka.externalClientProtocol" . )) -}} +{{- $saslMechanisms := .Values.auth.sasl.mechanisms -}} +{{- $tlsEndpointIdentificationAlgorithm := default "" .Values.auth.tls.endpointIdentificationAlgorithm -}} +{{- $tlsPasswordSecret := printf "$(kubectl get secret %s --namespace %s -o jsonpath='{.data.password}' | base64 -d | cut -d , -f 1)" .Values.auth.tls.existingSecret $releaseNamespace -}} +{{- $tlsPassword := ternary .Values.auth.tls.password $tlsPasswordSecret (eq .Values.auth.tls.existingSecret "") -}} +{{- $servicePort := int .Values.service.ports.client -}} + +{{- if and (or (eq .Values.service.type "LoadBalancer") .Values.externalAccess.enabled) (eq $externalClientProtocol "PLAINTEXT") }} +--------------------------------------------------------------------------------------------- + WARNING + + By specifying "serviceType=LoadBalancer" and not configuring the authentication + you have most likely exposed the Kafka service externally without any + authentication mechanism. + + For security reasons, we strongly suggest that you switch to "ClusterIP" or + "NodePort". As alternative, you can also configure the Kafka authentication. + +--------------------------------------------------------------------------------------------- +{{- end }} + +** Please be patient while the chart is being deployed ** + +Kafka can be accessed by consumers via port {{ $servicePort }} on the following DNS name from within your cluster: + + {{ $fullname }}.{{ $releaseNamespace }}.svc.{{ $clusterDomain }} + +Each Kafka broker can be accessed by producers via port {{ $servicePort }} on the following DNS name(s) from within your cluster: + +{{- $brokerList := list }} +{{- range $e, $i := until $replicaCount }} +{{- $brokerList = append $brokerList (printf "%s-%d.%s-headless.%s.svc.%s:%d" $fullname $i $fullname $releaseNamespace $clusterDomain $servicePort) }} +{{- end }} +{{ join "\n" $brokerList | nindent 4 }} +{{- if (include "kafka.client.saslAuthentication" .) }} + +You need to configure your Kafka client to access using SASL authentication. To do so, you need to create the 'kafka_jaas.conf' and 'client.properties' configuration files with the content below: + + - kafka_jaas.conf: + +KafkaClient { +{{- if $saslMechanisms | regexFind "scram" }} +org.apache.kafka.common.security.scram.ScramLoginModule required +{{- else }} +org.apache.kafka.common.security.plain.PlainLoginModule required +{{- end }} +username="{{ index .Values.auth.sasl.jaas.clientUsers 0 }}" +password="$(kubectl get secret {{ $fullname }}-jaas --namespace {{ $releaseNamespace }} -o jsonpath='{.data.client-passwords}' | base64 -d | cut -d , -f 1)"; +}; + + - client.properties: + +security.protocol={{ $clientProtocol }} +{{- if $saslMechanisms | regexFind "scram-sha-256" }} +sasl.mechanism=SCRAM-SHA-256 +{{- else if $saslMechanisms | regexFind "scram-sha-512" }} +sasl.mechanism=SCRAM-SHA-512 +{{- else }} +sasl.mechanism=PLAIN +{{- end }} +{{- if eq $clientProtocol "SASL_SSL" }} +ssl.truststore.type={{ upper .Values.auth.tls.type }} + {{- if eq .Values.auth.tls.type "jks" }} +ssl.truststore.location=/tmp/kafka.truststore.jks + {{- if not (empty $tlsPassword) }} +ssl.truststore.password={{ $tlsPassword }} + {{- end }} + {{- else if eq .Values.auth.tls.type "pem" }} +ssl.truststore.certificates=-----BEGIN CERTIFICATE----- \ +... \ +-----END CERTIFICATE----- + {{- end }} + {{- if eq $tlsEndpointIdentificationAlgorithm "" }} +ssl.endpoint.identification.algorithm= + {{- end }} +{{- end }} + +{{- else if (include "kafka.client.tlsEncryption" .) }} + +You need to configure your Kafka client to access using TLS authentication. To do so, you need to create the 'client.properties' configuration file with the content below: + +security.protocol={{ $clientProtocol }} +ssl.truststore.type={{ upper .Values.auth.tls.type }} +{{- if eq .Values.auth.tls.type "jks" }} +ssl.truststore.location=/tmp/kafka.truststore.{{ .Values.auth.tls.type }} + {{- if not (empty $tlsPassword) }} +ssl.truststore.password={{ $tlsPassword }} + {{- end }} +{{- else if eq .Values.auth.tls.type "pem" }} +ssl.truststore.certificates=-----BEGIN CERTIFICATE----- \ +... \ +-----END CERTIFICATE----- +{{- end }} +{{- if eq .Values.auth.clientProtocol "mtls" }} +ssl.keystore.type={{ upper .Values.auth.tls.type }} + {{- if eq .Values.auth.tls.type "jks" }} +ssl.keystore.location=/tmp/client.keystore.jks + {{- if not (empty $tlsPassword) }} +ssl.keystore.password={{ $tlsPassword }} + {{- end }} + {{- else if eq .Values.auth.tls.type "pem" }} +ssl.keystore.certificate.chain=-----BEGIN CERTIFICATE----- \ +... \ +-----END CERTIFICATE----- +ssl.keystore.key=-----BEGIN ENCRYPTED PRIVATE KEY----- \ +... \ +-----END ENCRYPTED PRIVATE KEY----- + {{- end }} +{{- end }} +{{- if eq $tlsEndpointIdentificationAlgorithm "" }} +ssl.endpoint.identification.algorithm= +{{- end }} + +{{- end }} + +To create a pod that you can use as a Kafka client run the following commands: + + kubectl run {{ $fullname }}-client --restart='Never' --image {{ template "kafka.image" . }} --namespace {{ $releaseNamespace }} --command -- sleep infinity + {{- if or (include "kafka.client.saslAuthentication" .) (include "kafka.client.tlsEncryption" .) }} + kubectl cp --namespace {{ $releaseNamespace }} /path/to/client.properties {{ $fullname }}-client:/tmp/client.properties + {{- end }} + {{- if (include "kafka.client.saslAuthentication" .) }} + kubectl cp --namespace {{ $releaseNamespace }} /path/to/kafka_jaas.conf {{ $fullname }}-client:/tmp/kafka_jaas.conf + {{- end }} + {{- if and (include "kafka.client.tlsEncryption" .) (eq .Values.auth.tls.type "jks") }} + kubectl cp --namespace {{ $releaseNamespace }} ./kafka.truststore.jks {{ $fullname }}-client:/tmp/kafka.truststore.jks + {{- if eq .Values.auth.clientProtocol "mtls" }} + kubectl cp --namespace {{ $releaseNamespace }} ./client.keystore.jks {{ $fullname }}-client:/tmp/client.keystore.jks + {{- end }} + {{- end }} + kubectl exec --tty -i {{ $fullname }}-client --namespace {{ $releaseNamespace }} -- bash + {{- if (include "kafka.client.saslAuthentication" .) }} + export KAFKA_OPTS="-Djava.security.auth.login.config=/tmp/kafka_jaas.conf" + {{- end }} + + PRODUCER: + kafka-console-producer.sh \ + {{ if or (include "kafka.client.saslAuthentication" .) (include "kafka.client.tlsEncryption" .) }}--producer.config /tmp/client.properties \{{ end }} + --broker-list {{ join "," $brokerList }} \ + --topic test + + CONSUMER: + kafka-console-consumer.sh \ + {{ if or (include "kafka.client.saslAuthentication" .) (include "kafka.client.tlsEncryption" .) }}--consumer.config /tmp/client.properties \{{ end }} + --bootstrap-server {{ $fullname }}.{{ $releaseNamespace }}.svc.{{ $clusterDomain }}:{{ .Values.service.ports.client }} \ + --topic test \ + --from-beginning + +{{- if .Values.externalAccess.enabled }} + +To connect to your Kafka server from outside the cluster, follow the instructions below: + +{{- if eq "NodePort" .Values.externalAccess.service.type }} +{{- if .Values.externalAccess.service.domain }} + + Kafka brokers domain: Use your provided hostname to reach Kafka brokers, {{ .Values.externalAccess.service.domain }} + +{{- else }} + + Kafka brokers domain: You can get the external node IP from the Kafka configuration file with the following commands (Check the EXTERNAL listener) + + 1. Obtain the pod name: + + kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ template "kafka.name" . }},app.kubernetes.io/instance={{ .Release.Name }},app.kubernetes.io/component=kafka" + + 2. Obtain pod configuration: + + kubectl exec -it KAFKA_POD -- cat /opt/bitnami/kafka/config/server.properties | grep advertised.listeners + +{{- end }} + + Kafka brokers port: You will have a different node port for each Kafka broker. You can get the list of configured node ports using the command below: + + echo "$(kubectl get svc --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ template "kafka.name" . }},app.kubernetes.io/instance={{ .Release.Name }},app.kubernetes.io/component=kafka,pod" -o jsonpath='{.items[*].spec.ports[0].nodePort}' | tr ' ' '\n')" + +{{- else if contains "LoadBalancer" .Values.externalAccess.service.type }} + + NOTE: It may take a few minutes for the LoadBalancer IPs to be available. + Watch the status with: 'kubectl get svc --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ template "kafka.name" . }},app.kubernetes.io/instance={{ .Release.Name }},app.kubernetes.io/component=kafka,pod" -w' + + Kafka Brokers domain: You will have a different external IP for each Kafka broker. You can get the list of external IPs using the command below: + + echo "$(kubectl get svc --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ template "kafka.name" . }},app.kubernetes.io/instance={{ .Release.Name }},app.kubernetes.io/component=kafka,pod" -o jsonpath='{.items[*].status.loadBalancer.ingress[0].ip}' | tr ' ' '\n')" + + Kafka Brokers port: {{ .Values.externalAccess.service.ports.external }} + +{{- else if eq "ClusterIP" .Values.externalAccess.service.type }} + + Kafka brokers domain: Use your provided hostname to reach Kafka brokers, {{ .Values.externalAccess.service.domain }} + + Kafka brokers port: You will have a different port for each Kafka broker starting at {{ .Values.externalAccess.service.ports.external }} + +{{- end }} + +{{- if not (eq $clientProtocol $externalClientProtocol) }} +{{- if (include "kafka.client.saslAuthentication" .) }} + +You need to configure your Kafka client to access using SASL authentication. To do so, you need to create the 'kafka_jaas.conf' and 'client.properties' configuration files with the content below: + + - kafka_jaas.conf: + +KafkaClient { +{{- if $saslMechanisms | regexFind "scram" }} +org.apache.kafka.common.security.scram.ScramLoginModule required +{{- else }} +org.apache.kafka.common.security.plain.PlainLoginModule required +{{- end }} +username="{{ index .Values.auth.sasl.jaas.clientUsers 0 }}" +password="$(kubectl get secret {{ $fullname }}-jaas --namespace {{ $releaseNamespace }} -o jsonpath='{.data.client-passwords}' | base64 -d | cut -d , -f 1)"; +}; + + - client.properties: + +security.protocol={{ $externalClientProtocol }} +{{- if $saslMechanisms | regexFind "scram-sha-256" }} +sasl.mechanism=SCRAM-SHA-256 +{{- else if $saslMechanisms | regexFind "scram-sha-512" }} +sasl.mechanism=SCRAM-SHA-512 +{{- else }} +sasl.mechanism=PLAIN +{{- end }} +{{- if eq $externalClientProtocol "SASL_SSL" }} +ssl.truststore.type={{ upper .Values.auth.tls.type }} + {{- if eq .Values.auth.tls.type "jks" }} +ssl.truststore.location=/tmp/kafka.truststore.jks + {{- if not (empty $tlsPassword) }} +ssl.truststore.password={{ $tlsPassword }} + {{- end }} + {{- else if eq .Values.auth.tls.type "pem" }} +ssl.truststore.certificates=-----BEGIN CERTIFICATE----- \ +... \ +-----END CERTIFICATE----- + {{- end }} + {{- if eq $tlsEndpointIdentificationAlgorithm "" }} +ssl.endpoint.identification.algorithm= + {{- end }} +{{- end }} + +{{- else if (include "kafka.externalClient.tlsEncryption" .) }} + +You need to configure your Kafka client to access using TLS authentication. To do so, you need to create the 'client.properties' configuration file with the content below: + +security.protocol={{ $externalClientProtocol }} +ssl.truststore.type={{ upper .Values.auth.tls.type }} +{{- if eq .Values.auth.tls.type "jks" }} +ssl.truststore.location=/tmp/kafka.truststore.{{ .Values.auth.tls.type }} + {{- if not (empty $tlsPassword) }} +ssl.truststore.password={{ $tlsPassword }} + {{- end }} +{{- else if eq .Values.auth.tls.type "pem" }} +ssl.truststore.certificates=-----BEGIN CERTIFICATE----- \ +... \ +-----END CERTIFICATE----- +{{- end }} +{{- if eq .Values.auth.externalClientProtocol "mtls" }} +ssl.keystore.type={{ upper .Values.auth.tls.type }} + {{- if eq .Values.auth.tls.type "jks" }} +ssl.keystore.location=/tmp/client.keystore.jks + {{- if not (empty $tlsPassword) }} +ssl.keystore.password={{ $tlsPassword }} + {{- end }} + {{- else if eq .Values.auth.tls.type "pem" }} +ssl.keystore.certificate.chain=-----BEGIN CERTIFICATE----- \ +... \ +-----END CERTIFICATE----- +ssl.keystore.key=-----BEGIN ENCRYPTED PRIVATE KEY----- \ +... \ +-----END ENCRYPTED PRIVATE KEY----- + {{- end }} +{{- end }} +{{- if eq $tlsEndpointIdentificationAlgorithm "" }} +ssl.endpoint.identification.algorithm= +{{- end }} + +{{- end }} +{{- end }} +{{- end }} +{{- end }} + +{{- include "kafka.checkRollingTags" . }} +{{- include "kafka.validateValues" . }} diff --git a/knowlg-automation/helm_charts/kafka/templates/_helpers.tpl b/knowlg-automation/helm_charts/kafka/templates/_helpers.tpl new file mode 100644 index 000000000..51ec867d5 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/templates/_helpers.tpl @@ -0,0 +1,509 @@ +{{/* vim: set filetype=mustache: */}} + +{{/* +Expand the name of the chart. +*/}} +{{- define "kafka.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified zookeeper name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "kafka.zookeeper.fullname" -}} +{{- if .Values.zookeeper.fullnameOverride -}} +{{- .Values.zookeeper.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default "zookeeper" .Values.zookeeper.nameOverride -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} + +{{/* + Create the name of the service account to use + */}} +{{- define "kafka.serviceAccountName" -}} +{{- if .Values.serviceAccount.create -}} + {{ default (include "common.names.fullname" .) .Values.serviceAccount.name }} +{{- else -}} + {{ default "default" .Values.serviceAccount.name }} +{{- end -}} +{{- end -}} + +{{/* +Return the proper Kafka image name +*/}} +{{- define "kafka.image" -}} +{{ include "common.images.image" (dict "imageRoot" .Values.image "global" .Values.global) }} +{{- end -}} + +{{/* +Return the proper image name (for the init container auto-discovery image) +*/}} +{{- define "kafka.externalAccess.autoDiscovery.image" -}} +{{ include "common.images.image" (dict "imageRoot" .Values.externalAccess.autoDiscovery.image "global" .Values.global) }} +{{- end -}} + +{{/* +Return the proper image name (for the init container volume-permissions image) +*/}} +{{- define "kafka.volumePermissions.image" -}} +{{ include "common.images.image" (dict "imageRoot" .Values.volumePermissions.image "global" .Values.global) }} +{{- end -}} + +{{/* +Create a default fully qualified Kafka exporter name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "kafka.metrics.kafka.fullname" -}} + {{- printf "%s-exporter" (include "common.names.fullname" .) | trunc 63 | trimSuffix "-" }} +{{- end -}} + +{{/* + Create the name of the service account to use for Kafka exporter pods + */}} +{{- define "kafka.metrics.kafka.serviceAccountName" -}} +{{- if .Values.metrics.kafka.serviceAccount.create -}} + {{ default (include "kafka.metrics.kafka.fullname" .) .Values.metrics.kafka.serviceAccount.name }} +{{- else -}} + {{ default "default" .Values.metrics.kafka.serviceAccount.name }} +{{- end -}} +{{- end -}} + +{{/* +Return the proper Kafka exporter image name +*/}} +{{- define "kafka.metrics.kafka.image" -}} +{{ include "common.images.image" (dict "imageRoot" .Values.metrics.kafka.image "global" .Values.global) }} +{{- end -}} + +{{/* +Return the proper JMX exporter image name +*/}} +{{- define "kafka.metrics.jmx.image" -}} +{{ include "common.images.image" (dict "imageRoot" .Values.metrics.jmx.image "global" .Values.global) }} +{{- end -}} + +{{/* +Return the proper Docker Image Registry Secret Names +*/}} +{{- define "kafka.imagePullSecrets" -}} +{{ include "common.images.pullSecrets" (dict "images" (list .Values.image .Values.externalAccess.autoDiscovery.image .Values.volumePermissions.image .Values.metrics.kafka.image .Values.metrics.jmx.image) "global" .Values.global) }} +{{- end -}} + +{{/* +Return the proper Storage Class +*/}} +{{- define "kafka.storageClass" -}} +{{/* +Helm 2.11 supports the assignment of a value to a variable defined in a different scope, +but Helm 2.9 and 2.10 does not support it, so we need to implement this if-else logic. +*/}} +{{- if .Values.global -}} + {{- if .Values.global.storageClass -}} + {{- if (eq "-" .Values.global.storageClass) -}} + {{- printf "storageClassName: \"\"" -}} + {{- else }} + {{- printf "storageClassName: %s" .Values.global.storageClass -}} + {{- end -}} + {{- else -}} + {{- if .Values.persistence.storageClass -}} + {{- if (eq "-" .Values.persistence.storageClass) -}} + {{- printf "storageClassName: \"\"" -}} + {{- else }} + {{- printf "storageClassName: %s" .Values.persistence.storageClass -}} + {{- end -}} + {{- end -}} + {{- end -}} +{{- else -}} + {{- if .Values.persistence.storageClass -}} + {{- if (eq "-" .Values.persistence.storageClass) -}} + {{- printf "storageClassName: \"\"" -}} + {{- else }} + {{- printf "storageClassName: %s" .Values.persistence.storageClass -}} + {{- end -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return true if authentication via SASL should be configured for client communications +*/}} +{{- define "kafka.client.saslAuthentication" -}} +{{- $saslProtocols := list "sasl" "sasl_tls" -}} +{{- if has .Values.auth.clientProtocol $saslProtocols -}} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Return true if authentication via SASL should be configured for inter-broker communications +*/}} +{{- define "kafka.interBroker.saslAuthentication" -}} +{{- $saslProtocols := list "sasl" "sasl_tls" -}} +{{- if has .Values.auth.interBrokerProtocol $saslProtocols -}} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Return true if encryption via TLS for client connections should be configured +*/}} +{{- define "kafka.client.tlsEncryption" -}} +{{- $tlsProtocols := list "tls" "mtls" "sasl_tls" -}} +{{- if (has .Values.auth.clientProtocol $tlsProtocols) -}} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Return the configured value for the external client protocol, defaults to the same value as clientProtocol +*/}} +{{- define "kafka.externalClientProtocol" -}} + {{- coalesce .Values.auth.externalClientProtocol .Values.auth.clientProtocol -}} +{{- end -}} + +{{/* +Return true if encryption via TLS for external client connections should be configured +*/}} +{{- define "kafka.externalClient.tlsEncryption" -}} +{{- $tlsProtocols := list "tls" "mtls" "sasl_tls" -}} +{{- if (has (include "kafka.externalClientProtocol" . ) $tlsProtocols) -}} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Return true if encryption via TLS for inter broker communication connections should be configured +*/}} +{{- define "kafka.interBroker.tlsEncryption" -}} +{{- $tlsProtocols := list "tls" "mtls" "sasl_tls" -}} +{{- if (has .Values.auth.interBrokerProtocol $tlsProtocols) -}} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Return true if encryption via TLS should be configured +*/}} +{{- define "kafka.tlsEncryption" -}} +{{- if or (include "kafka.client.tlsEncryption" .) (include "kafka.interBroker.tlsEncryption" .) (include "kafka.externalClient.tlsEncryption" .) -}} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Return the type of listener +Usage: +{{ include "kafka.listenerType" ( dict "protocol" .Values.path.to.the.Value ) }} +*/}} +{{- define "kafka.listenerType" -}} +{{- if eq .protocol "plaintext" -}} +PLAINTEXT +{{- else if or (eq .protocol "tls") (eq .protocol "mtls") -}} +SSL +{{- else if eq .protocol "sasl_tls" -}} +SASL_SSL +{{- else if eq .protocol "sasl" -}} +SASL_PLAINTEXT +{{- end -}} +{{- end -}} + +{{/* +Return the protocol used with zookeeper +*/}} +{{- define "kafka.zookeeper.protocol" -}} +{{- if and .Values.auth.zookeeper.tls.enabled .Values.zookeeper.auth.client.enabled .Values.auth.sasl.jaas.zookeeperUser -}} +SASL_SSL +{{- else if and .Values.auth.zookeeper.tls.enabled -}} +SSL +{{- else if and .Values.zookeeper.auth.client.enabled .Values.auth.sasl.jaas.zookeeperUser -}} +SASL +{{- else -}} +PLAINTEXT +{{- end -}} +{{- end -}} + +{{/* +Return the Kafka JAAS credentials secret +*/}} +{{- define "kafka.jaasSecretName" -}} +{{- $secretName := .Values.auth.sasl.jaas.existingSecret -}} +{{- if $secretName -}} + {{- printf "%s" (tpl $secretName $) -}} +{{- else -}} + {{- printf "%s-jaas" (include "common.names.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{/* +Return true if a JAAS credentials secret object should be created +*/}} +{{- define "kafka.createJaasSecret" -}} +{{- $secretName := .Values.auth.sasl.jaas.existingSecret -}} +{{- if and (or (include "kafka.client.saslAuthentication" .) (include "kafka.interBroker.saslAuthentication" .) (and .Values.zookeeper.auth.client.enabled .Values.auth.sasl.jaas.zookeeperUser)) (empty $secretName) -}} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Return true if a TLS credentials secret object should be created +*/}} +{{- define "kafka.createTlsSecret" -}} +{{- if and (include "kafka.tlsEncryption" .) (empty .Values.auth.tls.existingSecrets) (eq .Values.auth.tls.type "pem") .Values.auth.tls.autoGenerated }} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Return the Kafka configuration configmap +*/}} +{{- define "kafka.configmapName" -}} +{{- if .Values.existingConfigmap -}} + {{- printf "%s" (tpl .Values.existingConfigmap $) -}} +{{- else -}} + {{- printf "%s-configuration" (include "common.names.fullname" .) -}} +{{- end -}} +{{- end -}} + + +{{/* +Returns the secret name for the Kafka Provisioning client +*/}} +{{- define "kafka.client.passwordsSecretName" -}} +{{- if .Values.provisioning.auth.tls.passwordsSecret -}} + {{- printf "%s" (tpl .Values.provisioning.auth.tls.passwordsSecret $) -}} +{{- else -}} + {{- printf "%s-client-secret" (include "common.names.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{/* +Create the name of the service account to use for the Kafka Provisioning client +*/}} +{{- define "kafka.provisioning.serviceAccountName" -}} +{{- if .Values.provisioning.serviceAccount.create -}} + {{ default (include "common.names.fullname" .) .Values.provisioning.serviceAccount.name }} +{{- else -}} + {{ default "default" .Values.provisioning.serviceAccount.name }} +{{- end -}} +{{- end -}} + +{{/* +Return true if a configmap object should be created +*/}} +{{- define "kafka.createConfigmap" -}} +{{- if and .Values.config (not .Values.existingConfigmap) }} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Return the Kafka log4j ConfigMap name. +*/}} +{{- define "kafka.log4j.configMapName" -}} +{{- if .Values.existingLog4jConfigMap -}} + {{- printf "%s" (tpl .Values.existingLog4jConfigMap $) -}} +{{- else -}} + {{- printf "%s-log4j-configuration" (include "common.names.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{/* +Return true if a log4j ConfigMap object should be created. +*/}} +{{- define "kafka.log4j.createConfigMap" -}} +{{- if and .Values.log4j (not .Values.existingLog4jConfigMap) }} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Return the SASL mechanism to use for the Kafka exporter to access Kafka +The exporter uses a different nomenclature so we need to do this hack +*/}} +{{- define "kafka.metrics.kafka.saslMechanism" -}} +{{- $saslMechanisms := .Values.auth.sasl.mechanisms }} +{{- if contains "scram-sha-512" $saslMechanisms }} + {{- print "scram-sha512" -}} +{{- else if contains "scram-sha-256" $saslMechanisms }} + {{- print "scram-sha256" -}} +{{- else -}} + {{- print "plain" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the Kafka configuration configmap +*/}} +{{- define "kafka.metrics.jmx.configmapName" -}} +{{- if .Values.metrics.jmx.existingConfigmap -}} + {{- printf "%s" (tpl .Values.metrics.jmx.existingConfigmap $) -}} +{{- else -}} + {{- printf "%s-jmx-configuration" (include "common.names.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{/* +Return true if a configmap object should be created +*/}} +{{- define "kafka.metrics.jmx.createConfigmap" -}} +{{- if and .Values.metrics.jmx.enabled .Values.metrics.jmx.config (not .Values.metrics.jmx.existingConfigmap) }} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Check if there are rolling tags in the images +*/}} +{{- define "kafka.checkRollingTags" -}} +{{- include "common.warnings.rollingTag" .Values.image }} +{{- include "common.warnings.rollingTag" .Values.externalAccess.autoDiscovery.image }} +{{- include "common.warnings.rollingTag" .Values.metrics.kafka.image }} +{{- include "common.warnings.rollingTag" .Values.metrics.jmx.image }} +{{- include "common.warnings.rollingTag" .Values.volumePermissions.image }} +{{- end -}} + +{{/* +Compile all warnings into a single message, and call fail. +*/}} +{{- define "kafka.validateValues" -}} +{{- $messages := list -}} +{{- $messages := append $messages (include "kafka.validateValues.authProtocols" .) -}} +{{- $messages := append $messages (include "kafka.validateValues.nodePortListLength" .) -}} +{{- $messages := append $messages (include "kafka.validateValues.domainSpecified" .) -}} +{{- $messages := append $messages (include "kafka.validateValues.externalAccessServiceType" .) -}} +{{- $messages := append $messages (include "kafka.validateValues.externalAccessAutoDiscoveryRBAC" .) -}} +{{- $messages := append $messages (include "kafka.validateValues.externalAccessAutoDiscoveryIPsOrNames" .) -}} +{{- $messages := append $messages (include "kafka.validateValues.externalAccessServiceList" (dict "element" "loadBalancerIPs" "context" .)) -}} +{{- $messages := append $messages (include "kafka.validateValues.externalAccessServiceList" (dict "element" "loadBalancerNames" "context" .)) -}} +{{- $messages := append $messages (include "kafka.validateValues.externalAccessServiceList" (dict "element" "loadBalancerAnnotations" "context" . )) -}} +{{- $messages := append $messages (include "kafka.validateValues.saslMechanisms" .) -}} +{{- $messages := append $messages (include "kafka.validateValues.tlsSecrets" .) -}} +{{- $messages := append $messages (include "kafka.validateValues.tlsSecrets.length" .) -}} +{{- $messages := append $messages (include "kafka.validateValues.tlsPasswords" .) -}} +{{- $messages := without $messages "" -}} +{{- $message := join "\n" $messages -}} + +{{- if $message -}} +{{- printf "\nVALUES VALIDATION:\n%s" $message | fail -}} +{{- end -}} +{{- end -}} + +{{/* Validate values of Kafka - Authentication protocols for Kafka */}} +{{- define "kafka.validateValues.authProtocols" -}} +{{- $authProtocols := list "plaintext" "tls" "mtls" "sasl" "sasl_tls" -}} +{{- if or (not (has .Values.auth.clientProtocol $authProtocols)) (not (has .Values.auth.interBrokerProtocol $authProtocols)) (not (has (include "kafka.externalClientProtocol" . ) $authProtocols)) -}} +kafka: auth.clientProtocol auth.externalClientProtocol auth.interBrokerProtocol + Available authentication protocols are "plaintext", "tls", "mtls", "sasl" and "sasl_tls" +{{- end -}} +{{- end -}} + +{{/* Validate values of Kafka - number of replicas must be the same as NodePort list */}} +{{- define "kafka.validateValues.nodePortListLength" -}} +{{- $replicaCount := int .Values.replicaCount }} +{{- $nodePortListLength := len .Values.externalAccess.service.nodePorts }} +{{- if and .Values.externalAccess.enabled (not .Values.externalAccess.autoDiscovery.enabled) (not (eq $replicaCount $nodePortListLength )) (eq .Values.externalAccess.service.type "NodePort") -}} +kafka: .Values.externalAccess.service.nodePorts + Number of replicas and nodePort array length must be the same. Currently: replicaCount = {{ $replicaCount }} and nodePorts = {{ $nodePortListLength }} +{{- end -}} +{{- end -}} + +{{/* Validate values of Kafka - domain must be defined if external service type ClusterIP */}} +{{- define "kafka.validateValues.domainSpecified" -}} +{{- if and (eq .Values.externalAccess.service.type "ClusterIP") (eq .Values.externalAccess.service.domain "") -}} +kafka: .Values.externalAccess.service.domain + Domain must be specified if service type ClusterIP is set for external service +{{- end -}} +{{- end -}} + +{{/* Validate values of Kafka - service type for external access */}} +{{- define "kafka.validateValues.externalAccessServiceType" -}} +{{- if and (not (eq .Values.externalAccess.service.type "NodePort")) (not (eq .Values.externalAccess.service.type "LoadBalancer")) (not (eq .Values.externalAccess.service.type "ClusterIP")) -}} +kafka: externalAccess.service.type + Available service type for external access are NodePort, LoadBalancer or ClusterIP. +{{- end -}} +{{- end -}} + +{{/* Validate values of Kafka - RBAC should be enabled when autoDiscovery is enabled */}} +{{- define "kafka.validateValues.externalAccessAutoDiscoveryRBAC" -}} +{{- if and .Values.externalAccess.enabled .Values.externalAccess.autoDiscovery.enabled (not .Values.rbac.create ) }} +kafka: rbac.create + By specifying "externalAccess.enabled=true" and "externalAccess.autoDiscovery.enabled=true" + an initContainer will be used to auto-detect the external IPs/ports by querying the + K8s API. Please note this initContainer requires specific RBAC resources. You can create them + by specifying "--set rbac.create=true". +{{- end -}} +{{- end -}} + +{{/* Validate values of Kafka - LoadBalancerIPs or LoadBalancerNames should be set when autoDiscovery is disabled */}} +{{- define "kafka.validateValues.externalAccessAutoDiscoveryIPsOrNames" -}} +{{- $loadBalancerNameListLength := len .Values.externalAccess.service.loadBalancerNames -}} +{{- $loadBalancerIPListLength := len .Values.externalAccess.service.loadBalancerIPs -}} +{{- if and .Values.externalAccess.enabled (eq .Values.externalAccess.service.type "LoadBalancer") (not .Values.externalAccess.autoDiscovery.enabled) (eq $loadBalancerNameListLength 0) (eq $loadBalancerIPListLength 0) }} +kafka: externalAccess.service.loadBalancerNames or externalAccess.service.loadBalancerIPs + By specifying "externalAccess.enabled=true", "externalAccess.autoDiscovery.enabled=false" and + "externalAccess.service.type=LoadBalancer" at least one of externalAccess.service.loadBalancerNames + or externalAccess.service.loadBalancerIPs must be set and the length of those arrays must be equal + to the number of replicas. +{{- end -}} +{{- end -}} + +{{/* Validate values of Kafka - number of replicas must be the same as loadBalancerIPs list */}} +{{- define "kafka.validateValues.externalAccessServiceList" -}} +{{- $replicaCount := int .context.Values.replicaCount }} +{{- $listLength := len (get .context.Values.externalAccess.service .element) -}} +{{- if and .context.Values.externalAccess.enabled (not .context.Values.externalAccess.autoDiscovery.enabled) (eq .context.Values.externalAccess.service.type "LoadBalancer") (gt $listLength 0) (not (eq $replicaCount $listLength)) }} +kafka: externalAccess.service.{{ .element }} + Number of replicas and {{ .element }} array length must be the same. Currently: replicaCount = {{ $replicaCount }} and {{ .element }} = {{ $listLength }} +{{- end -}} +{{- end -}} + +{{/* Validate values of Kafka - SASL mechanisms must be provided when using SASL */}} +{{- define "kafka.validateValues.saslMechanisms" -}} +{{- if and (or (.Values.auth.clientProtocol | regexFind "sasl") (.Values.auth.interBrokerProtocol | regexFind "sasl") (and .Values.zookeeper.auth.client.enabled .Values.auth.sasl.jaas.zookeeperUser)) (not .Values.auth.sasl.mechanisms) }} +kafka: auth.sasl.mechanisms + The SASL mechanisms are required when either auth.clientProtocol or auth.interBrokerProtocol use SASL or Zookeeper user is provided. +{{- end }} +{{- if not (contains .Values.auth.sasl.interBrokerMechanism .Values.auth.sasl.mechanisms) }} +kafka: auth.sasl.mechanisms + auth.sasl.interBrokerMechanism must be provided and it should be one of the specified mechanisms at auth.saslMechanisms +{{- end -}} +{{- end -}} + +{{/* Validate values of Kafka - Secrets containing TLS certs must be provided when TLS authentication is enabled */}} +{{- define "kafka.validateValues.tlsSecrets" -}} +{{- if and (include "kafka.tlsEncryption" .) (eq .Values.auth.tls.type "jks") (empty .Values.auth.tls.existingSecrets) }} +kafka: auth.tls.existingSecrets + A secret containing the Kafka JKS keystores and truststore is required + when TLS encryption in enabled and TLS format is "JKS" +{{- else if and (include "kafka.tlsEncryption" .) (eq .Values.auth.tls.type "pem") (empty .Values.auth.tls.existingSecrets) (not .Values.auth.tls.autoGenerated) }} +kafka: auth.tls.existingSecrets + A secret containing the Kafka TLS certificates and keys is required + when TLS encryption in enabled and TLS format is "PEM" +{{- end -}} +{{- end -}} + +{{/* Validate values of Kafka - The number of secrets containing TLS certs should be equal to the number of replicas */}} +{{- define "kafka.validateValues.tlsSecrets.length" -}} +{{- $replicaCount := int .Values.replicaCount }} +{{- if and (include "kafka.tlsEncryption" .) (not (empty .Values.auth.tls.existingSecrets)) }} +{{- $existingSecretsLength := len .Values.auth.tls.existingSecrets }} +{{- if ne $replicaCount $existingSecretsLength }} +kafka: .Values.auth.tls.existingSecrets + Number of replicas and existingSecrets array length must be the same. Currently: replicaCount = {{ $replicaCount }} and existingSecrets = {{ $existingSecretsLength }} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* Validate values of Kafka provisioning - keyPasswordSecretKey, keystorePasswordSecretKey or truststorePasswordSecretKey must not be used without passwordsSecret */}} +{{- define "kafka.validateValues.tlsPasswords" -}} +{{- if and (include "kafka.client.tlsEncryption" .) (not .Values.auth.tls.passwordsSecret) }} +{{- if or .Values.auth.tls.keyPasswordSecretKey .Values.auth.tls.keystorePasswordSecretKey .Values.auth.tls.truststorePasswordSecretKey }} +kafka: auth.tls.keyPasswordSecretKey,auth.tls.keystorePasswordSecretKey,auth.tls.truststorePasswordSecretKey + auth.tls.keyPasswordSecretKey,auth.tls.keystorePasswordSecretKey,auth.tls.truststorePasswordSecretKey + must not be used without passwordsSecret setted. +{{- end -}} +{{- end -}} +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/templates/configmap.yaml b/knowlg-automation/helm_charts/kafka/templates/configmap.yaml new file mode 100644 index 000000000..509fd1c4f --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/templates/configmap.yaml @@ -0,0 +1,17 @@ +{{- if (include "kafka.createConfigmap" .) }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ printf "%s-configuration" (include "common.names.fullname" .) }} + namespace: {{ .Release.Namespace | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +data: + server.properties: |- + {{ .Values.config | nindent 4 }} +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/templates/extra-list.yaml b/knowlg-automation/helm_charts/kafka/templates/extra-list.yaml new file mode 100644 index 000000000..9ac65f9e1 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/templates/extra-list.yaml @@ -0,0 +1,4 @@ +{{- range .Values.extraDeploy }} +--- +{{ include "common.tplvalues.render" (dict "value" . "context" $) }} +{{- end }} diff --git a/knowlg-automation/helm_charts/kafka/templates/jaas-secret.yaml b/knowlg-automation/helm_charts/kafka/templates/jaas-secret.yaml new file mode 100644 index 000000000..8f632e56e --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/templates/jaas-secret.yaml @@ -0,0 +1,40 @@ +{{- if (include "kafka.createJaasSecret" .) }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ printf "%s-jaas" (include "common.names.fullname" .) }} + namespace: {{ .Release.Namespace | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +type: Opaque +data: + {{- if (include "kafka.client.saslAuthentication" .) }} + {{- $clientUsers := .Values.auth.sasl.jaas.clientUsers }} + {{- $clientPasswords := .Values.auth.sasl.jaas.clientPasswords }} + {{- if $clientPasswords }} + client-passwords: {{ join "," $clientPasswords | b64enc | quote }} + system-user-password: {{ index $clientPasswords 0 | b64enc | quote }} + {{- else }} + {{- $passwords := list }} + {{- range $clientUsers }} + {{- $passwords = append $passwords (randAlphaNum 10) }} + {{- end }} + client-passwords: {{ join "," $passwords | b64enc | quote }} + system-user-password: {{ index $passwords 0 | b64enc | quote }} + {{- end }} + {{- end }} + {{- $zookeeperUser := .Values.auth.sasl.jaas.zookeeperUser }} + {{- if and .Values.zookeeper.auth.client.enabled $zookeeperUser }} + {{- $zookeeperPassword := .Values.auth.sasl.jaas.zookeeperPassword }} + zookeeper-password: {{ default (randAlphaNum 10) $zookeeperPassword | b64enc | quote }} + {{- end }} + {{- if (include "kafka.interBroker.saslAuthentication" .) }} + {{- $interBrokerPassword := .Values.auth.sasl.jaas.interBrokerPassword }} + inter-broker-password: {{ default (randAlphaNum 10) $interBrokerPassword | b64enc | quote }} + {{- end }} +{{- end }} diff --git a/knowlg-automation/helm_charts/kafka/templates/jmx-configmap.yaml b/knowlg-automation/helm_charts/kafka/templates/jmx-configmap.yaml new file mode 100644 index 000000000..9207f6d1d --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/templates/jmx-configmap.yaml @@ -0,0 +1,64 @@ +{{- if (include "kafka.metrics.jmx.createConfigmap" .) }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ printf "%s-jmx-configuration" (include "common.names.fullname" .) }} + namespace: {{ .Release.Namespace | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: metrics + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +data: + jmx-kafka-prometheus.yml: |- + {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.jmx.config "context" $ ) | nindent 4 }} + rules: + - pattern: kafka.controller<>(Value) + name: kafka_controller_$1_$2_$4 + labels: + broker_id: "$3" + - pattern: kafka.controller<>(Value) + name: kafka_controller_$1_$2_$3 + - pattern: kafka.controller<>(Value) + name: kafka_controller_$1_$2_$3 + - pattern: kafka.controller<>(Count) + name: kafka_controller_$1_$2_$3 + - pattern: kafka.server<>(Value) + name: kafka_server_$1_$2_$4 + labels: + client_id: "$3" + - pattern : kafka.network<>(Value) + name: kafka_network_$1_$2_$4 + labels: + network_processor: $3 + - pattern : kafka.network<>(Count) + name: kafka_network_$1_$2_$4 + labels: + request: $3 + - pattern: kafka.server<>(Count|OneMinuteRate) + name: kafka_server_$1_$2_$4 + labels: + topic: $3 + - pattern: kafka.server<>(Value) + name: kafka_server_$1_$2_$3_$4 + - pattern: kafka.server<>(Count|Value|OneMinuteRate) + name: kafka_server_$1_total_$2_$3 + - pattern: kafka.server<>(queue-size) + name: kafka_server_$1_$2 + - pattern: java.lang<(.+)>(\w+) + name: java_lang_$1_$4_$3_$2 + - pattern: java.lang<>(\w+) + name: java_lang_$1_$3_$2 + - pattern : java.lang + - pattern: kafka.log<>Value + name: kafka_log_$1_$2 + labels: + topic: $3 + partition: $4 + {{- if .Values.metrics.jmx.extraRules }} + {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.jmx.extraRules "context" $ ) | nindent 6 }} + {{- end }} +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/templates/jmx-metrics-svc.yaml b/knowlg-automation/helm_charts/kafka/templates/jmx-metrics-svc.yaml new file mode 100644 index 000000000..35c79f41f --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/templates/jmx-metrics-svc.yaml @@ -0,0 +1,34 @@ +{{- if .Values.metrics.jmx.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ printf "%s-jmx-metrics" (include "common.names.fullname" .) }} + namespace: {{ .Release.Namespace | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: metrics + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if or .Values.metrics.jmx.service.annotations .Values.commonAnnotations }} + annotations: + {{- if .Values.metrics.jmx.service.annotations }} + {{ include "common.tplvalues.render" ( dict "value" .Values.metrics.jmx.service.annotations "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- end }} +spec: + type: ClusterIP + sessionAffinity: {{ .Values.metrics.jmx.service.sessionAffinity }} + {{- if .Values.metrics.jmx.service.clusterIP }} + clusterIP: {{ .Values.metrics.jmx.service.clusterIP }} + {{- end }} + ports: + - name: http-metrics + port: {{ .Values.metrics.jmx.service.ports.metrics }} + protocol: TCP + targetPort: metrics + selector: {{- include "common.labels.matchLabels" . | nindent 4 }} + app.kubernetes.io/component: kafka +{{- end }} diff --git a/knowlg-automation/helm_charts/kafka/templates/kafka-metrics-deployment.yaml b/knowlg-automation/helm_charts/kafka/templates/kafka-metrics-deployment.yaml new file mode 100644 index 000000000..bf731f20b --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/templates/kafka-metrics-deployment.yaml @@ -0,0 +1,171 @@ +{{- if .Values.metrics.kafka.enabled }} +{{- $replicaCount := int .Values.replicaCount -}} +{{- $releaseNamespace := .Release.Namespace -}} +{{- $clusterDomain := .Values.clusterDomain -}} +{{- $fullname := include "common.names.fullname" . -}} +{{- $servicePort := int .Values.service.ports.client -}} +apiVersion: {{ include "common.capabilities.deployment.apiVersion" . }} +kind: Deployment +metadata: + name: {{ include "kafka.metrics.kafka.fullname" . }} + namespace: {{ .Release.Namespace | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: cluster-metrics + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + replicas: 1 + selector: + matchLabels: {{- include "common.labels.matchLabels" . | nindent 6 }} + app.kubernetes.io/component: cluster-metrics + template: + metadata: + labels: {{- include "common.labels.standard" . | nindent 8 }} + app.kubernetes.io/component: cluster-metrics + {{- if .Values.metrics.kafka.podLabels }} + {{- include "common.tplvalues.render" (dict "value" .Values.metrics.kafka.podLabels "context" $) | nindent 8 }} + {{- end }} + annotations: + {{- if .Values.metrics.kafka.podAnnotations }} + {{- include "common.tplvalues.render" (dict "value" .Values.metrics.kafka.podAnnotations "context" $) | nindent 8 }} + {{- end }} + spec: + {{- include "kafka.imagePullSecrets" . | nindent 6 }} + {{- if .Values.metrics.kafka.hostAliases }} + hostAliases: {{- include "common.tplvalues.render" (dict "value" .Values.metrics.kafka.hostAliases "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.metrics.kafka.affinity }} + affinity: {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.kafka.affinity "context" $) | nindent 8 }} + {{- else }} + affinity: + podAffinity: {{- include "common.affinities.pods" (dict "type" .Values.metrics.kafka.podAffinityPreset "component" "metrics" "context" $) | nindent 10 }} + podAntiAffinity: {{- include "common.affinities.pods" (dict "type" .Values.metrics.kafka.podAntiAffinityPreset "component" "metrics" "context" $) | nindent 10 }} + nodeAffinity: {{- include "common.affinities.nodes" (dict "type" .Values.metrics.kafka.nodeAffinityPreset.type "key" .Values.metrics.kafka.nodeAffinityPreset.key "values" .Values.metrics.kafka.nodeAffinityPreset.values) | nindent 10 }} + {{- end }} + {{- if .Values.metrics.kafka.nodeSelector }} + nodeSelector: {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.kafka.nodeSelector "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.metrics.kafka.tolerations }} + tolerations: {{- include "common.tplvalues.render" (dict "value" .Values.metrics.kafka.tolerations "context" .) | nindent 8 }} + {{- end }} + {{- if .Values.metrics.kafka.topologySpreadConstraints }} + topologySpreadConstraints: {{- include "common.tplvalues.render" (dict "value" .Values.metrics.kafka.topologySpreadConstraints "context" .) | nindent 8 }} + {{- end }} + {{- if .Values.metrics.kafka.priorityClassName }} + priorityClassName: {{ .Values.metrics.kafka.priorityClassName }} + {{- end }} + {{- if .Values.metrics.kafka.schedulerName }} + schedulerName: {{ .Values.metrics.kafka.schedulerName }} + {{- end }} + {{- if .Values.metrics.kafka.podSecurityContext.enabled }} + securityContext: {{- omit .Values.metrics.kafka.podSecurityContext "enabled" | toYaml | nindent 8 }} + {{- end }} + serviceAccountName: {{ template "kafka.metrics.kafka.serviceAccountName" . }} + {{- if .Values.metrics.kafka.initContainers }} + initContainers: {{- include "common.tplvalues.render" (dict "value" .Values.metrics.kafka.initContainers "context" $) | nindent 8 }} + {{- end }} + containers: + - name: kafka-exporter + image: {{ include "kafka.metrics.kafka.image" . }} + imagePullPolicy: {{ .Values.metrics.kafka.image.pullPolicy | quote }} + {{- if .Values.metrics.kafka.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.metrics.kafka.containerSecurityContext "enabled" | toYaml | nindent 12 }} + {{- end }} + {{- if .Values.diagnosticMode.enabled }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 12 }} + {{- else if .Values.metrics.kafka.command }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.metrics.kafka.command "context" $) | nindent 12 }} + {{- else }} + command: + - bash + {{- end }} + {{- if .Values.diagnosticMode.enabled }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 12 }} + {{- else if .Values.metrics.kafka.args }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.metrics.kafka.args "context" $) | nindent 12 }} + {{- else }} + args: + - -ce + - | + kafka_exporter \ + {{- range $i, $e := until $replicaCount }} + --kafka.server={{ $fullname }}-{{ $i }}.{{ $fullname }}-headless.{{ $releaseNamespace }}.svc.{{ $clusterDomain }}:{{ $servicePort }} \ + {{- end }} + {{- if (include "kafka.client.saslAuthentication" .) }} + --sasl.enabled \ + --sasl.username=$SASL_USERNAME \ + --sasl.password=$SASL_USER_PASSWORD \ + --sasl.mechanism={{ include "kafka.metrics.kafka.saslMechanism" . }} \ + {{- end }} + {{- if (include "kafka.client.tlsEncryption" .) }} + --tls.enabled \ + {{- if .Values.metrics.kafka.certificatesSecret }} + --tls.key-file=/opt/bitnami/kafka-exporter/certs/{{ .Values.metrics.kafka.tlsKey }} \ + --tls.cert-file=/opt/bitnami/kafka-exporter/certs/{{ .Values.metrics.kafka.tlsCert }} \ + {{- if .Values.metrics.kafka.tlsCaSecret }} + --tls.ca-file=/opt/bitnami/kafka-exporter/cacert/{{ .Values.metrics.kafka.tlsCaCert }} \ + {{- else }} + --tls.ca-file=/opt/bitnami/kafka-exporter/certs/{{ .Values.metrics.kafka.tlsCaCert }} \ + {{- end }} + {{- end }} + {{- end }} + {{- range $key, $value := .Values.metrics.kafka.extraFlags }} + --{{ $key }}{{ if $value }}={{ $value }}{{ end }} \ + {{- end }} + --web.listen-address=:{{ .Values.metrics.kafka.containerPorts.metrics }} + {{- end }} + {{- if (include "kafka.client.saslAuthentication" .) }} + {{- $clientUsers := .Values.auth.sasl.jaas.clientUsers }} + env: + - name: SASL_USERNAME + value: {{ index $clientUsers 0 | quote }} + - name: SASL_USER_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "kafka.jaasSecretName" . }} + key: system-user-password + {{- end }} + ports: + - name: metrics + containerPort: {{ .Values.metrics.kafka.containerPorts.metrics }} + {{- if .Values.metrics.kafka.resources }} + resources: {{ toYaml .Values.metrics.kafka.resources | nindent 12 }} + {{- end }} + volumeMounts: + {{- if .Values.metrics.kafka.extraVolumeMounts }} + {{- include "common.tplvalues.render" (dict "value" .Values.metrics.kafka.extraVolumeMounts "context" $) | nindent 12 }} + {{- end }} + {{- if and (include "kafka.client.tlsEncryption" .) .Values.metrics.kafka.certificatesSecret }} + - name: kafka-exporter-certificates + mountPath: /opt/bitnami/kafka-exporter/certs/ + readOnly: true + {{- if .Values.metrics.kafka.tlsCaSecret }} + - name: kafka-exporter-ca-certificate + mountPath: /opt/bitnami/kafka-exporter/cacert/ + readOnly: true + {{- end }} + {{- end }} + {{- if .Values.metrics.kafka.sidecars }} + {{- include "common.tplvalues.render" (dict "value" .Values.metrics.kafka.sidecars "context" $) | nindent 8 }} + {{- end }} + volumes: + {{- if .Values.metrics.kafka.extraVolumes }} + {{- include "common.tplvalues.render" (dict "value" .Values.metrics.kafka.extraVolumes "context" $) | nindent 8 }} + {{- end }} + {{- if and (include "kafka.client.tlsEncryption" .) .Values.metrics.kafka.certificatesSecret }} + - name: kafka-exporter-certificates + secret: + secretName: {{ .Values.metrics.kafka.certificatesSecret }} + defaultMode: 0440 + {{- if .Values.metrics.kafka.tlsCaSecret }} + - name: kafka-exporter-ca-certificate + secret: + secretName: {{ .Values.metrics.kafka.tlsCaSecret }} + defaultMode: 0440 + {{- end }} + {{- end }} +{{- end }} diff --git a/knowlg-automation/helm_charts/kafka/templates/kafka-metrics-serviceaccount.yaml b/knowlg-automation/helm_charts/kafka/templates/kafka-metrics-serviceaccount.yaml new file mode 100644 index 000000000..f8e3eb305 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/templates/kafka-metrics-serviceaccount.yaml @@ -0,0 +1,16 @@ +{{- if and .Values.metrics.kafka.enabled .Values.metrics.kafka.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "kafka.metrics.kafka.serviceAccountName" . }} + namespace: {{ .Release.Namespace | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: cluster-metrics + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.metrics.kafka.serviceAccount.automountServiceAccountToken }} +{{- end }} diff --git a/knowlg-automation/helm_charts/kafka/templates/kafka-metrics-svc.yaml b/knowlg-automation/helm_charts/kafka/templates/kafka-metrics-svc.yaml new file mode 100644 index 000000000..9daae4a1a --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/templates/kafka-metrics-svc.yaml @@ -0,0 +1,34 @@ +{{- if .Values.metrics.kafka.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ printf "%s-metrics" (include "common.names.fullname" .) | trunc 63 | trimSuffix "-" }} + namespace: {{ .Release.Namespace | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: cluster-metrics + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if or .Values.metrics.kafka.service.annotations .Values.commonAnnotations }} + annotations: + {{- if .Values.metrics.kafka.service.annotations }} + {{ include "common.tplvalues.render" ( dict "value" .Values.metrics.kafka.service.annotations "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- end }} +spec: + type: ClusterIP + sessionAffinity: {{ .Values.metrics.kafka.service.sessionAffinity }} + {{- if .Values.metrics.kafka.service.clusterIP }} + clusterIP: {{ .Values.metrics.kafka.service.clusterIP }} + {{- end }} + ports: + - name: http-metrics + port: {{ .Values.metrics.kafka.service.ports.metrics }} + protocol: TCP + targetPort: metrics + selector: {{- include "common.labels.matchLabels" . | nindent 4 }} + app.kubernetes.io/component: cluster-metrics +{{- end }} diff --git a/knowlg-automation/helm_charts/kafka/templates/kafka-provisioning-secret.yaml b/knowlg-automation/helm_charts/kafka/templates/kafka-provisioning-secret.yaml new file mode 100644 index 000000000..0c0fb1bc1 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/templates/kafka-provisioning-secret.yaml @@ -0,0 +1,19 @@ +{{- if and .Values.provisioning.enabled (include "kafka.client.tlsEncryption" .) (not .Values.provisioning.auth.tls.passwordsSecret) }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "kafka.client.passwordsSecretName" . }} + namespace: {{ .Release.Namespace | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +type: Opaque +data: + truststore-password: {{ default "" .Values.provisioning.auth.tls.keystorePassword | b64enc | quote }} + keystore-password: {{ default "" .Values.provisioning.auth.tls.truststorePassword | b64enc | quote }} + key-password: {{ default "" .Values.provisioning.auth.tls.keyPassword | b64enc | quote }} +{{- end }} diff --git a/knowlg-automation/helm_charts/kafka/templates/kafka-provisioning-serviceaccount.yaml b/knowlg-automation/helm_charts/kafka/templates/kafka-provisioning-serviceaccount.yaml new file mode 100644 index 000000000..47614674c --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/templates/kafka-provisioning-serviceaccount.yaml @@ -0,0 +1,15 @@ +{{- if .Values.provisioning.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "kafka.provisioning.serviceAccountName" . }} + namespace: {{ .Release.Namespace | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.provisioning.serviceAccount.automountServiceAccountToken }} +{{- end }} diff --git a/knowlg-automation/helm_charts/kafka/templates/kafka-provisioning.yaml b/knowlg-automation/helm_charts/kafka/templates/kafka-provisioning.yaml new file mode 100644 index 000000000..765e88315 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/templates/kafka-provisioning.yaml @@ -0,0 +1,260 @@ +{{- if .Values.provisioning.enabled }} +{{- $replicaCount := int .Values.replicaCount }} +kind: Job +apiVersion: batch/v1 +metadata: + name: {{ printf "%s-provisioning" (include "common.names.fullname" .) }} + namespace: {{ .Release.Namespace | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: kafka-provisioning + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + annotations: + helm.sh/hook: post-install,post-upgrade + helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + template: + metadata: + labels: {{- include "common.labels.standard" . | nindent 8 }} + app.kubernetes.io/component: kafka-provisioning + {{- if .Values.provisioning.podLabels }} + {{- include "common.tplvalues.render" (dict "value" .Values.provisioning.podLabels "context" $) | nindent 8 }} + {{- end }} + annotations: + {{- if .Values.provisioning.podAnnotations }} + {{- include "common.tplvalues.render" (dict "value" .Values.provisioning.podAnnotations "context" $) | nindent 8 }} + {{- end }} + spec: + serviceAccountName: {{ template "kafka.provisioning.serviceAccountName" . }} + {{- include "kafka.imagePullSecrets" . | nindent 6 }} + {{- if .Values.provisioning.schedulerName }} + schedulerName: {{ .Values.provisioning.schedulerName | quote }} + {{- end }} + {{- if .Values.provisioning.podSecurityContext.enabled }} + securityContext: {{- omit .Values.provisioning.podSecurityContext "enabled" | toYaml | nindent 8 }} + {{- end }} + restartPolicy: OnFailure + terminationGracePeriodSeconds: 0 + {{- if .Values.provisioning.nodeSelector }} + nodeSelector: {{- include "common.tplvalues.render" ( dict "value" .Values.provisioning.nodeSelector "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.provisioning.tolerations }} + tolerations: {{- include "common.tplvalues.render" (dict "value" .Values.provisioning.tolerations "context" .) | nindent 8 }} + {{- end }} + {{- if or .Values.provisioning.initContainers .Values.provisioning.waitForKafka }} + initContainers: + {{- if .Values.provisioning.waitForKafka }} + - name: wait-for-available-kafka + image: {{ include "kafka.image" . }} + imagePullPolicy: {{ .Values.image.pullPolicy | quote }} + {{- if .Values.provisioning.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.provisioning.containerSecurityContext "enabled" | toYaml | nindent 12 }} + {{- end }} + command: + - /bin/bash + args: + - -ec + - | + wait-for-port \ + --host={{ include "common.names.fullname" . }} \ + --state=inuse \ + --timeout=120 \ + {{ .Values.service.ports.client | int64 }}; + echo "Kafka is available"; + {{- if .Values.provisioning.resources }} + resources: {{- toYaml .Values.provisioning.resources | nindent 12 }} + {{- end }} + {{- end }} + {{- if .Values.provisioning.initContainers }} + {{- include "common.tplvalues.render" ( dict "value" .Values.provisioning.initContainers "context" $ ) | nindent 8 }} + {{- end }} + {{- end }} + containers: + - name: kafka-provisioning + image: {{ include "kafka.image" . }} + imagePullPolicy: {{ .Values.image.pullPolicy | quote }} + {{- if .Values.provisioning.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.provisioning.containerSecurityContext "enabled" | toYaml | nindent 12 }} + {{- end }} + {{- if .Values.diagnosticMode.enabled }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 12 }} + {{- else if .Values.provisioning.command }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.provisioning.command "context" $) | nindent 12 }} + {{- else }} + command: + - /bin/bash + {{- end }} + {{- if .Values.diagnosticMode.enabled }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 12 }} + {{- else if .Values.provisioning.args }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.provisioning.args "context" $) | nindent 12 }} + {{- else }} + args: + - -ec + - | + echo "Configuring environment" + . /opt/bitnami/scripts/libkafka.sh + export CLIENT_CONF="${CLIENT_CONF:-/opt/bitnami/kafka/config/client.properties}" + if [ ! -f "$CLIENT_CONF" ]; then + touch $CLIENT_CONF + + kafka_common_conf_set "$CLIENT_CONF" security.protocol {{ include "kafka.listenerType" ( dict "protocol" .Values.auth.clientProtocol ) | quote }} + {{- if (include "kafka.client.tlsEncryption" .) }} + kafka_common_conf_set "$CLIENT_CONF" ssl.keystore.type {{ upper .Values.provisioning.auth.tls.type | quote }} + kafka_common_conf_set "$CLIENT_CONF" ssl.truststore.type {{ upper .Values.provisioning.auth.tls.type | quote }} + ! is_empty_value "$KAFKA_CLIENT_KEY_PASSWORD" && kafka_common_conf_set "$CLIENT_CONF" ssl.key.password "$KAFKA_CLIENT_KEY_PASSWORD" + {{- if eq (upper .Values.provisioning.auth.tls.type) "PEM" }} + file_to_multiline_property() { + awk 'NR > 1{print line" \\"}{line=$0;}END{print $0" "}' <"${1:?missing file}" + } + kafka_common_conf_set "$CLIENT_CONF" ssl.keystore.key "$(file_to_multiline_property "/certs/{{ .Values.provisioning.auth.tls.key }}")" + kafka_common_conf_set "$CLIENT_CONF" ssl.keystore.certificate.chain "$(file_to_multiline_property "/certs/{{ .Values.provisioning.auth.tls.caCert }}")" + kafka_common_conf_set "$CLIENT_CONF" ssl.truststore.certificates "$(file_to_multiline_property "/certs/{{ .Values.provisioning.auth.tls.cert }}")" + {{- else if eq (upper .Values.provisioning.auth.tls.type) "JKS" }} + kafka_common_conf_set "$CLIENT_CONF" ssl.keystore.location "/certs/{{ .Values.provisioning.auth.tls.keystore }}" + kafka_common_conf_set "$CLIENT_CONF" ssl.truststore.location "/certs/{{ .Values.provisioning.auth.tls.truststore }}" + ! is_empty_value "$KAFKA_CLIENT_KEYSTORE_PASSWORD" && kafka_common_conf_set "$CLIENT_CONF" ssl.keystore.password "$KAFKA_CLIENT_KEYSTORE_PASSWORD" + ! is_empty_value "$KAFKA_CLIENT_TRUSTSTORE_PASSWORD" && kafka_common_conf_set "$CLIENT_CONF" ssl.truststore.password "$KAFKA_CLIENT_TRUSTSTORE_PASSWORD" + {{- end }} + {{- end }} + {{- if (include "kafka.client.saslAuthentication" .) }} + {{- if contains "plain" .Values.auth.sasl.mechanisms }} + kafka_common_conf_set "$CLIENT_CONF" sasl.mechanism PLAIN + kafka_common_conf_set "$CLIENT_CONF" sasl.jaas.config "org.apache.kafka.common.security.plain.PlainLoginModule required username=\"$SASL_USERNAME\" password=\"$SASL_USER_PASSWORD\";" + {{- else if contains "scram-sha-256" .Values.auth.sasl.mechanisms }} + kafka_common_conf_set "$CLIENT_CONF" sasl.mechanism SCRAM-SHA-256 + kafka_common_conf_set "$CLIENT_CONF" sasl.jaas.config "org.apache.kafka.common.security.scram.ScramLoginModule required username=\"$SASL_USERNAME\" password=\"$SASL_USER_PASSWORD\";" + {{- else if contains "scram-sha-512" .Values.auth.sasl.mechanisms }} + kafka_common_conf_set "$CLIENT_CONF" sasl.mechanism SCRAM-SHA-512 + kafka_common_conf_set "$CLIENT_CONF" sasl.jaas.config "org.apache.kafka.common.security.scram.ScramLoginModule required username=\"$SASL_USERNAME\" password=\"$SASL_USER_PASSWORD\";" + {{- end }} + {{- end }} + fi + + echo "Running pre-provisioning script if any given" + {{ .Values.provisioning.preScript | nindent 14 }} + + kafka_provisioning_commands=( + {{- range $topic := .Values.provisioning.topics }} + "/opt/bitnami/kafka/bin/kafka-topics.sh \ + --create \ + --if-not-exists \ + --bootstrap-server ${KAFKA_SERVICE} \ + --replication-factor {{ $topic.replicationFactor | default $.Values.provisioning.replicationFactor }} \ + --partitions {{ $topic.partitions | default $.Values.provisioning.numPartitions }} \ + {{- range $name, $value := $topic.config }} + --config {{ $name }}={{ $value }} \ + {{- end }} + --command-config ${CLIENT_CONF} \ + --topic {{ $topic.name }}" + {{- end }} + {{- range $command := .Values.provisioning.extraProvisioningCommands }} + {{- $command | quote | nindent 16 }} + {{- end }} + ) + + echo "Starting provisioning" + for ((index=0; index < ${#kafka_provisioning_commands[@]}; index+={{ .Values.provisioning.parallel }})) + do + for j in $(seq ${index} $((${index}+{{ .Values.provisioning.parallel }}-1))) + do + ${kafka_provisioning_commands[j]} & # Async command + done + wait # Wait the end of the jobs + done + + echo "Running post-provisioning script if any given" + {{ .Values.provisioning.postScript | nindent 14 }} + + echo "Provisioning succeeded" + {{- end }} + env: + - name: BITNAMI_DEBUG + value: {{ ternary "true" "false" (or .Values.image.debug .Values.diagnosticMode.enabled) | quote }} + {{- if (include "kafka.client.tlsEncryption" .) }} + - name: KAFKA_CLIENT_KEY_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "kafka.client.passwordsSecretName" . }} + key: {{ .Values.provisioning.auth.tls.keyPasswordSecretKey }} + - name: KAFKA_CLIENT_KEYSTORE_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "kafka.client.passwordsSecretName" . }} + key: {{ .Values.provisioning.auth.tls.keystorePasswordSecretKey }} + - name: KAFKA_CLIENT_TRUSTSTORE_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "kafka.client.passwordsSecretName" . }} + key: {{ .Values.provisioning.auth.tls.truststorePasswordSecretKey }} + {{- end }} + - name: KAFKA_SERVICE + value: {{ printf "%s:%d" (include "common.names.fullname" .) (.Values.service.ports.client | int64) }} + {{- if (include "kafka.client.saslAuthentication" .) }} + {{- $clientUsers := .Values.auth.sasl.jaas.clientUsers }} + - name: SASL_USERNAME + value: {{ index $clientUsers 0 | quote }} + - name: SASL_USER_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "kafka.jaasSecretName" . }} + key: system-user-password + {{- end }} + {{- if .Values.provisioning.extraEnvVars }} + {{- include "common.tplvalues.render" ( dict "value" .Values.provisioning.extraEnvVars "context" $) | nindent 12 }} + {{- end }} + {{- if or .Values.provisioning.extraEnvVarsCM .Values.provisioning.extraEnvVarsSecret }} + envFrom: + {{- if .Values.provisioning.extraEnvVarsCM }} + - configMapRef: + name: {{ include "common.tplvalues.render" (dict "value" .Values.provisioning.extraEnvVarsCM "context" $) }} + {{- end }} + {{- if .Values.provisioning.extraEnvVarsSecret }} + - secretRef: + name: {{ include "common.tplvalues.render" (dict "value" .Values.provisioning.extraEnvVarsSecret "context" $) }} + {{- end }} + {{- end }} + {{- if .Values.provisioning.resources }} + resources: {{- toYaml .Values.provisioning.resources | nindent 12 }} + {{- end }} + volumeMounts: + {{- if or .Values.log4j .Values.existingLog4jConfigMap }} + - name: log4j-config + mountPath: {{ .Values.persistence.mountPath }}/config/log4j.properties + subPath: log4j.properties + {{- end }} + {{- if (include "kafka.client.tlsEncryption" .) }} + {{- if not (empty .Values.provisioning.auth.tls.certificatesSecret) }} + - name: kafka-client-certs + mountPath: /certs + readOnly: true + {{- end }} + {{- end }} + {{- if .Values.provisioning.extraVolumeMounts }} + {{- include "common.tplvalues.render" (dict "value" .Values.provisioning.extraVolumeMounts "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.provisioning.sidecars }} + {{- include "common.tplvalues.render" (dict "value" .Values.provisioning.sidecars "context" $) | nindent 8 }} + {{- end }} + volumes: + {{- if or .Values.log4j .Values.existingLog4jConfigMap }} + - name: log4j-config + configMap: + name: {{ include "kafka.log4j.configMapName" . }} + {{ end }} + {{- if (include "kafka.client.tlsEncryption" .) }} + {{- if not (empty .Values.provisioning.auth.tls.certificatesSecret) }} + - name: kafka-client-certs + secret: + secretName: {{ .Values.provisioning.auth.tls.certificatesSecret }} + defaultMode: 256 + {{- end }} + {{- end }} + {{- if .Values.provisioning.extraVolumes }} + {{- include "common.tplvalues.render" (dict "value" .Values.provisioning.extraVolumes "context" $) | nindent 8 }} + {{- end }} +{{- end }} diff --git a/knowlg-automation/helm_charts/kafka/templates/log4j-configmap.yaml b/knowlg-automation/helm_charts/kafka/templates/log4j-configmap.yaml new file mode 100644 index 000000000..8f7bc6c14 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/templates/log4j-configmap.yaml @@ -0,0 +1,17 @@ +{{- if (include "kafka.log4j.createConfigMap" .) }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "kafka.log4j.configMapName" . }} + namespace: {{ .Release.Namespace | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +data: + log4j.properties: |- + {{- include "common.tplvalues.render" ( dict "value" .Values.log4j "context" $ ) | nindent 4 }} +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/templates/networkpolicy-egress.yaml b/knowlg-automation/helm_charts/kafka/templates/networkpolicy-egress.yaml new file mode 100644 index 000000000..068024a0e --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/templates/networkpolicy-egress.yaml @@ -0,0 +1,22 @@ +{{- if and .Values.networkPolicy.enabled .Values.networkPolicy.egressRules.customRules }} +kind: NetworkPolicy +apiVersion: {{ include "common.capabilities.networkPolicy.apiVersion" . }} +metadata: + name: {{ printf "%s-egress" (include "common.names.fullname" .) }} + namespace: {{ .Release.Namespace | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + podSelector: + matchLabels: + {{- include "common.labels.matchLabels" . | nindent 6 }} + policyTypes: + - Egress + egress: + {{- include "common.tplvalues.render" (dict "value" .Values.networkPolicy.egressRules.customRules "context" $) | nindent 4 }} +{{- end }} diff --git a/knowlg-automation/helm_charts/kafka/templates/networkpolicy-ingress.yaml b/knowlg-automation/helm_charts/kafka/templates/networkpolicy-ingress.yaml new file mode 100644 index 000000000..258dcabb6 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/templates/networkpolicy-ingress.yaml @@ -0,0 +1,53 @@ +{{- if .Values.networkPolicy.enabled }} +kind: NetworkPolicy +apiVersion: {{ include "common.capabilities.networkPolicy.apiVersion" . }} +metadata: + name: {{ printf "%s-ingress" (include "common.names.fullname" .) }} + namespace: {{ .Release.Namespace | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + podSelector: + matchLabels: + {{- include "common.labels.matchLabels" . | nindent 6 }} + policyTypes: + - Ingress + ingress: + # Allow client connections + - ports: + - port: {{ .Values.containerPorts.client }} + {{- if not .Values.networkPolicy.allowExternal }} + from: + - podSelector: + matchLabels: + {{ template "common.names.fullname" . }}-client: "true" + {{- if .Values.networkPolicy.explicitNamespacesSelector }} + namespaceSelector: {{- toYaml .Values.networkPolicy.explicitNamespacesSelector | nindent 12 }} + {{- end }} + {{- end }} + # Allow communication inter-broker + - ports: + - port: {{ .Values.containerPorts.internal }} + from: + - podSelector: + matchLabels: + {{- include "common.labels.matchLabels" . | nindent 14 }} + # Allow External connection + {{- if .Values.externalAccess.enabled }} + - ports: + - port: {{ .Values.containerPorts.external }} + {{- if .Values.externalAccess.from }} + from: {{- include "common.tplvalues.render" ( dict "value" .Values.networkPolicy.externalAccess.from "context" $ ) | nindent 8 }} + {{- end }} + {{- end }} + {{- if .Values.metrics.kafka.enabled }} + # Allow prometheus scrapes + - ports: + - port: {{ .Values.metrics.kafka.containerPorts.metrics }} + {{- end }} +{{- end }} diff --git a/knowlg-automation/helm_charts/kafka/templates/poddisruptionbudget.yaml b/knowlg-automation/helm_charts/kafka/templates/poddisruptionbudget.yaml new file mode 100644 index 000000000..e0a60151d --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/templates/poddisruptionbudget.yaml @@ -0,0 +1,26 @@ +{{- $replicaCount := int .Values.replicaCount }} +{{- if and .Values.pdb.create (gt $replicaCount 1) }} +apiVersion: {{ include "common.capabilities.policy.apiVersion" . }} +kind: PodDisruptionBudget +metadata: + name: {{ include "common.names.fullname" . }} + namespace: {{ .Release.Namespace | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: kafka + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + {{- if .Values.pdb.minAvailable }} + minAvailable: {{ .Values.pdb.minAvailable }} + {{- end }} + {{- if .Values.pdb.maxUnavailable }} + maxUnavailable: {{ .Values.pdb.maxUnavailable }} + {{- end }} + selector: + matchLabels: {{- include "common.labels.matchLabels" . | nindent 6 }} + app.kubernetes.io/component: kafka +{{- end }} diff --git a/knowlg-automation/helm_charts/kafka/templates/prometheusrule.yaml b/knowlg-automation/helm_charts/kafka/templates/prometheusrule.yaml new file mode 100644 index 000000000..bce728a37 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/templates/prometheusrule.yaml @@ -0,0 +1,20 @@ +{{- if and (or .Values.metrics.kafka.enabled .Values.metrics.jmx.enabled) .Values.metrics.prometheusRule.enabled .Values.metrics.prometheusRule.groups }} +apiVersion: monitoring.coreos.com/v1 +kind: PrometheusRule +metadata: + name: {{ include "common.names.fullname" . }} + namespace: {{ default (include "common.names.namespace" .) .Values.metrics.prometheusRule.namespace }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: metrics + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" (dict "value" .Values.commonLabels "context" .) | nindent 4 }} + {{- end }} + {{- if .Values.metrics.prometheusRule.labels }} + {{- include "common.tplvalues.render" (dict "value" .Values.metrics.prometheusRule.labels "context" .) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" (dict "value" .Values.commonAnnotations "context" .) | nindent 4 }} + {{- end }} +spec: + groups: {{- include "common.tplvalues.render" (dict "value" .Values.metrics.prometheusRule.groups "context" .) | nindent 4 }} +{{- end }} diff --git a/knowlg-automation/helm_charts/kafka/templates/role.yaml b/knowlg-automation/helm_charts/kafka/templates/role.yaml new file mode 100644 index 000000000..63215b3b8 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/templates/role.yaml @@ -0,0 +1,24 @@ +{{- if .Values.rbac.create -}} +apiVersion: {{ include "common.capabilities.rbac.apiVersion" . }} +kind: Role +metadata: + name: {{ include "common.names.fullname" . }} + namespace: {{ .Release.Namespace | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: kafka + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +rules: + - apiGroups: + - "" + resources: + - services + verbs: + - get + - list + - watch +{{- end -}} diff --git a/knowlg-automation/helm_charts/kafka/templates/rolebinding.yaml b/knowlg-automation/helm_charts/kafka/templates/rolebinding.yaml new file mode 100644 index 000000000..fb5e3a157 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/templates/rolebinding.yaml @@ -0,0 +1,23 @@ +{{- if .Values.rbac.create }} +apiVersion: {{ include "common.capabilities.rbac.apiVersion" . }} +kind: RoleBinding +metadata: + name: {{ include "common.names.fullname" . }} + namespace: {{ .Release.Namespace | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: kafka + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +roleRef: + kind: Role + name: {{ include "common.names.fullname" . }} + apiGroup: rbac.authorization.k8s.io +subjects: + - kind: ServiceAccount + name: {{ template "kafka.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/knowlg-automation/helm_charts/kafka/templates/scripts-configmap.yaml b/knowlg-automation/helm_charts/kafka/templates/scripts-configmap.yaml new file mode 100644 index 000000000..57e125053 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/templates/scripts-configmap.yaml @@ -0,0 +1,202 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ printf "%s-scripts" (include "common.names.fullname" .) }} + namespace: {{ .Release.Namespace | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +data: + {{- $fullname := include "common.names.fullname" . }} + {{- $releaseNamespace := .Release.Namespace }} + {{- $clusterDomain := .Values.clusterDomain }} + {{- $interBrokerPort := .Values.service.ports.internal }} + {{- $clientPort := .Values.service.ports.client }} + {{- $jksTruststoreSecret := .Values.auth.tls.jksTruststoreSecret -}} + {{- $jksTruststore := .Values.auth.tls.jksTruststore -}} + {{- $jksKeystoreSAN := .Values.auth.tls.jksKeystoreSAN -}} + {{- if .Values.externalAccess.autoDiscovery.enabled }} + auto-discovery.sh: |- + #!/bin/bash + + SVC_NAME="${MY_POD_NAME}-external" + + {{- if eq .Values.externalAccess.service.type "LoadBalancer" }} + # Auxiliary functions + retry_while() { + local -r cmd="${1:?cmd is missing}" + local -r retries="${2:-12}" + local -r sleep_time="${3:-5}" + local return_value=1 + + read -r -a command <<< "$cmd" + for ((i = 1 ; i <= retries ; i+=1 )); do + "${command[@]}" && return_value=0 && break + sleep "$sleep_time" + done + return $return_value + } + k8s_svc_lb_ip() { + local namespace=${1:?namespace is missing} + local service=${2:?service is missing} + local service_ip=$(kubectl get svc "$service" -n "$namespace" -o jsonpath="{.status.loadBalancer.ingress[0].ip}") + local service_hostname=$(kubectl get svc "$service" -n "$namespace" -o jsonpath="{.status.loadBalancer.ingress[0].hostname}") + + if [[ -n ${service_ip} ]]; then + echo "${service_ip}" + else + echo "${service_hostname}" + fi + } + k8s_svc_lb_ip_ready() { + local namespace=${1:?namespace is missing} + local service=${2:?service is missing} + [[ -n "$(k8s_svc_lb_ip "$namespace" "$service")" ]] + } + # Wait until LoadBalancer IP is ready + retry_while "k8s_svc_lb_ip_ready {{ $releaseNamespace }} $SVC_NAME" || exit 1 + # Obtain LoadBalancer external IP + k8s_svc_lb_ip "{{ $releaseNamespace }}" "$SVC_NAME" | tee "$SHARED_FILE" + {{- else if eq .Values.externalAccess.service.type "NodePort" }} + k8s_svc_node_port() { + local namespace=${1:?namespace is missing} + local service=${2:?service is missing} + local index=${3:-0} + local node_port="$(kubectl get svc "$service" -n "$namespace" -o jsonpath="{.spec.ports[$index].nodePort}")" + echo "$node_port" + } + k8s_svc_node_port "{{ $releaseNamespace }}" "$SVC_NAME" | tee "$SHARED_FILE" + {{- end }} + {{- end }} + setup.sh: |- + #!/bin/bash + + ID="${MY_POD_NAME#"{{ $fullname }}-"}" + if [[ -f "{{ .Values.logsDirs | splitList "," | first }}/meta.properties" ]]; then + export KAFKA_CFG_BROKER_ID="$(grep "broker.id" "{{ .Values.logsDirs | splitList "," | first }}/meta.properties" | awk -F '=' '{print $2}')" + else + export KAFKA_CFG_BROKER_ID="$((ID + {{ .Values.minBrokerId }}))" + fi + + {{- if eq .Values.brokerRackAssignment "aws-az" }} + export KAFKA_CFG_BROKER_RACK=$(curl "http://169.254.169.254/latest/meta-data/placement/availability-zone-id") + {{- end }} + + {{- if .Values.externalAccess.enabled }} + # Configure external ip and port + {{- if eq .Values.externalAccess.service.type "LoadBalancer" }} + {{- if .Values.externalAccess.autoDiscovery.enabled }} + export EXTERNAL_ACCESS_HOST="$(<${SHARED_FILE})" + {{- else }} + export EXTERNAL_ACCESS_HOST=$(echo '{{ .Values.externalAccess.service.loadBalancerNames | default .Values.externalAccess.service.loadBalancerIPs }}' | tr -d '[]' | cut -d ' ' -f "$(($ID + 1))") + {{- end }} + export EXTERNAL_ACCESS_PORT={{ .Values.externalAccess.service.ports.external }} + {{- else if eq .Values.externalAccess.service.type "NodePort" }} + {{- if and .Values.externalAccess.service.usePodIPs .Values.externalAccess.autoDiscovery.enabled }} + export EXTERNAL_ACCESS_HOST="${MY_POD_IP}" + {{- else if or .Values.externalAccess.service.useHostIPs .Values.externalAccess.autoDiscovery.enabled }} + export EXTERNAL_ACCESS_HOST="${HOST_IP}" + {{- else if .Values.externalAccess.service.domain }} + export EXTERNAL_ACCESS_HOST={{ .Values.externalAccess.service.domain }} + {{- else }} + export EXTERNAL_ACCESS_HOST=$(curl -s https://ipinfo.io/ip) + {{- end }} + {{- if .Values.externalAccess.autoDiscovery.enabled }} + export EXTERNAL_ACCESS_PORT="$(<${SHARED_FILE})" + {{- else }} + export EXTERNAL_ACCESS_PORT=$(echo '{{ .Values.externalAccess.service.nodePorts }}' | tr -d '[]' | cut -d ' ' -f "$(($ID + 1))") + {{- end }} + {{- else }} + export EXTERNAL_ACCESS_HOST={{ .Values.externalAccess.service.domain }} + export EXTERNAL_ACCESS_PORT="$((ID + {{ .Values.externalAccess.service.ports.external }}))" + {{- end }} + + # Configure Kafka advertised listeners + {{- if .Values.advertisedListeners }} + export KAFKA_CFG_ADVERTISED_LISTENERS={{ join "," .Values.advertisedListeners }} + {{- else }} + export KAFKA_CFG_ADVERTISED_LISTENERS="INTERNAL://${MY_POD_NAME}.{{ $fullname }}-headless.{{ $releaseNamespace }}.svc.{{ $clusterDomain }}:{{ $interBrokerPort }},CLIENT://${MY_POD_NAME}.{{ $fullname }}-headless.{{ $releaseNamespace }}.svc.{{ $clusterDomain }}:{{ $clientPort }},EXTERNAL://${EXTERNAL_ACCESS_HOST}:${EXTERNAL_ACCESS_PORT}" + {{- end }} + {{- end }} + + {{- if (include "kafka.tlsEncryption" .) }} + mkdir -p /opt/bitnami/kafka/config/certs + {{- if eq .Values.auth.tls.type "jks" }} + {{- if not (empty .Values.auth.tls.existingSecrets) }} + JKS_TRUSTSTORE={{ printf "/%s/%s" (ternary "certs-${ID}" "truststore" (empty $jksTruststoreSecret)) (default "kafka.truststore.jks" $jksTruststore) | quote }} + JKS_KEYSTORE={{ printf "/certs-${ID}/%s" (default "kafka.keystore.jks" $jksKeystoreSAN) | quote }} + {{- else }} + JKS_TRUSTSTORE={{ printf "/%s/%s" (ternary "certs" "truststore" (empty $jksTruststoreSecret)) (default "kafka.truststore.jks" $jksTruststore) | quote }} + JKS_KEYSTORE={{ printf "/certs/%s" (default "kafka-${ID}.keystore.jks" $jksKeystoreSAN) | quote }} + {{- end }} + if [[ -f "$JKS_TRUSTSTORE" ]] && [[ -f "$JKS_KEYSTORE" ]]; then + cp "$JKS_TRUSTSTORE" "/opt/bitnami/kafka/config/certs/kafka.truststore.jks" + cp "$JKS_KEYSTORE" "/opt/bitnami/kafka/config/certs/kafka.keystore.jks" + else + echo "Couldn't find the expected Java Key Stores (JKS) files! They are mandatory when encryption via TLS is enabled." + exit 1 + fi + export KAFKA_TLS_TRUSTSTORE_FILE="/opt/bitnami/kafka/config/certs/kafka.truststore.jks" + + {{- else if eq .Values.auth.tls.type "pem" }} + + {{- if or (not (empty .Values.auth.tls.existingSecrets)) .Values.auth.tls.autoGenerated }} + PEM_CA="/certs-${ID}/ca.crt" + PEM_CERT="/certs-${ID}/tls.crt" + PEM_KEY="/certs-${ID}/tls.key" + {{- else }} + PEM_CA="/certs/kafka.truststore.pem" + PEM_CERT="/certs/kafka-${ID}.keystore.pem" + PEM_KEY="/certs/kafka-${ID}.keystore.key" + {{- end }} + if [[ -f "$PEM_CERT" ]] && [[ -f "$PEM_KEY" ]]; then + CERT_DIR="/opt/bitnami/kafka/config/certs" + PEM_CA_LOCATION="${CERT_DIR}/kafka.truststore.pem" + PEM_CERT_LOCATION="${CERT_DIR}/kafka.keystore.pem" + {{- if .Values.auth.tls.pemChainIncluded }} + cat $PEM_CERT | csplit - -s -z '/\-*END CERTIFICATE\-*/+1' '{*}' -f ${CERT_DIR}/xx + FIND_CA_RESULT=$(find ${CERT_DIR} -not -name 'xx00' -name 'xx*') + if [[ $(echo $FIND_CA_RESULT | wc -l) < 1 ]]; then + echo "auth.tls.pemChainIncluded was set, but PEM chain only contained 1 cert" + exit 1 + fi + echo $FIND_CA_RESULT | sort | xargs cat >> "$PEM_CA_LOCATION" + cat ${CERT_DIR}/xx00 > "$PEM_CERT_LOCATION" + {{- else }} + if [[ -f "$PEM_CA" ]]; then + cp "$PEM_CA" "$PEM_CA_LOCATION" + cp "$PEM_CERT" "$PEM_CERT_LOCATION" + else + echo "PEM_CA not provided, and auth.tls.pemChainIncluded was not true. One of these values must be set when using PEM type for TLS." + exit 1 + fi + {{- end }} + + # Ensure the key used PEM format with PKCS#8 + openssl pkcs8 -topk8 -nocrypt -in "$PEM_KEY" > "/opt/bitnami/kafka/config/certs/kafka.keystore.key" + else + echo "Couldn't find the expected PEM files! They are mandatory when encryption via TLS is enabled." + exit 1 + fi + export KAFKA_TLS_TRUSTSTORE_FILE="/opt/bitnami/kafka/config/certs/kafka.truststore.pem" + {{- end }} + {{- end }} + + # Configure zookeeper client + {{- if and (not (empty .Values.auth.zookeeper.tls.existingSecret)) .Values.auth.zookeeper.tls.enabled }} + JKS_TRUSTSTORE={{ printf "/kafka-zookeeper-cert/%s" (.Values.auth.zookeeper.tls.existingSecretTruststoreKey) | quote }} + JKS_KEYSTORE={{ printf "/kafka-zookeeper-cert/%s" (.Values.auth.zookeeper.tls.existingSecretKeystoreKey) | quote }} + if [[ -f "$JKS_TRUSTSTORE" ]] && [[ -f "$JKS_KEYSTORE" ]]; then + CERT_DIR="/opt/bitnami/kafka/config/certs" + TRUSTSTORE_LOCATION="${CERT_DIR}/zookeeper.truststore.jks" + cp "$JKS_TRUSTSTORE" "$TRUSTSTORE_LOCATION" + cp "$JKS_KEYSTORE" "${CERT_DIR}/zookeeper.keystore.jks" + export KAFKA_ZOOKEEPER_TLS_TRUSTSTORE_FILE="${TRUSTSTORE_LOCATION}" + fi + {{- end }} + + exec /entrypoint.sh /run.sh diff --git a/knowlg-automation/helm_charts/kafka/templates/serviceaccount.yaml b/knowlg-automation/helm_charts/kafka/templates/serviceaccount.yaml new file mode 100644 index 000000000..73091f5d7 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/templates/serviceaccount.yaml @@ -0,0 +1,20 @@ +{{- if .Values.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "kafka.serviceAccountName" . }} + namespace: {{ .Release.Namespace | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: kafka + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + annotations: + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.serviceAccount.annotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.serviceAccount.annotations "context" $ ) | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.serviceAccount.automountServiceAccountToken }} +{{- end }} diff --git a/knowlg-automation/helm_charts/kafka/templates/servicemonitor-jmx-metrics.yaml b/knowlg-automation/helm_charts/kafka/templates/servicemonitor-jmx-metrics.yaml new file mode 100644 index 000000000..1919feebb --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/templates/servicemonitor-jmx-metrics.yaml @@ -0,0 +1,53 @@ +{{- if and .Values.metrics.jmx.enabled .Values.metrics.serviceMonitor.enabled }} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ printf "%s-jmx-metrics" (include "common.names.fullname" .) }} + {{- if .Values.metrics.serviceMonitor.namespace }} + namespace: {{ .Values.metrics.serviceMonitor.namespace }} + {{- else }} + namespace: {{ .Release.Namespace | quote }} + {{- end }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: metrics + {{- if .Values.metrics.serviceMonitor.labels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.serviceMonitor.labels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + {{- if .Values.metrics.serviceMonitor.jobLabel }} + jobLabel: {{ .Values.metrics.serviceMonitor.jobLabel }} + {{- end }} + selector: + matchLabels: {{- include "common.labels.matchLabels" . | nindent 6 }} + {{- if .Values.metrics.serviceMonitor.selector }} + {{- include "common.tplvalues.render" (dict "value" .Values.metrics.serviceMonitor.selector "context" $) | nindent 6 }} + {{- end }} + app.kubernetes.io/component: metrics + endpoints: + - port: http-metrics + path: "/" + {{- if .Values.metrics.serviceMonitor.interval }} + interval: {{ .Values.metrics.serviceMonitor.interval }} + {{- end }} + {{- if .Values.metrics.serviceMonitor.scrapeTimeout }} + scrapeTimeout: {{ .Values.metrics.serviceMonitor.scrapeTimeout }} + {{- end }} + {{- if .Values.metrics.serviceMonitor.relabelings }} + relabelings: {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.serviceMonitor.relabelings "context" $) | nindent 6 }} + {{- end }} + {{- if .Values.metrics.serviceMonitor.metricRelabelings }} + metricRelabelings: {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.serviceMonitor.metricRelabelings "context" $) | nindent 6 }} + {{- end }} + {{- if .Values.metrics.serviceMonitor.honorLabels }} + honorLabels: {{ .Values.metrics.serviceMonitor.honorLabels }} + {{- end }} + namespaceSelector: + matchNames: + - {{ .Release.Namespace }} +{{- end }} diff --git a/knowlg-automation/helm_charts/kafka/templates/servicemonitor-metrics.yaml b/knowlg-automation/helm_charts/kafka/templates/servicemonitor-metrics.yaml new file mode 100644 index 000000000..343194667 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/templates/servicemonitor-metrics.yaml @@ -0,0 +1,53 @@ +{{- if and .Values.metrics.kafka.enabled .Values.metrics.serviceMonitor.enabled }} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ printf "%s-metrics" (include "common.names.fullname" .) }} + {{- if .Values.metrics.serviceMonitor.namespace }} + namespace: {{ .Values.metrics.serviceMonitor.namespace }} + {{- else }} + namespace: {{ .Release.Namespace | quote }} + {{- end }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: cluster-metrics + {{- if .Values.metrics.serviceMonitor.labels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.serviceMonitor.labels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + {{- if .Values.metrics.serviceMonitor.jobLabel }} + jobLabel: {{ .Values.metrics.serviceMonitor.jobLabel }} + {{- end }} + selector: + matchLabels: {{- include "common.labels.matchLabels" . | nindent 6 }} + {{- if .Values.metrics.serviceMonitor.selector }} + {{- include "common.tplvalues.render" (dict "value" .Values.metrics.serviceMonitor.selector "context" $) | nindent 6 }} + {{- end }} + app.kubernetes.io/component: cluster-metrics + endpoints: + - port: http-metrics + path: "/metrics" + {{- if .Values.metrics.serviceMonitor.interval }} + interval: {{ .Values.metrics.serviceMonitor.interval }} + {{- end }} + {{- if .Values.metrics.serviceMonitor.scrapeTimeout }} + scrapeTimeout: {{ .Values.metrics.serviceMonitor.scrapeTimeout }} + {{- end }} + {{- if .Values.metrics.serviceMonitor.relabelings }} + relabelings: {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.serviceMonitor.relabelings "context" $) | nindent 6 }} + {{- end }} + {{- if .Values.metrics.serviceMonitor.metricRelabelings }} + metricRelabelings: {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.serviceMonitor.metricRelabelings "context" $) | nindent 6 }} + {{- end }} + {{- if .Values.metrics.serviceMonitor.honorLabels }} + honorLabels: {{ .Values.metrics.serviceMonitor.honorLabels }} + {{- end }} + namespaceSelector: + matchNames: + - {{ .Release.Namespace }} +{{- end }} diff --git a/knowlg-automation/helm_charts/kafka/templates/statefulset.yaml b/knowlg-automation/helm_charts/kafka/templates/statefulset.yaml new file mode 100644 index 000000000..2146a3d99 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/templates/statefulset.yaml @@ -0,0 +1,610 @@ +{{- $replicaCount := int .Values.replicaCount }} +{{- $fullname := include "common.names.fullname" . }} +{{- $releaseNamespace := .Release.Namespace }} +{{- $clusterDomain := .Values.clusterDomain }} +{{- $interBrokerPort := .Values.service.ports.internal }} +{{- $clientPort := .Values.service.ports.client }} +{{- $interBrokerProtocol := include "kafka.listenerType" (dict "protocol" .Values.auth.interBrokerProtocol) -}} +{{- $clientProtocol := include "kafka.listenerType" (dict "protocol" .Values.auth.clientProtocol) -}} +{{- $externalClientProtocol := include "kafka.listenerType" (dict "protocol" (include "kafka.externalClientProtocol" . )) -}} +apiVersion: {{ include "common.capabilities.statefulset.apiVersion" . }} +kind: StatefulSet +metadata: + name: {{ include "common.names.fullname" . }} + namespace: {{ .Release.Namespace | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: kafka + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + podManagementPolicy: {{ .Values.podManagementPolicy }} + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: {{- include "common.labels.matchLabels" . | nindent 6 }} + app.kubernetes.io/component: kafka + serviceName: {{ printf "%s-headless" (include "common.names.fullname" .) | trunc 63 | trimSuffix "-" }} + updateStrategy: {{- include "common.tplvalues.render" (dict "value" .Values.updateStrategy "context" $ ) | nindent 4 }} + template: + metadata: + labels: {{- include "common.labels.standard" . | nindent 8 }} + app.kubernetes.io/component: kafka + {{- if .Values.podLabels }} + {{- include "common.tplvalues.render" (dict "value" .Values.podLabels "context" $) | nindent 8 }} + {{- end }} + annotations: + {{- if (include "kafka.createConfigmap" .) }} + checksum/configuration: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} + {{- end }} + {{- if (include "kafka.createJaasSecret" .) }} + checksum/jaas-secret: {{ include (print $.Template.BasePath "/jaas-secret.yaml") . | sha256sum }} + {{- end }} + {{- if (include "kafka.createTlsSecret" .) }} + checksum/tls-secret: {{ include (print $.Template.BasePath "/tls-secrets.yaml") . | sha256sum }} + {{- end }} + {{- if .Values.externalAccess.enabled }} + checksum/scripts: {{ include (print $.Template.BasePath "/scripts-configmap.yaml") . | sha256sum }} + {{- end }} + {{- if (include "kafka.metrics.jmx.createConfigmap" .) }} + checksum/jmx-configuration: {{ include (print $.Template.BasePath "/jmx-configmap.yaml") . | sha256sum }} + {{- end }} + {{- if .Values.podAnnotations }} + {{- include "common.tplvalues.render" (dict "value" .Values.podAnnotations "context" $) | nindent 8 }} + {{- end }} + spec: + {{- include "kafka.imagePullSecrets" . | nindent 6 }} + {{- if .Values.hostAliases }} + hostAliases: {{- include "common.tplvalues.render" (dict "value" .Values.hostAliases "context" $) | nindent 8 }} + {{- end }} + hostNetwork: {{ .Values.hostNetwork }} + hostIPC: {{ .Values.hostIPC }} + {{- if .Values.schedulerName }} + schedulerName: {{ .Values.schedulerName | quote }} + {{- end }} + {{- if .Values.affinity }} + affinity: {{- include "common.tplvalues.render" (dict "value" .Values.affinity "context" $) | nindent 8 }} + {{- else }} + affinity: + podAffinity: {{- include "common.affinities.pods" (dict "type" .Values.podAffinityPreset "component" "kafka" "context" $) | nindent 10 }} + podAntiAffinity: {{- include "common.affinities.pods" (dict "type" .Values.podAntiAffinityPreset "component" "kafka" "context" $) | nindent 10 }} + nodeAffinity: {{- include "common.affinities.nodes" (dict "type" .Values.nodeAffinityPreset.type "key" .Values.nodeAffinityPreset.key "values" .Values.nodeAffinityPreset.values) | nindent 10 }} + {{- end }} + {{- if .Values.nodeSelector }} + nodeSelector: {{- include "common.tplvalues.render" (dict "value" .Values.nodeSelector "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.tolerations }} + tolerations: {{- include "common.tplvalues.render" (dict "value" .Values.tolerations "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.topologySpreadConstraints }} + topologySpreadConstraints: {{- include "common.tplvalues.render" (dict "value" .Values.topologySpreadConstraints "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.terminationGracePeriodSeconds }} + terminationGracePeriodSeconds: {{ .Values.terminationGracePeriodSeconds }} + {{- end }} + {{- if .Values.priorityClassName }} + priorityClassName: {{ .Values.priorityClassName }} + {{- end }} + {{- if .Values.podSecurityContext.enabled }} + securityContext: {{- omit .Values.podSecurityContext "enabled" | toYaml | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "kafka.serviceAccountName" . }} + {{- if or (and .Values.volumePermissions.enabled .Values.persistence.enabled) (and .Values.externalAccess.enabled .Values.externalAccess.autoDiscovery.enabled) .Values.initContainers }} + initContainers: + {{- if and .Values.volumePermissions.enabled .Values.persistence.enabled }} + - name: volume-permissions + image: {{ include "kafka.volumePermissions.image" . }} + imagePullPolicy: {{ .Values.volumePermissions.image.pullPolicy | quote }} + command: + - /bin/bash + args: + - -ec + - | + mkdir -p "{{ .Values.persistence.mountPath }}" "{{ .Values.logPersistence.mountPath }}" + chown -R {{ .Values.containerSecurityContext.runAsUser }}:{{ .Values.podSecurityContext.fsGroup }} "{{ .Values.persistence.mountPath }}" "{{ .Values.logPersistence.mountPath }}" + find "{{ .Values.persistence.mountPath }}" -mindepth 1 -maxdepth 1 -not -name ".snapshot" -not -name "lost+found" | xargs -r chown -R {{ .Values.containerSecurityContext.runAsUser }}:{{ .Values.podSecurityContext.fsGroup }} + find "{{ .Values.logPersistence.mountPath }}" -mindepth 1 -maxdepth 1 -not -name ".snapshot" -not -name "lost+found" | xargs -r chown -R {{ .Values.containerSecurityContext.runAsUser }}:{{ .Values.podSecurityContext.fsGroup }} + {{- if eq ( toString ( .Values.volumePermissions.containerSecurityContext.runAsUser )) "auto" }} + securityContext: {{- omit .Values.volumePermissions.containerSecurityContext "runAsUser" | toYaml | nindent 12 }} + {{- else }} + securityContext: {{- .Values.volumePermissions.containerSecurityContext | toYaml | nindent 12 }} + {{- end }} + {{- if .Values.volumePermissions.resources }} + resources: {{- toYaml .Values.volumePermissions.resources | nindent 12 }} + {{- end }} + volumeMounts: + - name: data + mountPath: {{ .Values.persistence.mountPath }} + - name: logs + mountPath: {{ .Values.logPersistence.mountPath }} + {{- end }} + {{- if and .Values.externalAccess.enabled .Values.externalAccess.autoDiscovery.enabled }} + - name: auto-discovery + image: {{ include "kafka.externalAccess.autoDiscovery.image" . }} + imagePullPolicy: {{ .Values.externalAccess.autoDiscovery.image.pullPolicy | quote }} + command: + - /scripts/auto-discovery.sh + env: + - name: MY_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: SHARED_FILE + value: "/shared/info.txt" + {{- if .Values.externalAccess.autoDiscovery.resources }} + resources: {{- toYaml .Values.externalAccess.autoDiscovery.resources | nindent 12 }} + {{- end }} + volumeMounts: + - name: shared + mountPath: /shared + - name: logs + mountPath: {{ .Values.logPersistence.mountPath }} + - name: scripts + mountPath: /scripts/auto-discovery.sh + subPath: auto-discovery.sh + {{- end }} + {{- if .Values.initContainers }} + {{- include "common.tplvalues.render" ( dict "value" .Values.initContainers "context" $ ) | nindent 8 }} + {{- end }} + {{- end }} + containers: + - name: kafka + image: {{ include "kafka.image" . }} + imagePullPolicy: {{ .Values.image.pullPolicy | quote }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 12 }} + {{- end }} + {{- if .Values.diagnosticMode.enabled }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 12 }} + {{- else if .Values.command }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.command "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.diagnosticMode.enabled }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 12 }} + {{- else if .Values.args }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.args "context" $) | nindent 12 }} + {{- end }} + env: + - name: BITNAMI_DEBUG + value: {{ ternary "true" "false" (or .Values.image.debug .Values.diagnosticMode.enabled) | quote }} + - name: MY_POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: MY_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: KAFKA_CFG_ZOOKEEPER_CONNECT + {{- if .Values.zookeeper.enabled }} + value: {{ printf "%s%s" (include "kafka.zookeeper.fullname" .) (tpl .Values.zookeeperChrootPath .) | quote }} + {{- else }} + value: {{ include "common.tplvalues.render" (dict "value" (printf "%s%s" (join "," .Values.externalZookeeper.servers) (tpl .Values.zookeeperChrootPath .)) "context" $) }} + {{- end }} + - name: KAFKA_INTER_BROKER_LISTENER_NAME + value: {{ .Values.interBrokerListenerName | quote }} + - name: KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP + {{- if .Values.listenerSecurityProtocolMap }} + value: {{ .Values.listenerSecurityProtocolMap | quote }} + {{- else if .Values.externalAccess.enabled }} + value: "INTERNAL:{{ $interBrokerProtocol }},CLIENT:{{ $clientProtocol }},EXTERNAL:{{ $externalClientProtocol }}" + {{- else }} + value: "INTERNAL:{{ $interBrokerProtocol }},CLIENT:{{ $clientProtocol }}" + {{- end }} + {{- if or ($clientProtocol | regexFind "SASL") ($externalClientProtocol | regexFind "SASL") ($interBrokerProtocol | regexFind "SASL") .Values.auth.sasl.jaas.zookeeperUser }} + - name: KAFKA_CFG_SASL_ENABLED_MECHANISMS + value: {{ upper .Values.auth.sasl.mechanisms | quote }} + - name: KAFKA_CFG_SASL_MECHANISM_INTER_BROKER_PROTOCOL + value: {{ upper .Values.auth.sasl.interBrokerMechanism | quote }} + {{- end }} + - name: KAFKA_CFG_LISTENERS + {{- if .Values.listeners }} + value: {{ join "," .Values.listeners }} + {{- else if .Values.externalAccess.enabled }} + value: "INTERNAL://:{{ $interBrokerPort }},CLIENT://:9092,EXTERNAL://:9094" + {{- else }} + value: "INTERNAL://:{{ $interBrokerPort }},CLIENT://:9092" + {{- end }} + {{- if .Values.externalAccess.enabled }} + {{- if .Values.externalAccess.autoDiscovery.enabled }} + - name: SHARED_FILE + value: "/shared/info.txt" + {{- end }} + {{- if eq .Values.externalAccess.service.type "NodePort" }} + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + {{- end }} + {{- else }} + - name: KAFKA_CFG_ADVERTISED_LISTENERS + {{- if .Values.advertisedListeners }} + value: {{ join "," .Values.advertisedListeners }} + {{- else }} + value: "INTERNAL://$(MY_POD_NAME).{{ $fullname }}-headless.{{ $releaseNamespace }}.svc.{{ $clusterDomain }}:{{ $interBrokerPort }},CLIENT://$(MY_POD_NAME).{{ $fullname }}-headless.{{ $releaseNamespace }}.svc.{{ $clusterDomain }}:{{ $clientPort }}" + {{- end }} + {{- end }} + - name: ALLOW_PLAINTEXT_LISTENER + value: {{ ternary "yes" "no" .Values.allowPlaintextListener | quote }} + {{- if or (include "kafka.client.saslAuthentication" .) (include "kafka.interBroker.saslAuthentication" .) }} + - name: KAFKA_OPTS + value: "-Djava.security.auth.login.config=/opt/bitnami/kafka/config/kafka_jaas.conf" + {{- if (include "kafka.client.saslAuthentication" .) }} + - name: KAFKA_CLIENT_USERS + value: {{ join "," .Values.auth.sasl.jaas.clientUsers | quote }} + - name: KAFKA_CLIENT_PASSWORDS + valueFrom: + secretKeyRef: + name: {{ include "kafka.jaasSecretName" . }} + key: client-passwords + {{- end }} + {{- if (include "kafka.interBroker.saslAuthentication" .) }} + - name: KAFKA_INTER_BROKER_USER + value: {{ .Values.auth.sasl.jaas.interBrokerUser | quote }} + - name: KAFKA_INTER_BROKER_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "kafka.jaasSecretName" . }} + key: inter-broker-password + {{- end }} + {{- end }} + {{- if and .Values.zookeeper.auth.client.enabled .Values.auth.sasl.jaas.zookeeperUser }} + - name: KAFKA_ZOOKEEPER_USER + value: {{ .Values.auth.sasl.jaas.zookeeperUser | quote }} + - name: KAFKA_ZOOKEEPER_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "kafka.jaasSecretName" . }} + key: zookeeper-password + {{- end }} + - name: KAFKA_ZOOKEEPER_PROTOCOL + value: {{ include "kafka.zookeeper.protocol" . }} + {{- if .Values.auth.zookeeper.tls.enabled }} + - name: KAFKA_ZOOKEEPER_TLS_TYPE + value: {{ upper .Values.auth.zookeeper.tls.type | quote }} + - name: KAFKA_ZOOKEEPER_TLS_VERIFY_HOSTNAME + value: {{ .Values.auth.zookeeper.tls.verifyHostname | quote }} + {{- if .Values.auth.zookeeper.tls.passwordsSecret }} + - name: KAFKA_ZOOKEEPER_TLS_KEYSTORE_PASSWORD + valueFrom: + secretKeyRef: + name: {{ .Values.auth.zookeeper.tls.passwordsSecret }} + key: {{ .Values.auth.zookeeper.tls.passwordsSecretKeystoreKey | quote }} + - name: KAFKA_ZOOKEEPER_TLS_TRUSTSTORE_PASSWORD + valueFrom: + secretKeyRef: + name: {{ .Values.auth.zookeeper.tls.passwordsSecret }} + key: {{ .Values.auth.zookeeper.tls.passwordsSecretTruststoreKey | quote }} + {{- end }} + {{- end }} + {{- if (include "kafka.tlsEncryption" .) }} + - name: KAFKA_TLS_TYPE + value: {{ upper .Values.auth.tls.type | quote }} + - name: KAFKA_CFG_SSL_ENDPOINT_IDENTIFICATION_ALGORITHM + value: {{ default "" .Values.auth.tls.endpointIdentificationAlgorithm | quote }} + - name: KAFKA_TLS_CLIENT_AUTH + value: {{ ternary "required" "none" (or (eq (include "kafka.externalClientProtocol" . ) "mtls") (eq .Values.auth.clientProtocol "mtls")) | quote }} + - name: KAFKA_CERTIFICATE_PASSWORD + {{- if .Values.auth.tls.existingSecret }} + valueFrom: + secretKeyRef: + name: {{ .Values.auth.tls.existingSecret }} + key: password + {{- else }} + value: {{ default "" .Values.auth.tls.password | quote }} + {{- end }} + {{- end }} + {{- if .Values.metrics.jmx.enabled }} + - name: JMX_PORT + value: "5555" + {{- end }} + - name: KAFKA_VOLUME_DIR + value: {{ .Values.persistence.mountPath | quote }} + - name: KAFKA_LOG_DIR + value: {{ .Values.logPersistence.mountPath | quote }} + - name: KAFKA_CFG_DELETE_TOPIC_ENABLE + value: {{ .Values.deleteTopicEnable | quote }} + - name: KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE + value: {{ .Values.autoCreateTopicsEnable | quote }} + - name: KAFKA_HEAP_OPTS + value: {{ .Values.heapOpts | quote }} + - name: KAFKA_CFG_LOG_FLUSH_INTERVAL_MESSAGES + value: {{ .Values.logFlushIntervalMessages | replace "_" "" | quote }} + - name: KAFKA_CFG_LOG_FLUSH_INTERVAL_MS + value: {{ .Values.logFlushIntervalMs | quote }} + - name: KAFKA_CFG_LOG_RETENTION_BYTES + value: {{ .Values.logRetentionBytes | replace "_" "" | quote }} + - name: KAFKA_CFG_LOG_RETENTION_CHECK_INTERVAL_MS + value: {{ .Values.logRetentionCheckIntervalMs | quote }} + - name: KAFKA_CFG_LOG_RETENTION_HOURS + value: {{ .Values.logRetentionHours | quote }} + - name: KAFKA_CFG_MESSAGE_MAX_BYTES + value: {{ .Values.maxMessageBytes | replace "_" "" | quote }} + - name: KAFKA_CFG_LOG_SEGMENT_BYTES + value: {{ .Values.logSegmentBytes | replace "_" "" | quote }} + - name: KAFKA_CFG_LOG_DIRS + value: {{ .Values.logsDirs | quote }} + - name: KAFKA_CFG_DEFAULT_REPLICATION_FACTOR + value: {{ .Values.defaultReplicationFactor | quote }} + - name: KAFKA_CFG_OFFSETS_TOPIC_REPLICATION_FACTOR + value: {{ .Values.offsetsTopicReplicationFactor | quote }} + - name: KAFKA_CFG_TRANSACTION_STATE_LOG_REPLICATION_FACTOR + value: {{ .Values.transactionStateLogReplicationFactor | quote }} + - name: KAFKA_CFG_TRANSACTION_STATE_LOG_MIN_ISR + value: {{ .Values.transactionStateLogMinIsr | quote }} + - name: KAFKA_CFG_NUM_IO_THREADS + value: {{ .Values.numIoThreads | quote }} + - name: KAFKA_CFG_NUM_NETWORK_THREADS + value: {{ .Values.numNetworkThreads | quote }} + - name: KAFKA_CFG_NUM_PARTITIONS + value: {{ .Values.numPartitions | quote }} + - name: KAFKA_CFG_NUM_RECOVERY_THREADS_PER_DATA_DIR + value: {{ .Values.numRecoveryThreadsPerDataDir | quote }} + - name: KAFKA_CFG_SOCKET_RECEIVE_BUFFER_BYTES + value: {{ .Values.socketReceiveBufferBytes | quote }} + - name: KAFKA_CFG_SOCKET_REQUEST_MAX_BYTES + value: {{ .Values.socketRequestMaxBytes | replace "_" "" | quote }} + - name: KAFKA_CFG_SOCKET_SEND_BUFFER_BYTES + value: {{ .Values.socketSendBufferBytes | quote }} + - name: KAFKA_CFG_ZOOKEEPER_CONNECTION_TIMEOUT_MS + value: {{ .Values.zookeeperConnectionTimeoutMs | quote }} + - name: KAFKA_CFG_AUTHORIZER_CLASS_NAME + value: {{ .Values.authorizerClassName | quote }} + - name: KAFKA_CFG_ALLOW_EVERYONE_IF_NO_ACL_FOUND + value: {{ .Values.allowEveryoneIfNoAclFound | quote }} + - name: KAFKA_CFG_SUPER_USERS + value: {{ .Values.superUsers | quote }} + {{- if .Values.extraEnvVars }} + {{- include "common.tplvalues.render" ( dict "value" .Values.extraEnvVars "context" $) | nindent 12 }} + {{- end }} + {{- if or .Values.extraEnvVarsCM .Values.extraEnvVarsSecret }} + envFrom: + {{- if .Values.extraEnvVarsCM }} + - configMapRef: + name: {{ include "common.tplvalues.render" (dict "value" .Values.extraEnvVarsCM "context" $) }} + {{- end }} + {{- if .Values.extraEnvVarsSecret }} + - secretRef: + name: {{ include "common.tplvalues.render" (dict "value" .Values.extraEnvVarsSecret "context" $) }} + {{- end }} + {{- end }} + ports: + - name: kafka-client + containerPort: {{ .Values.containerPorts.client }} + - name: kafka-internal + containerPort: {{ .Values.containerPorts.internal }} + {{- if .Values.externalAccess.enabled }} + - name: kafka-external + containerPort: {{ .Values.containerPorts.external }} + {{- end }} + {{- if not .Values.diagnosticMode.enabled }} + {{- if .Values.customLivenessProbe }} + livenessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.customLivenessProbe "context" $) | nindent 12 }} + {{- else if .Values.livenessProbe.enabled }} + livenessProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.livenessProbe "enabled") "context" $) | nindent 12 }} + tcpSocket: + port: kafka-client + {{- end }} + {{- if .Values.customReadinessProbe }} + readinessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.customReadinessProbe "context" $) | nindent 12 }} + {{- else if .Values.readinessProbe.enabled }} + readinessProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.readinessProbe "enabled") "context" $) | nindent 12 }} + tcpSocket: + port: kafka-client + {{- end }} + {{- if .Values.customStartupProbe }} + startupProbe: {{- include "common.tplvalues.render" (dict "value" .Values.customStartupProbe "context" $) | nindent 12 }} + {{- else if .Values.startupProbe.enabled }} + startupProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.startupProbe "enabled") "context" $) | nindent 12 }} + tcpSocket: + port: kafka-client + {{- end }} + {{- end }} + {{- if .Values.lifecycleHooks }} + lifecycle: {{- include "common.tplvalues.render" (dict "value" .Values.lifecycleHooks "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.resources }} + resources: {{- toYaml .Values.resources | nindent 12 }} + {{- end }} + volumeMounts: + - name: data + mountPath: {{ .Values.persistence.mountPath }} + - name: logs + mountPath: {{ .Values.logPersistence.mountPath }} + {{- if or .Values.config .Values.existingConfigmap }} + - name: kafka-config + mountPath: {{ .Values.persistence.mountPath }}/config/server.properties + subPath: server.properties + {{- end }} + {{- if or .Values.log4j .Values.existingLog4jConfigMap }} + - name: log4j-config + mountPath: {{ .Values.persistence.mountPath }}/config/log4j.properties + subPath: log4j.properties + {{- end }} + - name: scripts + mountPath: /scripts/setup.sh + subPath: setup.sh + {{- if and .Values.externalAccess.enabled .Values.externalAccess.autoDiscovery.enabled }} + - name: shared + mountPath: /shared + {{- end }} + {{- if (include "kafka.tlsEncryption" .) }} + {{- if not (empty .Values.auth.tls.existingSecrets) }} + {{- range $index, $_ := .Values.auth.tls.existingSecrets }} + - name: kafka-certs-{{ $index }} + mountPath: /certs-{{ $index }} + readOnly: true + {{- end }} + {{- else if .Values.auth.tls.autoGenerated }} + {{- range $index := until $replicaCount }} + - name: kafka-certs-{{ $index }} + mountPath: /certs-{{ $index }} + readOnly: true + {{- end }} + {{- end }} + {{- if and .Values.auth.zookeeper.tls.enabled .Values.auth.zookeeper.tls.existingSecret }} + - name: kafka-zookeeper-cert + mountPath: /kafka-zookeeper-cert + readOnly: true + {{- end }} + {{- if .Values.auth.tls.jksTruststoreSecret }} + - name: kafka-truststore + mountPath: /truststore + readOnly: true + {{- end }} + {{- end }} + {{- if .Values.extraVolumeMounts }} + {{- include "common.tplvalues.render" (dict "value" .Values.extraVolumeMounts "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.metrics.jmx.enabled }} + - name: jmx-exporter + image: {{ include "kafka.metrics.jmx.image" . }} + imagePullPolicy: {{ .Values.metrics.jmx.image.pullPolicy | quote }} + {{- if .Values.metrics.jmx.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.metrics.jmx.containerSecurityContext "enabled" | toYaml | nindent 12 }} + {{- end }} + {{- if .Values.diagnosticMode.enabled }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 12 }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 12 }} + {{- else }} + command: + - java + args: + - -XX:MaxRAMPercentage=100 + - -XshowSettings:vm + - -jar + - jmx_prometheus_httpserver.jar + - "5556" + - /etc/jmx-kafka/jmx-kafka-prometheus.yml + {{- end }} + ports: + - name: metrics + containerPort: {{ .Values.metrics.jmx.containerPorts.metrics }} + {{- if .Values.metrics.jmx.resources }} + resources: {{- toYaml .Values.metrics.jmx.resources | nindent 12 }} + {{- end }} + volumeMounts: + - name: jmx-config + mountPath: /etc/jmx-kafka + {{- end }} + {{- if .Values.sidecars }} + {{- include "common.tplvalues.render" (dict "value" .Values.sidecars "context" $) | nindent 8 }} + {{- end }} + volumes: + {{- if or .Values.config .Values.existingConfigmap }} + - name: kafka-config + configMap: + name: {{ include "kafka.configmapName" . }} + {{- end }} + {{- if or .Values.log4j .Values.existingLog4jConfigMap }} + - name: log4j-config + configMap: + name: {{ include "kafka.log4j.configMapName" . }} + {{ end }} + - name: scripts + configMap: + name: {{ include "common.names.fullname" . }}-scripts + defaultMode: 0755 + {{- if and .Values.externalAccess.enabled .Values.externalAccess.autoDiscovery.enabled }} + - name: shared + emptyDir: {} + {{- end }} + {{- if .Values.metrics.jmx.enabled }} + - name: jmx-config + configMap: + name: {{ include "kafka.metrics.jmx.configmapName" . }} + {{- end }} + {{- if (include "kafka.tlsEncryption" .) }} + {{- if not (empty .Values.auth.tls.existingSecrets) }} + {{- range $index, $secret := .Values.auth.tls.existingSecrets }} + - name: kafka-certs-{{ $index }} + secret: + secretName: {{ tpl $secret $ }} + defaultMode: 256 + {{- end }} + {{- else if .Values.auth.tls.autoGenerated }} + {{- range $index := until $replicaCount }} + - name: kafka-certs-{{ $index }} + secret: + secretName: {{ printf "%s-%d-tls" (include "common.names.fullname" $) $index }} + defaultMode: 256 + {{- end }} + {{- end }} + {{- if and .Values.auth.zookeeper.tls.enabled .Values.auth.zookeeper.tls.existingSecret }} + - name: kafka-zookeeper-cert + secret: + secretName: {{ .Values.auth.zookeeper.tls.existingSecret }} + defaultMode: 256 + {{- end }} + {{- if .Values.auth.tls.jksTruststoreSecret }} + - name: kafka-truststore + secret: + secretName: {{ .Values.auth.tls.jksTruststoreSecret }} + defaultMode: 256 + {{- end }} + {{- end }} + {{- if .Values.extraVolumes }} + {{- include "common.tplvalues.render" (dict "value" .Values.extraVolumes "context" $) | nindent 8 }} + {{- end }} +{{- if not .Values.persistence.enabled }} + - name: data + emptyDir: {} +{{- else if .Values.persistence.existingClaim }} + - name: data + persistentVolumeClaim: + claimName: {{ printf "%s" (tpl .Values.persistence.existingClaim .) }} +{{- end }} +{{- if not .Values.logPersistence.enabled }} + - name: logs + emptyDir: {} +{{- else if .Values.logPersistence.existingClaim }} + - name: logs + persistentVolumeClaim: + claimName: {{ printf "%s" (tpl .Values.logPersistence.existingClaim .) }} +{{- end }} + {{- if or (and .Values.persistence.enabled (not .Values.persistence.existingClaim)) (and .Values.logPersistence.enabled (not .Values.logPersistence.existingClaim)) }} + volumeClaimTemplates: + {{- end }} +{{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }} + - metadata: + name: data + {{- if .Values.persistence.annotations }} + annotations: {{- include "common.tplvalues.render" (dict "value" .Values.persistence.annotations "context" $) | nindent 10 }} + {{- end }} + {{- if .Values.persistence.labels }} + labels: {{- include "common.tplvalues.render" (dict "value" .Values.persistence.labels "context" $) | nindent 10 }} + {{- end }} + spec: + accessModes: + {{- range .Values.persistence.accessModes }} + - {{ . | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.persistence.size | quote }} + {{ include "kafka.storageClass" . | nindent 8 }} + {{- if .Values.persistence.selector }} + selector: {{- include "common.tplvalues.render" (dict "value" .Values.persistence.selector "context" $) | nindent 10 }} + {{- end -}} +{{- end }} +{{- if and .Values.logPersistence.enabled (not .Values.logPersistence.existingClaim) }} + - metadata: + name: logs + {{- if .Values.logPersistence.annotations }} + annotations: {{- include "common.tplvalues.render" (dict "value" .Values.logPersistence.annotations "context" $) | nindent 10 }} + {{- end }} + spec: + accessModes: + {{- range .Values.logPersistence.accessModes }} + - {{ . | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.logPersistence.size | quote }} + {{ include "kafka.storageClass" . | nindent 8 }} + {{- if .Values.logPersistence.selector }} + selector: {{- include "common.tplvalues.render" (dict "value" .Values.logPersistence.selector "context" $) | nindent 10 }} + {{- end -}} +{{- end }} diff --git a/knowlg-automation/helm_charts/kafka/templates/svc-external-access.yaml b/knowlg-automation/helm_charts/kafka/templates/svc-external-access.yaml new file mode 100644 index 000000000..8d77a4710 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/templates/svc-external-access.yaml @@ -0,0 +1,63 @@ +{{- if .Values.externalAccess.enabled }} +{{- $fullName := include "common.names.fullname" . }} +{{- $replicaCount := .Values.replicaCount | int }} +{{- $root := . }} + +{{- range $i, $e := until $replicaCount }} +{{- $targetPod := printf "%s-%d" (printf "%s" $fullName) $i }} +{{- $_ := set $ "targetPod" $targetPod }} +apiVersion: v1 +kind: Service +metadata: + name: {{ printf "%s-%d-external" (include "common.names.fullname" $) $i | trunc 63 | trimSuffix "-" }} + namespace: {{ $root.Release.Namespace | quote }} + labels: {{- include "common.labels.standard" $ | nindent 4 }} + app.kubernetes.io/component: kafka + pod: {{ $targetPod }} + {{- if $root.Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" $root.Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if $root.Values.externalAccess.service.labels }} + {{- include "common.tplvalues.render" ( dict "value" $root.Values.externalAccess.service.labels "context" $) | nindent 4 }} + {{- end }} + {{- if or $root.Values.externalAccess.service.annotations $root.Values.commonAnnotations $root.Values.externalAccess.service.loadBalancerAnnotations }} + annotations: + {{- if and (not (empty $root.Values.externalAccess.service.loadBalancerAnnotations)) (eq (len $root.Values.externalAccess.service.loadBalancerAnnotations) $replicaCount) }} + {{ include "common.tplvalues.render" ( dict "value" (index $root.Values.externalAccess.service.loadBalancerAnnotations $i) "context" $) | nindent 4 }} + {{- end }} + {{- if $root.Values.externalAccess.service.annotations }} + {{- include "common.tplvalues.render" ( dict "value" $root.Values.externalAccess.service.annotations "context" $) | nindent 4 }} + {{- end }} + {{- if $root.Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" $root.Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- end }} +spec: + type: {{ $root.Values.externalAccess.service.type }} + {{- if eq $root.Values.externalAccess.service.type "LoadBalancer" }} + {{- if and (not (empty $root.Values.externalAccess.service.loadBalancerIPs)) (eq (len $root.Values.externalAccess.service.loadBalancerIPs) $replicaCount) }} + loadBalancerIP: {{ index $root.Values.externalAccess.service.loadBalancerIPs $i }} + {{- end }} + {{- if $root.Values.externalAccess.service.loadBalancerSourceRanges }} + loadBalancerSourceRanges: {{- toYaml $root.Values.externalAccess.service.loadBalancerSourceRanges | nindent 4 }} + {{- end }} + {{- end }} + publishNotReadyAddresses: {{ $root.Values.externalAccess.service.publishNotReadyAddresses }} + ports: + - name: tcp-kafka + port: {{ $root.Values.externalAccess.service.ports.external }} + {{- if not (empty $root.Values.externalAccess.service.nodePorts) }} + nodePort: {{ index $root.Values.externalAccess.service.nodePorts $i }} + {{- else }} + nodePort: null + {{- end }} + targetPort: kafka-external + {{- if $root.Values.externalAccess.service.extraPorts }} + {{- include "common.tplvalues.render" (dict "value" $root.Values.externalAccess.service.extraPorts "context" $) | nindent 4 }} + {{- end }} + selector: {{- include "common.labels.matchLabels" $ | nindent 4 }} + app.kubernetes.io/component: kafka + statefulset.kubernetes.io/pod-name: {{ $targetPod }} +--- +{{- end }} +{{- end }} diff --git a/knowlg-automation/helm_charts/kafka/templates/svc-headless.yaml b/knowlg-automation/helm_charts/kafka/templates/svc-headless.yaml new file mode 100644 index 000000000..af462126a --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/templates/svc-headless.yaml @@ -0,0 +1,37 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ printf "%s-headless" (include "common.names.fullname" .) | trunc 63 | trimSuffix "-" }} + namespace: {{ .Release.Namespace | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: kafka + {{- if .Values.service.headless.labels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.service.headless.labels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if or .Values.service.headless.annotations .Values.commonAnnotations }} + annotations: + {{- if .Values.service.headless.annotations }} + {{- include "common.tplvalues.render" (dict "value" .Values.service.headless.annotations "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" (dict "value" .Values.commonAnnotations "context" $) | nindent 4 }} + {{- end }} + {{- end }} +spec: + type: ClusterIP + clusterIP: None + publishNotReadyAddresses: {{ .Values.service.headless.publishNotReadyAddresses }} + ports: + - name: tcp-client + port: {{ .Values.service.ports.client }} + protocol: TCP + targetPort: kafka-client + - name: tcp-internal + port: {{ .Values.service.ports.internal }} + protocol: TCP + targetPort: kafka-internal + selector: {{- include "common.labels.matchLabels" . | nindent 4 }} + app.kubernetes.io/component: kafka diff --git a/knowlg-automation/helm_charts/kafka/templates/svc.yaml b/knowlg-automation/helm_charts/kafka/templates/svc.yaml new file mode 100644 index 000000000..8e0472a1d --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/templates/svc.yaml @@ -0,0 +1,63 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ template "common.names.fullname" . }} + namespace: {{ .Release.Namespace | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: kafka + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if or .Values.service.annotations .Values.commonAnnotations }} + annotations: + {{- if .Values.service.annotations }} + {{ include "common.tplvalues.render" ( dict "value" .Values.service.annotations "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- end }} +spec: + type: {{ .Values.service.type }} + {{- if and .Values.service.clusterIP (eq .Values.service.type "ClusterIP") }} + clusterIP: {{ .Values.service.clusterIP }} + {{- end }} + {{- if or (eq .Values.service.type "LoadBalancer") (eq .Values.service.type "NodePort") }} + externalTrafficPolicy: {{ .Values.service.externalTrafficPolicy | quote }} + {{- end }} + {{- if and (eq .Values.service.type "LoadBalancer") (not (empty .Values.service.loadBalancerSourceRanges)) }} + loadBalancerSourceRanges: {{ .Values.service.loadBalancerSourceRanges }} + {{- end }} + {{- if and (eq .Values.service.type "LoadBalancer") (not (empty .Values.service.loadBalancerIP)) }} + loadBalancerIP: {{ .Values.service.loadBalancerIP }} + {{- end }} + {{- if .Values.service.sessionAffinity }} + sessionAffinity: {{ .Values.service.sessionAffinity }} + {{- end }} + {{- if .Values.service.sessionAffinityConfig }} + sessionAffinityConfig: {{- include "common.tplvalues.render" (dict "value" .Values.service.sessionAffinityConfig "context" $) | nindent 4 }} + {{- end }} + ports: + - name: tcp-client + port: {{ .Values.service.ports.client }} + protocol: TCP + targetPort: kafka-client + {{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePorts.client)) }} + nodePort: {{ .Values.service.nodePorts.client }} + {{- else if eq .Values.service.type "ClusterIP" }} + nodePort: null + {{- end }} + {{- if .Values.externalAccess.enabled }} + - name: tcp-external + port: {{ .Values.service.ports.external }} + protocol: TCP + targetPort: kafka-external + {{- if (not (empty .Values.service.nodePorts.external)) }} + nodePort: {{ .Values.service.nodePorts.external }} + {{- end }} + {{- end }} + {{- if .Values.service.extraPorts }} + {{- include "common.tplvalues.render" (dict "value" .Values.service.extraPorts "context" $) | nindent 4 }} + {{- end }} + selector: {{- include "common.labels.matchLabels" . | nindent 4 }} + app.kubernetes.io/component: kafka diff --git a/knowlg-automation/helm_charts/kafka/templates/tls-secrets.yaml b/knowlg-automation/helm_charts/kafka/templates/tls-secrets.yaml new file mode 100644 index 000000000..fdf350e87 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/templates/tls-secrets.yaml @@ -0,0 +1,30 @@ +{{- if (include "kafka.createTlsSecret" .) }} +{{- $replicaCount := int .Values.replicaCount }} +{{- $releaseNamespace := .Release.Namespace }} +{{- $clusterDomain := .Values.clusterDomain }} +{{- $fullname := include "common.names.fullname" . }} +{{- $ca := genCA "kafka-ca" 365 }} +{{- range $i := until $replicaCount }} +{{- $replicaHost := printf "%s-%d.%s-headless" $fullname $i $fullname }} +{{- $altNames := list (printf "%s.%s.svc.%s" $replicaHost $releaseNamespace $clusterDomain) (printf "%s.%s.svc.%s" $fullname $releaseNamespace $clusterDomain) (printf "%s.%s" $replicaHost $releaseNamespace) (printf "%s.%s" $fullname $releaseNamespace) $replicaHost $fullname }} +{{- $cert := genSignedCert $replicaHost nil $altNames 365 $ca }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ printf "%s-%d-tls" (include "common.names.fullname" $) $i }} + namespace: {{ $.Release.Namespace | quote }} + labels: {{- include "common.labels.standard" $ | nindent 4 }} + {{- if $.Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" $.Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if $.Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" $.Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +type: kubernetes.io/tls +data: + ca.crt: {{ $ca.Cert | b64enc | quote }} + tls.crt: {{ $cert.Cert | b64enc | quote }} + tls.key: {{ $cert.Key | b64enc | quote }} +--- +{{- end }} +{{- end }} diff --git a/knowlg-automation/helm_charts/kafka/values.yaml b/knowlg-automation/helm_charts/kafka/values.yaml new file mode 100644 index 000000000..cbf1e8fb6 --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/values.yaml @@ -0,0 +1,1781 @@ +## @section Global parameters +## Global Docker image parameters +## Please, note that this will override the image parameters, including dependencies, configured to use the global value +## Current available global Docker image parameters: imageRegistry, imagePullSecrets and storageClass + +## @param global.imageRegistry Global Docker image registry +## @param global.imagePullSecrets Global Docker registry secret names as an array +## @param global.storageClass Global StorageClass for Persistent Volume(s) +## +global: + imageRegistry: "" + ## E.g. + ## imagePullSecrets: + ## - myRegistryKeySecretName + ## + imagePullSecrets: [] + storageClass: "" + +## @section Common parameters + +## @param kubeVersion Override Kubernetes version +## +kubeVersion: "" +## @param nameOverride String to partially override common.names.fullname +## +nameOverride: "" +## @param fullnameOverride String to fully override common.names.fullname +## +fullnameOverride: "" +## @param clusterDomain Default Kubernetes cluster domain +## +clusterDomain: cluster.local +## @param commonLabels Labels to add to all deployed objects +## +commonLabels: {} +## @param commonAnnotations Annotations to add to all deployed objects +## +commonAnnotations: {} +## @param extraDeploy Array of extra objects to deploy with the release +## +extraDeploy: [] +## Enable diagnostic mode in the statefulset +## +diagnosticMode: + ## @param diagnosticMode.enabled Enable diagnostic mode (all probes will be disabled and the command will be overridden) + ## + enabled: false + ## @param diagnosticMode.command Command to override all containers in the statefulset + ## + command: + - sleep + ## @param diagnosticMode.args Args to override all containers in the statefulset + ## + args: + - infinity + +## @section Kafka parameters + +## Bitnami Kafka image version +## ref: https://hub.docker.com/r/bitnami/kafka/tags/ +## @param image.registry Kafka image registry +## @param image.repository Kafka image repository +## @param image.tag Kafka image tag (immutable tags are recommended) +## @param image.digest Kafka image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag +## @param image.pullPolicy Kafka image pull policy +## @param image.pullSecrets Specify docker-registry secret names as an array +## @param image.debug Specify if debug values should be set +## +image: + registry: docker.io + repository: bitnami/kafka + tag: 3.3.1-debian-11-r25 + digest: "" + ## Specify a imagePullPolicy + ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' + ## ref: https://kubernetes.io/docs/user-guide/images/#pre-pulling-images + ## + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## e.g: + ## pullSecrets: + ## - myRegistryKeySecretName + ## + pullSecrets: [] + ## Set to true if you would like to see extra information on logs + ## + debug: false +## @param config Configuration file for Kafka. Auto-generated based on other parameters when not specified +## Specify content for server.properties +## NOTE: This will override any KAFKA_CFG_ environment variables (including those set by the chart) +## The server.properties is auto-generated based on other parameters when this parameter is not specified +## e.g: +## config: |- +## broker.id=-1 +## listeners=PLAINTEXT://:9092 +## advertised.listeners=PLAINTEXT://KAFKA_IP:9092 +## num.network.threads=3 +## num.io.threads=8 +## socket.send.buffer.bytes=102400 +## socket.receive.buffer.bytes=102400 +## socket.request.max.bytes=104857600 +## log.dirs=/bitnami/kafka/data +## num.partitions=1 +## num.recovery.threads.per.data.dir=1 +## offsets.topic.replication.factor=1 +## transaction.state.log.replication.factor=1 +## transaction.state.log.min.isr=1 +## log.flush.interval.messages=10000 +## log.flush.interval.ms=1000 +## log.retention.hours=168 +## log.retention.bytes=1073741824 +## log.segment.bytes=1073741824 +## log.retention.check.interval.ms=300000 +## zookeeper.connect=ZOOKEEPER_SERVICE_NAME +## zookeeper.connection.timeout.ms=6000 +## group.initial.rebalance.delay.ms=0 +## +config: "" +## @param existingConfigmap ConfigMap with Kafka Configuration +## NOTE: This will override `config` AND any KAFKA_CFG_ environment variables +## +existingConfigmap: "" +## @param log4j An optional log4j.properties file to overwrite the default of the Kafka brokers +## An optional log4j.properties file to overwrite the default of the Kafka brokers +## ref: https://github.com/apache/kafka/blob/trunk/config/log4j.properties +## +log4j: "" +## @param existingLog4jConfigMap The name of an existing ConfigMap containing a log4j.properties file +## The name of an existing ConfigMap containing a log4j.properties file +## NOTE: this will override `log4j` +## +existingLog4jConfigMap: "" +## @param heapOpts Kafka Java Heap size +## +heapOpts: -Xmx1024m -Xms1024m +## @param deleteTopicEnable Switch to enable topic deletion or not +## +deleteTopicEnable: false +## @param autoCreateTopicsEnable Switch to enable auto creation of topics. Enabling auto creation of topics not recommended for production or similar environments +## +autoCreateTopicsEnable: true +## @param logFlushIntervalMessages The number of messages to accept before forcing a flush of data to disk +## +logFlushIntervalMessages: _10000 +## @param logFlushIntervalMs The maximum amount of time a message can sit in a log before we force a flush +## +logFlushIntervalMs: 1000 +## @param logRetentionBytes A size-based retention policy for logs +## +logRetentionBytes: _1073741824 +## @param logRetentionCheckIntervalMs The interval at which log segments are checked to see if they can be deleted +## +logRetentionCheckIntervalMs: 300000 +## @param logRetentionHours The minimum age of a log file to be eligible for deletion due to age +## +logRetentionHours: 168 +## @param logSegmentBytes The maximum size of a log segment file. When this size is reached a new log segment will be created +## +logSegmentBytes: _1073741824 +## @param logsDirs A comma separated list of directories in which kafka's log data is kept +## ref: https://kafka.apache.org/documentation/#brokerconfigs_log.dirs +logsDirs: /bitnami/kafka/data +## @param maxMessageBytes The largest record batch size allowed by Kafka +## +maxMessageBytes: _1000012 +## @param defaultReplicationFactor Default replication factors for automatically created topics +## +defaultReplicationFactor: 1 +## @param offsetsTopicReplicationFactor The replication factor for the offsets topic +## +offsetsTopicReplicationFactor: 1 +## @param transactionStateLogReplicationFactor The replication factor for the transaction topic +## +transactionStateLogReplicationFactor: 1 +## @param transactionStateLogMinIsr Overridden min.insync.replicas config for the transaction topic +## +transactionStateLogMinIsr: 1 +## @param numIoThreads The number of threads doing disk I/O +## +numIoThreads: 8 +## @param numNetworkThreads The number of threads handling network requests +## +numNetworkThreads: 3 +## @param numPartitions The default number of log partitions per topic +## +numPartitions: 1 +## @param numRecoveryThreadsPerDataDir The number of threads per data directory to be used for log recovery at startup and flushing at shutdown +## +numRecoveryThreadsPerDataDir: 1 +## @param socketReceiveBufferBytes The receive buffer (SO_RCVBUF) used by the socket server +## +socketReceiveBufferBytes: 102400 +## @param socketRequestMaxBytes The maximum size of a request that the socket server will accept (protection against OOM) +## +socketRequestMaxBytes: _104857600 +## @param socketSendBufferBytes The send buffer (SO_SNDBUF) used by the socket server +## +socketSendBufferBytes: 102400 +## @param zookeeperConnectionTimeoutMs Timeout in ms for connecting to ZooKeeper +## +zookeeperConnectionTimeoutMs: 6000 +## @param zookeeperChrootPath Path which puts data under some path in the global ZooKeeper namespace +## ref: https://kafka.apache.org/documentation/#brokerconfigs_zookeeper.connect +## +zookeeperChrootPath: "" +## @param authorizerClassName The Authorizer is configured by setting authorizer.class.name=kafka.security.authorizer.AclAuthorizer in server.properties +## +authorizerClassName: "" +## @param allowEveryoneIfNoAclFound By default, if a resource has no associated ACLs, then no one is allowed to access that resource except super users +## +allowEveryoneIfNoAclFound: true +## @param superUsers You can add super users in server.properties +## +superUsers: User:admin +## Authentication parameters +## https://github.com/bitnami/containers/tree/main/bitnami/kafka#security +## +auth: + ## Authentication protocol for client and inter-broker communications + ## This table shows the security provided on each protocol: + ## | Method | Authentication | Encryption via TLS | + ## | plaintext | None | No | + ## | tls | None | Yes | + ## | mtls | Yes (two-way authentication) | Yes | + ## | sasl | Yes (via SASL) | No | + ## | sasl_tls | Yes (via SASL) | Yes | + ## @param auth.clientProtocol Authentication protocol for communications with clients. Allowed protocols: `plaintext`, `tls`, `mtls`, `sasl` and `sasl_tls` + ## @param auth.externalClientProtocol Authentication protocol for communications with external clients. Defaults to value of `auth.clientProtocol`. Allowed protocols: `plaintext`, `tls`, `mtls`, `sasl` and `sasl_tls` + ## @param auth.interBrokerProtocol Authentication protocol for inter-broker communications. Allowed protocols: `plaintext`, `tls`, `mtls`, `sasl` and `sasl_tls` + ## + clientProtocol: plaintext + # Note: empty by default for backwards compatibility reasons, find more information at + # https://github.com/bitnami/charts/pull/8902/ + externalClientProtocol: "" + interBrokerProtocol: plaintext + ## SASL configuration + ## + sasl: + ## @param auth.sasl.mechanisms SASL mechanisms when either `auth.interBrokerProtocol`, `auth.clientProtocol` or `auth.externalClientProtocol` are `sasl`. Allowed types: `plain`, `scram-sha-256`, `scram-sha-512` + ## + mechanisms: plain,scram-sha-256,scram-sha-512 + ## @param auth.sasl.interBrokerMechanism SASL mechanism for inter broker communication. + ## + interBrokerMechanism: plain + ## JAAS configuration for SASL authentication. + ## + jaas: + ## @param auth.sasl.jaas.clientUsers Kafka client user list + ## + ## clientUsers: + ## - user1 + ## - user2 + ## + clientUsers: + - user + ## @param auth.sasl.jaas.clientPasswords Kafka client passwords. This is mandatory if more than one user is specified in clientUsers + ## + ## clientPasswords: + ## - password1 + ## - password2" + ## + clientPasswords: [] + ## @param auth.sasl.jaas.interBrokerUser Kafka inter broker communication user for SASL authentication + ## + interBrokerUser: admin + ## @param auth.sasl.jaas.interBrokerPassword Kafka inter broker communication password for SASL authentication + ## + interBrokerPassword: "" + ## @param auth.sasl.jaas.zookeeperUser Kafka ZooKeeper user for SASL authentication + ## + zookeeperUser: "" + ## @param auth.sasl.jaas.zookeeperPassword Kafka ZooKeeper password for SASL authentication + ## + zookeeperPassword: "" + ## @param auth.sasl.jaas.existingSecret Name of the existing secret containing credentials for clientUsers, interBrokerUser and zookeeperUser + ## Create this secret running the command below where SECRET_NAME is the name of the secret you want to create: + ## kubectl create secret generic SECRET_NAME --from-literal=client-passwords=CLIENT_PASSWORD1,CLIENT_PASSWORD2 --from-literal=inter-broker-password=INTER_BROKER_PASSWORD --from-literal=zookeeper-password=ZOOKEEPER_PASSWORD + ## + existingSecret: "" + ## TLS configuration + ## + tls: + ## @param auth.tls.type Format to use for TLS certificates. Allowed types: `jks` and `pem` + ## + type: jks + ## @param auth.tls.pemChainIncluded Flag to denote that the Certificate Authority (CA) certificates are bundled with the endpoint cert. + ## Certificates must be in proper order, where the top certificate is the leaf and the bottom certificate is the top-most intermediate CA. + ## + pemChainIncluded: false + ## @param auth.tls.existingSecrets Array existing secrets containing the TLS certificates for the Kafka brokers + ## When using 'jks' format for certificates, each secret should contain a truststore and a keystore. + ## Create these secrets following the steps below: + ## 1) Generate your truststore and keystore files. Helpful script: https://raw.githubusercontent.com/confluentinc/confluent-platform-security-tools/master/kafka-generate-ssl.sh + ## 2) Rename your truststore to `kafka.truststore.jks`. + ## 3) Rename your keystores to `kafka-X.keystore.jks` where X is the ID of each Kafka broker. + ## 4) Run the command below one time per broker to create its associated secret (SECRET_NAME_X is the name of the secret you want to create): + ## kubectl create secret generic SECRET_NAME_0 --from-file=kafka.truststore.jks=./kafka.truststore.jks --from-file=kafka.keystore.jks=./kafka-0.keystore.jks + ## kubectl create secret generic SECRET_NAME_1 --from-file=kafka.truststore.jks=./kafka.truststore.jks --from-file=kafka.keystore.jks=./kafka-1.keystore.jks + ## ... + ## + ## When using 'pem' format for certificates, each secret should contain a public CA certificate, a public certificate and one private key. + ## Create these secrets following the steps below: + ## 1) Create a certificate key and signing request per Kafka broker, and sign the signing request with your CA + ## 2) Rename your CA file to `kafka.ca.crt`. + ## 3) Rename your certificates to `kafka-X.tls.crt` where X is the ID of each Kafka broker. + ## 3) Rename your keys to `kafka-X.tls.key` where X is the ID of each Kafka broker. + ## 4) Run the command below one time per broker to create its associated secret (SECRET_NAME_X is the name of the secret you want to create): + ## kubectl create secret generic SECRET_NAME_0 --from-file=ca.crt=./kafka.ca.crt --from-file=tls.crt=./kafka-0.tls.crt --from-file=tls.key=./kafka-0.tls.key + ## kubectl create secret generic SECRET_NAME_1 --from-file=ca.crt=./kafka.ca.crt --from-file=tls.crt=./kafka-1.tls.crt --from-file=tls.key=./kafka-1.tls.key + ## ... + ## + existingSecrets: [] + ## @param auth.tls.autoGenerated Generate automatically self-signed TLS certificates for Kafka brokers. Currently only supported if `auth.tls.type` is `pem` + ## Note: ignored when using 'jks' format or `auth.tls.existingSecrets` is not empty + ## + autoGenerated: false + ## @param auth.tls.password Password to access the JKS files or PEM key when they are password-protected. + ## Note: ignored when using 'existingSecret'. + ## + password: "" + ## @param auth.tls.existingSecret Name of the secret containing the password to access the JKS files or PEM key when they are password-protected. (`key`: `password`) + ## + existingSecret: "" + ## @param auth.tls.jksTruststoreSecret Name of the existing secret containing your truststore if truststore not existing or different from the ones in the `auth.tls.existingSecrets` + ## Note: ignored when using 'pem' format for certificates. + ## + jksTruststoreSecret: "" + ## @param auth.tls.jksKeystoreSAN The secret key from the `auth.tls.existingSecrets` containing the keystore with a SAN certificate + ## The SAN certificate in it should be issued with Subject Alternative Names for all headless services: + ## - kafka-0.kafka-headless.kafka.svc.cluster.local + ## - kafka-1.kafka-headless.kafka.svc.cluster.local + ## - kafka-2.kafka-headless.kafka.svc.cluster.local + ## Note: ignored when using 'pem' format for certificates. + ## + jksKeystoreSAN: "" + ## @param auth.tls.jksTruststore The secret key from the `auth.tls.existingSecrets` or `auth.tls.jksTruststoreSecret` containing the truststore + ## Note: ignored when using 'pem' format for certificates. + ## + jksTruststore: "" + ## @param auth.tls.endpointIdentificationAlgorithm The endpoint identification algorithm to validate server hostname using server certificate + ## Disable server host name verification by setting it to an empty string. + ## ref: https://docs.confluent.io/current/kafka/authentication_ssl.html#optional-settings + ## + endpointIdentificationAlgorithm: https + ## Zookeeper client configuration for kafka brokers + ## + zookeeper: + ## TLS configuration + ## + tls: + ## @param auth.zookeeper.tls.enabled Enable TLS for Zookeeper client connections. + ## + enabled: false + ## @param auth.zookeeper.tls.type Format to use for TLS certificates. Allowed types: `jks` and `pem`. + ## + type: jks + ## @param auth.zookeeper.tls.verifyHostname Hostname validation. + ## + verifyHostname: true + ## @param auth.zookeeper.tls.existingSecret Name of the existing secret containing the TLS certificates for ZooKeeper client communications. + ## + existingSecret: "" + ## @param auth.zookeeper.tls.existingSecretKeystoreKey The secret key from the auth.zookeeper.tls.existingSecret containing the Keystore. + ## + existingSecretKeystoreKey: zookeeper.keystore.jks + ## @param auth.zookeeper.tls.existingSecretTruststoreKey The secret key from the auth.zookeeper.tls.existingSecret containing the Truststore. + ## + existingSecretTruststoreKey: zookeeper.truststore.jks + ## @param auth.zookeeper.tls.passwordsSecret Existing secret containing Keystore and Truststore passwords. + ## + passwordsSecret: "" + ## @param auth.zookeeper.tls.passwordsSecretKeystoreKey The secret key from the auth.zookeeper.tls.passwordsSecret containing the password for the Keystore. + ## + passwordsSecretKeystoreKey: keystore-password + ## @param auth.zookeeper.tls.passwordsSecretTruststoreKey The secret key from the auth.zookeeper.tls.passwordsSecret containing the password for the Truststore. + ## + passwordsSecretTruststoreKey: truststore-password +## @param listeners The address(es) the socket server listens on. Auto-calculated it's set to an empty array +## When it's set to an empty array, the listeners will be configured +## based on the authentication protocols (auth.clientProtocol, auth.externalClientProtocol and auth.interBrokerProtocol parameters) +## +listeners: [] +## @param advertisedListeners The address(es) (hostname:port) the broker will advertise to producers and consumers. Auto-calculated it's set to an empty array +## When it's set to an empty array, the advertised listeners will be configured +## based on the authentication protocols (auth.clientProtocol, auth.externalClientProtocol and auth.interBrokerProtocol parameters) +## +advertisedListeners: [] +## @param listenerSecurityProtocolMap The protocol->listener mapping. Auto-calculated it's set to nil +## When it's nil, the listeners will be configured based on the authentication protocols (auth.clientProtocol, auth.externalClientProtocol and auth.interBrokerProtocol parameters) +## +listenerSecurityProtocolMap: "" +## @param allowPlaintextListener Allow to use the PLAINTEXT listener +## +allowPlaintextListener: true +## @param interBrokerListenerName The listener that the brokers should communicate on +## +interBrokerListenerName: INTERNAL +## @param command Override Kafka container command +## +command: + - /scripts/setup.sh +## @param args Override Kafka container arguments +## +args: [] +## @param extraEnvVars Extra environment variables to add to Kafka pods +## ref: https://github.com/bitnami/containers/tree/main/bitnami/kafka#configuration +## e.g: +## extraEnvVars: +## - name: KAFKA_CFG_BACKGROUND_THREADS +## value: "10" +## +extraEnvVars: [] +## @param extraEnvVarsCM ConfigMap with extra environment variables +## +extraEnvVarsCM: "" +## @param extraEnvVarsSecret Secret with extra environment variables +## +extraEnvVarsSecret: "" + +## @section Statefulset parameters + +## @param replicaCount Number of Kafka nodes +## +replicaCount: 1 +## @param minBrokerId Minimal broker.id value, nodes increment their `broker.id` respectively +## Brokers increment their ID starting at this minimal value. +## E.g., with `minBrokerId=100` and 3 nodes, IDs will be 100, 101, 102 for brokers 0, 1, and 2, respectively. +## +minBrokerId: 0 +## @param brokerRackAssignment Set Broker Assignment for multi tenant environment Allowed values: `aws-az` +## ref: https://cwiki.apache.org/confluence/display/KAFKA/KIP-392%3A+Allow+consumers+to+fetch+from+closest+replica +## +brokerRackAssignment: "" +## @param containerPorts.client Kafka client container port +## @param containerPorts.internal Kafka inter-broker container port +## @param containerPorts.external Kafka external container port +## +containerPorts: + client: 9092 + internal: 9093 + external: 9094 +## Configure extra options for Kafka containers' liveness, readiness and startup probes +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#configure-probes +## @param livenessProbe.enabled Enable livenessProbe on Kafka containers +## @param livenessProbe.initialDelaySeconds Initial delay seconds for livenessProbe +## @param livenessProbe.periodSeconds Period seconds for livenessProbe +## @param livenessProbe.timeoutSeconds Timeout seconds for livenessProbe +## @param livenessProbe.failureThreshold Failure threshold for livenessProbe +## @param livenessProbe.successThreshold Success threshold for livenessProbe +## +livenessProbe: + enabled: true + initialDelaySeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + periodSeconds: 10 + successThreshold: 1 +## @param readinessProbe.enabled Enable readinessProbe on Kafka containers +## @param readinessProbe.initialDelaySeconds Initial delay seconds for readinessProbe +## @param readinessProbe.periodSeconds Period seconds for readinessProbe +## @param readinessProbe.timeoutSeconds Timeout seconds for readinessProbe +## @param readinessProbe.failureThreshold Failure threshold for readinessProbe +## @param readinessProbe.successThreshold Success threshold for readinessProbe +## +readinessProbe: + enabled: true + initialDelaySeconds: 5 + failureThreshold: 6 + timeoutSeconds: 5 + periodSeconds: 10 + successThreshold: 1 +## @param startupProbe.enabled Enable startupProbe on Kafka containers +## @param startupProbe.initialDelaySeconds Initial delay seconds for startupProbe +## @param startupProbe.periodSeconds Period seconds for startupProbe +## @param startupProbe.timeoutSeconds Timeout seconds for startupProbe +## @param startupProbe.failureThreshold Failure threshold for startupProbe +## @param startupProbe.successThreshold Success threshold for startupProbe +## +startupProbe: + enabled: false + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 1 + failureThreshold: 15 + successThreshold: 1 +## @param customLivenessProbe Custom livenessProbe that overrides the default one +## +customLivenessProbe: {} +## @param customReadinessProbe Custom readinessProbe that overrides the default one +## +customReadinessProbe: {} +## @param customStartupProbe Custom startupProbe that overrides the default one +## +customStartupProbe: {} +## @param lifecycleHooks lifecycleHooks for the Kafka container to automate configuration before or after startup +## +lifecycleHooks: {} +## Kafka resource requests and limits +## ref: https://kubernetes.io/docs/user-guide/compute-resources/ +## @param resources.limits The resources limits for the container +## @param resources.requests The requested resources for the container +## +resources: + limits: {} + requests: {} +## Kafka pods' Security Context +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod +## @param podSecurityContext.enabled Enable security context for the pods +## @param podSecurityContext.fsGroup Set Kafka pod's Security Context fsGroup +## +podSecurityContext: + enabled: true + fsGroup: 1001 +## Kafka containers' Security Context +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container +## @param containerSecurityContext.enabled Enable Kafka containers' Security Context +## @param containerSecurityContext.runAsUser Set Kafka containers' Security Context runAsUser +## @param containerSecurityContext.runAsNonRoot Set Kafka containers' Security Context runAsNonRoot +## @param containerSecurityContext.allowPrivilegeEscalation Force the child process to be run as nonprivilege +## e.g: +## containerSecurityContext: +## enabled: true +## capabilities: +## drop: ["NET_RAW"] +## readOnlyRootFilesystem: true +## +containerSecurityContext: + enabled: true + runAsUser: 1001 + runAsNonRoot: true + allowPrivilegeEscalation: false +## @param hostAliases Kafka pods host aliases +## https://kubernetes.io/docs/concepts/services-networking/add-entries-to-pod-etc-hosts-with-host-aliases/ +## +hostAliases: [] +## @param hostNetwork Specify if host network should be enabled for Kafka pods +## +hostNetwork: false +## @param hostIPC Specify if host IPC should be enabled for Kafka pods +## +hostIPC: false +## @param podLabels Extra labels for Kafka pods +## Ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ +## +podLabels: {} +## @param podAnnotations Extra annotations for Kafka pods +## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ +## +podAnnotations: {} +## @param podAffinityPreset Pod affinity preset. Ignored if `affinity` is set. Allowed values: `soft` or `hard` +## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity +## +podAffinityPreset: "" +## @param podAntiAffinityPreset Pod anti-affinity preset. Ignored if `affinity` is set. Allowed values: `soft` or `hard` +## Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity +## +podAntiAffinityPreset: soft +## Node affinity preset +## Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity +## +nodeAffinityPreset: + ## @param nodeAffinityPreset.type Node affinity preset type. Ignored if `affinity` is set. Allowed values: `soft` or `hard` + ## + type: "" + ## @param nodeAffinityPreset.key Node label key to match Ignored if `affinity` is set. + ## E.g. + ## key: "kubernetes.io/e2e-az-name" + ## + key: "" + ## @param nodeAffinityPreset.values Node label values to match. Ignored if `affinity` is set. + ## E.g. + ## values: + ## - e2e-az1 + ## - e2e-az2 + ## + values: [] +## @param affinity Affinity for pod assignment +## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity +## Note: podAffinityPreset, podAntiAffinityPreset, and nodeAffinityPreset will be ignored when it's set +## +affinity: {} +## @param nodeSelector Node labels for pod assignment +## Ref: https://kubernetes.io/docs/user-guide/node-selection/ +## +nodeSelector: {} +## @param tolerations Tolerations for pod assignment +## Ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ +## +tolerations: [] +## @param topologySpreadConstraints Topology Spread Constraints for pod assignment spread across your cluster among failure-domains. Evaluated as a template +## Ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/#spread-constraints-for-pods +## +topologySpreadConstraints: [] +## @param terminationGracePeriodSeconds Seconds the pod needs to gracefully terminate +## ref: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#hook-handler-execution +## +terminationGracePeriodSeconds: "" +## @param podManagementPolicy StatefulSet controller supports relax its ordering guarantees while preserving its uniqueness and identity guarantees. There are two valid pod management policies: OrderedReady and Parallel +## ref: https://kubernetes.io/docs/tutorials/stateful-application/basic-stateful-set/#pod-management-policy +## +podManagementPolicy: Parallel +## @param priorityClassName Name of the existing priority class to be used by kafka pods +## Ref: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/ +## +priorityClassName: "" +## @param schedulerName Name of the k8s scheduler (other than default) +## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ +## +schedulerName: "" +## @param updateStrategy.type Kafka statefulset strategy type +## @param updateStrategy.rollingUpdate Kafka statefulset rolling update configuration parameters +## ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#update-strategies +## +updateStrategy: + type: RollingUpdate + rollingUpdate: {} +## @param extraVolumes Optionally specify extra list of additional volumes for the Kafka pod(s) +## e.g: +## extraVolumes: +## - name: kafka-jaas +## secret: +## secretName: kafka-jaas +## +extraVolumes: [] +## @param extraVolumeMounts Optionally specify extra list of additional volumeMounts for the Kafka container(s) +## extraVolumeMounts: +## - name: kafka-jaas +## mountPath: /bitnami/kafka/config/kafka_jaas.conf +## subPath: kafka_jaas.conf +## +extraVolumeMounts: [] +## @param sidecars Add additional sidecar containers to the Kafka pod(s) +## e.g: +## sidecars: +## - name: your-image-name +## image: your-image +## imagePullPolicy: Always +## ports: +## - name: portname +## containerPort: 1234 +## +sidecars: [] +## @param initContainers Add additional Add init containers to the Kafka pod(s) +## e.g: +## initContainers: +## - name: your-image-name +## image: your-image +## imagePullPolicy: Always +## ports: +## - name: portname +## containerPort: 1234 +## +initContainers: [] +## Kafka Pod Disruption Budget +## ref: https://kubernetes.io/docs/concepts/workloads/pods/disruptions/ +## @param pdb.create Deploy a pdb object for the Kafka pod +## @param pdb.minAvailable Maximum number/percentage of unavailable Kafka replicas +## @param pdb.maxUnavailable Maximum number/percentage of unavailable Kafka replicas +## +pdb: + create: false + minAvailable: "" + maxUnavailable: 1 + +## @section Traffic Exposure parameters + +## Service parameters +## +service: + ## @param service.type Kubernetes Service type + ## + type: ClusterIP + ## @param service.ports.client Kafka svc port for client connections + ## @param service.ports.internal Kafka svc port for inter-broker connections + ## @param service.ports.external Kafka svc port for external connections + ## + ports: + client: 9092 + internal: 9093 + external: 9094 + ## @param service.nodePorts.client Node port for the Kafka client connections + ## @param service.nodePorts.external Node port for the Kafka external connections + ## NOTE: choose port between <30000-32767> + ## + nodePorts: + client: "" + external: "" + ## @param service.sessionAffinity Control where client requests go, to the same pod or round-robin + ## Values: ClientIP or None + ## ref: https://kubernetes.io/docs/user-guide/services/ + ## + sessionAffinity: None + ## @param service.sessionAffinityConfig Additional settings for the sessionAffinity + ## sessionAffinityConfig: + ## clientIP: + ## timeoutSeconds: 300 + ## + sessionAffinityConfig: {} + ## @param service.clusterIP Kafka service Cluster IP + ## e.g.: + ## clusterIP: None + ## + clusterIP: "" + ## @param service.loadBalancerIP Kafka service Load Balancer IP + ## ref: https://kubernetes.io/docs/user-guide/services/#type-loadbalancer + ## + loadBalancerIP: "" + ## @param service.loadBalancerSourceRanges Kafka service Load Balancer sources + ## ref: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/#restrict-access-for-loadbalancer-service + ## e.g: + ## loadBalancerSourceRanges: + ## - 10.10.10.0/24 + ## + loadBalancerSourceRanges: [] + ## @param service.externalTrafficPolicy Kafka service external traffic policy + ## ref https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip + ## + externalTrafficPolicy: Cluster + ## @param service.annotations Additional custom annotations for Kafka service + ## + annotations: {} + ## Headless service properties + ## + headless: + ## @param service.headless.publishNotReadyAddresses Indicates that any agent which deals with endpoints for this Service should disregard any indications of ready/not-ready + ## ref: https://kubernetes.io/docs/reference/kubernetes-api/service-resources/service-v1/ + publishNotReadyAddresses: false + ## @param service.headless.annotations Annotations for the headless service. + ## + annotations: {} + ## @param service.headless.labels Labels for the headless service. + ## + labels: {} + ## @param service.extraPorts Extra ports to expose in the Kafka service (normally used with the `sidecar` value) + ## + extraPorts: [] +## External Access to Kafka brokers configuration +## +externalAccess: + ## @param externalAccess.enabled Enable Kubernetes external cluster access to Kafka brokers + ## + enabled: false + ## External IPs auto-discovery configuration + ## An init container is used to auto-detect LB IPs or node ports by querying the K8s API + ## Note: RBAC might be required + ## + autoDiscovery: + ## @param externalAccess.autoDiscovery.enabled Enable using an init container to auto-detect external IPs/ports by querying the K8s API + ## + enabled: false + ## Bitnami Kubectl image + ## ref: https://hub.docker.com/r/bitnami/kubectl/tags/ + ## @param externalAccess.autoDiscovery.image.registry Init container auto-discovery image registry + ## @param externalAccess.autoDiscovery.image.repository Init container auto-discovery image repository + ## @param externalAccess.autoDiscovery.image.tag Init container auto-discovery image tag (immutable tags are recommended) + ## @param externalAccess.autoDiscovery.image.digest Petete image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag + ## @param externalAccess.autoDiscovery.image.pullPolicy Init container auto-discovery image pull policy + ## @param externalAccess.autoDiscovery.image.pullSecrets Init container auto-discovery image pull secrets + ## + image: + registry: docker.io + repository: bitnami/kubectl + tag: 1.25.5-debian-11-r2 + digest: "" + ## Specify a imagePullPolicy + ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' + ## ref: https://kubernetes.io/docs/user-guide/images/#pre-pulling-images + ## + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets (secrets must be manually created in the namespace) + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## e.g: + ## pullSecrets: + ## - myRegistryKeySecretName + ## + pullSecrets: [] + ## Init Container resource requests and limits + ## ref: https://kubernetes.io/docs/user-guide/compute-resources/ + ## @param externalAccess.autoDiscovery.resources.limits The resources limits for the auto-discovery init container + ## @param externalAccess.autoDiscovery.resources.requests The requested resources for the auto-discovery init container + ## + resources: + limits: {} + requests: {} + ## Parameters to configure K8s service(s) used to externally access Kafka brokers + ## Note: A new service per broker will be created + ## + service: + ## @param externalAccess.service.type Kubernetes Service type for external access. It can be NodePort, LoadBalancer or ClusterIP + ## + type: LoadBalancer + ## @param externalAccess.service.ports.external Kafka port used for external access when service type is LoadBalancer + ## + ports: + external: 9094 + ## @param externalAccess.service.loadBalancerIPs Array of load balancer IPs for each Kafka broker. Length must be the same as replicaCount + ## e.g: + ## loadBalancerIPs: + ## - X.X.X.X + ## - Y.Y.Y.Y + ## + loadBalancerIPs: [] + ## @param externalAccess.service.loadBalancerNames Array of load balancer Names for each Kafka broker. Length must be the same as replicaCount + ## e.g: + ## loadBalancerNames: + ## - broker1.external.example.com + ## - broker2.external.example.com + ## + loadBalancerNames: [] + ## @param externalAccess.service.loadBalancerAnnotations Array of load balancer annotations for each Kafka broker. Length must be the same as replicaCount + ## e.g: + ## loadBalancerAnnotations: + ## - external-dns.alpha.kubernetes.io/hostname: broker1.external.example.com. + ## - external-dns.alpha.kubernetes.io/hostname: broker2.external.example.com. + ## + loadBalancerAnnotations: [] + ## @param externalAccess.service.loadBalancerSourceRanges Address(es) that are allowed when service is LoadBalancer + ## ref: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/#restrict-access-for-loadbalancer-service + ## e.g: + ## loadBalancerSourceRanges: + ## - 10.10.10.0/24 + ## + loadBalancerSourceRanges: [] + ## @param externalAccess.service.nodePorts Array of node ports used for each Kafka broker. Length must be the same as replicaCount + ## e.g: + ## nodePorts: + ## - 30001 + ## - 30002 + ## + nodePorts: [] + ## @param externalAccess.service.useHostIPs Use service host IPs to configure Kafka external listener when service type is NodePort + ## + useHostIPs: false + ## @param externalAccess.service.usePodIPs using the MY_POD_IP address for external access. + ## + usePodIPs: false + ## @param externalAccess.service.domain Domain or external ip used to configure Kafka external listener when service type is NodePort or ClusterIP + ## NodePort: If not specified, the container will try to get the kubernetes node external IP + ## ClusterIP: Must be specified, ingress IP or domain where tcp for external ports is configured + ## + domain: "" + ## @param externalAccess.service.publishNotReadyAddresses Indicates that any agent which deals with endpoints for this Service should disregard any indications of ready/not-ready + ## ref: https://kubernetes.io/docs/reference/kubernetes-api/service-resources/service-v1/ + publishNotReadyAddresses: false + ## @param externalAccess.service.labels Service labels for external access + ## + labels: {} + ## @param externalAccess.service.annotations Service annotations for external access + ## + annotations: {} + ## @param externalAccess.service.extraPorts Extra ports to expose in the Kafka external service + ## + extraPorts: [] +## Network policies +## Ref: https://kubernetes.io/docs/concepts/services-networking/network-policies/ +## +networkPolicy: + ## @param networkPolicy.enabled Specifies whether a NetworkPolicy should be created + ## + enabled: false + ## @param networkPolicy.allowExternal Don't require client label for connections + ## When set to false, only pods with the correct client label will have network access to the port Kafka is + ## listening on. When true, zookeeper accept connections from any source (with the correct destination port). + ## + allowExternal: true + ## @param networkPolicy.explicitNamespacesSelector A Kubernetes LabelSelector to explicitly select namespaces from which traffic could be allowed + ## If explicitNamespacesSelector is missing or set to {}, only client Pods that are in the networkPolicy's namespace + ## and that match other criteria, the ones that have the good label, can reach the kafka. + ## But sometimes, we want the kafka to be accessible to clients from other namespaces, in this case, we can use this + ## LabelSelector to select these namespaces, note that the networkPolicy's namespace should also be explicitly added. + ## + ## e.g: + ## explicitNamespacesSelector: + ## matchLabels: + ## role: frontend + ## matchExpressions: + ## - {key: role, operator: In, values: [frontend]} + ## + explicitNamespacesSelector: {} + ## @param networkPolicy.externalAccess.from customize the from section for External Access on tcp-external port + ## e.g: + ## - ipBlock: + ## cidr: 172.9.0.0/16 + ## except: + ## - 172.9.1.0/24 + ## + externalAccess: + from: [] + ## @param networkPolicy.egressRules.customRules [object] Custom network policy rule + ## + egressRules: + ## Additional custom egress rules + ## e.g: + ## customRules: + ## - to: + ## - namespaceSelector: + ## matchLabels: + ## label: example + customRules: [] + +## @section Persistence parameters + +## Enable persistence using Persistent Volume Claims +## ref: https://kubernetes.io/docs/user-guide/persistent-volumes/ +## +persistence: + ## @param persistence.enabled Enable Kafka data persistence using PVC, note that ZooKeeper persistence is unaffected + ## + enabled: true + ## @param persistence.existingClaim A manually managed Persistent Volume and Claim + ## If defined, PVC must be created manually before volume will be bound + ## The value is evaluated as a template + ## + existingClaim: "" + ## @param persistence.storageClass PVC Storage Class for Kafka data volume + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. + ## + storageClass: "" + ## @param persistence.accessModes Persistent Volume Access Modes + ## + accessModes: + - ReadWriteOnce + ## @param persistence.size PVC Storage Request for Kafka data volume + ## + size: 8Gi + ## @param persistence.annotations Annotations for the PVC + ## + annotations: {} + ## @param persistence.labels Labels for the PVC + ## + labels: {} + ## @param persistence.selector Selector to match an existing Persistent Volume for Kafka data PVC. If set, the PVC can't have a PV dynamically provisioned for it + ## selector: + ## matchLabels: + ## app: my-app + ## + selector: {} + ## @param persistence.mountPath Mount path of the Kafka data volume + ## + mountPath: /bitnami/kafka +## Log Persistence parameters +## +logPersistence: + ## @param logPersistence.enabled Enable Kafka logs persistence using PVC, note that ZooKeeper persistence is unaffected + ## + enabled: false + ## @param logPersistence.existingClaim A manually managed Persistent Volume and Claim + ## If defined, PVC must be created manually before volume will be bound + ## The value is evaluated as a template + ## + existingClaim: "" + ## @param logPersistence.storageClass PVC Storage Class for Kafka logs volume + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. + ## + storageClass: "" + ## @param logPersistence.accessModes Persistent Volume Access Modes + ## + accessModes: + - ReadWriteOnce + ## @param logPersistence.size PVC Storage Request for Kafka logs volume + ## + size: 8Gi + ## @param logPersistence.annotations Annotations for the PVC + ## + annotations: {} + ## @param logPersistence.selector Selector to match an existing Persistent Volume for Kafka log data PVC. If set, the PVC can't have a PV dynamically provisioned for it + ## selector: + ## matchLabels: + ## app: my-app + ## + selector: {} + ## @param logPersistence.mountPath Mount path of the Kafka logs volume + ## + mountPath: /opt/bitnami/kafka/logs + +## @section Volume Permissions parameters +## + +## Init containers parameters: +## volumePermissions: Change the owner and group of the persistent volume(s) mountpoint(s) to 'runAsUser:fsGroup' on each node +## +volumePermissions: + ## @param volumePermissions.enabled Enable init container that changes the owner and group of the persistent volume + ## + enabled: false + ## @param volumePermissions.image.registry Init container volume-permissions image registry + ## @param volumePermissions.image.repository Init container volume-permissions image repository + ## @param volumePermissions.image.tag Init container volume-permissions image tag (immutable tags are recommended) + ## @param volumePermissions.image.digest Init container volume-permissions image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag + ## @param volumePermissions.image.pullPolicy Init container volume-permissions image pull policy + ## @param volumePermissions.image.pullSecrets Init container volume-permissions image pull secrets + ## + image: + registry: docker.io + repository: bitnami/bitnami-shell + tag: 11-debian-11-r63 + digest: "" + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## Example: + ## pullSecrets: + ## - myRegistryKeySecretName + ## + pullSecrets: [] + ## Init container resource requests and limits + ## ref: https://kubernetes.io/docs/user-guide/compute-resources/ + ## @param volumePermissions.resources.limits Init container volume-permissions resource limits + ## @param volumePermissions.resources.requests Init container volume-permissions resource requests + ## + resources: + limits: {} + requests: {} + ## Init container' Security Context + ## Note: the chown of the data folder is done to containerSecurityContext.runAsUser + ## and not the below volumePermissions.containerSecurityContext.runAsUser + ## @param volumePermissions.containerSecurityContext.runAsUser User ID for the init container + ## + containerSecurityContext: + runAsUser: 0 + +## @section Other Parameters + +## ServiceAccount for Kafka +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ +## +serviceAccount: + ## @param serviceAccount.create Enable creation of ServiceAccount for Kafka pods + ## + create: true + ## @param serviceAccount.name The name of the service account to use. If not set and `create` is `true`, a name is generated + ## If not set and create is true, a name is generated using the kafka.serviceAccountName template + ## + name: "" + ## @param serviceAccount.automountServiceAccountToken Allows auto mount of ServiceAccountToken on the serviceAccount created + ## Can be set to false if pods using this serviceAccount do not need to use K8s API + ## + automountServiceAccountToken: true + ## @param serviceAccount.annotations Additional custom annotations for the ServiceAccount + ## + annotations: {} +## Role Based Access Control +## ref: https://kubernetes.io/docs/admin/authorization/rbac/ +## +rbac: + ## @param rbac.create Whether to create & use RBAC resources or not + ## binding Kafka ServiceAccount to a role + ## that allows Kafka pods querying the K8s API + ## + create: false + +## @section Metrics parameters + +## Prometheus Exporters / Metrics +## +metrics: + ## Prometheus Kafka exporter: exposes complimentary metrics to JMX exporter + ## + kafka: + ## @param metrics.kafka.enabled Whether or not to create a standalone Kafka exporter to expose Kafka metrics + ## + enabled: false + ## Bitnami Kafka exporter image + ## ref: https://hub.docker.com/r/bitnami/kafka-exporter/tags/ + ## @param metrics.kafka.image.registry Kafka exporter image registry + ## @param metrics.kafka.image.repository Kafka exporter image repository + ## @param metrics.kafka.image.tag Kafka exporter image tag (immutable tags are recommended) + ## @param metrics.kafka.image.digest Kafka exporter image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag + ## @param metrics.kafka.image.pullPolicy Kafka exporter image pull policy + ## @param metrics.kafka.image.pullSecrets Specify docker-registry secret names as an array + ## + image: + registry: docker.io + repository: bitnami/kafka-exporter + tag: 1.6.0-debian-11-r40 + digest: "" + ## Specify a imagePullPolicy + ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' + ## ref: https://kubernetes.io/docs/user-guide/images/#pre-pulling-images + ## + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets (secrets must be manually created in the namespace) + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## e.g: + ## pullSecrets: + ## - myRegistryKeySecretName + ## + pullSecrets: [] + + ## @param metrics.kafka.certificatesSecret Name of the existing secret containing the optional certificate and key files + ## for Kafka exporter client authentication + ## + certificatesSecret: "" + ## @param metrics.kafka.tlsCert The secret key from the certificatesSecret if 'client-cert' key different from the default (cert-file) + ## + tlsCert: cert-file + ## @param metrics.kafka.tlsKey The secret key from the certificatesSecret if 'client-key' key different from the default (key-file) + ## + tlsKey: key-file + ## @param metrics.kafka.tlsCaSecret Name of the existing secret containing the optional ca certificate for Kafka exporter client authentication + ## + tlsCaSecret: "" + ## @param metrics.kafka.tlsCaCert The secret key from the certificatesSecret or tlsCaSecret if 'ca-cert' key different from the default (ca-file) + ## + tlsCaCert: ca-file + ## @param metrics.kafka.extraFlags Extra flags to be passed to Kafka exporter + ## e.g: + ## extraFlags: + ## tls.insecure-skip-tls-verify: "" + ## web.telemetry-path: "/metrics" + ## + extraFlags: {} + ## @param metrics.kafka.command Override Kafka exporter container command + ## + command: [] + ## @param metrics.kafka.args Override Kafka exporter container arguments + ## + args: [] + ## @param metrics.kafka.containerPorts.metrics Kafka exporter metrics container port + ## + containerPorts: + metrics: 9308 + ## Kafka exporter resource requests and limits + ## ref: https://kubernetes.io/docs/user-guide/compute-resources/ + ## @param metrics.kafka.resources.limits The resources limits for the container + ## @param metrics.kafka.resources.requests The requested resources for the container + ## + resources: + limits: {} + requests: {} + ## Kafka exporter pods' Security Context + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod + ## @param metrics.kafka.podSecurityContext.enabled Enable security context for the pods + ## @param metrics.kafka.podSecurityContext.fsGroup Set Kafka exporter pod's Security Context fsGroup + ## + podSecurityContext: + enabled: true + fsGroup: 1001 + ## Kafka exporter containers' Security Context + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container + ## @param metrics.kafka.containerSecurityContext.enabled Enable Kafka exporter containers' Security Context + ## @param metrics.kafka.containerSecurityContext.runAsUser Set Kafka exporter containers' Security Context runAsUser + ## @param metrics.kafka.containerSecurityContext.runAsNonRoot Set Kafka exporter containers' Security Context runAsNonRoot + ## e.g: + ## containerSecurityContext: + ## enabled: true + ## capabilities: + ## drop: ["NET_RAW"] + ## readOnlyRootFilesystem: true + ## + containerSecurityContext: + enabled: true + runAsUser: 1001 + runAsNonRoot: true + ## @param metrics.kafka.hostAliases Kafka exporter pods host aliases + ## https://kubernetes.io/docs/concepts/services-networking/add-entries-to-pod-etc-hosts-with-host-aliases/ + ## + hostAliases: [] + ## @param metrics.kafka.podLabels Extra labels for Kafka exporter pods + ## Ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + ## + podLabels: {} + ## @param metrics.kafka.podAnnotations Extra annotations for Kafka exporter pods + ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ + ## + podAnnotations: {} + ## @param metrics.kafka.podAffinityPreset Pod affinity preset. Ignored if `metrics.kafka.affinity` is set. Allowed values: `soft` or `hard` + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity + ## + podAffinityPreset: "" + ## @param metrics.kafka.podAntiAffinityPreset Pod anti-affinity preset. Ignored if `metrics.kafka.affinity` is set. Allowed values: `soft` or `hard` + ## Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity + ## + podAntiAffinityPreset: soft + ## Node metrics.kafka.affinity preset + ## Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity + ## + nodeAffinityPreset: + ## @param metrics.kafka.nodeAffinityPreset.type Node affinity preset type. Ignored if `metrics.kafka.affinity` is set. Allowed values: `soft` or `hard` + ## + type: "" + ## @param metrics.kafka.nodeAffinityPreset.key Node label key to match Ignored if `metrics.kafka.affinity` is set. + ## E.g. + ## key: "kubernetes.io/e2e-az-name" + ## + key: "" + ## @param metrics.kafka.nodeAffinityPreset.values Node label values to match. Ignored if `metrics.kafka.affinity` is set. + ## E.g. + ## values: + ## - e2e-az1 + ## - e2e-az2 + ## + values: [] + ## @param metrics.kafka.affinity Affinity for pod assignment + ## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity + ## Note: metrics.kafka.podAffinityPreset, metrics.kafka.podAntiAffinityPreset, and metrics.kafka.nodeAffinityPreset will be ignored when it's set + ## + affinity: {} + ## @param metrics.kafka.nodeSelector Node labels for pod assignment + ## Ref: https://kubernetes.io/docs/user-guide/node-selection/ + ## + nodeSelector: {} + ## @param metrics.kafka.tolerations Tolerations for pod assignment + ## Ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ + ## + tolerations: [] + ## @param metrics.kafka.schedulerName Name of the k8s scheduler (other than default) for Kafka exporter + ## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ + ## + schedulerName: "" + ## @param metrics.kafka.priorityClassName Kafka exporter pods' priorityClassName + ## + priorityClassName: "" + ## @param metrics.kafka.topologySpreadConstraints Topology Spread Constraints for pod assignment + ## https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ + ## The value is evaluated as a template + ## + topologySpreadConstraints: [] + ## @param metrics.kafka.extraVolumes Optionally specify extra list of additional volumes for the Kafka exporter pod(s) + ## e.g: + ## extraVolumes: + ## - name: kafka-jaas + ## secret: + ## secretName: kafka-jaas + ## + extraVolumes: [] + ## @param metrics.kafka.extraVolumeMounts Optionally specify extra list of additional volumeMounts for the Kafka exporter container(s) + ## extraVolumeMounts: + ## - name: kafka-jaas + ## mountPath: /bitnami/kafka/config/kafka_jaas.conf + ## subPath: kafka_jaas.conf + ## + extraVolumeMounts: [] + ## @param metrics.kafka.sidecars Add additional sidecar containers to the Kafka exporter pod(s) + ## e.g: + ## sidecars: + ## - name: your-image-name + ## image: your-image + ## imagePullPolicy: Always + ## ports: + ## - name: portname + ## containerPort: 1234 + ## + sidecars: [] + ## @param metrics.kafka.initContainers Add init containers to the Kafka exporter pods + ## e.g: + ## initContainers: + ## - name: your-image-name + ## image: your-image + ## imagePullPolicy: Always + ## ports: + ## - name: portname + ## containerPort: 1234 + ## + initContainers: [] + ## Kafka exporter service configuration + ## + service: + ## @param metrics.kafka.service.ports.metrics Kafka exporter metrics service port + ## + ports: + metrics: 9308 + ## @param metrics.kafka.service.clusterIP Static clusterIP or None for headless services + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#choosing-your-own-ip-address + ## + clusterIP: "" + ## @param metrics.kafka.service.sessionAffinity Control where client requests go, to the same pod or round-robin + ## Values: ClientIP or None + ## ref: https://kubernetes.io/docs/user-guide/services/ + ## + sessionAffinity: None + ## @param metrics.kafka.service.annotations [object] Annotations for the Kafka exporter service + ## + annotations: + prometheus.io/scrape: "true" + prometheus.io/port: "{{ .Values.metrics.kafka.service.ports.metrics }}" + prometheus.io/path: "/metrics" + ## Kafka exporter pods ServiceAccount + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ + ## + serviceAccount: + ## @param metrics.kafka.serviceAccount.create Enable creation of ServiceAccount for Kafka exporter pods + ## + create: true + ## @param metrics.kafka.serviceAccount.name The name of the service account to use. If not set and `create` is `true`, a name is generated + ## If not set and create is true, a name is generated using the kafka.metrics.kafka.serviceAccountName template + ## + name: "" + ## @param metrics.kafka.serviceAccount.automountServiceAccountToken Allows auto mount of ServiceAccountToken on the serviceAccount created + ## Can be set to false if pods using this serviceAccount do not need to use K8s API + ## + automountServiceAccountToken: true + ## Prometheus JMX exporter: exposes the majority of Kafkas metrics + ## + jmx: + ## @param metrics.jmx.enabled Whether or not to expose JMX metrics to Prometheus + ## + enabled: false + ## Bitnami JMX exporter image + ## ref: https://hub.docker.com/r/bitnami/jmx-exporter/tags/ + ## @param metrics.jmx.image.registry JMX exporter image registry + ## @param metrics.jmx.image.repository JMX exporter image repository + ## @param metrics.jmx.image.tag JMX exporter image tag (immutable tags are recommended) + ## @param metrics.jmx.image.digest JMX exporter image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag + ## @param metrics.jmx.image.pullPolicy JMX exporter image pull policy + ## @param metrics.jmx.image.pullSecrets Specify docker-registry secret names as an array + ## + image: + registry: docker.io + repository: bitnami/jmx-exporter + tag: 0.17.2-debian-11-r29 + digest: "" + ## Specify a imagePullPolicy + ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' + ## ref: https://kubernetes.io/docs/user-guide/images/#pre-pulling-images + ## + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets (secrets must be manually created in the namespace) + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## e.g: + ## pullSecrets: + ## - myRegistryKeySecretName + ## + pullSecrets: [] + ## Prometheus JMX exporter containers' Security Context + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container + ## @param metrics.jmx.containerSecurityContext.enabled Enable Prometheus JMX exporter containers' Security Context + ## @param metrics.jmx.containerSecurityContext.runAsUser Set Prometheus JMX exporter containers' Security Context runAsUser + ## @param metrics.jmx.containerSecurityContext.runAsNonRoot Set Prometheus JMX exporter containers' Security Context runAsNonRoot + ## e.g: + ## containerSecurityContext: + ## enabled: true + ## capabilities: + ## drop: ["NET_RAW"] + ## readOnlyRootFilesystem: true + ## + containerSecurityContext: + enabled: true + runAsUser: 1001 + runAsNonRoot: true + ## @param metrics.jmx.containerPorts.metrics Prometheus JMX exporter metrics container port + ## + containerPorts: + metrics: 5556 + ## Prometheus JMX exporter resource requests and limits + ## ref: https://kubernetes.io/docs/user-guide/compute-resources/ + ## @param metrics.jmx.resources.limits The resources limits for the JMX exporter container + ## @param metrics.jmx.resources.requests The requested resources for the JMX exporter container + ## + resources: + limits: {} + requests: {} + ## Prometheus JMX exporter service configuration + ## + service: + ## @param metrics.jmx.service.ports.metrics Prometheus JMX exporter metrics service port + ## + ports: + metrics: 5556 + ## @param metrics.jmx.service.clusterIP Static clusterIP or None for headless services + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#choosing-your-own-ip-address + ## + clusterIP: "" + ## @param metrics.jmx.service.sessionAffinity Control where client requests go, to the same pod or round-robin + ## Values: ClientIP or None + ## ref: https://kubernetes.io/docs/user-guide/services/ + ## + sessionAffinity: None + ## @param metrics.jmx.service.annotations [object] Annotations for the Prometheus JMX exporter service + ## + annotations: + prometheus.io/scrape: "true" + prometheus.io/port: "{{ .Values.metrics.jmx.service.ports.metrics }}" + prometheus.io/path: "/" + ## @param metrics.jmx.whitelistObjectNames Allows setting which JMX objects you want to expose to via JMX stats to JMX exporter + ## Only whitelisted values will be exposed via JMX exporter. They must also be exposed via Rules. To expose all metrics + ## (warning its crazy excessive and they aren't formatted in a prometheus style) (1) `whitelistObjectNames: []` + ## (2) commented out above `overrideConfig`. + ## + whitelistObjectNames: + - kafka.controller:* + - kafka.server:* + - java.lang:* + - kafka.network:* + - kafka.log:* + ## @param metrics.jmx.config [string] Configuration file for JMX exporter + ## Specify content for jmx-kafka-prometheus.yml. Evaluated as a template + ## + ## Credits to the incubator/kafka chart for the JMX configuration. + ## https://github.com/helm/charts/tree/master/incubator/kafka + ## + config: |- + jmxUrl: service:jmx:rmi:///jndi/rmi://127.0.0.1:5555/jmxrmi + lowercaseOutputName: true + lowercaseOutputLabelNames: true + ssl: false + {{- if .Values.metrics.jmx.whitelistObjectNames }} + whitelistObjectNames: ["{{ join "\",\"" .Values.metrics.jmx.whitelistObjectNames }}"] + {{- end }} + ## @param metrics.jmx.existingConfigmap Name of existing ConfigMap with JMX exporter configuration + ## NOTE: This will override metrics.jmx.config + ## + existingConfigmap: "" + ## @param metrics.jmx.extraRules Add extra rules to JMX exporter configuration + ## e.g: + ## extraRules: |- + ## - pattern: kafka.server<>(connection-count) + ## name: kafka_server_socket_server_metrics_$3 + ## labels: + ## listener: $1 + extraRules: "" + ## Prometheus Operator ServiceMonitor configuration + ## + serviceMonitor: + ## @param metrics.serviceMonitor.enabled if `true`, creates a Prometheus Operator ServiceMonitor (requires `metrics.kafka.enabled` or `metrics.jmx.enabled` to be `true`) + ## + enabled: false + ## @param metrics.serviceMonitor.namespace Namespace in which Prometheus is running + ## + namespace: "" + ## @param metrics.serviceMonitor.interval Interval at which metrics should be scraped + ## ref: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#endpoint + ## + interval: "" + ## @param metrics.serviceMonitor.scrapeTimeout Timeout after which the scrape is ended + ## ref: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#endpoint + ## + scrapeTimeout: "" + ## @param metrics.serviceMonitor.labels Additional labels that can be used so ServiceMonitor will be discovered by Prometheus + ## + labels: {} + ## @param metrics.serviceMonitor.selector Prometheus instance selector labels + ## ref: https://github.com/bitnami/charts/tree/main/bitnami/prometheus-operator#prometheus-configuration + ## + selector: {} + ## @param metrics.serviceMonitor.relabelings RelabelConfigs to apply to samples before scraping + ## + relabelings: [] + ## @param metrics.serviceMonitor.metricRelabelings MetricRelabelConfigs to apply to samples before ingestion + ## + metricRelabelings: [] + ## @param metrics.serviceMonitor.honorLabels Specify honorLabels parameter to add the scrape endpoint + ## + honorLabels: false + ## @param metrics.serviceMonitor.jobLabel The name of the label on the target service to use as the job name in prometheus. + ## + jobLabel: "" + + prometheusRule: + ## @param metrics.prometheusRule.enabled if `true`, creates a Prometheus Operator PrometheusRule (requires `metrics.kafka.enabled` or `metrics.jmx.enabled` to be `true`) + ## + enabled: false + ## @param metrics.prometheusRule.namespace Namespace in which Prometheus is running + ## + namespace: "" + ## @param metrics.prometheusRule.labels Additional labels that can be used so PrometheusRule will be discovered by Prometheus + ## + labels: {} + ## @param metrics.prometheusRule.groups Prometheus Rule Groups for Kafka + ## + groups: [] + +## @section Kafka provisioning parameters + +## Kafka provisioning +## +provisioning: + ## @param provisioning.enabled Enable kafka provisioning Job + ## + enabled: true + ## @param provisioning.numPartitions Default number of partitions for topics when unspecified + ## + numPartitions: 1 + ## @param provisioning.replicationFactor Default replication factor for topics when unspecified + ## + replicationFactor: 1 + ## @param provisioning.topics Kafka topics to provision + + topics: + - name: "${input_topic}" + partitions: 1 + replicationFactor: 1 + # https://kafka.apache.org/documentation/#topicconfigs + config: + max.message.bytes: 64000 + flush.messages: 1 + - name: "${output_telemetry_route_topic}" + partitions: 1 + replicationFactor: 1 + # https://kafka.apache.org/documentation/#topicconfigs + config: + max.message.bytes: 64000 + flush.messages: 1 + - name: "${output_summary_route_topic}" + partitions: 1 + replicationFactor: 1 + # https://kafka.apache.org/documentation/#topicconfigs + config: + max.message.bytes: 64000 + flush.messages: 1 + - name: "${output_failed_topic}" + partitions: 1 + replicationFactor: 1 + # https://kafka.apache.org/documentation/#topicconfigs + config: + max.message.bytes: 64000 + flush.messages: 1 + - name: "${output_duplicate_topic}" + partitions: 1 + replicationFactor: 1 + # https://kafka.apache.org/documentation/#topicconfigs + config: + max.message.bytes: 64000 + flush.messages: 1 + + ## @param provisioning.nodeSelector Node labels for pod assignment + ## Ref: https://kubernetes.io/docs/user-guide/node-selection/ + ## + nodeSelector: {} + ## @param provisioning.tolerations Tolerations for pod assignment + ## Ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ + ## + tolerations: [] + ## @param provisioning.extraProvisioningCommands Extra commands to run to provision cluster resources + ## - echo "Allow user to consume from any topic" + ## - >- + ## /opt/bitnami/kafka/bin/kafka-acls.sh + ## --bootstrap-server $KAFKA_SERVICE + ## --command-config $CLIENT_CONF + ## --add + ## --allow-principal User:user + ## --consumer --topic '*' + ## - "/opt/bitnami/kafka/bin/kafka-acls.sh + ## --bootstrap-server $KAFKA_SERVICE + ## --command-config $CLIENT_CONF + ## --list" + ## + extraProvisioningCommands: [] + ## @param provisioning.parallel Number of provisioning commands to run at the same time + ## + parallel: 1 + ## @param provisioning.preScript Extra bash script to run before topic provisioning. $CLIENT_CONF is path to properties file with most needed configurations + ## + preScript: "" + ## @param provisioning.postScript Extra bash script to run after topic provisioning. $CLIENT_CONF is path to properties file with most needed configurations + ## + postScript: "" + ## Auth Configuration for kafka provisioning Job + ## + auth: + ## TLS configuration for kafka provisioning Job + ## + tls: + ## @param provisioning.auth.tls.type Format to use for TLS certificates. Allowed types: `jks` and `pem`. + ## Note: ignored if auth.tls.clientProtocol different from one of these values: "tls" "mtls" "sasl_tls". + ## + type: jks + ## @param provisioning.auth.tls.certificatesSecret Existing secret containing the TLS certificates for the Kafka provisioning Job. + ## When using 'jks' format for certificates, the secret should contain a truststore and a keystore. + ## When using 'pem' format for certificates, the secret should contain a public CA certificate, a public certificate and one private key. + ## + certificatesSecret: "" + ## @param provisioning.auth.tls.cert The secret key from the certificatesSecret if 'cert' key different from the default (tls.crt) + ## + cert: tls.crt + ## @param provisioning.auth.tls.key The secret key from the certificatesSecret if 'key' key different from the default (tls.key) + ## + key: tls.key + ## @param provisioning.auth.tls.caCert The secret key from the certificatesSecret if 'caCert' key different from the default (ca.crt) + ## + caCert: ca.crt + ## @param provisioning.auth.tls.keystore The secret key from the certificatesSecret if 'keystore' key different from the default (keystore.jks) + ## + keystore: keystore.jks + ## @param provisioning.auth.tls.truststore The secret key from the certificatesSecret if 'truststore' key different from the default (truststore.jks) + ## + truststore: truststore.jks + ## @param provisioning.auth.tls.passwordsSecret Name of the secret containing passwords to access the JKS files or PEM key when they are password-protected. + ## It should contain two keys called "keystore-password" and "truststore-password", or "key-password" if using a password-protected PEM key. + ## + passwordsSecret: "" + ## @param provisioning.auth.tls.keyPasswordSecretKey The secret key from the passwordsSecret if 'keyPasswordSecretKey' key different from the default (key-password) + ## Note: must not be used if `passwordsSecret` is not defined. + ## + keyPasswordSecretKey: key-password + ## @param provisioning.auth.tls.keystorePasswordSecretKey The secret key from the passwordsSecret if 'keystorePasswordSecretKey' key different from the default (keystore-password) + ## Note: must not be used if `passwordsSecret` is not defined. + ## + keystorePasswordSecretKey: keystore-password + ## @param provisioning.auth.tls.truststorePasswordSecretKey The secret key from the passwordsSecret if 'truststorePasswordSecretKey' key different from the default (truststore-password) + ## Note: must not be used if `passwordsSecret` is not defined. + ## + truststorePasswordSecretKey: truststore-password + ## @param provisioning.auth.tls.keyPassword Password to access the password-protected PEM key if necessary. Ignored if 'passwordsSecret' is provided. + ## + keyPassword: "" + ## @param provisioning.auth.tls.keystorePassword Password to access the JKS keystore. Ignored if 'passwordsSecret' is provided. + ## + keystorePassword: "" + ## @param provisioning.auth.tls.truststorePassword Password to access the JKS truststore. Ignored if 'passwordsSecret' is provided. + ## + truststorePassword: "" + ## @param provisioning.command Override provisioning container command + ## + command: [] + ## @param provisioning.args Override provisioning container arguments + ## + args: [] + ## @param provisioning.extraEnvVars Extra environment variables to add to the provisioning pod + ## e.g: + ## extraEnvVars: + ## - name: KAFKA_CFG_BACKGROUND_THREADS + ## value: "10" + ## + extraEnvVars: [] + ## @param provisioning.extraEnvVarsCM ConfigMap with extra environment variables + ## + extraEnvVarsCM: "" + ## @param provisioning.extraEnvVarsSecret Secret with extra environment variables + ## + extraEnvVarsSecret: "" + ## @param provisioning.podAnnotations Extra annotations for Kafka provisioning pods + ## + podAnnotations: {} + ## @param provisioning.podLabels Extra labels for Kafka provisioning pods + ## Ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + ## + podLabels: {} + ## Kafka provisioning pods ServiceAccount + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ + ## + serviceAccount: + ## @param provisioning.serviceAccount.create Enable creation of ServiceAccount for Kafka provisioning pods + ## + create: false + ## @param provisioning.serviceAccount.name The name of the service account to use. If not set and `create` is `true`, a name is generated + ## If not set and create is true, a name is generated using the provisioning.serviceAccount.name template + ## + name: "" + ## @param provisioning.serviceAccount.automountServiceAccountToken Allows auto mount of ServiceAccountToken on the serviceAccount created + ## Can be set to false if pods using this serviceAccount do not need to use K8s API + ## + automountServiceAccountToken: true + ## Kafka provisioning resource requests and limits + ## ref: https://kubernetes.io/docs/user-guide/compute-resources/ + ## @param provisioning.resources.limits The resources limits for the Kafka provisioning container + ## @param provisioning.resources.requests The requested resources for the Kafka provisioning container + ## + resources: + limits: {} + requests: {} + ## Kafka provisioning pods' Security Context + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod + ## @param provisioning.podSecurityContext.enabled Enable security context for the pods + ## @param provisioning.podSecurityContext.fsGroup Set Kafka provisioning pod's Security Context fsGroup + ## + podSecurityContext: + enabled: true + fsGroup: 1001 + ## Kafka provisioning containers' Security Context + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container + ## @param provisioning.containerSecurityContext.enabled Enable Kafka provisioning containers' Security Context + ## @param provisioning.containerSecurityContext.runAsUser Set Kafka provisioning containers' Security Context runAsUser + ## @param provisioning.containerSecurityContext.runAsNonRoot Set Kafka provisioning containers' Security Context runAsNonRoot + ## e.g: + ## containerSecurityContext: + ## enabled: true + ## capabilities: + ## drop: ["NET_RAW"] + ## readOnlyRootFilesystem: true + ## + containerSecurityContext: + enabled: true + runAsUser: 1001 + runAsNonRoot: true + ## @param provisioning.schedulerName Name of the k8s scheduler (other than default) for kafka provisioning + ## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ + ## + schedulerName: "" + ## @param provisioning.extraVolumes Optionally specify extra list of additional volumes for the Kafka provisioning pod(s) + ## e.g: + ## extraVolumes: + ## - name: kafka-jaas + ## secret: + ## secretName: kafka-jaas + ## + extraVolumes: [] + ## @param provisioning.extraVolumeMounts Optionally specify extra list of additional volumeMounts for the Kafka provisioning container(s) + ## extraVolumeMounts: + ## - name: kafka-jaas + ## mountPath: /bitnami/kafka/config/kafka_jaas.conf + ## subPath: kafka_jaas.conf + ## + extraVolumeMounts: [] + ## @param provisioning.sidecars Add additional sidecar containers to the Kafka provisioning pod(s) + ## e.g: + ## sidecars: + ## - name: your-image-name + ## image: your-image + ## imagePullPolicy: Always + ## ports: + ## - name: portname + ## containerPort: 1234 + ## + sidecars: [] + ## @param provisioning.initContainers Add additional Add init containers to the Kafka provisioning pod(s) + ## e.g: + ## initContainers: + ## - name: your-image-name + ## image: your-image + ## imagePullPolicy: Always + ## ports: + ## - name: portname + ## containerPort: 1234 + ## + initContainers: [] + ## @param provisioning.waitForKafka If true use an init container to wait until kafka is ready before starting provisioning + ## + waitForKafka: true + +## @section ZooKeeper chart parameters + +## ZooKeeper chart configuration +## https://github.com/bitnami/charts/blob/main/bitnami/zookeeper/values.yaml +## +zookeeper: + ## @param zookeeper.enabled Switch to enable or disable the ZooKeeper helm chart + ## + enabled: true + ## @param zookeeper.replicaCount Number of ZooKeeper nodes + ## + replicaCount: 1 + ## ZooKeeper authenticaiton + ## + auth: + client: + ## @param zookeeper.auth.client.enabled Enable ZooKeeper auth + ## + enabled: false + ## @param zookeeper.auth.client.clientUser User that will use ZooKeeper clients to auth + ## + clientUser: "" + ## @param zookeeper.auth.client.clientPassword Password that will use ZooKeeper clients to auth + ## + clientPassword: "" + ## @param zookeeper.auth.client.serverUsers Comma, semicolon or whitespace separated list of user to be created. Specify them as a string, for example: "user1,user2,admin" + ## + serverUsers: "" + ## @param zookeeper.auth.client.serverPasswords Comma, semicolon or whitespace separated list of passwords to assign to users when created. Specify them as a string, for example: "pass4user1, pass4user2, pass4admin" + ## + serverPasswords: "" + ## ZooKeeper Persistence parameters + ## ref: https://kubernetes.io/docs/user-guide/persistent-volumes/ + ## @param zookeeper.persistence.enabled Enable persistence on ZooKeeper using PVC(s) + ## @param zookeeper.persistence.storageClass Persistent Volume storage class + ## @param zookeeper.persistence.accessModes Persistent Volume access modes + ## @param zookeeper.persistence.size Persistent Volume size + ## + persistence: + enabled: true + storageClass: "" + accessModes: + - ReadWriteOnce + size: 8Gi + +## External Zookeeper Configuration +## All of these values are only used if `zookeeper.enabled=false` +## +externalZookeeper: + ## @param externalZookeeper.servers List of external zookeeper servers to use. Typically used in combination with 'zookeeperChrootPath'. + ## + servers: [] diff --git a/knowlg-automation/helm_charts/logstash/.helmignore b/knowlg-automation/helm_charts/logstash/.helmignore new file mode 100644 index 000000000..ff5fca451 --- /dev/null +++ b/knowlg-automation/helm_charts/logstash/.helmignore @@ -0,0 +1,22 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj +files/conf/README.md \ No newline at end of file diff --git a/knowlg-automation/helm_charts/logstash/Chart.lock b/knowlg-automation/helm_charts/logstash/Chart.lock new file mode 100644 index 000000000..d1e9ffaf9 --- /dev/null +++ b/knowlg-automation/helm_charts/logstash/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: common + repository: https://charts.bitnami.com/bitnami + version: 2.2.4 +digest: sha256:634d19e9b7f6e4c07d7c04a0161ab96b3f83335ebdd70b35b952319ef0a2586b +generated: "2023-03-23T12:09:58.799549989+05:30" diff --git a/knowlg-automation/helm_charts/logstash/Chart.yaml b/knowlg-automation/helm_charts/logstash/Chart.yaml new file mode 100644 index 000000000..862479cbc --- /dev/null +++ b/knowlg-automation/helm_charts/logstash/Chart.yaml @@ -0,0 +1,26 @@ +annotations: + category: LogManagement + licenses: Apache-2.0 +apiVersion: v2 +appVersion: 8.6.2 +dependencies: + - name: common + repository: https://charts.bitnami.com/bitnami + tags: + - bitnami-common + version: 2.x.x +description: Logstash is an open source data processing engine. It ingests data from multiple sources, processes it, and sends the output to final destination in real-time. It is a core component of the ELK stack. +home: https://github.com/bitnami/charts/tree/main/bitnami/logstash +icon: https://bitnami.com/assets/stacks/logstash/img/logstash-stack-220x234.png +keywords: + - logstash + - logging + - elk +maintainers: + - name: Bitnami + url: https://github.com/bitnami/charts +name: logstash +sources: + - https://github.com/bitnami/containers/tree/main/bitnami/logstash + - https://www.elastic.co/products/logstash +version: 5.1.15 diff --git a/knowlg-automation/helm_charts/logstash/README.md b/knowlg-automation/helm_charts/logstash/README.md new file mode 100644 index 000000000..52fb6c8e9 --- /dev/null +++ b/knowlg-automation/helm_charts/logstash/README.md @@ -0,0 +1,340 @@ + + +# Logstash packaged by Bitnami + +Logstash is an open source data processing engine. It ingests data from multiple sources, processes it, and sends the output to final destination in real-time. It is a core component of the ELK stack. + +[Overview of Logstash](http://logstash.net) + +Trademarks: This software listing is packaged by Bitnami. The respective trademarks mentioned in the offering are owned by the respective companies, and use of them does not imply any affiliation or endorsement. + +## TL;DR + +```console +helm repo add my-repo https://charts.bitnami.com/bitnami +helm install my-release my-repo/logstash +``` + +## Introduction + +This chart bootstraps a [logstash](https://github.com/bitnami/containers/tree/main/bitnami/logstash) deployment on a [Kubernetes](https://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager. + +Bitnami charts can be used with [Kubeapps](https://kubeapps.dev/) for deployment and management of Helm Charts in clusters. + +## Prerequisites + +- Kubernetes 1.19+ +- Helm 3.2.0+ + +## Installing the Chart + +To install the chart with the release name `my-release`: + +```console +helm repo add my-repo https://charts.bitnami.com/bitnami +helm install my-release my-repo/logstash +``` + +These commands deploy logstash on the Kubernetes cluster in the default configuration. The [configuration](#configuration-and-installation-details) section lists the parameters that can be configured during installation. + +> **Tip**: List all releases using `helm list` + +## Uninstalling the Chart + +To uninstall/delete the `my-release` statefulset: + +```console +helm delete my-release +``` + +The command removes all the Kubernetes components associated with the chart and deletes the release. Use the option `--purge` to delete all history too. + +## Parameters + +### Global parameters + +| Name | Description | Value | +| ------------------------- | ----------------------------------------------- | ----- | +| `global.imageRegistry` | Global Docker image registry | `""` | +| `global.imagePullSecrets` | Global Docker registry secret names as an array | `[]` | +| `global.storageClass` | Global StorageClass for Persistent Volume(s) | `""` | + +### Common parameters + +| Name | Description | Value | +| ------------------------ | ---------------------------------------------------------------------------------------- | --------------- | +| `kubeVersion` | Force target Kubernetes version (using Helm capabilities if not set) | `""` | +| `nameOverride` | String to partially override logstash.fullname template (will maintain the release name) | `""` | +| `fullnameOverride` | String to fully override logstash.fullname template | `""` | +| `clusterDomain` | Default Kubernetes cluster domain | `cluster.local` | +| `commonAnnotations` | Annotations to add to all deployed objects | `{}` | +| `commonLabels` | Labels to add to all deployed objects | `{}` | +| `extraDeploy` | Array of extra objects to deploy with the release (evaluated as a template). | `[]` | +| `diagnosticMode.enabled` | Enable diagnostic mode (all probes will be disabled and the command will be overridden) | `false` | +| `diagnosticMode.command` | Command to override all containers in the deployment | `["sleep"]` | +| `diagnosticMode.args` | Args to override all containers in the deployment | `["infinity"]` | + +### Logstash parameters + +| Name | Description | Value | +| --------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | +| `image.registry` | Logstash image registry | `docker.io` | +| `image.repository` | Logstash image repository | `bitnami/logstash` | +| `image.tag` | Logstash image tag (immutable tags are recommended) | `8.6.2-debian-11-r3` | +| `image.digest` | Logstash image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag | `""` | +| `image.pullPolicy` | Logstash image pull policy | `IfNotPresent` | +| `image.pullSecrets` | Specify docker-registry secret names as an array | `[]` | +| `image.debug` | Specify if debug logs should be enabled | `false` | +| `hostAliases` | Add deployment host aliases | `[]` | +| `configFileName` | Logstash configuration file name. It must match the name of the configuration file mounted as a configmap. | `logstash.conf` | +| `enableMonitoringAPI` | Whether to enable the Logstash Monitoring API or not Kubernetes cluster domain | `true` | +| `monitoringAPIPort` | Logstash Monitoring API Port | `9600` | +| `extraEnvVars` | Array containing extra env vars to configure Logstash | `[]` | +| `extraEnvVarsSecret` | To add secrets to environment | `""` | +| `extraEnvVarsCM` | To add configmaps to environment | `""` | +| `input` | Input Plugins configuration | `""` | +| `filter` | Filter Plugins configuration | `""` | +| `output` | Output Plugins configuration | `""` | +| `existingConfiguration` | Name of existing ConfigMap object with the Logstash configuration (`input`, `filter`, and `output` will be ignored). | `""` | +| `enableMultiplePipelines` | Allows user to use multiple pipelines | `false` | +| `extraVolumes` | Array to add extra volumes (evaluated as a template) | `[]` | +| `extraVolumeMounts` | Array to add extra mounts (normally used with extraVolumes, evaluated as a template) | `[]` | +| `serviceAccount.create` | Enable creation of ServiceAccount for Logstash pods | `true` | +| `serviceAccount.name` | The name of the service account to use. If not set and `create` is `true`, a name is generated | `""` | +| `serviceAccount.automountServiceAccountToken` | Allows automount of ServiceAccountToken on the serviceAccount created | `true` | +| `serviceAccount.annotations` | Additional custom annotations for the ServiceAccount | `{}` | +| `containerPorts` | Array containing the ports to open in the Logstash container (evaluated as a template) | `[]` | +| `initContainers` | Add additional init containers to the Logstash pod(s) | `[]` | +| `sidecars` | Add additional sidecar containers to the Logstash pod(s) | `[]` | +| `replicaCount` | Number of Logstash replicas to deploy | `1` | +| `updateStrategy.type` | Update strategy type (`RollingUpdate`, or `OnDelete`) | `RollingUpdate` | +| `podManagementPolicy` | Pod management policy | `OrderedReady` | +| `podAnnotations` | Pod annotations | `{}` | +| `podLabels` | Extra labels for Logstash pods | `{}` | +| `podAffinityPreset` | Pod affinity preset. Ignored if `affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `podAntiAffinityPreset` | Pod anti-affinity preset. Ignored if `affinity` is set. Allowed values: `soft` or `hard` | `soft` | +| `nodeAffinityPreset.type` | Node affinity preset type. Ignored if `affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `nodeAffinityPreset.key` | Node label key to match. Ignored if `affinity` is set. | `""` | +| `nodeAffinityPreset.values` | Node label values to match. Ignored if `affinity` is set. | `[]` | +| `affinity` | Affinity for pod assignment | `{}` | +| `nodeSelector` | Node labels for pod assignment | `{}` | +| `tolerations` | Tolerations for pod assignment | `[]` | +| `priorityClassName` | Pod priority | `""` | +| `schedulerName` | Name of the k8s scheduler (other than default) | `""` | +| `terminationGracePeriodSeconds` | In seconds, time the given to the Logstash pod needs to terminate gracefully | `""` | +| `topologySpreadConstraints` | Topology Spread Constraints for pod assignment | `[]` | +| `podSecurityContext.enabled` | Enabled Logstash pods' Security Context | `true` | +| `podSecurityContext.fsGroup` | Set Logstash pod's Security Context fsGroup | `1001` | +| `containerSecurityContext.enabled` | Enabled Logstash containers' Security Context | `true` | +| `containerSecurityContext.runAsUser` | Set Logstash containers' Security Context runAsUser | `1001` | +| `containerSecurityContext.runAsNonRoot` | Set Logstash container's Security Context runAsNonRoot | `true` | +| `command` | Override default container command (useful when using custom images) | `[]` | +| `args` | Override default container args (useful when using custom images) | `[]` | +| `lifecycleHooks` | for the Logstash container(s) to automate configuration before or after startup | `{}` | +| `resources.limits` | The resources limits for the Logstash container | `{}` | +| `resources.requests` | The requested resources for the Logstash container | `{}` | +| `startupProbe.enabled` | Enable startupProbe | `false` | +| `startupProbe.initialDelaySeconds` | Initial delay seconds for startupProbe | `60` | +| `startupProbe.periodSeconds` | Period seconds for startupProbe | `10` | +| `startupProbe.timeoutSeconds` | Timeout seconds for startupProbe | `5` | +| `startupProbe.failureThreshold` | Failure threshold for startupProbe | `6` | +| `startupProbe.successThreshold` | Success threshold for startupProbe | `1` | +| `livenessProbe.enabled` | Enable livenessProbe | `true` | +| `livenessProbe.initialDelaySeconds` | Initial delay seconds for livenessProbe | `60` | +| `livenessProbe.periodSeconds` | Period seconds for livenessProbe | `10` | +| `livenessProbe.timeoutSeconds` | Timeout seconds for livenessProbe | `5` | +| `livenessProbe.failureThreshold` | Failure threshold for livenessProbe | `6` | +| `livenessProbe.successThreshold` | Success threshold for livenessProbe | `1` | +| `readinessProbe.enabled` | Enable readinessProbe | `true` | +| `readinessProbe.initialDelaySeconds` | Initial delay seconds for readinessProbe | `60` | +| `readinessProbe.periodSeconds` | Period seconds for readinessProbe | `10` | +| `readinessProbe.timeoutSeconds` | Timeout seconds for readinessProbe | `5` | +| `readinessProbe.failureThreshold` | Failure threshold for readinessProbe | `6` | +| `readinessProbe.successThreshold` | Success threshold for readinessProbe | `1` | +| `customStartupProbe` | Custom startup probe for the Web component | `{}` | +| `customLivenessProbe` | Custom liveness probe for the Web component | `{}` | +| `customReadinessProbe` | Custom readiness probe for the Web component | `{}` | +| `service.type` | Kubernetes service type (`ClusterIP`, `NodePort`, or `LoadBalancer`) | `ClusterIP` | +| `service.ports` | Logstash service ports (evaluated as a template) | `[]` | +| `service.loadBalancerIP` | loadBalancerIP if service type is `LoadBalancer` | `""` | +| `service.loadBalancerSourceRanges` | Addresses that are allowed when service is LoadBalancer | `[]` | +| `service.externalTrafficPolicy` | External traffic policy, configure to Local to preserve client source IP when using an external loadBalancer | `""` | +| `service.clusterIP` | Static clusterIP or None for headless services | `""` | +| `service.annotations` | Annotations for Logstash service | `{}` | +| `service.sessionAffinity` | Session Affinity for Kubernetes service, can be "None" or "ClientIP" | `None` | +| `service.sessionAffinityConfig` | Additional settings for the sessionAffinity | `{}` | +| `persistence.enabled` | Enable Logstash data persistence using PVC | `false` | +| `persistence.existingClaim` | A manually managed Persistent Volume and Claim | `""` | +| `persistence.storageClass` | PVC Storage Class for Logstash data volume | `""` | +| `persistence.accessModes` | PVC Access Mode for Logstash data volume | `["ReadWriteOnce"]` | +| `persistence.size` | PVC Storage Request for Logstash data volume | `2Gi` | +| `persistence.annotations` | Annotations for the PVC | `{}` | +| `persistence.mountPath` | Mount path of the Logstash data volume | `/bitnami/logstash/data` | +| `persistence.selector` | Selector to match an existing Persistent Volume for WordPress data PVC | `{}` | +| `volumePermissions.enabled` | Enable init container that changes the owner and group of the persistent volume(s) mountpoint to `runAsUser:fsGroup` | `false` | +| `volumePermissions.securityContext.runAsUser` | User ID for the volumePermissions init container | `0` | +| `volumePermissions.image.registry` | Init container volume-permissions image registry | `docker.io` | +| `volumePermissions.image.repository` | Init container volume-permissions image repository | `bitnami/bitnami-shell` | +| `volumePermissions.image.tag` | Init container volume-permissions image tag (immutable tags are recommended) | `11-debian-11-r90` | +| `volumePermissions.image.digest` | Init container volume-permissions image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag | `""` | +| `volumePermissions.image.pullPolicy` | Init container volume-permissions image pull policy | `IfNotPresent` | +| `volumePermissions.image.pullSecrets` | Specify docker-registry secret names as an array | `[]` | +| `volumePermissions.resources.limits` | Init container volume-permissions resource limits | `{}` | +| `volumePermissions.resources.requests` | Init container volume-permissions resource requests | `{}` | +| `ingress.enabled` | Enable ingress controller resource | `false` | +| `ingress.selfSigned` | Create a TLS secret for this ingress record using self-signed certificates generated by Helm | `false` | +| `ingress.pathType` | Ingress Path type | `ImplementationSpecific` | +| `ingress.apiVersion` | Override API Version (automatically detected if not set) | `""` | +| `ingress.hostname` | Default host for the ingress resource | `logstash.local` | +| `ingress.path` | The Path to Logstash. You may need to set this to '/*' in order to use this with ALB ingress controllers. | `/` | +| `ingress.annotations` | Additional annotations for the Ingress resource. To enable certificate autogeneration, place here your cert-manager annotations. | `{}` | +| `ingress.tls` | Enable TLS configuration for the hostname defined at ingress.hostname parameter | `false` | +| `ingress.extraHosts` | The list of additional hostnames to be covered with this ingress record. | `[]` | +| `ingress.extraPaths` | Any additional arbitrary paths that may need to be added to the ingress under the main host. | `[]` | +| `ingress.extraRules` | The list of additional rules to be added to this ingress record. Evaluated as a template | `[]` | +| `ingress.extraTls` | The tls configuration for additional hostnames to be covered with this ingress record. | `[]` | +| `ingress.secrets` | If you're providing your own certificates, please use this to add the certificates as secrets | `[]` | +| `ingress.ingressClassName` | IngressClass that will be be used to implement the Ingress (Kubernetes 1.18+) | `""` | +| `pdb.create` | If true, create a pod disruption budget for pods. | `false` | +| `pdb.minAvailable` | Minimum number / percentage of pods that should remain scheduled | `1` | +| `pdb.maxUnavailable` | Maximum number / percentage of pods that may be made unavailable | `""` | + +Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example, + +```console +helm install my-release \ + --set enableMonitoringAPI=false my-repo/logstash +``` + +The above command disables the Logstash Monitoring API. + +Alternatively, a YAML file that specifies the values for the parameters can be provided while installing the chart. For example, + +```console +helm install my-release -f values.yaml my-repo/logstash +``` + +> **Tip**: You can use the default [values.yaml](values.yaml) + +## Configuration and installation details + +### [Rolling vs Immutable tags](https://docs.bitnami.com/containers/how-to/understand-rolling-tags-containers/) + +It is strongly recommended to use immutable tags in a production environment. This ensures your deployment does not change automatically if the same tag is updated with a different image. + +Bitnami will release a new chart updating its containers if a new version of the main container, significant changes, or critical vulnerabilities exist. + +### Expose the Logstash service + +The service(s) created by the deployment can be exposed within or outside the cluster using any of the following approaches: + +- **Ingress**: Set `ingress.enabled=true` to expose Logstash through Ingress. +- **ClusterIP**: Set `service.type=ClusterIP` to choose this service type. +- **NodePort**: Set `service.type=NodePort` to choose this service type. +- **LoadBalancer**: Set `service.type=LoadBalancer` to choose this service type. + +For more information, refer to the [chart documentation on exposing the Logstash service](https://docs.bitnami.com/kubernetes/apps/logstash/get-started/expose-service/). + +### Use custom configuration + +By default, this Helm chart provides a basic configuration for Logstash: listening to HTTP requests on port 8080 and writing them to the standard output. + +This Logstash configuration can be adjusted using the *input*, *filter*, and *output* parameters, which allow specification of the input, filter and output plugins configuration respectively. In addition to these options, the chart also supports reading configuration from an external ConfigMap via the *existingConfiguration* parameter. + +Refer to the [chart documentation for more information on customizing the Logstash deployment](https://docs.bitnami.com/kubernetes/apps/logstash/configuration/customize-deployment/). + +### Create and use multiple pipelines + +The chart supports the use of [multiple pipelines](https://www.elastic.co/guide/en/logstash/master/multiple-pipelines.html) by setting the *enableMultiplePipelines* parameter to *true*. + +The chart supports setting an external ConfigMap with all the configuration files via the *existingConfiguration* parameter. + +For more information and an example, refer to the chart documentation on [using multiple pipelines](https://docs.bitnami.com/kubernetes/apps/logstash/configuration/use-multiple-pipelines/). + +### Add extra environment variables + +To add extra environment variables, use the `extraEnvVars` property. + +```yaml +extraEnvVars: + - name: ELASTICSEARCH_HOST + value: "x.y.z" +``` + +To add extra environment variables from an external ConfigMap or secret, use the `extraEnvVarsCM` and `extraEnvVarsSecret` properties. Note that the secret and ConfigMap should be already available in the namespace. + +```yaml +extraEnvVarsSecret: logstash-secrets +extraEnvVarsCM: logstash-configmap +``` + +### Set Pod affinity + +This chart allows you to set custom Pod affinity using the `affinity` parameter. Find more information about Pod affinity in the [kubernetes documentation](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity). + +As an alternative, use one of the preset configurations for pod affinity, pod anti-affinity, and node affinity available at the [bitnami/common](https://github.com/bitnami/charts/tree/main/bitnami/common#affinities) chart. To do so, set the `podAffinityPreset`, `podAntiAffinityPreset`, or `nodeAffinityPreset` parameters. + +## Persistence + +The [Bitnami Logstash](https://github.com/bitnami/containers/tree/main/bitnami/logstash) image stores the Logstash data at the `/bitnami/logstash/data` path of the container. + +Persistent Volume Claims (PVCs) are used to keep the data across deployments. This is known to work in GCE, AWS, and minikube. + +See the [Parameters](#parameters) section to configure the PVC or to disable persistence. + +## Troubleshooting + +Find more information about how to deal with common errors related to Bitnami's Helm charts in [this troubleshooting guide](https://docs.bitnami.com/general/how-to/troubleshoot-helm-chart-issues). + +## Upgrading + +### To 5.0.0 + +This major release is no longer contains the metrics section because the container `bitnami/logstash-exporter` has been deprecated due to the upstream project is not maintained. + +### To 4.0.0 + +This major release updates the chart to use Logstash 8. In addition, this chart has been standardized adding missing values and renaming others, in order to get aligned with the rest of the assets in the Bitnami charts repository. + +The following values have been renamed: + +- `securityContext` has been splitted between `containerSecurityContext` and `podSecurityContext`. +- Liveness and readiness probes httpGet field can not be modified. For customization, use customLivenessProbe and customReadinessProbe instead. +- `lifecycle` renamed as `lifecycleHooks`. +- `service.ports` is now evaluated as a template with array structure. +- Enabling `ingress.tls` no longer auto generates certificates. Use `ingress.selfSigned` to enable the creation of autogenerated certificates. +- `podDisruptionBudget.*` renamed as `pdb.*`. + +### To 3.0.0 + +This version standardizes the way of defining Ingress rules. When configuring a single hostname for the Ingress rule, set the `ingress.hostname` value. When defining more than one, set the `ingress.extraHosts` array. Apart from this case, no issues are expected to appear when upgrading. + +### To 2.0.0 + +This version drops support of including files in the `files/` folder, as it was working only under certain circumstances and the chart already provides alternative mechanisms like the `input` , `output` and `filter`, the `existingConfiguration` or the `extraDeploy` values. + +### To 1.2.0 + +This version introduces `bitnami/common`, a [library chart](https://helm.sh/docs/topics/library_charts/#helm) as a dependency. More documentation about this new utility could be found [here](https://github.com/bitnami/charts/tree/main/bitnami/common#bitnami-common-library-chart). Please, make sure that you have updated the chart dependencies before executing any upgrade. + +### To 1.0.0 + +[On November 13, 2020, Helm v2 support formally ended](https://github.com/helm/charts#status-of-the-project). Subsequently, a major version of the chart was released to incorporate the different features added in Helm v3 and to be consistent with the Helm project itself regarding the Helm v2 EOL. + +[Learn more about this change and related upgrade considerations](https://docs.bitnami.com/kubernetes/apps/logstash/administration/upgrade-helm3/). + +## License + +Copyright © 2023 Bitnami + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/knowlg-automation/helm_charts/logstash/charts/common-2.2.4.tgz b/knowlg-automation/helm_charts/logstash/charts/common-2.2.4.tgz new file mode 100644 index 0000000000000000000000000000000000000000..9016f32e2cfd76371466da567554893ec977e7c3 GIT binary patch literal 12364 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 literal 0 HcmV?d00001 diff --git a/knowlg-automation/helm_charts/logstash/templates/NOTES.txt b/knowlg-automation/helm_charts/logstash/templates/NOTES.txt new file mode 100644 index 000000000..061e5c724 --- /dev/null +++ b/knowlg-automation/helm_charts/logstash/templates/NOTES.txt @@ -0,0 +1,63 @@ +CHART NAME: {{ .Chart.Name }} +CHART VERSION: {{ .Chart.Version }} +APP VERSION: {{ .Chart.AppVersion }} + +** Please be patient while the chart is being deployed ** + +{{- if .Values.diagnosticMode.enabled }} +The chart has been deployed in diagnostic mode. All probes have been disabled and the command has been overwritten with: + + command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 4 }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 4 }} + +Get the list of pods by executing: + + kubectl get pods --namespace {{ .Release.Namespace }} -l app.kubernetes.io/instance={{ .Release.Name }} + +Access the pod you want to debug by executing + + kubectl exec --namespace {{ .Release.Namespace }} -ti -- bash + +In order to replicate the container startup scripts execute this command: + + /opt/bitnami/scripts/logstash/entrypoint.sh /opt/bitnami/scripts/logstash/run.sh + +{{- else }} + +Logstash can be accessed through following DNS names from within your cluster: + + Logstash: {{ include "common.names.fullname" . }}.{{ .Release.Namespace }}.svc.{{ .Values.clusterDomain }} + +To access Logstash from outside the cluster execute the following commands: + +{{- if .Values.ingress.enabled }} + + You should be able to access your new Logstash server(s) through: + + curl {{ if .Values.ingress.tls }}https{{- else }}http{{ end }}://{{ .Values.ingress.hostname }} + +{{- else if contains "NodePort" .Values.service.type }} + + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "common.names.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo "http://${NODE_IP}:${NODE_PORT}" + +{{- else if contains "LoadBalancer" .Values.service.type }} + + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "common.names.fullname" . }}' + export SERVICE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].port}" services {{ include "common.names.fullname" . }}) + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "common.names.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') + echo "http://${SERVICE_IP}:${SERVICE_PORT}" + +{{- else if contains "ClusterIP" .Values.service.type }} + + export SERVICE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].port}" services {{ include "common.names.fullname" . }}) + kubectl port-forward svc/{{ include "common.names.fullname" . }} ${SERVICE_PORT}:${SERVICE_PORT} & + echo "http://127.0.0.1:${SERVICE_PORT}" + +{{- end }} +{{- end }} + +{{- include "logstash.validateValues" . }} +{{- include "logstash.checkRollingTags" . }} diff --git a/knowlg-automation/helm_charts/logstash/templates/_helpers.tpl b/knowlg-automation/helm_charts/logstash/templates/_helpers.tpl new file mode 100644 index 000000000..33ecbd57e --- /dev/null +++ b/knowlg-automation/helm_charts/logstash/templates/_helpers.tpl @@ -0,0 +1,67 @@ +{{/* vim: set filetype=mustache: */}} + +{{/* +Return the proper Logstash image name +*/}} +{{- define "logstash.image" -}} +{{ include "common.images.image" (dict "imageRoot" .Values.image "global" .Values.global) }} +{{- end -}} + + +{{/* +Return the proper Docker Image Registry Secret Names +*/}} +{{- define "logstash.imagePullSecrets" -}} +{{- include "common.images.pullSecrets" (dict "images" (list .Values.image) "global" .Values.global) -}} +{{- end -}} + +{{/* +Return the Logstash configuration configmap. +*/}} +{{- define "logstash.configmapName" -}} +{{- if .Values.existingConfiguration -}} + {{- printf "%s" (tpl .Values.existingConfiguration $) -}} +{{- else -}} + {{- printf "%s" (include "common.names.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{/* +Create the name of the service account to use +*/}} +{{- define "logstash.serviceAccountName" -}} +{{- if .Values.serviceAccount.create -}} + {{ default (include "common.names.fullname" .) .Values.serviceAccount.name }} +{{- else -}} + {{ default "default" .Values.serviceAccount.name }} +{{- end -}} +{{- end -}} + +{{/* +Check if there are rolling tags in the images +*/}} +{{- define "logstash.checkRollingTags" -}} +{{- include "common.warnings.rollingTag" .Values.image }} +{{- include "common.warnings.rollingTag" .Values.volumePermissions.image }} +{{- end -}} + +{{/* +Compile all warnings into a single message, and call fail. +*/}} +{{- define "logstash.validateValues" -}} +{{- $messages := list -}} +{{- $messages := without $messages "" -}} +{{- $message := join "\n" $messages -}} + +{{- if $message -}} +{{- printf "\nVALUES VALIDATION:\n%s" $message | fail -}} +{{- end -}} +{{- end -}} + + +{{/* +Return the proper image name (for the init container volume-permissions image) +*/}} +{{- define "logstash.volumePermissions.image" -}} +{{- include "common.images.image" ( dict "imageRoot" .Values.volumePermissions.image "global" .Values.global ) -}} +{{- end -}} diff --git a/knowlg-automation/helm_charts/logstash/templates/configuration-cm.yaml b/knowlg-automation/helm_charts/logstash/templates/configuration-cm.yaml new file mode 100644 index 000000000..2e86cb3d3 --- /dev/null +++ b/knowlg-automation/helm_charts/logstash/templates/configuration-cm.yaml @@ -0,0 +1,39 @@ +{{ if and (or .Values.input .Values.filter .Values.output) (not .Values.existingConfiguration) }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "common.names.fullname" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if or .Values.ingress.annotations .Values.commonAnnotations }} + annotations: + {{- if .Values.ingress.annotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.ingress.annotations "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- end }} +data: +{{- if (or .Values.input .Values.filter .Values.output) }} + logstash.conf: |- +{{- if .Values.input }} + input { + {{- include "common.tplvalues.render" (dict "value" .Values.input "context" $) | nindent 6 }} + } +{{- end }} +{{- if .Values.filter }} + filter { + {{- include "common.tplvalues.render" (dict "value" .Values.filter "context" $) | nindent 6 }} + } +{{- end }} +{{- if .Values.output }} + output { + {{- include "common.tplvalues.render" (dict "value" .Values.output "context" $) | nindent 6 }} + } +{{- end }} +{{- end }} +{{- end }} diff --git a/knowlg-automation/helm_charts/logstash/templates/extra-list.yaml b/knowlg-automation/helm_charts/logstash/templates/extra-list.yaml new file mode 100644 index 000000000..9ac65f9e1 --- /dev/null +++ b/knowlg-automation/helm_charts/logstash/templates/extra-list.yaml @@ -0,0 +1,4 @@ +{{- range .Values.extraDeploy }} +--- +{{ include "common.tplvalues.render" (dict "value" . "context" $) }} +{{- end }} diff --git a/knowlg-automation/helm_charts/logstash/templates/headless-svc.yaml b/knowlg-automation/helm_charts/logstash/templates/headless-svc.yaml new file mode 100644 index 000000000..82ff92f49 --- /dev/null +++ b/knowlg-automation/helm_charts/logstash/templates/headless-svc.yaml @@ -0,0 +1,22 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ printf "%s-headless" (include "common.names.fullname" .) | trunc 63 | trimSuffix "-" }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + annotations: + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" (dict "value" .Values.commonAnnotations "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.service.annotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.service.annotations "context" $) | nindent 4 }} + {{- end }} +spec: + type: ClusterIP + clusterIP: None + ports: + {{- include "common.tplvalues.render" (dict "value" .Values.service.ports "context" $) | nindent 4 }} + selector: {{ include "common.labels.matchLabels" . | nindent 4 }} diff --git a/knowlg-automation/helm_charts/logstash/templates/ingress.yaml b/knowlg-automation/helm_charts/logstash/templates/ingress.yaml new file mode 100644 index 000000000..61515de2d --- /dev/null +++ b/knowlg-automation/helm_charts/logstash/templates/ingress.yaml @@ -0,0 +1,62 @@ +{{- if .Values.ingress.enabled }} +apiVersion: {{ include "common.capabilities.ingress.apiVersion" . }} +kind: Ingress +metadata: + name: {{ template "common.names.fullname" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if or .Values.ingress.annotations .Values.commonAnnotations }} + annotations: + {{- if .Values.ingress.annotations }} + {{- include "common.tplvalues.render" (dict "value" .Values.ingress.annotations "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" (dict "value" .Values.commonAnnotations "context" $) | nindent 4 }} + {{- end }} + {{- end }} +spec: + {{- if and .Values.ingress.ingressClassName (eq "true" (include "common.ingress.supportsIngressClassname" .)) }} + ingressClassName: {{ .Values.ingress.ingressClassName | quote }} + {{- end }} + rules: + {{- if .Values.ingress.hostname }} + - host: {{ .Values.ingress.hostname }} + http: + paths: + - path: {{ .Values.ingress.path }} + {{- if eq "true" (include "common.ingress.supportsPathType" .) }} + pathType: {{ .Values.ingress.pathType }} + {{- end }} + backend: {{- include "common.ingress.backend" (dict "serviceName" (include "common.names.fullname" .) "servicePort" "http" "context" $) | nindent 14 }} + {{- if .Values.ingress.extraPaths }} + {{- toYaml .Values.ingress.extraPaths | nindent 10 }} + {{- end }} + {{- end }} + {{- range .Values.ingress.extraHosts }} + - host: {{ .name | quote }} + http: + paths: + - path: {{ default "/" .path }} + {{- if eq "true" (include "common.ingress.supportsPathType" $) }} + pathType: {{ default "ImplementationSpecific" .pathType }} + {{- end }} + backend: {{- include "common.ingress.backend" (dict "serviceName" (include "common.names.fullname" $) "servicePort" "http" "context" $) | nindent 14 }} + {{- end }} + {{- if .Values.ingress.extraRules }} + {{- include "common.tplvalues.render" (dict "value" .Values.ingress.extraRules "context" $) | nindent 4 }} + {{- end }} + {{- if or (and .Values.ingress.tls (or (include "common.ingress.certManagerRequest" ( dict "annotations" .Values.ingress.annotations )) .Values.ingress.selfSigned)) .Values.ingress.extraTls }} + tls: + {{- if and .Values.ingress.tls (or (include "common.ingress.certManagerRequest" ( dict "annotations" .Values.ingress.annotations )) .Values.ingress.selfSigned) }} + - hosts: + - {{ .Values.ingress.hostname | quote }} + secretName: {{ printf "%s-tls" .Values.ingress.hostname }} + {{- end }} + {{- if .Values.ingress.extraTls }} + {{- include "common.tplvalues.render" (dict "value" .Values.ingress.extraTls "context" $) | nindent 4 }} + {{- end }} + {{- end }} +{{- end }} diff --git a/knowlg-automation/helm_charts/logstash/templates/pdb.yaml b/knowlg-automation/helm_charts/logstash/templates/pdb.yaml new file mode 100644 index 000000000..fbdb5d7fd --- /dev/null +++ b/knowlg-automation/helm_charts/logstash/templates/pdb.yaml @@ -0,0 +1,23 @@ +{{- if .Values.pdb.create }} +apiVersion: {{ include "common.capabilities.policy.apiVersion" . }} +kind: PodDisruptionBudget +metadata: + name: {{ include "common.names.fullname" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" (dict "value" .Values.commonLabels "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" (dict "value" .Values.commonAnnotations "context" $) | nindent 4 }} + {{- end }} +spec: + {{- if .Values.pdb.minAvailable }} + minAvailable: {{ .Values.pdb.minAvailable }} + {{- end }} + {{- if .Values.pdb.maxUnavailable }} + maxUnavailable: {{ .Values.pdb.maxUnavailable }} + {{- end }} + selector: + matchLabels: {{ include "common.labels.matchLabels" . | nindent 6 }} +{{- end }} diff --git a/knowlg-automation/helm_charts/logstash/templates/serviceaccount.yaml b/knowlg-automation/helm_charts/logstash/templates/serviceaccount.yaml new file mode 100644 index 000000000..21c8ec5af --- /dev/null +++ b/knowlg-automation/helm_charts/logstash/templates/serviceaccount.yaml @@ -0,0 +1,21 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "logstash.serviceAccountName" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if or .Values.commonAnnotations .Values.serviceAccount.annotations }} + annotations: + {{- if .Values.serviceAccount.annotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.serviceAccount.annotations "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- end }} +automountServiceAccountToken: {{ .Values.serviceAccount.automountServiceAccountToken }} +{{- end -}} diff --git a/knowlg-automation/helm_charts/logstash/templates/sts.yaml b/knowlg-automation/helm_charts/logstash/templates/sts.yaml new file mode 100644 index 000000000..e77c3a9df --- /dev/null +++ b/knowlg-automation/helm_charts/logstash/templates/sts.yaml @@ -0,0 +1,219 @@ +apiVersion: {{ include "common.capabilities.statefulset.apiVersion" . }} +kind: StatefulSet +metadata: + name: {{ include "common.names.fullname" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + serviceName: {{ printf "%s-headless" (include "common.names.fullname" .) }} + replicas: {{ .Values.replicaCount }} + podManagementPolicy: {{ .Values.podManagementPolicy }} + {{- if .Values.updateStrategy }} + updateStrategy: {{- toYaml .Values.updateStrategy | nindent 4 }} + {{- end }} + selector: + matchLabels: {{- include "common.labels.matchLabels" . | nindent 6 }} + template: + metadata: + labels: {{- include "common.labels.standard" . | nindent 8 }} + {{- if .Values.podLabels }} + {{- include "common.tplvalues.render" (dict "value" .Values.podLabels "context" $) | nindent 8 }} + {{- end }} + annotations: + checksum/configuration: {{ include (print $.Template.BasePath "/configuration-cm.yaml") . | sha256sum }} + {{- if .Values.podAnnotations }} + {{- include "common.tplvalues.render" (dict "value" .Values.podAnnotations "context" $) | nindent 8 }} + {{- end }} + spec: + {{- include "logstash.imagePullSecrets" . | nindent 6 }} + serviceAccountName: {{ template "logstash.serviceAccountName" . }} + {{- if .Values.hostAliases }} + hostAliases: {{- include "common.tplvalues.render" (dict "value" .Values.hostAliases "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.affinity }} + affinity: {{- include "common.tplvalues.render" ( dict "value" .Values.affinity "context" $) | nindent 8 }} + {{- else }} + affinity: + podAffinity: {{- include "common.affinities.pods" (dict "type" .Values.podAffinityPreset "context" $) | nindent 10 }} + podAntiAffinity: {{- include "common.affinities.pods" (dict "type" .Values.podAntiAffinityPreset "context" $) | nindent 10 }} + nodeAffinity: {{- include "common.affinities.nodes" (dict "type" .Values.nodeAffinityPreset.type "key" .Values.nodeAffinityPreset.key "values" .Values.nodeAffinityPreset.values) | nindent 10 }} + {{- end }} + {{- if .Values.nodeSelector }} + nodeSelector: {{- include "common.tplvalues.render" ( dict "value" .Values.nodeSelector "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.tolerations }} + tolerations: {{- include "common.tplvalues.render" (dict "value" .Values.tolerations "context" .) | nindent 8 }} + {{- end }} + {{- if .Values.schedulerName }} + schedulerName: {{ .Values.schedulerName }} + {{- end }} + {{- if .Values.topologySpreadConstraints }} + topologySpreadConstraints: {{- include "common.tplvalues.render" (dict "value" .Values.topologySpreadConstraints "context" .) | nindent 8 }} + {{- end }} + {{- if .Values.priorityClassName }} + priorityClassName: {{ .Values.priorityClassName | quote }} + {{- end }} + {{- if .Values.terminationGracePeriodSeconds }} + terminationGracePeriodSeconds: {{ .Values.terminationGracePeriodSeconds }} + {{- end }} + {{- if .Values.podSecurityContext.enabled }} + securityContext: {{- omit .Values.podSecurityContext "enabled" | toYaml | nindent 8 }} + {{- end }} + initContainers: + {{- if and .Values.persistence.enabled .Values.volumePermissions.enabled }} + - name: volume-permissions + image: {{ include "logstash.volumePermissions.image" . }} + imagePullPolicy: {{ .Values.volumePermissions.image.pullPolicy | quote }} + command: + - /bin/bash + - -ec + - | + mkdir -p "{{ .Values.persistence.mountPath }}" + chown "{{ .Values.containerSecurityContext.runAsUser }}:{{ .Values.podSecurityContext.fsGroup }}" "{{ .Values.persistence.mountPath }}" + find "{{ .Values.persistence.mountPath }}" -mindepth 1 -maxdepth 1 -not -name ".snapshot" -not -name "lost+found" | xargs -r chown -R "{{ .Values.containerSecurityContext.runAsUser }}:{{ .Values.podSecurityContext.fsGroup }}" + securityContext: {{ .Values.volumePermissions.securityContext | toYaml | nindent 12 }} + {{- if .Values.volumePermissions.resources }} + resources: {{- toYaml .Values.volumePermissions.resources | nindent 12 }} + {{- end }} + volumeMounts: + - name: data + mountPath: {{ .Values.persistence.mountPath }} + {{- end }} + {{- if .Values.initContainers }} + {{- include "common.tplvalues.render" (dict "value" .Values.initContainers "context" $) | nindent 8 }} + {{- end }} + containers: + - name: logstash + image: {{ include "logstash.image" . }} + imagePullPolicy: {{ .Values.image.pullPolicy | quote }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 12 }} + {{- end }} + {{- if .Values.diagnosticMode.enabled }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 12 }} + {{- else if .Values.command }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.command "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.diagnosticMode.enabled }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 12 }} + {{- else if .Values.args }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.args "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.lifecycleHooks }} + lifecycle: {{- include "common.tplvalues.render" (dict "value" .Values.lifecycleHooks "context" $) | nindent 12 }} + {{- end }} + env: + - name: BITNAMI_DEBUG + value: {{ ternary "true" "false" (or .Values.image.debug .Values.diagnosticMode.enabled) | quote }} + {{- if .Values.configFileName }} + - name: LOGSTASH_CONF_FILENAME + value: {{ .Values.configFileName | quote }} + {{- end }} + - name: LOGSTASH_ENABLE_MULTIPLE_PIPELINES + value: {{ ternary "true" "false" .Values.enableMultiplePipelines | quote }} + - name: LOGSTASH_EXPOSE_API + value: {{ ternary "yes" "no" .Values.enableMonitoringAPI | quote }} + - name: LOGSTASH_API_PORT_NUMBER + value: {{ .Values.monitoringAPIPort | quote }} + {{- if .Values.persistence.enabled }} + - name: LOGSTASH_DATA_DIR + value: {{ .Values.persistence.mountPath }} + {{- end }} + {{- if .Values.extraEnvVars }} + {{- include "common.tplvalues.render" (dict "value" .Values.extraEnvVars "context" $) | nindent 12 }} + {{- end }} + envFrom: + {{- if .Values.extraEnvVarsCM }} + - configMapRef: + name: {{ .Values.extraEnvVarsCM }} + {{- end }} + {{- if .Values.extraEnvVarsSecret }} + - secretRef: + name: {{ .Values.extraEnvVarsSecret }} + {{- end }} + {{- if .Values.containerPorts }} + ports: {{ toYaml .Values.containerPorts | nindent 12 }} + {{- end }} + {{- if not .Values.diagnosticMode.enabled }} + {{- if .Values.customLivenessProbe }} + livenessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.customLivenessProbe "context" $) | nindent 12 }} + {{- else if .Values.livenessProbe.enabled }} + livenessProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.livenessProbe "enabled") "context" $) | nindent 12 }} + httpGet: + path: / + port: monitoring + {{- end }} + {{- if .Values.customReadinessProbe }} + readinessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.customReadinessProbe "context" $) | nindent 12 }} + {{- else if .Values.readinessProbe.enabled }} + readinessProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.readinessProbe "enabled") "context" $) | nindent 12 }} + httpGet: + path: / + port: monitoring + {{- end }} + {{- if .Values.customStartupProbe }} + startupProbe: {{- include "common.tplvalues.render" (dict "value" .Values.customStartupProbe "context" $) | nindent 12 }} + {{- else if .Values.startupProbe.enabled }} + startupProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.startupProbe "enabled") "context" $) | nindent 12 }} + tcpSocket: + port: monitoring + {{- end }} + {{- end }} + {{- if .Values.resources }} + resources: {{- toYaml .Values.resources | nindent 12 }} + {{- end }} + volumeMounts: + {{- if .Values.persistence.enabled }} + - name: data + mountPath: {{ .Values.persistence.mountPath }} + {{- end }} + {{- if or .Values.input .Values.filter .Values.output .Values.existingConfiguration }} + - name: configurations + mountPath: /bitnami/logstash/config + {{- end }} + {{- if .Values.extraVolumeMounts }} + {{- include "common.tplvalues.render" ( dict "value" .Values.extraVolumeMounts "context" $ ) | nindent 12 }} + {{- end }} + {{- if .Values.sidecars }} + {{- include "common.tplvalues.render" ( dict "value" .Values.sidecars "context" $) | nindent 8 }} + {{- end }} + volumes: + {{- if or .Values.input .Values.filter .Values.output .Values.existingConfiguration }} + - name: configurations + configMap: + name: {{ include "logstash.configmapName" . }} + {{- end }} + {{- if and .Values.persistence.enabled }} + - name: data + persistentVolumeClaim: + claimName: {{ .Values.persistence.existingClaim | default (include "common.names.fullname" .) }} + {{- end }} + {{- if .Values.extraVolumes }} + {{- include "common.tplvalues.render" ( dict "value" .Values.extraVolumes "context" $ ) | nindent 8 }} + {{- end }} + {{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }} + volumeClaimTemplates: + - metadata: + name: data + {{- if .Values.persistence.annotations }} + annotations: {{- include "common.tplvalues.render" (dict "value" .Values.persistence.annotations "context" $) | nindent 10 }} + {{- end }} + spec: + accessModes: + {{- range .Values.persistence.accessModes }} + - {{ . | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.persistence.size | quote }} + {{- if .Values.persistence.selector }} + selector: {{- include "common.tplvalues.render" ( dict "value" .Values.persistence.selector "context" $) | nindent 10 }} + {{- end }} + {{ include "common.storage.class" (dict "persistence" .Values.persistence "global" .Values.global) }} + {{- end }} diff --git a/knowlg-automation/helm_charts/logstash/templates/svc.yaml b/knowlg-automation/helm_charts/logstash/templates/svc.yaml new file mode 100644 index 000000000..295116b24 --- /dev/null +++ b/knowlg-automation/helm_charts/logstash/templates/svc.yaml @@ -0,0 +1,39 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "common.names.fullname" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + annotations: + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" (dict "value" .Values.commonAnnotations "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.service.annotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.service.annotations "context" $) | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.service.type }} + {{- if and .Values.service.loadBalancerIP (eq .Values.service.type "LoadBalancer") }} + loadBalancerIP: {{ .Values.service.loadBalancerIP }} + {{- end }} + {{- if and (eq .Values.service.type "LoadBalancer") .Values.service.loadBalancerSourceRanges }} + loadBalancerSourceRanges: {{- toYaml .Values.service.loadBalancerSourceRanges | nindent 4 }} + {{- end }} + {{- if and .Values.service.clusterIP (eq .Values.service.type "ClusterIP") }} + clusterIP: {{ .Values.service.clusterIP }} + {{- end }} + {{- if or (eq .Values.service.type "LoadBalancer") (eq .Values.service.type "NodePort") }} + externalTrafficPolicy: {{ .Values.service.externalTrafficPolicy | quote }} + {{- end }} + {{- if .Values.service.sessionAffinity }} + sessionAffinity: {{ .Values.service.sessionAffinity }} + {{- end }} + {{- if .Values.service.sessionAffinityConfig }} + sessionAffinityConfig: {{- include "common.tplvalues.render" (dict "value" .Values.service.sessionAffinityConfig "context" $) | nindent 4 }} + {{- end }} + ports: + {{- include "common.tplvalues.render" (dict "value" .Values.service.ports "context" $) | nindent 4 }} + selector: {{ include "common.labels.matchLabels" . | nindent 4 }} diff --git a/knowlg-automation/helm_charts/logstash/templates/tls-secret.yaml b/knowlg-automation/helm_charts/logstash/templates/tls-secret.yaml new file mode 100644 index 000000000..d9dfbe943 --- /dev/null +++ b/knowlg-automation/helm_charts/logstash/templates/tls-secret.yaml @@ -0,0 +1,44 @@ +{{- if .Values.ingress.enabled }} +{{- if .Values.ingress.secrets }} +{{- range .Values.ingress.secrets }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ .name }} + namespace: {{ include "common.names.namespace" $ | quote }} + labels: {{- include "common.labels.standard" $ | nindent 4 }} + {{- if $.Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if $.Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +type: kubernetes.io/tls +data: + tls.crt: {{ .certificate | b64enc }} + tls.key: {{ .key | b64enc }} +--- +{{- end }} +{{- else if and .Values.ingress.tls .Values.ingress.selfSigned }} +{{- $secretName := printf "%s-tls" .Values.ingress.hostname }} +{{- $ca := genCA "logstash-ca" 365 }} +{{- $cert := genSignedCert .Values.ingress.hostname nil (list .Values.ingress.hostname) 365 $ca }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ $secretName }} + namespace: {{ include "common.names.namespace" $ | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +type: kubernetes.io/tls +data: + tls.crt: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "tls.crt" "defaultValue" $cert.Cert "context" $) }} + tls.key: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "tls.key" "defaultValue" $cert.Key "context" $) }} + ca.crt: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "ca.crt" "defaultValue" $ca.Cert "context" $) }} +{{- end }} +{{- end }} diff --git a/knowlg-automation/helm_charts/logstash/values.yaml b/knowlg-automation/helm_charts/logstash/values.yaml new file mode 100644 index 000000000..83812fe45 --- /dev/null +++ b/knowlg-automation/helm_charts/logstash/values.yaml @@ -0,0 +1,662 @@ +## @section Global parameters +## Global Docker image parameters +## Please, note that this will override the image parameters, including dependencies, configured to use the global value +## Current available global Docker image parameters: imageRegistry, imagePullSecrets and storageClass + +## @param global.imageRegistry Global Docker image registry +## @param global.imagePullSecrets Global Docker registry secret names as an array +## @param global.storageClass Global StorageClass for Persistent Volume(s) +## +global: + imageRegistry: "" + ## E.g. + ## imagePullSecrets: + ## - myRegistryKeySecretName + ## + imagePullSecrets: [] + storageClass: "" + +## @section Common parameters + +## @param kubeVersion Force target Kubernetes version (using Helm capabilities if not set) +## +kubeVersion: "" +## @param nameOverride String to partially override logstash.fullname template (will maintain the release name) +## +nameOverride: "" +## @param fullnameOverride String to fully override logstash.fullname template +## +fullnameOverride: "" +## @param clusterDomain Default Kubernetes cluster domain +## +clusterDomain: cluster.local +## @param commonAnnotations Annotations to add to all deployed objects +## +commonAnnotations: {} +## @param commonLabels Labels to add to all deployed objects +## +commonLabels: {} +## @param extraDeploy Array of extra objects to deploy with the release (evaluated as a template). +## +extraDeploy: [] + +## Enable diagnostic mode in the deployment +## +diagnosticMode: + ## @param diagnosticMode.enabled Enable diagnostic mode (all probes will be disabled and the command will be overridden) + ## + enabled: false + ## @param diagnosticMode.command Command to override all containers in the deployment + ## + command: + - sleep + ## @param diagnosticMode.args Args to override all containers in the deployment + ## + args: + - infinity + +## @section Logstash parameters + +## Bitnami Logstash image +## ref: https://hub.docker.com/r/bitnami/logstash/tags/ +## @param image.registry Logstash image registry +## @param image.repository Logstash image repository +## @param image.tag Logstash image tag (immutable tags are recommended) +## @param image.digest Logstash image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag +## @param image.pullPolicy Logstash image pull policy +## @param image.pullSecrets Specify docker-registry secret names as an array +## @param image.debug Specify if debug logs should be enabled +## +image: + registry: docker.io + repository: bitnami/logstash + tag: 8.6.2-debian-11-r3 + digest: "" + ## Specify a imagePullPolicy. Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' + ## ref: https://kubernetes.io/docs/user-guide/images/#pre-pulling-images + ## + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets (secrets must be manually created in the namespace) + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## Example: + ## pullSecrets: + ## - myRegistryKeySecretName + ## + pullSecrets: [] + ## Set to true if you would like to see extra information on logs + ## + debug: false +## @param hostAliases Add deployment host aliases +## https://kubernetes.io/docs/concepts/services-networking/add-entries-to-pod-etc-hosts-with-host-aliases/ +## +hostAliases: [] +## @param configFileName Logstash configuration file name. It must match the name of the configuration file mounted as a configmap. +## +configFileName: logstash.conf +## @param enableMonitoringAPI Whether to enable the Logstash Monitoring API or not Kubernetes cluster domain +## +enableMonitoringAPI: true +## @param monitoringAPIPort Logstash Monitoring API Port +## +monitoringAPIPort: 9600 +## @param extraEnvVars Array containing extra env vars to configure Logstash +## For example: +## extraEnvVars: +## - name: ELASTICSEARCH_HOST +## value: "x.y.z" +## +extraEnvVars: [] +## @param extraEnvVarsSecret To add secrets to environment +## +extraEnvVarsSecret: "" +## @param extraEnvVarsCM To add configmaps to environment +## +extraEnvVarsCM: "" +## @param input [string] Input Plugins configuration +## ref: https://www.elastic.co/guide/en/logstash/current/input-plugins.html +## +input: |- + # udp { + # port => 1514 + # type => syslog + # } + # tcp { + # port => 1514 + # type => syslog + # } + http { port => 8080 } +## @param filter Filter Plugins configuration +## ref: https://www.elastic.co/guide/en/logstash/current/filter-plugins.html +## e.g: +## filter: |- +## grok { +## match => { "message" => "%{COMBINEDAPACHELOG}" } +## } +## date { +## match => [ "timestamp" , "dd/MMM/yyyy:HH:mm:ss Z" ] +## } +## +filter: "" +## @param output [string] Output Plugins configuration +## ref: https://www.elastic.co/guide/en/logstash/current/output-plugins.html +## +output: |- + # elasticsearch { + # hosts => ["${ELASTICSEARCH_HOST}:${ELASTICSEARCH_PORT}"] + # manage_template => false + # index => "%{[@metadata][beat]}-%{+YYYY.MM.dd}" + # } + # gelf { + # host => "${GRAYLOG_HOST}" + # port => ${GRAYLOG_PORT} + # } + stdout {} +## @param existingConfiguration Name of existing ConfigMap object with the Logstash configuration (`input`, `filter`, and `output` will be ignored). +## +existingConfiguration: "" +## @param enableMultiplePipelines Allows user to use multiple pipelines +## ref: https://www.elastic.co/guide/en/logstash/master/multiple-pipelines.html +## +enableMultiplePipelines: false +## @param extraVolumes Array to add extra volumes (evaluated as a template) +## extraVolumes: +## - name: myvolume +## configMap: +## name: myconfigmap +## +extraVolumes: [] +## @param extraVolumeMounts Array to add extra mounts (normally used with extraVolumes, evaluated as a template) +## extraVolumeMounts: +## - mountPath: /opt/bitnami/desired-path +## name: myvolume +## readOnly: true +## +extraVolumeMounts: [] + + ## ServiceAccount for Logstash + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ + ## +serviceAccount: + ## @param serviceAccount.create Enable creation of ServiceAccount for Logstash pods + ## + create: true + ## @param serviceAccount.name The name of the service account to use. If not set and `create` is `true`, a name is generated + ## If not set and create is true, a name is generated using the logstash.serviceAccountName template + ## + name: "" + ## @param serviceAccount.automountServiceAccountToken Allows automount of ServiceAccountToken on the serviceAccount created + ## Can be set to false if pods using this serviceAccount do not need to use K8s API + ## + automountServiceAccountToken: true + ## @param serviceAccount.annotations Additional custom annotations for the ServiceAccount + ## + annotations: {} + +## @param containerPorts [array] Array containing the ports to open in the Logstash container (evaluated as a template) +## +containerPorts: + - name: http + containerPort: 8080 + protocol: TCP + - name: monitoring + containerPort: 9600 + protocol: TCP + ## - name: syslog-udp + ## containerPort: 1514 + ## protocol: UDP + ## - name: syslog-tcp + ## containerPort: 1514 + ## protocol: TCP + ## +## @param initContainers Add additional init containers to the Logstash pod(s) +## ref: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ +## e.g: +## initContainers: +## - name: your-image-name +## image: your-image +## imagePullPolicy: Always +## command: ['sh', '-c', 'echo "hello world"'] +## +initContainers: [] +## @param sidecars Add additional sidecar containers to the Logstash pod(s) +## e.g: +## sidecars: +## - name: your-image-name +## image: your-image +## imagePullPolicy: Always +## ports: +## - name: portname +## containerPort: 1234 +## +sidecars: [] +## @param replicaCount Number of Logstash replicas to deploy +## +replicaCount: 1 +## @param updateStrategy.type Update strategy type (`RollingUpdate`, or `OnDelete`) +## ref: https://kubernetes.io/docs/tutorials/stateful-application/basic-stateful-set/#updating-statefulsets +## +updateStrategy: + type: RollingUpdate +## @param podManagementPolicy Pod management policy +## https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#pod-management-policies +## +podManagementPolicy: OrderedReady +## @param podAnnotations Pod annotations +## Ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ +## +podAnnotations: {} +## @param podLabels Extra labels for Logstash pods +## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ +## +podLabels: {} +## @param podAffinityPreset Pod affinity preset. Ignored if `affinity` is set. Allowed values: `soft` or `hard` +## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity +## +podAffinityPreset: "" +## @param podAntiAffinityPreset Pod anti-affinity preset. Ignored if `affinity` is set. Allowed values: `soft` or `hard` +## Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity +## +podAntiAffinityPreset: soft +## Node affinity preset +## Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity +## +nodeAffinityPreset: + ## @param nodeAffinityPreset.type Node affinity preset type. Ignored if `affinity` is set. Allowed values: `soft` or `hard` + ## + type: "" + ## @param nodeAffinityPreset.key Node label key to match. Ignored if `affinity` is set. + ## E.g. + ## key: "kubernetes.io/e2e-az-name" + ## + key: "" + ## @param nodeAffinityPreset.values Node label values to match. Ignored if `affinity` is set. + ## E.g. + ## values: + ## - e2e-az1 + ## - e2e-az2 + ## + values: [] +## @param affinity Affinity for pod assignment +## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity +## Note: podAffinityPreset, podAntiAffinityPreset, and nodeAffinityPreset will be ignored when it's set +## +affinity: {} +## @param nodeSelector Node labels for pod assignment +## ref: https://kubernetes.io/docs/user-guide/node-selection/ +## +nodeSelector: {} +## @param tolerations Tolerations for pod assignment +## ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ +## +tolerations: [] +## @param priorityClassName Pod priority +## ref: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/ +## +priorityClassName: "" +## @param schedulerName Name of the k8s scheduler (other than default) +## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ +## +schedulerName: "" +## @param terminationGracePeriodSeconds In seconds, time the given to the Logstash pod needs to terminate gracefully +## ref: https://kubernetes.io/docs/concepts/workloads/pods/pod/#termination-of-pods +## +terminationGracePeriodSeconds: "" +## @param topologySpreadConstraints Topology Spread Constraints for pod assignment +## https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ +## The value is evaluated as a template +## +topologySpreadConstraints: [] + +## K8s Security Context for Logstash pods +## Configure Pods Security Context +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod +## @param podSecurityContext.enabled Enabled Logstash pods' Security Context +## @param podSecurityContext.fsGroup Set Logstash pod's Security Context fsGroup +## +podSecurityContext: + enabled: true + fsGroup: 1001 +## Configure Container Security Context +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod +## @param containerSecurityContext.enabled Enabled Logstash containers' Security Context +## @param containerSecurityContext.runAsUser Set Logstash containers' Security Context runAsUser +## @param containerSecurityContext.runAsNonRoot Set Logstash container's Security Context runAsNonRoot +## +containerSecurityContext: + enabled: true + runAsUser: 1001 + runAsNonRoot: true +## @param command Override default container command (useful when using custom images) +## +command: [] +## @param args Override default container args (useful when using custom images) +## +args: [] +## @param lifecycleHooks for the Logstash container(s) to automate configuration before or after startup +## +lifecycleHooks: {} +## Logstash containers' resource requests and limits +## ref: https://kubernetes.io/docs/user-guide/compute-resources/ +## We usually recommend not to specify default resources and to leave this as a conscious +## choice for the user. This also increases chances charts run on environments with little +## resources, such as Minikube. If you do want to specify resources, uncomment the following +## lines, adjust them as necessary, and remove the curly braces after 'resources:'. +## @param resources.limits The resources limits for the Logstash container +## @param resources.requests The requested resources for the Logstash container +## +resources: + ## Example: + ## limits: + ## cpu: 100m + ## memory: 128Mi + limits: {} + ## Examples: + ## requests: + ## cpu: 100m + ## memory: 128Mi + requests: {} +## Configure extra options for Logstash containers' liveness, readiness and startup probes +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes +## @param startupProbe.enabled Enable startupProbe +## @param startupProbe.initialDelaySeconds Initial delay seconds for startupProbe +## @param startupProbe.periodSeconds Period seconds for startupProbe +## @param startupProbe.timeoutSeconds Timeout seconds for startupProbe +## @param startupProbe.failureThreshold Failure threshold for startupProbe +## @param startupProbe.successThreshold Success threshold for startupProbe +## +startupProbe: + enabled: false + initialDelaySeconds: 60 + periodSeconds: 10 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 6 +## @param livenessProbe.enabled Enable livenessProbe +## @param livenessProbe.initialDelaySeconds Initial delay seconds for livenessProbe +## @param livenessProbe.periodSeconds Period seconds for livenessProbe +## @param livenessProbe.timeoutSeconds Timeout seconds for livenessProbe +## @param livenessProbe.failureThreshold Failure threshold for livenessProbe +## @param livenessProbe.successThreshold Success threshold for livenessProbe +## +livenessProbe: + enabled: true + initialDelaySeconds: 60 + periodSeconds: 10 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 6 +## @param readinessProbe.enabled Enable readinessProbe +## @param readinessProbe.initialDelaySeconds Initial delay seconds for readinessProbe +## @param readinessProbe.periodSeconds Period seconds for readinessProbe +## @param readinessProbe.timeoutSeconds Timeout seconds for readinessProbe +## @param readinessProbe.failureThreshold Failure threshold for readinessProbe +## @param readinessProbe.successThreshold Success threshold for readinessProbe +## +readinessProbe: + enabled: true + initialDelaySeconds: 60 + periodSeconds: 10 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 6 +## @param customStartupProbe Custom startup probe for the Web component +## +customStartupProbe: {} +## @param customLivenessProbe Custom liveness probe for the Web component +## +customLivenessProbe: {} +## @param customReadinessProbe Custom readiness probe for the Web component +## +customReadinessProbe: {} +## Service parameters +## +service: + ## @param service.type Kubernetes service type (`ClusterIP`, `NodePort`, or `LoadBalancer`) + ## + type: ClusterIP + ## @param service.ports [array] Logstash service ports (evaluated as a template) + ## + ports: + - name: http + port: 8080 + targetPort: http + protocol: TCP + ## - name: syslog-udp + ## port: 1514 + ## targetPort: syslog-udp + ## protocol: UDP + ## - name: syslog-tcp + ## port: 1514 + ## targetPort: syslog-tcp + ## protocol: TCP + ## + ## @param service.loadBalancerIP loadBalancerIP if service type is `LoadBalancer` + ## + loadBalancerIP: "" + ## @param service.loadBalancerSourceRanges Addresses that are allowed when service is LoadBalancer + ## https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/#restrict-access-for-loadbalancer-service + ## e.g: + ## loadBalancerSourceRanges: + ## - 10.10.10.0/24 + ## + loadBalancerSourceRanges: [] + ## @param service.externalTrafficPolicy External traffic policy, configure to Local to preserve client source IP when using an external loadBalancer + ## ref https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip + ## + externalTrafficPolicy: "" + ## @param service.clusterIP Static clusterIP or None for headless services + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#choosing-your-own-ip-address + ## e.g: + ## clusterIP: None + ## + clusterIP: "" + ## @param service.annotations Annotations for Logstash service + ## + annotations: {} + ## @param service.sessionAffinity Session Affinity for Kubernetes service, can be "None" or "ClientIP" + ## If "ClientIP", consecutive client requests will be directed to the same Pod + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies + ## + sessionAffinity: None + ## @param service.sessionAffinityConfig Additional settings for the sessionAffinity + ## sessionAffinityConfig: + ## clientIP: + ## timeoutSeconds: 300 + ## + sessionAffinityConfig: {} +## Persistence parameters +## +persistence: + ## @param persistence.enabled Enable Logstash data persistence using PVC + ## + enabled: false + ## @param persistence.existingClaim A manually managed Persistent Volume and Claim + ## If defined, PVC must be created manually before volume will be bound + ## The value is evaluated as a template + ## + existingClaim: "" + ## @param persistence.storageClass PVC Storage Class for Logstash data volume + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. + ## + storageClass: "" + ## @param persistence.accessModes PVC Access Mode for Logstash data volume + ## + accessModes: + - ReadWriteOnce + ## @param persistence.size PVC Storage Request for Logstash data volume + ## + size: 2Gi + ## @param persistence.annotations Annotations for the PVC + ## + annotations: {} + ## @param persistence.mountPath Mount path of the Logstash data volume + ## + mountPath: /bitnami/logstash/data + ## @param persistence.selector Selector to match an existing Persistent Volume for WordPress data PVC + ## If set, the PVC can't have a PV dynamically provisioned for it + ## E.g. + ## selector: + ## matchLabels: + ## app: my-app + ## + selector: {} +## Init Container parameters +## Change the owner and group of the persistent volume(s) mountpoint(s) to 'runAsUser:fsGroup' on each component +## values from the securityContext section of the component +## +volumePermissions: + ## @param volumePermissions.enabled Enable init container that changes the owner and group of the persistent volume(s) mountpoint to `runAsUser:fsGroup` + ## + enabled: false + ## The security context for the volumePermissions init container + ## @param volumePermissions.securityContext.runAsUser User ID for the volumePermissions init container + ## + securityContext: + runAsUser: 0 + ## @param volumePermissions.image.registry Init container volume-permissions image registry + ## @param volumePermissions.image.repository Init container volume-permissions image repository + ## @param volumePermissions.image.tag Init container volume-permissions image tag (immutable tags are recommended) + ## @param volumePermissions.image.digest Init container volume-permissions image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag + ## @param volumePermissions.image.pullPolicy Init container volume-permissions image pull policy + ## @param volumePermissions.image.pullSecrets Specify docker-registry secret names as an array + ## + image: + registry: docker.io + repository: bitnami/bitnami-shell + tag: 11-debian-11-r90 + digest: "" + ## Specify a imagePullPolicy + ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' + ## ref: https://kubernetes.io/docs/user-guide/images/#pre-pulling-images + ## + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets (secrets must be manually created in the namespace) + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## Example: + ## pullSecrets: + ## - myRegistryKeySecretName + ## + pullSecrets: [] + ## Init Container resource requests and limits + ## ref: https://kubernetes.io/docs/user-guide/compute-resources/ + ## We usually recommend not to specify default resources and to leave this as a conscious + ## choice for the user. This also increases chances charts run on environments with little + ## resources, such as Minikube. If you do want to specify resources, uncomment the following + ## lines, adjust them as necessary, and remove the curly braces after 'resources:'. + ## @param volumePermissions.resources.limits Init container volume-permissions resource limits + ## @param volumePermissions.resources.requests Init container volume-permissions resource requests + ## + resources: + ## Example: + ## limits: + ## cpu: 100m + ## memory: 128Mi + limits: {} + ## Examples: + ## requests: + ## cpu: 100m + ## memory: 128Mi + requests: {} +## Configure the ingress resource that allows you to access the +## Logstash installation. Set up the URL +## ref: https://kubernetes.io/docs/user-guide/ingress/ +## +ingress: + ## @param ingress.enabled Enable ingress controller resource + ## + enabled: false + ## @param ingress.selfSigned Create a TLS secret for this ingress record using self-signed certificates generated by Helm + ## + selfSigned: false + ## @param ingress.pathType Ingress Path type + ## + pathType: ImplementationSpecific + ## @param ingress.apiVersion Override API Version (automatically detected if not set) + ## + apiVersion: "" + ## @param ingress.hostname Default host for the ingress resource + ## + hostname: logstash.local + ## @param ingress.path The Path to Logstash. You may need to set this to '/*' in order to use this with ALB ingress controllers. + ## + path: / + ## @param ingress.annotations Additional annotations for the Ingress resource. To enable certificate autogeneration, place here your cert-manager annotations. + ## For a full list of possible ingress annotations, please see + ## ref: https://github.com/kubernetes/ingress-nginx/blob/master/docs/user-guide/nginx-configuration/annotations.md + ## Use this parameter to set the required annotations for cert-manager, see + ## ref: https://cert-manager.io/docs/usage/ingress/#supported-annotations + ## + ## e.g: + ## annotations: + ## kubernetes.io/ingress.class: nginx + ## cert-manager.io/cluster-issuer: cluster-issuer-name + ## + annotations: {} + ## @param ingress.tls Enable TLS configuration for the hostname defined at ingress.hostname parameter + ## TLS certificates will be retrieved from a TLS secret with name: {{- printf "%s-tls" .Values.ingress.hostname }} + ## You can use the ingress.secrets parameter to create this TLS secret or relay on cert-manager to create it + ## + tls: false + ## @param ingress.extraHosts The list of additional hostnames to be covered with this ingress record. + ## Most likely the hostname above will be enough, but in the event more hosts are needed, this is an array + ## extraHosts: + ## - name: logstash.local + ## path: / + ## + extraHosts: [] + ## @param ingress.extraPaths Any additional arbitrary paths that may need to be added to the ingress under the main host. + ## For example: The ALB ingress controller requires a special rule for handling SSL redirection. + ## extraPaths: + ## - path: /* + ## backend: + ## serviceName: ssl-redirect + ## servicePort: use-annotation + ## + extraPaths: [] + ## @param ingress.extraRules The list of additional rules to be added to this ingress record. Evaluated as a template + ## Useful when looking for additional customization, such as using different backend + ## + extraRules: [] + ## @param ingress.extraTls The tls configuration for additional hostnames to be covered with this ingress record. + ## see: https://kubernetes.io/docs/concepts/services-networking/ingress/#tls + ## extraTls: + ## - hosts: + ## - logstash.local + ## secretName: logstash.local-tls + ## + extraTls: [] + ## @param ingress.secrets If you're providing your own certificates, please use this to add the certificates as secrets + ## key and certificate should start with -----BEGIN CERTIFICATE----- or + ## -----BEGIN RSA PRIVATE KEY----- + ## + ## name should line up with a tlsSecret set further up + ## If you're using cert-manager, this is unneeded, as it will create the secret for you if it is not set + ## + ## It is also possible to create and manage the certificates outside of this helm chart + ## Please see README.md for more information + ## + ## secrets: + ## - name: logstash.local-tls + ## key: + ## certificate: + ## + secrets: [] + ## @param ingress.ingressClassName IngressClass that will be be used to implement the Ingress (Kubernetes 1.18+) + ## This is supported in Kubernetes 1.18+ and required if you have more than one IngressClass marked as the default for your cluster . + ## ref: https://kubernetes.io/blog/2020/04/02/improvements-to-the-ingress-api-in-kubernetes-1.18/ + ## + ingressClassName: "" + +## Pod disruption budget configuration +## ref: https://kubernetes.io/docs/concepts/workloads/pods/disruptions/ +## @param pdb.create If true, create a pod disruption budget for pods. +## @param pdb.minAvailable Minimum number / percentage of pods that should remain scheduled +## @param pdb.maxUnavailable Maximum number / percentage of pods that may be made unavailable +## +pdb: + create: false + minAvailable: 1 + maxUnavailable: "" diff --git a/knowlg-automation/helm_charts/neo4j/Chart.yaml b/knowlg-automation/helm_charts/neo4j/Chart.yaml new file mode 100644 index 000000000..8c0cc8611 --- /dev/null +++ b/knowlg-automation/helm_charts/neo4j/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: neo4j +version: 0.1.0 \ No newline at end of file diff --git a/knowlg-automation/helm_charts/neo4j/templates/deployment.yaml b/knowlg-automation/helm_charts/neo4j/templates/deployment.yaml new file mode 100644 index 000000000..72a40f869 --- /dev/null +++ b/knowlg-automation/helm_charts/neo4j/templates/deployment.yaml @@ -0,0 +1,51 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Chart.Name }} + namespace: {{ .Values.namespace }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ .Chart.Name }} + template: + metadata: + labels: + app: {{ .Chart.Name }} + spec: + containers: + - name: {{ .Chart.Name }} + image: {{ .Values.image }} + imagePullPolicy: Always + env: ## Env variables to pass + - name: NEO4J_dbms_security_auth__enabled + value: "false" + ports: + - containerPort: 7474 + name: cypher-port + - containerPort: 7687 + name: bolt-port-1 + - containerPort: 8687 + name: bolt-port-2 + +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ .Chart.Name }}-db + namespace: {{ .Values.namespace }} + labels: + app: {{ .Chart.Name }} +spec: + ports: + - name: cypher-shell-port + port: 7474 + targetPort: 7474 + - name: bolt-port-1 + port: 7687 + targetPort: 7687 + - name: bolt-port-2 + port: 8687 + targetPort: 8687 + selector: + app: {{ .Chart.Name }} \ No newline at end of file diff --git a/knowlg-automation/helm_charts/neo4j/values.yaml b/knowlg-automation/helm_charts/neo4j/values.yaml new file mode 100644 index 000000000..d96c03466 --- /dev/null +++ b/knowlg-automation/helm_charts/neo4j/values.yaml @@ -0,0 +1,18 @@ +namespace: knowlg-db +replicaCount: 1 +image: neo4j:3.3.0 +authEnabled: false +network: + port: 7474 +neo4j: + name: neo4j + resources: + cpu: "0.5" + memory: "2Gi" + +volumes: + data: + mode: defaultStorageClass + defaultStorageClass: + requests: + storage: 2Gi \ No newline at end of file diff --git a/knowlg-automation/helm_charts/redis/Chart.yaml b/knowlg-automation/helm_charts/redis/Chart.yaml new file mode 100644 index 000000000..554bef77b --- /dev/null +++ b/knowlg-automation/helm_charts/redis/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: redis +version: 0.1.0 \ No newline at end of file diff --git a/knowlg-automation/helm_charts/redis/templates/deployment.yaml b/knowlg-automation/helm_charts/redis/templates/deployment.yaml new file mode 100644 index 000000000..d57727710 --- /dev/null +++ b/knowlg-automation/helm_charts/redis/templates/deployment.yaml @@ -0,0 +1,34 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Chart.Name }} + namespace: {{ .Values.namespace }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ .Chart.Name }} + template: + metadata: + labels: + app: {{ .Chart.Name }} + spec: + containers: + - name: {{ .Chart.Name }} + image: redis + ports: + - containerPort: {{ .Values.network.port }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ .Chart.Name }}-db + namespace: {{ .Values.namespace }} + labels: + app: {{ .Chart.Name }} +spec: + ports: + - port: {{ .Values.network.port }} + targetPort: {{ .Values.network.targetport }} + selector: + app: {{ .Chart.Name }} \ No newline at end of file diff --git a/knowlg-automation/helm_charts/redis/values.yaml b/knowlg-automation/helm_charts/redis/values.yaml new file mode 100644 index 000000000..209d4b932 --- /dev/null +++ b/knowlg-automation/helm_charts/redis/values.yaml @@ -0,0 +1,5 @@ +namespace: knowlg-db +replicaCount: 1 +network: + port: 6379 + targetport: 6379 \ No newline at end of file diff --git a/knowlg-automation/helm_charts/superset-helm/Chart.lock b/knowlg-automation/helm_charts/superset-helm/Chart.lock new file mode 100644 index 000000000..bfd0d0e68 --- /dev/null +++ b/knowlg-automation/helm_charts/superset-helm/Chart.lock @@ -0,0 +1,9 @@ +dependencies: +- name: redis + repository: https://charts.bitnami.com/bitnami + version: 12.3.3 +- name: postgresql + repository: https://charts.bitnami.com/bitnami + version: 10.2.0 +digest: sha256:51635f1352f64f364f1454e0c26ce3b22f7d85e5a7d3e940f8f8d0aed0f82534 +generated: "2021-08-17T15:20:27.029281275+05:30" diff --git a/knowlg-automation/helm_charts/superset-helm/Chart.yaml b/knowlg-automation/helm_charts/superset-helm/Chart.yaml new file mode 100644 index 000000000..177da04a3 --- /dev/null +++ b/knowlg-automation/helm_charts/superset-helm/Chart.yaml @@ -0,0 +1,37 @@ +# apiVersion: v2 +# name: superset +# description: A Helm chart for Apache Superset + +# # A chart can be either an 'application' or a 'library' chart. +# # +# # Application charts are a collection of templates that can be packaged into versioned archives +# # to be deployed. +# # +# # Library charts provide useful utilities or functions for the chart developer. They're included as +# # a dependency of application charts to inject those utilities and functions into the rendering +# # pipeline. Library charts do not define any templates and therefore cannot be deployed. +# type: application + +# # This is the chart version. This version number should be incremented each time you make changes +# # to the chart and its templates, including the app version. +# version: 1.0.0 + +# # This is the version number of the application being deployed. This version number should be +# # incremented each time you make changes to the application. +# appVersion: 1.20 + + +apiVersion: v2 +appVersion: "1.0" +description: Apache Superset is a modern, enterprise-ready business intelligence web application +name: superset +maintainers: + - name: craig-rueda + email: craig@craigrueda.com + url: https://github.com/craig-rueda +version: 0.3.5 +dependencies: +- name: redis + version: 12.3.3 + repository: https://charts.bitnami.com/bitnami + condition: redis.enabled diff --git a/knowlg-automation/helm_charts/superset-helm/LICENSE b/knowlg-automation/helm_charts/superset-helm/LICENSE new file mode 100644 index 000000000..abbe4f211 --- /dev/null +++ b/knowlg-automation/helm_charts/superset-helm/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Anand Parthasarathy + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/knowlg-automation/helm_charts/superset-helm/README.md b/knowlg-automation/helm_charts/superset-helm/README.md new file mode 100644 index 000000000..af705a8e4 --- /dev/null +++ b/knowlg-automation/helm_charts/superset-helm/README.md @@ -0,0 +1,2 @@ +# superset-helm +Helm chart for Apache Superset diff --git a/knowlg-automation/helm_charts/superset-helm/charts/redis-12.3.3.tgz b/knowlg-automation/helm_charts/superset-helm/charts/redis-12.3.3.tgz new file mode 100644 index 0000000000000000000000000000000000000000..e98483df9b9557e8595b627fc48657500838506c GIT binary patch literal 66002 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

~JsQ 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 DPb|^@ literal 0 HcmV?d00001 diff --git a/knowlg-automation/helm_charts/superset-helm/templates/_helpers.tpl b/knowlg-automation/helm_charts/superset-helm/templates/_helpers.tpl new file mode 100644 index 000000000..78840f449 --- /dev/null +++ b/knowlg-automation/helm_charts/superset-helm/templates/_helpers.tpl @@ -0,0 +1,105 @@ +{{/* + + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/}} +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "superset.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "superset.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "superset.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{- define "superset-config" }} +import os +from cachelib.redis import RedisCache + +def env(key, default=None): + return os.getenv(key, default) + +MAPBOX_API_KEY = env('MAPBOX_API_KEY', '') +CACHE_CONFIG = { + 'CACHE_TYPE': 'redis', + 'CACHE_DEFAULT_TIMEOUT': 300, + 'CACHE_KEY_PREFIX': 'superset_', + 'CACHE_REDIS_HOST': env('REDIS_HOST'), + 'CACHE_REDIS_PORT': env('REDIS_PORT'), + 'CACHE_REDIS_PASSWORD': env('REDIS_PASSWORD'), + 'CACHE_REDIS_DB': env('REDIS_DB', 1), +} +DATA_CACHE_CONFIG = CACHE_CONFIG + +SQLALCHEMY_DATABASE_URI = f"postgresql+psycopg2://{env('DB_USER')}:{env('DB_PASS')}@{env('DB_HOST')}:{env('DB_PORT')}/{env('DB_NAME')}" +SQLALCHEMY_TRACK_MODIFICATIONS = True +SECRET_KEY = env('SECRET_KEY', 'thisISaSECRET_1234') + +# Flask-WTF flag for CSRF +WTF_CSRF_ENABLED = True +# Add endpoints that need to be exempt from CSRF protection +WTF_CSRF_EXEMPT_LIST = [] +# A CSRF token that expires in 1 year +WTF_CSRF_TIME_LIMIT = 60 * 60 * 24 * 365 +class CeleryConfig(object): + BROKER_URL = f"redis://{env('REDIS_HOST')}:{env('REDIS_PORT')}/0" + CELERY_IMPORTS = ('superset.sql_lab', ) + CELERY_RESULT_BACKEND = f"redis://{env('REDIS_HOST')}:{env('REDIS_PORT')}/0" + CELERY_ANNOTATIONS = {'tasks.add': {'rate_limit': '10/s'}} + +CELERY_CONFIG = CeleryConfig +RESULTS_BACKEND = RedisCache( + host=env('REDIS_HOST'), + port=env('REDIS_PORT'), + key_prefix='superset_results' +) + +{{ if .Values.configOverrides }} +{{- $oauth_enabled := .Values.oauth_enabled -}} +# Overrides +{{- range $key, $value := .Values.configOverrides }} +{{- if or (ne $key "oauth") (default $oauth_enabled false) }} +# {{ $key }} +{{ tpl $value $ }} +{{- end }} +{{- end }} +{{- end }} + +{{- end }} diff --git a/knowlg-automation/helm_charts/superset-helm/templates/configmap-superset.yaml b/knowlg-automation/helm_charts/superset-helm/templates/configmap-superset.yaml new file mode 100644 index 000000000..a7d7b0933 --- /dev/null +++ b/knowlg-automation/helm_charts/superset-helm/templates/configmap-superset.yaml @@ -0,0 +1,32 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +{{- if .Values.extraConfigs }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "superset.fullname" . }}-extra-config + labels: + app: {{ template "superset.name" . }} + chart: {{ template "superset.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +data: +{{- range $path, $config := .Values.extraConfigs }} + {{ $path }}: | +{{- tpl $config $ | nindent 4 -}} +{{- end -}} +{{- end -}} diff --git a/knowlg-automation/helm_charts/superset-helm/templates/deployment.yaml b/knowlg-automation/helm_charts/superset-helm/templates/deployment.yaml new file mode 100644 index 000000000..d1c08f02c --- /dev/null +++ b/knowlg-automation/helm_charts/superset-helm/templates/deployment.yaml @@ -0,0 +1,128 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ template "superset.fullname" . }} + labels: + app: {{ template "superset.name" . }} + chart: {{ template "superset.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +{{- if .Values.supersetNode.deploymentAnnotations }} + annotations: + {{ toYaml .Values.supersetNode.deploymentAnnotations | nindent 4 }} +{{- end }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ template "superset.name" . }} + release: {{ .Release.Name }} + template: + metadata: + annotations: + # Force reload on config changes + checksum/superset_config.py: {{ include "superset-config" . | sha256sum }} + checksum/superset_init.sh: {{ tpl .Values.init.initscript . | sha256sum }} + checksum/connections: {{ .Values.supersetNode.connections | toYaml | sha256sum }} + checksum/extraConfigs: {{ .Values.extraConfigs | toYaml | sha256sum }} + checksum/extraSecrets: {{ .Values.extraSecrets | toYaml | sha256sum }} + checksum/extraSecretEnv: {{ .Values.extraSecretEnv | toYaml | sha256sum }} + checksum/configOverrides: {{ .Values.configOverrides | toYaml | sha256sum }} + {{- if .Values.supersetNode.forceReload }} + # Optionally force the thing to reload + force-reload: {{ randAlphaNum 5 | quote }} + {{- end }} + {{- if .Values.supersetNode.podAnnotations }} + {{ toYaml .Values.supersetNode.podAnnotations | nindent 8 }} + {{- end }} + labels: + app: {{ template "superset.name" . }} + release: {{ .Release.Name }} + spec: + {{- if .Values.serviceAccountName }} + serviceAccountName: {{ .Values.serviceAccountName }} + {{- end }} + securityContext: + runAsUser: {{ .Values.runAsUser }} + {{- if .Values.supersetNode.initContainers }} + initContainers: + {{- tpl (toYaml .Values.supersetNode.initContainers) . | nindent 6 }} + {{- end }} + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + command: {{ tpl (toJson .Values.supersetNode.command) . }} + env: + - name: "SUPERSET_PORT" + value: {{ .Values.service.port | quote}} + {{- if .Values.extraEnv }} + {{- range $key, $value := .Values.extraEnv }} + - name: {{ $key | quote}} + value: {{ $value | quote }} + {{- end }} + {{- end }} + envFrom: + - secretRef: + name: {{ tpl .Values.envFromSecret . | quote }} + {{- range .Values.envFromSecrets }} + - secretRef: + name: {{ tpl . $ | quote }} + {{- end }} + volumeMounts: + - name: superset-config + mountPath: {{ .Values.configMountPath | quote }} + readOnly: true + {{- if .Values.extraConfigs }} + - name: superset-extra-config + mountPath: {{ .Values.extraConfigMountPath | quote }} + readOnly: true + {{- end }} + ports: + - name: http + containerPort: {{ .Values.service.port }} + protocol: TCP + resources: +{{ toYaml .Values.resources | indent 12 }} + {{- with .Values.nodeSelector }} + nodeSelector: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: +{{ toYaml . | indent 8 }} + {{- end }} +{{- if .Values.imagePullSecrets }} + imagePullSecrets: +{{ toYaml .Values.imagePullSecrets | indent 8 }} + {{- end }} + + volumes: + - name: superset-config + secret: + secretName: {{ tpl .Values.configFromSecret . }} + {{- if .Values.extraConfigs }} + - name: superset-extra-config + configMap: + name: {{ template "superset.fullname" . }}-extra-config + {{- end }} diff --git a/knowlg-automation/helm_charts/superset-helm/templates/ingress.yaml b/knowlg-automation/helm_charts/superset-helm/templates/ingress.yaml new file mode 100644 index 000000000..7c1dd72f0 --- /dev/null +++ b/knowlg-automation/helm_charts/superset-helm/templates/ingress.yaml @@ -0,0 +1,56 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +{{ if .Values.ingress.enabled -}} +{{- $fullName := include "superset.fullname" . -}} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + app: {{ template "superset.name" . }} + chart: {{ template "superset.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +{{- with .Values.ingress.annotations }} + annotations: +{{ toYaml . | indent 4 }} +{{- end }} +spec: +{{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} +{{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ . }} + http: + paths: + - path: {{ $.Values.ingress.path }} + pathType: {{ $.Values.ingress.pathType }} + backend: + service: + name: {{ $fullName }} + port: + name: http + {{- end }} +{{- end }} diff --git a/knowlg-automation/helm_charts/superset-helm/templates/init-job.yaml b/knowlg-automation/helm_charts/superset-helm/templates/init-job.yaml new file mode 100644 index 000000000..45ab2235b --- /dev/null +++ b/knowlg-automation/helm_charts/superset-helm/templates/init-job.yaml @@ -0,0 +1,80 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +{{- if .Values.init.enabled }} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ template "superset.name" . }}-init-db + annotations: + "helm.sh/hook": post-install,post-upgrade + "helm.sh/hook-delete-policy": "before-hook-creation" +spec: + template: + metadata: + name: {{ template "superset.name" . }}-init-db + spec: + securityContext: + runAsUser: {{ .Values.runAsUser }} + {{- if .Values.init.initContainers }} + initContainers: + {{- tpl (toYaml .Values.init.initContainers) . | nindent 6 }} + {{- end }} + containers: + - name: {{ template "superset.name" . }}-init-db + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + {{ if .Values.extraEnv }} + env: + {{- range $key, $value := .Values.extraEnv }} + - name: {{ $key | quote }} + value: {{ $value | quote }} + {{- end }} + {{- end }} + envFrom: + - secretRef: + name: {{ tpl .Values.envFromSecret . }} + {{- range .Values.envFromSecrets }} + - secretRef: + name: {{ tpl . $ }} + {{- end }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + volumeMounts: + - name: superset-config + mountPath: {{ .Values.configMountPath | quote }} + readOnly: true + {{- if .Values.extraConfigs }} + - name: superset-extra-config + mountPath: {{ .Values.extraConfigMountPath | quote }} + readOnly: true + {{- end }} + command: {{ tpl (toJson .Values.init.command) . }} + resources: +{{ toYaml .Values.init.resources | indent 10 }} +{{- if .Values.imagePullSecrets }} + imagePullSecrets: +{{ toYaml .Values.imagePullSecrets | indent 8 }} + {{- end }} + volumes: + - name: superset-config + secret: + secretName: {{ tpl .Values.configFromSecret . }} + {{- if .Values.extraConfigs }} + - name: superset-extra-config + configMap: + name: {{ template "superset.fullname" . }}-extra-config + {{- end }} + restartPolicy: Never +{{- end }} diff --git a/knowlg-automation/helm_charts/superset-helm/templates/secret-env.yaml b/knowlg-automation/helm_charts/superset-helm/templates/secret-env.yaml new file mode 100644 index 000000000..278ecc401 --- /dev/null +++ b/knowlg-automation/helm_charts/superset-helm/templates/secret-env.yaml @@ -0,0 +1,39 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "superset.fullname" . }}-env + labels: + app: {{ template "superset.fullname" . }} + chart: {{ template "superset.chart" . }} + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" +type: Opaque +stringData: + REDIS_HOST: {{ tpl .Values.supersetNode.connections.redis_host . | quote }} + REDIS_PORT: {{ .Values.supersetNode.connections.redis_port | quote }} + DB_HOST: {{ tpl .Values.supersetNode.connections.db_host . | quote }} + DB_PORT: {{ .Values.supersetNode.connections.db_port | quote }} + DB_USER: {{ .Values.supersetNode.connections.db_user | quote }} + DB_PASS: {{ .Values.supersetNode.connections.db_pass | quote }} + DB_NAME: {{ .Values.supersetNode.connections.db_name | quote }} + {{- if .Values.extraSecretEnv }} + {{- range $key, $value := .Values.extraSecretEnv }} + {{ $key }}: {{ $value | quote }} + {{- end }} + {{- end }} diff --git a/knowlg-automation/helm_charts/superset-helm/templates/secret-superset-config.yaml b/knowlg-automation/helm_charts/superset-helm/templates/secret-superset-config.yaml new file mode 100644 index 000000000..ddf0befcd --- /dev/null +++ b/knowlg-automation/helm_charts/superset-helm/templates/secret-superset-config.yaml @@ -0,0 +1,40 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "superset.fullname" . }}-config + labels: + app: {{ template "superset.fullname" . }} + chart: {{ template "superset.chart" . }} + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" +type: Opaque +stringData: + superset_config.py: | +{{- include "superset-config" . | nindent 4 }} + superset_init.sh: | +{{- tpl .Values.init.initscript . | nindent 4 }} + superset_bootstrap.sh: | +{{- tpl .Values.bootstrapScript . | nindent 4 }} + +{{- if .Values.extraSecrets }} +{{- range $path, $config := .Values.extraSecrets }} + {{ $path }}: | +{{- tpl $config $ | nindent 4 -}} +{{- end -}} +{{- end -}} diff --git a/knowlg-automation/helm_charts/superset-helm/templates/service.yaml b/knowlg-automation/helm_charts/superset-helm/templates/service.yaml new file mode 100644 index 000000000..0124ad2a9 --- /dev/null +++ b/knowlg-automation/helm_charts/superset-helm/templates/service.yaml @@ -0,0 +1,42 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +apiVersion: v1 +kind: Service +metadata: + name: {{ template "superset.fullname" . }} + labels: + app: {{ template "superset.name" . }} + chart: {{ template "superset.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +{{- with .Values.service.annotations }} + annotations: +{{- toYaml . | nindent 4 }} +{{- end }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + app: {{ template "superset.name" . }} + release: {{ .Release.Name }} + {{- if .Values.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.service.loadBalancerIP }} + {{- end }} diff --git a/knowlg-automation/helm_charts/superset-helm/values.yaml b/knowlg-automation/helm_charts/superset-helm/values.yaml new file mode 100644 index 000000000..ab652a449 --- /dev/null +++ b/knowlg-automation/helm_charts/superset-helm/values.yaml @@ -0,0 +1,312 @@ +# global: +postgres: + adminUser: "postgres" + adminPassword: "postgres" + db_port: 5432 + superset: + db_name: "superset" + db_username: "superset" + db_password: "superset$123" + +replicaCount: 1 +oauth_enabled: False + +adminUser: + username: "admin" + firstname: "Superset" + lastname: "Admin" + email: "admin@superset.com" + password: "admin123" + +oauth: + enabled: false + client_id: "client_id" + client_secret: "client_secret" + email_whitelist_regex: "" + whitelist_domain: "" + user_registration_role: "Gamma" + +runAsUser: 1000 + +# Install additional packages and do any other bootstrap configuration in this script +# For production clusters it's recommended to build own image with this step done in CI +bootstrapScript: | + #!/bin/bash + #rm -rf /var/lib/apt/lists/* && \ + pip install \ + sqlalchemy==1.3.24 \ + psycopg2==2.8.5 \ + redis==3.2.1 && \ + if [ ! -f ~/bootstrap ]; then echo "Running Superset with uid {{ .Values.runAsUser }}" > ~/bootstrap; fi + +## The name of the secret which we will use to generate a superset_config.py file +## Note: this secret must have the key superset_config.py in it and can include other files as well +## +configFromSecret: '{{ template "superset.fullname" . }}-config' + +## The name of the secret which we will use to populate env vars in deployed pods +## This can be useful for secret keys, etc. +## +envFromSecret: '{{ template "superset.fullname" . }}-env' +## This can be a list of template strings +envFromSecrets: [] + +## Extra environment variables that will be passed into pods +## +extraEnv: {} + # Extend timeout to allow long running queries. + # GUNICORN_TIMEOUT: 300 + + + # OAUTH_HOME_DOMAIN: .. + # # If a whitelist is not set, any address that can use your OAuth2 endpoint will be able to login. + # # this includes any random Gmail address if your OAuth2 Web App is set to External. + # OAUTH_WHITELIST_REGEX: ... + +## Extra environment variables to pass as secrets +## +extraSecretEnv: {} + # MAPBOX_API_KEY: ... + # # Google API Keys: https://console.cloud.google.com/apis/credentials + # GOOGLE_KEY: ... + # GOOGLE_SECRET: ... + +extraConfigs: {} + # datasources-init.yaml: | + # databases: + # - allow_csv_upload: true + # allow_ctas: true + # allow_cvas: true + # database_name: example-db + # extra: "{\r\n \"metadata_params\": {},\r\n \"engine_params\": {},\r\n \"\ + # metadata_cache_timeout\": {},\r\n \"schemas_allowed_for_csv_upload\": []\r\n\ + # }" + # sqlalchemy_uri: example://example-db.local + # tables: [] + +extraSecrets: {} + +# A dictionary of overrides to append at the end of superset_config.py - the name does not matter +# WARNING: the order is not guaranteed +configOverrides: + enable_feature_flags: | + FEATURE_FLAGS = { + "DASHBOARD_NATIVE_FILTERS": True, + "DASHBOARD_CROSS_FILTERS": True, + "DASHBOARD_NATIVE_FILTERS_SET": True, + "ENABLE_TEMPLATE_PROCESSING": True, + } + + data_cache_config: | + DATA_CACHE_CONFIG = { + 'CACHE_TYPE': 'redis', + 'CACHE_DEFAULT_TIMEOUT': 600, + 'CACHE_KEY_PREFIX': 'superset_', + 'CACHE_REDIS_URL': 'redis://{{ template "superset.fullname" . }}-redis-headless:6379/1' + } + + sql_alchemy_config: | + SQLALCHEMY_DATABASE_URI = 'postgresql://{{ tpl .Values.postgres.superset.db_username . }}:{{ tpl .Values.postgres.superset.db_password . }}@postgresql-hl.postgresql.svc.cluster.local:5432/{{ tpl .Values.postgres.superset.db_name . }}' + SQLALCHEMY_TRACK_MODIFICATIONS = True + SECRET_KEY = 'thisISaSECRET_1234' + + #map_box_key: | + # MAPBOX_API_KEY='' + + oauth: | + from flask_appbuilder.security.manager import (AUTH_DB, AUTH_OAUTH) + AUTH_TYPE = AUTH_OAUTH + + OAUTH_PROVIDERS = [ + { + "name": "google", + "whitelist": [ "{{ .Values.oauth.email_whitelist_regex }}" ], + "icon": "fa-google", + "token_key": "access_token", + "remote_app": { + "client_id": "{{ .Values.oauth.client_id }}", + "client_secret": "{{ .Values.oauth.client_secret }}", + "api_base_url": "https://www.googleapis.com/oauth2/v2/", + "client_kwargs": {"scope": "email profile"}, + "request_token_url": None, + "access_token_url": "https://accounts.google.com/o/oauth2/token", + "authorize_url": "https://accounts.google.com/o/oauth2/auth", + "authorize_params": {"hd": "{{ .Values.oauth.whitelist_domain }}"} + } + } + ] + # Map Authlib roles to superset roles + AUTH_ROLE_ADMIN = 'Admin' + AUTH_ROLE_PUBLIC = 'Public' + # Will allow user self registration, allowing to create Flask users from Authorized User + AUTH_USER_REGISTRATION = True + # The default user self registration role + AUTH_USER_REGISTRATION_ROLE = "{{ oauth.user_registration_role }}" + +configMountPath: "/etc/superset" + +extraConfigMountPath: "/app/configs" + +image: + repository: amancevice/superset + tag: 1.5.0 + pullPolicy: Always + +imagePullSecrets: [] + + +service: + type: ClusterIP + port: 8088 + annotations: {} + +ingress: + enabled: false + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + ## Extend timeout to allow long running queries. + # nginx.ingress.kubernetes.io/proxy-connect-timeout: "300" + # nginx.ingress.kubernetes.io/proxy-read-timeout: "300" + # nginx.ingress.kubernetes.io/proxy-send-timeout: "300" + path: / + pathType: ImplementationSpecific + hosts: + - chart-example.local + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +## +## Superset node configuration +supersetNode: + connections: + redis_host: '{{ template "superset.fullname" . }}-redis-headless' + redis_port: "6379" + db_host: '{{ template "superset.fullname" . }}-postgres-headless' + db_port: "{{ .Values.postgres.db_port }}" + db_user: "{{ .Values.postgres.superset.db_username }}" + db_pass: "{{ .Values.postgres.superset.db_password }}" + db_name: "{{ .Values.postgres.superset.db_name }}" + forceReload: false # If true, forces deployment to reload on each upgrade + # initContainers: + # - name: wait-for-postgres + # image: busybox:latest + # imagePullPolicy: IfNotPresent + # envFrom: + # - secretRef: + # name: '{{ tpl .Values.envFromSecret . }}' + # command: [ "/bin/sh", "-c", "until nc -zv $DB_HOST $DB_PORT -w1; do echo 'waiting for db'; sleep 1; done" ] + ## Annotations to be added to supersetNode deployment + deploymentAnnotations: {} + ## Annotations to be added to supersetNode pods + podAnnotations: {} + +## +## Init job configuration +init: + # Configure resources + # Warning: fab command consumes a lot of ram and can + # cause the process to be killed due to OOM if it exceeds limit + resources: {} + # limits: + # cpu: + # memory: + # requests: + # cpu: + # memory: + command: + - "/bin/sh" + - "-c" + - ". {{ .Values.configMountPath }}/superset_bootstrap.sh; . {{ .Values.configMountPath }}/superset_init.sh" + enabled: true + createAdmin: true + # initContainers: + # - name: wait-for-postgres + # image: busybox:latest + # imagePullPolicy: IfNotPresent + # envFrom: + # - secretRef: + # name: '{{ tpl .Values.envFromSecret . }}' + # command: [ "/bin/sh", "-c", "until nc -zv $DB_HOST $DB_PORT -w1; do echo 'waiting for db'; sleep 1; done" ] + initscript: |- + #!/bin/sh + echo "Upgrading DB schema..." + superset db upgrade + echo "Initializing roles..." + superset init + {{ if .Values.init.createAdmin }} + echo "Creating admin user..." + superset fab create-admin \ + --username {{ .Values.adminUser.username }} \ + --firstname {{ .Values.adminUser.firstname }} \ + --lastname {{ .Values.adminUser.lastname }} \ + --email {{ .Values.adminUser.email }} \ + --password {{ .Values.adminUser.password }} \ + || true + {{ end }} + if [ -f "{{ .Values.extraConfigMountPath }}/import_datasources.yaml" ]; then + echo "Importing database connections.... " + superset import_datasources -p {{ .Values.extraConfigMountPath }}/import_datasources.yaml + fi + +## Configuration values for the Redis dependency. +## ref: https://github.com/kubernetes/charts/blob/master/stable/redis/README.md +redis: + ## + ## Use the redis chart dependency. + ## Set to false if bringing your own redis. + enabled: false + usePassword: false + ## + ## If you are bringing your own redis, you can set the host in redisHost. + ## redisHost: + ## + ## Redis password + ## + ## password: superset + ## + ## Master configuration + master: + ## + ## Image configuration + # image: + ## + ## docker registry secret names (list) + # pullSecrets: nil + ## + ## Configure persistance + persistence: + ## + ## Use a PVC to persist data. + enabled: false + ## + ## Persistant class + # storageClass: classname + ## + ## Access mode: + accessModes: + - ReadWriteOnce + ## + ## Disable cluster management by default. + cluster: + enabled: false + +nodeSelector: {} + +tolerations: [] + +affinity: {} diff --git a/knowlg-automation/helm_charts/taxonomy/Chart.yaml b/knowlg-automation/helm_charts/taxonomy/Chart.yaml new file mode 100644 index 000000000..8462df87c --- /dev/null +++ b/knowlg-automation/helm_charts/taxonomy/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: taxonomy +version: 0.1.0 diff --git a/knowlg-automation/helm_charts/taxonomy/taxonomy-service_application.conf b/knowlg-automation/helm_charts/taxonomy/taxonomy-service_application.conf new file mode 100644 index 000000000..b48fa7441 --- /dev/null +++ b/knowlg-automation/helm_charts/taxonomy/taxonomy-service_application.conf @@ -0,0 +1,399 @@ +# This is the main configuration file for the application. +# https://www.playframework.com/documentation/latest/ConfigFile +# ~~~~~ +# Play uses HOCON as its configuration file format. HOCON has a number +# of advantages over other config formats, but there are two things that +# can be used when modifying settings. +# +# You can include other configuration files in this main application.conf file: +#include "extra-config.conf" +# +# You can declare variables and substitute for them: +#mykey = ${some.value} +# +# And if an environment variable exists when there is no other substitution, then +# HOCON will fall back to substituting environment variable: +#mykey = ${JAVA_HOME} + +## Akka +# https://www.playframework.com/documentation/latest/ScalaAkka#Configuration +# https://www.playframework.com/documentation/latest/JavaAkka#Configuration +# ~~~~~ +# Play uses Akka internally and exposes Akka Streams and actors in Websockets and +# other streaming HTTP responses. +akka { + # "akka.log-config-on-start" is extraordinarly useful because it log the complete + # configuration at INFO level, including defaults and overrides, so it s worth + # putting at the very top. + # + # Put the following in your conf/logback.xml file: + # + # + # + # And then uncomment this line to debug the configuration. + # + #log-config-on-start = true + default-dispatcher { + # This will be used if you have set "executor = "fork-join-executor"" + fork-join-executor { + # Min number of threads to cap factor-based parallelism number to + parallelism-min = 8 + + # The parallelism factor is used to determine thread pool size using the + # following formula: ceil(available processors * factor). Resulting size + # is then bounded by the parallelism-min and parallelism-max values. + parallelism-factor = 32.0 + + # Max number of threads to cap factor-based parallelism number to + parallelism-max = 64 + + # Setting to "FIFO" to use queue like peeking mode which "poll" or "LIFO" to use stack + # like peeking mode which "pop". + task-peeking-mode = "FIFO" + } + } + actors-dispatcher { + type = "Dispatcher" + executor = "fork-join-executor" + fork-join-executor { + parallelism-min = 8 + parallelism-factor = 32.0 + parallelism-max = 64 + } + # Throughput for default Dispatcher, set to 1 for as fair as possible + throughput = 1 + } + actor { + deployment { + /contentActor + { + router = smallest-mailbox-pool + nr-of-instances = 10 + dispatcher = actors-dispatcher + } + } + } +} + +## Secret key +# http://www.playframework.com/documentation/latest/ApplicationSecret +# ~~~~~ +# The secret key is used to sign Play's session cookie. +# This must be changed for production, but we don't recommend you change it in this file. +play.http.secret.key="jd5ECm/o0BXwQCe8PfZY1NoUkB9HN41QjA80p22MKyRIcP5RW4qHw8sZztCzv87M" + +## Modules +# https://www.playframework.com/documentation/latest/Modules +# ~~~~~ +# Control which modules are loaded when Play starts. Note that modules are +# the replacement for "GlobalSettings", which are deprecated in 2.5.x. +# Please see https://www.playframework.com/documentation/latest/GlobalSettings +# for more information. +# +# You can also extend Play functionality by using one of the publically available +# Play modules: https://playframework.com/documentation/latest/ModuleDirectory +play.modules { + # By default, Play will load any class called Module that is defined + # in the root package (the "app" directory), or you can define them + # explicitly below. + # If there are any built-in modules that you want to enable, you can list them here. + enabled += modules.TaxonomyModule + + # If there are any built-in modules that you want to disable, you can list them here. + #disabled += "" +} + +## IDE +# https://www.playframework.com/documentation/latest/IDE +# ~~~~~ +# Depending on your IDE, you can add a hyperlink for errors that will jump you +# directly to the code location in the IDE in dev mode. The following line makes +# use of the IntelliJ IDEA REST interface: +#play.editor="http://localhost:63342/api/file/?file=%s&line=%s" + +## Internationalisation +# https://www.playframework.com/documentation/latest/JavaI18N +# https://www.playframework.com/documentation/latest/ScalaI18N +# ~~~~~ +# Play comes with its own i18n settings, which allow the user's preferred language +# to map through to internal messages, or allow the language to be stored in a cookie. +play.i18n { + # The application languages + langs = [ "en" ] + + # Whether the language cookie should be secure or not + #langCookieSecure = true + + # Whether the HTTP only attribute of the cookie should be set to true + #langCookieHttpOnly = true +} + +## Play HTTP settings +# ~~~~~ +play.http { + ## Router + # https://www.playframework.com/documentation/latest/JavaRouting + # https://www.playframework.com/documentation/latest/ScalaRouting + # ~~~~~ + # Define the Router object to use for this application. + # This router will be looked up first when the application is starting up, + # so make sure this is the entry point. + # Furthermore, it's assumed your route file is named properly. + # So for an application router like `my.application.Router`, + # you may need to define a router file `conf/my.application.routes`. + # Default to Routes in the root package (aka "apps" folder) (and conf/routes) + #router = my.application.Router + + ## Action Creator + # https://www.playframework.com/documentation/latest/JavaActionCreator + # ~~~~~ + #actionCreator = null + + ## ErrorHandler + # https://www.playframework.com/documentation/latest/JavaRouting + # https://www.playframework.com/documentation/latest/ScalaRouting + # ~~~~~ + # If null, will attempt to load a class called ErrorHandler in the root package, + #errorHandler = null + + ## Session & Flash + # https://www.playframework.com/documentation/latest/JavaSessionFlash + # https://www.playframework.com/documentation/latest/ScalaSessionFlash + # ~~~~~ + session { + # Sets the cookie to be sent only over HTTPS. + #secure = true + + # Sets the cookie to be accessed only by the server. + #httpOnly = true + + # Sets the max-age field of the cookie to 5 minutes. + # NOTE: this only sets when the browser will discard the cookie. Play will consider any + # cookie value with a valid signature to be a valid session forever. To implement a server side session timeout, + # you need to put a timestamp in the session and check it at regular intervals to possibly expire it. + #maxAge = 300 + + # Sets the domain on the session cookie. + #domain = "example.com" + } + + flash { + # Sets the cookie to be sent only over HTTPS. + #secure = true + + # Sets the cookie to be accessed only by the server. + #httpOnly = true + } +} + +play.server.http.idleTimeout = 60s +play.http.parser.maxDiskBuffer = 10MB +parsers.anyContent.maxLength = 10MB + +## Netty Provider +# https://www.playframework.com/documentation/latest/SettingsNetty +# ~~~~~ +play.server.netty { + # Whether the Netty wire should be logged + log.wire = true + + # If you run Play on Linux, you can use Netty's native socket transport + # for higher performance with less garbage. + transport = "native" +} + +## WS (HTTP Client) +# https://www.playframework.com/documentation/latest/ScalaWS#Configuring-WS +# ~~~~~ +# The HTTP client primarily used for REST APIs. The default client can be +# configured directly, but you can also create different client instances +# with customized settings. You must enable this by adding to build.sbt: +# +# libraryDependencies += ws // or javaWs if using java +# +play.ws { + # Sets HTTP requests not to follow 302 requests + #followRedirects = false + + # Sets the maximum number of open HTTP connections for the client. + #ahc.maxConnectionsTotal = 50 + + ## WS SSL + # https://www.playframework.com/documentation/latest/WsSSL + # ~~~~~ + ssl { + # Configuring HTTPS with Play WS does not require programming. You can + # set up both trustManager and keyManager for mutual authentication, and + # turn on JSSE debugging in development with a reload. + #debug.handshake = true + #trustManager = { + # stores = [ + # { type = "JKS", path = "exampletrust.jks" } + # ] + #} + } +} + +## Cache +# https://www.playframework.com/documentation/latest/JavaCache +# https://www.playframework.com/documentation/latest/ScalaCache +# ~~~~~ +# Play comes with an integrated cache API that can reduce the operational +# overhead of repeated requests. You must enable this by adding to build.sbt: +# +# libraryDependencies += cache +# +play.cache { + # If you want to bind several caches, you can bind the individually + #bindCaches = ["db-cache", "user-cache", "session-cache"] +} + +## Filter Configuration +# https://www.playframework.com/documentation/latest/Filters +# ~~~~~ +# There are a number of built-in filters that can be enabled and configured +# to give Play greater security. +# +play.filters { + + # Enabled filters are run automatically against Play. + # CSRFFilter, AllowedHostFilters, and SecurityHeadersFilters are enabled by default. + enabled = [filters.AccessLogFilter] + + # Disabled filters remove elements from the enabled list. + # disabled += filters.CSRFFilter + + + ## CORS filter configuration + # https://www.playframework.com/documentation/latest/CorsFilter + # ~~~~~ + # CORS is a protocol that allows web applications to make requests from the browser + # across different domains. + # NOTE: You MUST apply the CORS configuration before the CSRF filter, as CSRF has + # dependencies on CORS settings. + cors { + # Filter paths by a whitelist of path prefixes + #pathPrefixes = ["/some/path", ...] + + # The allowed origins. If null, all origins are allowed. + #allowedOrigins = ["http://www.example.com"] + + # The allowed HTTP methods. If null, all methods are allowed + #allowedHttpMethods = ["GET", "POST"] + } + + ## Security headers filter configuration + # https://www.playframework.com/documentation/latest/SecurityHeaders + # ~~~~~ + # Defines security headers that prevent XSS attacks. + # If enabled, then all options are set to the below configuration by default: + headers { + # The X-Frame-Options header. If null, the header is not set. + #frameOptions = "DENY" + + # The X-XSS-Protection header. If null, the header is not set. + #xssProtection = "1; mode=block" + + # The X-Content-Type-Options header. If null, the header is not set. + #contentTypeOptions = "nosniff" + + # The X-Permitted-Cross-Domain-Policies header. If null, the header is not set. + #permittedCrossDomainPolicies = "master-only" + + # The Content-Security-Policy header. If null, the header is not set. + #contentSecurityPolicy = "default-src 'self'" + } + + ## Allowed hosts filter configuration + # https://www.playframework.com/documentation/latest/AllowedHostsFilter + # ~~~~~ + # Play provides a filter that lets you configure which hosts can access your application. + # This is useful to prevent cache poisoning attacks. + hosts { + # Allow requests to example.com, its subdomains, and localhost:9000. + #allowed = [".example.com", "localhost:9000"] + } +} + +play.http.parser.maxMemoryBuffer = 50MB +akka.http.parsing.max-content-length = 50MB +schema.base_path="https://sunbirddev.blob.core.windows.net/sunbird-content-dev/schemas/local" + +# Cassandra Configuration +cassandra { + lp { + connection: "cassandra-db.knowlg-db.svc.cluster.local:9042" + } + lpa { + connection: "cassandra-db.knowlg-db.svc.cluster.local:9042" + } +} + +# Redis Configuration +redis { + host: "redis-db.knowlg-db.svc.cluster.local" + port: 6379 + maxConnections: 128 +} + +# Configuration +akka.request_timeout: 30 +environment.id: 10000000 +graph { + dir: "/data/graphDB" + ids: ["domain"] + passport.key.base: "jd5ECm/o0BXwQCe8PfZY1NoUkB9HN41QjA80p22MKyRIcP5RW4qHw8sZztCzv87M" +} +route { + domain: "bolt://neo4j-db.knowlg-db.svc.cluster.local:7687" + all: "bolt://neo4j-db.knowlg-db.svc.cluster.local:8687" + bolt { + write { + domain: "bolt://neo4j-db.knowlg-db.svc.cluster.local:7687" + all: "bolt://neo4j-db.knowlg-db.svc.cluster.local:8687" + }, + read { + domain: "bolt://neo4j-db.knowlg-db.svc.cluster.local:7687" + all: "bolt://neo4j-db.knowlg-db.svc.cluster.local:8687" + } + } +} + +shard.id: 1 +platform { + auth.check.enabled: false + cache.ttl: 3600000 +} + +# Cloud Storage Config +cloud_storage_type: "azure" +azure_storage_key: "" +azure_storage_secret: "" +azure_storage_container: "" + +installation.id: ekstep + +kafka { + urls : "kafka.knowlg-db.svc.cluster.local:9092" +} + +channel { + default: "org.sunbird" +} + +languageCode { + assamese : "as" + bengali : "bn" + english : "en" + gujarati : "gu" + hindi : "hi" + kannada : "ka" + marathi : "mr" + odia : "or" + tamil : "ta" + telugu : "te" +} +objectcategorydefinition.keyspace="dev_category_store" + +# Framework master category validation Supported values are Yes/No +master.category.validation.enabled="Yes" diff --git a/knowlg-automation/helm_charts/taxonomy/taxonomy-service_logback.xml b/knowlg-automation/helm_charts/taxonomy/taxonomy-service_logback.xml new file mode 100644 index 000000000..21cb53d9e --- /dev/null +++ b/knowlg-automation/helm_charts/taxonomy/taxonomy-service_logback.xml @@ -0,0 +1,31 @@ + + + + + + + + + + %d %msg%n + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/knowlg-automation/helm_charts/taxonomy/templates/deployment.yaml b/knowlg-automation/helm_charts/taxonomy/templates/deployment.yaml new file mode 100644 index 000000000..064f43fb2 --- /dev/null +++ b/knowlg-automation/helm_charts/taxonomy/templates/deployment.yaml @@ -0,0 +1,74 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Chart.Name }} + namespace: {{ .Values.namespace }} + annotations: + reloader.stakater.com/auto: "true" +spec: + replicas: {{ .Values.replicaCount }} + strategy: + rollingUpdate: + maxSurge: {{ .Values.strategy.maxsurge }} + maxUnavailable: {{ .Values.strategy.maxunavailable }} + selector: + matchLabels: + app: {{ .Chart.Name }} + template: + metadata: + labels: + app: {{ .Chart.Name }} + spec: + volumes: + - name: {{ .Chart.Name }}-config + configMap: + name: {{ .Chart.Name }}-config + - name: {{ .Chart.Name }}-xml-config + configMap: + name: {{ .Chart.Name }}-xml-config + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.repository }}:{{ .Values.image_tag }}" + imagePullPolicy: Never + env: + - name: JAVA_OPTIONS + value: {{ .Values.env.javaoptions | quote }} + - name: _JAVA_OPTIONS + value: -Dlog4j2.formatMsgNoLookups=true + envFrom: + - configMapRef: + name: {{ .Chart.Name }}-config + resources: +{{ toYaml .Values.resources | indent 10 }} + ports: + - containerPort: {{ .Values.network.port }} + {{- if .Values.healthcheck }} + livenessProbe: +{{ toYaml .Values.livenessProbe | indent 10 }} + readinessProbe: +{{ toYaml .Values.readinessProbe | indent 10 }} + {{- end }} + volumeMounts: + - name: {{ .Chart.Name }}-config + mountPath: /home/sunbird/taxonomy-service-1.0-SNAPSHOT/config/application.conf + subPath: taxonomy-service_application.conf + - name: {{ .Chart.Name }}-xml-config + mountPath: /home/sunbird/taxonomy-service-1.0-SNAPSHOT/config/logback.xml + subPath: taxonomy-service_logback.xml + +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ .Chart.Name }}-service + namespace: {{ .Values.namespace }} + labels: + app: {{ .Chart.Name }} +spec: + ports: + - name: http-{{ .Chart.Name }} + protocol: TCP + port: {{ .Values.network.targetport }} + selector: + app: {{ .Chart.Name }} diff --git a/knowlg-automation/helm_charts/taxonomy/values.yaml b/knowlg-automation/helm_charts/taxonomy/values.yaml new file mode 100644 index 000000000..76033d47e --- /dev/null +++ b/knowlg-automation/helm_charts/taxonomy/values.yaml @@ -0,0 +1,28 @@ +### Default variable file for taxonomy-service ### + +namespace: knowlg-api + +env: + javaoptions: -Xmx600m + +replicaCount: 1 +repository: taxonomy-service +image_tag: R5.2.0 +resources: + requests: + cpu: 100m + memory: 100Mi + limits: + cpu: 1 + memory: 1024Mi +network: + port: 9006 + targetport: 9000 +strategy: + type: RollingUpdate + maxsurge: 25% + maxunavailable: 25% + + + + diff --git a/knowlg-automation/terraform/aks.yaml b/knowlg-automation/terraform/aks.yaml new file mode 100644 index 000000000..2a07d48b4 --- /dev/null +++ b/knowlg-automation/terraform/aks.yaml @@ -0,0 +1,8 @@ +╷ +│ Warning: No outputs found +│  +│ The state file either has no outputs defined, or all the defined outputs are empty. Please define an +│ output in your configuration with the `output` keyword and run `terraform refresh` for it to become +│ available. If you are using interpolation, please verify the interpolated value is not empty. You +│ can use the `terraform console` command to assist. +╵ diff --git a/knowlg-automation/terraform/aws/data.tf b/knowlg-automation/terraform/aws/data.tf new file mode 100644 index 000000000..8fc4b38cc --- /dev/null +++ b/knowlg-automation/terraform/aws/data.tf @@ -0,0 +1 @@ +data "aws_caller_identity" "current" {} diff --git a/knowlg-automation/terraform/aws/druid_cluster_vars.tf b/knowlg-automation/terraform/aws/druid_cluster_vars.tf new file mode 120000 index 000000000..ac4ac492d --- /dev/null +++ b/knowlg-automation/terraform/aws/druid_cluster_vars.tf @@ -0,0 +1 @@ +../helm/druid_cluster_vars.tf \ No newline at end of file diff --git a/knowlg-automation/terraform/aws/druid_operator.tf b/knowlg-automation/terraform/aws/druid_operator.tf new file mode 120000 index 000000000..9f33d5a27 --- /dev/null +++ b/knowlg-automation/terraform/aws/druid_operator.tf @@ -0,0 +1 @@ +../helm/druid_operator.tf \ No newline at end of file diff --git a/knowlg-automation/terraform/aws/druid_operator_vars.tf b/knowlg-automation/terraform/aws/druid_operator_vars.tf new file mode 120000 index 000000000..dfb76fa2f --- /dev/null +++ b/knowlg-automation/terraform/aws/druid_operator_vars.tf @@ -0,0 +1 @@ +../helm/druid_operator_vars.tf \ No newline at end of file diff --git a/knowlg-automation/terraform/aws/druid_raw.tf b/knowlg-automation/terraform/aws/druid_raw.tf new file mode 120000 index 000000000..24b2b90ea --- /dev/null +++ b/knowlg-automation/terraform/aws/druid_raw.tf @@ -0,0 +1 @@ +../helm/druid_raw.tf \ No newline at end of file diff --git a/knowlg-automation/terraform/aws/eks_addons.tf b/knowlg-automation/terraform/aws/eks_addons.tf new file mode 100644 index 000000000..3c99e4f94 --- /dev/null +++ b/knowlg-automation/terraform/aws/eks_addons.tf @@ -0,0 +1,7 @@ +resource "aws_eks_addon" "addons" { + for_each = { for addon in var.eks_addons : addon.name => addon } + cluster_name = aws_eks_cluster.eks_master.id + addon_name = each.value.name + addon_version = each.value.version + resolve_conflicts = "OVERWRITE" +} \ No newline at end of file diff --git a/knowlg-automation/terraform/aws/eks_master.tf b/knowlg-automation/terraform/aws/eks_master.tf new file mode 100644 index 000000000..f63b5ea4e --- /dev/null +++ b/knowlg-automation/terraform/aws/eks_master.tf @@ -0,0 +1,20 @@ +resource "aws_eks_cluster" "eks_master" { + name = "${var.building_block}-${var.env}-eks" + role_arn = aws_iam_role.eks_master_role.arn + version = var.eks_version + + vpc_config { + subnet_ids = aws_subnet.public_subnets[*].id + } + + tags = merge( + { + Name = "${var.building_block}-${var.env}-eks" + }, + local.common_tags, + var.additional_tags) + + depends_on = [ + aws_iam_role_policy_attachment.AmazonEKSClusterPolicy + ] +} \ No newline at end of file diff --git a/knowlg-automation/terraform/aws/eks_master_policy_attachment.tf b/knowlg-automation/terraform/aws/eks_master_policy_attachment.tf new file mode 100644 index 000000000..0a0f30020 --- /dev/null +++ b/knowlg-automation/terraform/aws/eks_master_policy_attachment.tf @@ -0,0 +1,4 @@ +resource "aws_iam_role_policy_attachment" "AmazonEKSClusterPolicy" { + policy_arn = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy" + role = aws_iam_role.eks_master_role.name +} \ No newline at end of file diff --git a/knowlg-automation/terraform/aws/eks_master_role.tf b/knowlg-automation/terraform/aws/eks_master_role.tf new file mode 100644 index 000000000..3aa2924a3 --- /dev/null +++ b/knowlg-automation/terraform/aws/eks_master_role.tf @@ -0,0 +1,23 @@ +resource "aws_iam_role" "eks_master_role" { + name = var.eks_master_role + assume_role_policy = < $PWD/aks.yaml +# sed -i '' '1d; $d' $PWD/aks.yaml + +# fi +echo "$(terraform -chdir=$x output kube_config)" > $PWD/aks.yaml +sed -i '' '1d; $d' $PWD/aks.yaml +export KUBECONFIG=$PWD/aks.yaml +export KUBE_CONFIG_PATH=$PWD/aks.yaml +terraform -chdir=$x apply --auto-approve \ No newline at end of file diff --git a/knowlg-automation/terraform/destroy b/knowlg-automation/terraform/destroy new file mode 100644 index 000000000..396268045 --- /dev/null +++ b/knowlg-automation/terraform/destroy @@ -0,0 +1,5 @@ +echo "Enter the environment to deploy in: [Options azure/local]" +read x +echo "Terraform destroying $x infra" +terraform -chdir=$x destroy $1 +terraform -chdir=$x destroy $1 diff --git a/knowlg-automation/terraform/helm/druid_cluster_vars.tf b/knowlg-automation/terraform/helm/druid_cluster_vars.tf new file mode 100644 index 000000000..337f0e658 --- /dev/null +++ b/knowlg-automation/terraform/helm/druid_cluster_vars.tf @@ -0,0 +1,59 @@ +variable "druid_cluster_release_name" { + type = string + description = "Druid cluster helm release name." + default = "druid-cluster" +} + +variable "druid_cluster_namespace" { + type = string + description = "Druid namespace." + default = "druid-raw" +} + +variable "druid_cluster_chart_path" { + type = string + description = "Druid cluster chart path." + default = "../../helm_charts/druid-cluster" +} + +variable "druid_cluster_chart_install_timeout" { + type = number + description = "Druid cluster chart install timeout." + default = 3000 +} + +variable "druid_cluster_create_namespace" { + type = bool + description = "Create druid cluster namespace." + default = true +} + +variable "druid_cluster_wait_for_jobs" { + type = bool + description = "Druid cluster wait for jobs paramater." + default = true +} + +variable "druid_cluster_chart_template" { + type = string + description = "Druid cluster chart values.yaml path." + default = "../terraform_helm_templates/druid_cluster.yaml.tfpl" +} + +variable "druid_user" { + type = string + description = "Druid user name." + default = "druid" +} + +variable "druid_password" { + type = string + description = "Druid password." + default = "druid" +} + +variable "druid_worker_capacity" { + type = number + description = "Druid middle manager worker capacity." + default = 2 +} \ No newline at end of file diff --git a/knowlg-automation/terraform/helm/druid_operator.tf b/knowlg-automation/terraform/helm/druid_operator.tf new file mode 100644 index 000000000..700692841 --- /dev/null +++ b/knowlg-automation/terraform/helm/druid_operator.tf @@ -0,0 +1,8 @@ +resource "helm_release" "druid_operator" { + name = var.druid_operator_release_name + chart = var.druid_operator_chart_path + timeout = var.druid_operator_chart_install_timeout + namespace = var.druid_operator_namespace + create_namespace = var.druid_operator_create_namespace + wait_for_jobs = var.druid_operator_wait_for_jobs +} \ No newline at end of file diff --git a/knowlg-automation/terraform/helm/druid_operator_vars.tf b/knowlg-automation/terraform/helm/druid_operator_vars.tf new file mode 100644 index 000000000..316805ed9 --- /dev/null +++ b/knowlg-automation/terraform/helm/druid_operator_vars.tf @@ -0,0 +1,35 @@ +variable "druid_operator_release_name" { + type = string + description = "Druid operator helm release name." + default = "druid-operator" +} + +variable "druid_operator_namespace" { + type = string + description = "Druid operator namespace." + default = "druid-raw" +} + +variable "druid_operator_chart_path" { + type = string + description = "Druid operator chart path." + default = "../../helm_charts/druid-operator" +} + +variable "druid_operator_chart_install_timeout" { + type = number + description = "Druid operator chart install timeout." + default = 600 +} + +variable "druid_operator_create_namespace" { + type = bool + description = "Create druid operator namespace." + default = true +} + +variable "druid_operator_wait_for_jobs" { + type = bool + description = "Druid operator wait for jobs paramater." + default = true +} \ No newline at end of file diff --git a/knowlg-automation/terraform/helm/druid_raw.tf b/knowlg-automation/terraform/helm/druid_raw.tf new file mode 100644 index 000000000..c82cb52f7 --- /dev/null +++ b/knowlg-automation/terraform/helm/druid_raw.tf @@ -0,0 +1,25 @@ +resource "helm_release" "druid_cluster" { + name = var.druid_cluster_release_name + chart = var.druid_cluster_chart_path + timeout = var.druid_cluster_chart_install_timeout + namespace = var.druid_cluster_namespace + create_namespace = var.druid_cluster_create_namespace + depends_on = [helm_release.druid_operator, helm_release.postgres] + wait_for_jobs = var.druid_cluster_wait_for_jobs + values = [ + templatefile(var.druid_cluster_chart_template, + { + druid_namespace = var.druid_cluster_namespace + druid_user = var.druid_user + druid_password = var.druid_password + druid_worker_capacity = var.druid_worker_capacity + env = var.env + kubernetes_storage_class = var.kubernetes_storage_class + druid_deepstorage_type = var.druid_deepstorage_type + s3_bucket = coalesce(local.s3_bucket, "") + s3_access_key = coalesce(local.s3_access_key, "") + s3_secret_key = coalesce(local.s3_secret_key, "") + } + ) + ] +} \ No newline at end of file diff --git a/knowlg-automation/terraform/helm/flink.tf b/knowlg-automation/terraform/helm/flink.tf new file mode 100644 index 000000000..ffbbf5ff8 --- /dev/null +++ b/knowlg-automation/terraform/helm/flink.tf @@ -0,0 +1,18 @@ +resource "helm_release" "flink" { + name = var.flink_release_name + chart = var.flink_chart_path + namespace = "${var.flink_namespace}-${var.env}" + create_namespace = var.flink_create_namespace + depends_on = [helm_release.kafka] + wait_for_jobs = var.flink_wait_for_jobs + timeout = var.flink_chart_install_timeout + + values = [ + templatefile(var.flink_chart_template, + { + checkpoint_store_type = var.flink_checkpoint_store_type + s3_access_key = coalesce(local.s3_access_key, "") + s3_secret_key = coalesce(local.s3_secret_key, "") + }) + ] +} diff --git a/knowlg-automation/terraform/helm/flink_vars.tf b/knowlg-automation/terraform/helm/flink_vars.tf new file mode 100644 index 000000000..0e25dc6e3 --- /dev/null +++ b/knowlg-automation/terraform/helm/flink_vars.tf @@ -0,0 +1,47 @@ +variable "flink_release_name" { + type = string + description = "Flink helm release name." + default = "druid-validator" +} + +variable "flink_namespace" { + type = string + description = "Flink namespace." + default = "flink" +} + +variable "flink_chart_path" { + type = string + description = "Flink chart path." + default = "../../helm_charts/flink" +} + +variable "flink_chart_install_timeout" { + type = number + description = "Flink chart install timeout." + default = 900 +} + +variable "flink_create_namespace" { + type = bool + description = "Create flink namespace." + default = true +} + +variable "flink_wait_for_jobs" { + type = bool + description = "Flink wait for jobs paramater." + default = false +} + +variable "flink_chart_template" { + type = string + description = "Flink chart values.yaml path." + default = "../terraform_helm_templates/flink.yaml.tfpl" +} + +variable "flink_kubernetes_service_name" { + type = string + description = "Flink kubernetes service name." + default = "druid-validator-jobmanager" +} \ No newline at end of file diff --git a/knowlg-automation/terraform/helm/kafka.tf b/knowlg-automation/terraform/helm/kafka.tf new file mode 100644 index 000000000..32f441040 --- /dev/null +++ b/knowlg-automation/terraform/helm/kafka.tf @@ -0,0 +1,19 @@ +resource "helm_release" "kafka" { + name = var.kafka_release_name + chart = var.kafka_chart_path + namespace = var.kafka_namespace + create_namespace = var.kafka_create_namespace + dependency_update = var.kafka_chart_dependecy_update + wait_for_jobs = var.kafka_wait_for_jobs + values = [ + templatefile(var.kafka_chart_custom_values_yaml, + { + input_topic = "${var.env}.${var.kafka_input_topic}" + output_telemetry_route_topic = "${var.env}.${var.kafka_output_telemetry_route_topic}" + output_summary_route_topic = "${var.env}.${var.kafka_output_summary_route_topic}" + output_failed_topic = "${var.env}.${var.kafka_output_failed_topic}" + output_duplicate_topic = "${var.env}.${var.kafka_output_duplicate_topic}" + } + ) + ] +} \ No newline at end of file diff --git a/knowlg-automation/terraform/helm/kafka_vars.tf b/knowlg-automation/terraform/helm/kafka_vars.tf new file mode 100644 index 000000000..744b7021b --- /dev/null +++ b/knowlg-automation/terraform/helm/kafka_vars.tf @@ -0,0 +1,77 @@ +variable "kafka_release_name" { + type = string + description = "Kafka helm release name." + default = "kafka" +} + +variable "kafka_namespace" { + type = string + description = "Kafka namespace." + default = "kafka" +} + +variable "kafka_chart_path" { + type = string + description = "Kafka chart path." + default = "../../helm_charts/kafka" +} + +variable "kafka_chart_install_timeout" { + type = number + description = "Kafka chart install timeout." + default = 3000 +} + +variable "kafka_create_namespace" { + type = bool + description = "Create kafka namespace." + default = true +} + +variable "kafka_wait_for_jobs" { + type = bool + description = "Kafka wait for jobs paramater." + default = true +} + +variable "kafka_chart_custom_values_yaml" { + type = string + description = "Kafka chart values.yaml path." + default = "../../helm_charts/kafka/values.yaml" +} + +variable "kafka_chart_dependecy_update" { + type = bool + description = "Kafka chart dependency update." + default = true +} + +variable "kafka_input_topic" { + type = string + description = "Kafka input topic." + default = "telemetry.denorm" +} + +variable "kafka_output_telemetry_route_topic" { + type = string + description = "Kafka output telemetry route topic" + default = "druid.events.telemetry" +} + +variable "kafka_output_summary_route_topic" { + type = string + description = "Kafka output summary route topic" + default = "druid.events.summary" +} + +variable "kafka_output_failed_topic" { + type = string + description = "Kafka output failed topic" + default = "telemetry.failed" +} + +variable "kafka_output_duplicate_topic" { + type = string + description = "Kafka output duplicate topic" + default = "telemetry.duplicate" +} \ No newline at end of file diff --git a/knowlg-automation/terraform/helm/monitoring.tf b/knowlg-automation/terraform/helm/monitoring.tf new file mode 100644 index 000000000..07845ca93 --- /dev/null +++ b/knowlg-automation/terraform/helm/monitoring.tf @@ -0,0 +1,10 @@ +resource "helm_release" "monitoring" { + name = var.monitoring_release_name + repository = var.monitoring_chart_repository + chart = var.monitoring_chart_name + version = var.monitoring_chart_version + namespace = var.monitoring_namespace + create_namespace = var.monitoring_create_namespace + wait_for_jobs = var.monitoring_wait_for_jobs + timeout = var.monitoring_install_timeout +} \ No newline at end of file diff --git a/knowlg-automation/terraform/helm/monitoring_vars.tf b/knowlg-automation/terraform/helm/monitoring_vars.tf new file mode 100644 index 000000000..b6807e77a --- /dev/null +++ b/knowlg-automation/terraform/helm/monitoring_vars.tf @@ -0,0 +1,53 @@ +variable "monitoring_release_name" { + type = string + description = "Monitoring helm release name." + default = "monitoring" +} + +variable "monitoring_namespace" { + type = string + description = "Superset namespace." + default = "monitoring" +} + +variable "monitoring_chart_install_timeout" { + type = number + description = "Monitoring chart install timeout." + default = 3000 +} + +variable "monitoring_create_namespace" { + type = bool + description = "Create monitoring namespace." + default = true +} + +variable "monitoring_wait_for_jobs" { + type = bool + description = "Monitoring wait for jobs paramater." + default = true +} + +variable "monitoring_chart_repository" { + type = string + description = "Monitoring chart repository url." + default = "https://prometheus-community.github.io/helm-charts" +} + +variable "monitoring_chart_name" { + type = string + description = "Monitoring chart name." + default = "kube-prometheus-stack" +} + +variable "monitoring_chart_version" { + type = string + description = "Monitoring chart version." + default = "44.2.1" +} + +variable "monitoring_install_timeout" { + type = number + description = "Monitoring chart install timeout." + default = 1200 +} \ No newline at end of file diff --git a/knowlg-automation/terraform/helm/postgres.tf b/knowlg-automation/terraform/helm/postgres.tf new file mode 100644 index 000000000..d338f36dc --- /dev/null +++ b/knowlg-automation/terraform/helm/postgres.tf @@ -0,0 +1,14 @@ +resource "helm_release" "postgres" { + name = var.postgresql_release_name + repository = var.postgresql_repository + chart = var.postgresql_name + version = var.postgresql_version + timeout = var.postgresql_install_timeout + namespace = var.postgresql_namespace + create_namespace = var.postgresql_create_namespace + dependency_update = var.postgresql_dependecy_update + + values = [ + file(var.postgresql_template) + ] +} \ No newline at end of file diff --git a/knowlg-automation/terraform/helm/postgresql_vars.tf b/knowlg-automation/terraform/helm/postgresql_vars.tf new file mode 100644 index 000000000..b3dd378b6 --- /dev/null +++ b/knowlg-automation/terraform/helm/postgresql_vars.tf @@ -0,0 +1,53 @@ +variable "postgresql_release_name" { + type = string + description = "Postgresql helm release name." + default = "postgresql" +} + +variable "postgresql_namespace" { + type = string + description = "Postgresql namespace." + default = "postgresql" +} + +variable "postgresql_install_timeout" { + type = number + description = "Postgresql chart install timeout." + default = 600 +} + +variable "postgresql_create_namespace" { + type = bool + description = "Create postgresql namespace." + default = true +} + +variable "postgresql_template" { + type = string + description = "Postgresql chart custom values.yaml path." + default = "../terraform_helm_templates/postgres.yaml.tfpl" +} + +variable "postgresql_dependecy_update" { + type = bool + description = "Postgresql chart dependency update." + default = true +} + +variable "postgresql_repository" { + type = string + description = "Postgresql chart repository url." + default = "https://charts.bitnami.com/bitnami" +} + +variable "postgresql_name" { + type = string + description = "Postgresql chart name." + default = "postgresql" +} + +variable "postgresql_version" { + type = string + description = "postgresql chart version." + default = "11.9.1" +} \ No newline at end of file diff --git a/knowlg-automation/terraform/helm/superset.tf b/knowlg-automation/terraform/helm/superset.tf new file mode 100644 index 000000000..f7f3f0993 --- /dev/null +++ b/knowlg-automation/terraform/helm/superset.tf @@ -0,0 +1,10 @@ +resource "helm_release" "superset" { + name = var.superset_release_name + chart = var.superset_chart_path + namespace = var.superset_namespace + create_namespace = var.superset_create_namespace + wait_for_jobs = var.superset_wait_for_jobs + values = [ + templatefile(var.superset_chart_custom_values_yaml,{}) + ] +} \ No newline at end of file diff --git a/knowlg-automation/terraform/helm/superset_vars.tf b/knowlg-automation/terraform/helm/superset_vars.tf new file mode 100644 index 000000000..3405640e4 --- /dev/null +++ b/knowlg-automation/terraform/helm/superset_vars.tf @@ -0,0 +1,41 @@ +variable "superset_release_name" { + type = string + description = "Superset helm release name." + default = "superset" +} + +variable "superset_namespace" { + type = string + description = "Superset namespace." + default = "superset" +} + +variable "superset_chart_path" { + type = string + description = "Superset chart path." + default = "../../helm_charts/superset-helm" +} + +variable "superset_chart_install_timeout" { + type = number + description = "Superset chart install timeout." + default = 3000 +} + +variable "superset_create_namespace" { + type = bool + description = "Create superset namespace." + default = true +} + +variable "superset_wait_for_jobs" { + type = bool + description = "Superset wait for jobs paramater." + default = true +} + +variable "superset_chart_custom_values_yaml" { + type = string + description = "Superset chart values.yaml path." + default = "../../helm_charts/superset-helm/values.yaml" +} \ No newline at end of file diff --git a/knowlg-automation/terraform/local/cassandra-provision.tf b/knowlg-automation/terraform/local/cassandra-provision.tf new file mode 100644 index 000000000..6a753a42b --- /dev/null +++ b/knowlg-automation/terraform/local/cassandra-provision.tf @@ -0,0 +1,11 @@ +resource "helm_release" "cassandra" { + name = "cassandra" + chart = var.CASSANDRA_CHART + namespace = var.CASSANDRA_NAMESPACE + create_namespace = true + dependency_update = true + depends_on = [kind_cluster.one-click] + wait_for_jobs = true + +} + \ No newline at end of file diff --git a/knowlg-automation/terraform/local/elasticsearch-provision.tf b/knowlg-automation/terraform/local/elasticsearch-provision.tf new file mode 100644 index 000000000..f2e51fb5b --- /dev/null +++ b/knowlg-automation/terraform/local/elasticsearch-provision.tf @@ -0,0 +1,11 @@ + +resource "helm_release" "elasticsearch" { + name = "elasticsearch" + chart = var.ELASTICSEARCH_CHART + namespace = var.ELASTICSEARCH_NAMESPACE + create_namespace = true + dependency_update = true + depends_on = [kind_cluster.one-click] + wait_for_jobs = true + +} diff --git a/knowlg-automation/terraform/local/kafka-provision.tf b/knowlg-automation/terraform/local/kafka-provision.tf new file mode 100644 index 000000000..dfd5691b7 --- /dev/null +++ b/knowlg-automation/terraform/local/kafka-provision.tf @@ -0,0 +1,67 @@ + +provider "helm" { + kubernetes { + config_path = var.kind_cluster_config_path + config_context = var.kube_config_context + } +} + +resource "helm_release" "kafka" { + name = "kafka" + chart = var.KAFKA_CHART + namespace = var.KAFKA_NAMESPACE + create_namespace = true + dependency_update = true + depends_on = [kind_cluster.one-click] + wait_for_jobs = true + values = [ + templatefile("../../helm_charts/kafka/values.yaml", + { + #kafka_namespace: "kafka", + # kafka_image_repository: "bitnami/kafka" + # kafka_image_tag: "2.8.1-debian-10-r31" + # kafka_delete_topic_enable: true + # kafka_replica_count: 1 + # # Kubernetes Service type for external access. It can be NodePort or LoadBalancer + # # service_type: "ClusterIP" + # service_type: "NodePort" + # service_port: 9092 + # # PV config + # kafka_persistence_size: "2Gi" + # #Zookeeper configs + # zookeeper_enabled: true + # zookeeper_heapsize: 256 + # zookeeper_replica_count: 1 + input_topic = "dev.telemetry.denorm" + output_telemetry_route_topic = "dev.druid.events.telemetry" + output_summary_route_topic = "dev.druid.events.summary" + output_failed_topic = "dev.telemetry.failed" + output_duplicate_topic = "dev.telemetry.duplicate" + } + ) + ] +} + +# # data "kubernetes_service" "kafka" { +# # metadata { +# # namespace = "kafka" +# # name = "kafka" +# # } +# # depends_on = [kind_cluster.one-click, helm_release.kafka] +# # } + +# # data "kubernetes_service" "zookeeper" { +# # metadata { +# # namespace = "kafka" +# # name = "kafka-zookeeper" +# # } +# # depends_on = [kind_cluster.one-click, helm_release.kafka] +# # } + +# # output "kafka-service-ip" { +# # value = data.kubernetes_service.kafka.spec.0.cluster_ip +# # } + +# # output "zookeeper-service-ip" { +# # value = data.kubernetes_service.zookeeper.spec.0.cluster_ip +# # } diff --git a/knowlg-automation/terraform/local/logstash-provision.tf b/knowlg-automation/terraform/local/logstash-provision.tf new file mode 100644 index 000000000..075944e6f --- /dev/null +++ b/knowlg-automation/terraform/local/logstash-provision.tf @@ -0,0 +1,11 @@ + +resource "helm_release" "logstash" { + name = "logstash" + chart = var.LOGSTASH_CHART + namespace = var.LOGSTASH_NAMESPACE + create_namespace = true + dependency_update = true + depends_on = [kind_cluster.one-click] + wait_for_jobs = true + +} diff --git a/knowlg-automation/terraform/local/main.tf b/knowlg-automation/terraform/local/main.tf new file mode 100644 index 000000000..de3663fdd --- /dev/null +++ b/knowlg-automation/terraform/local/main.tf @@ -0,0 +1,55 @@ +provider "kind" { +} + +resource "kind_cluster" "one-click" { + name = var.kind_cluster_name + kubeconfig_path = pathexpand(var.kind_cluster_config_path) + wait_for_ready = true + + kind_config { + kind = "Cluster" + api_version = "kind.x-k8s.io/v1alpha4" + + node { + role = "control-plane" + + kubeadm_config_patches = [ + "kind: InitConfiguration\nnodeRegistration:\n kubeletExtraArgs:\n node-labels: \"ingress-ready=true\"\n" + ] + extra_port_mappings { + container_port = 80 + host_port = 80 + } + extra_port_mappings { + container_port = 443 + host_port = 443 + } + } + + node { + role = "worker" + kubeadm_config_patches = [ + "kind: InitConfiguration\nnodeRegistration:\n kubeletExtraArgs:\n node-labels: \"worker-node=true\"\n" + ] + } + + node { + role = "worker" + kubeadm_config_patches = [ + "kind: InitConfiguration\nnodeRegistration:\n kubeletExtraArgs:\n node-labels: \"worker-node=true\"\n" + ] + } + + node { + role = "worker" + kubeadm_config_patches = [ + "kind: InitConfiguration\nnodeRegistration:\n kubeletExtraArgs:\n node-labels: \"worker-node=true\"\n" + ] + } + } + +} + + + + diff --git a/knowlg-automation/terraform/local/neo4j-provision.tf b/knowlg-automation/terraform/local/neo4j-provision.tf new file mode 100644 index 000000000..731495c19 --- /dev/null +++ b/knowlg-automation/terraform/local/neo4j-provision.tf @@ -0,0 +1,10 @@ +resource "helm_release" "neo4j" { + name = "neo4j" + chart = var.NEO4J_CHART + namespace = var.NEO4J_NAMESPACE + create_namespace = true + dependency_update = true + depends_on = [kind_cluster.one-click] + wait_for_jobs = true + +} \ No newline at end of file diff --git a/knowlg-automation/terraform/local/postgresql/custom-values.yaml b/knowlg-automation/terraform/local/postgresql/custom-values.yaml new file mode 100644 index 000000000..1cebeda98 --- /dev/null +++ b/knowlg-automation/terraform/local/postgresql/custom-values.yaml @@ -0,0 +1,24 @@ +auth: + enablePostgresUser: true + postgresPassword: postgres + database: "druid_raw" + username: "druid" + password: "druid" + existingSecret: "" + +primary: + persistence: + size: 1Gi + enabled: true + mountPath: /bitnami/postgresql + storageClass: "" + + initdb: + user: "postgres" + password: "postgres" + scripts: + 00_create_superset_db.sql: | + CREATE DATABASE superset; + 01_create_superset_user.sql: | + CREATE USER superset WITH ENCRYPTED PASSWORD 'superset$123'; + GRANT ALL PRIVILEGES ON DATABASE superset TO superset; \ No newline at end of file diff --git a/knowlg-automation/terraform/local/redis-provision.tf b/knowlg-automation/terraform/local/redis-provision.tf new file mode 100644 index 000000000..9d35a879b --- /dev/null +++ b/knowlg-automation/terraform/local/redis-provision.tf @@ -0,0 +1,10 @@ +resource "helm_release" "redis" { + name = "redis" + chart = var.REDIS_CHART + namespace = var.REDIS_NAMESPACE + create_namespace = true + dependency_update = true + depends_on = [kind_cluster.one-click] + wait_for_jobs = true + +} \ No newline at end of file diff --git a/knowlg-automation/terraform/local/taxonomy-provision.tf b/knowlg-automation/terraform/local/taxonomy-provision.tf new file mode 100644 index 000000000..f123d208c --- /dev/null +++ b/knowlg-automation/terraform/local/taxonomy-provision.tf @@ -0,0 +1,11 @@ +resource "helm_release" "taxonomy" { + name = "taxonomy" + chart = var.TAXONOMY_CHART + namespace = var.TAXONOMY_NAMESPACE + create_namespace = true + dependency_update = true + depends_on = [kind_cluster.one-click] + wait_for_jobs = true + +} + \ No newline at end of file diff --git a/knowlg-automation/terraform/local/vars.tf b/knowlg-automation/terraform/local/vars.tf new file mode 100644 index 000000000..ca02f67f3 --- /dev/null +++ b/knowlg-automation/terraform/local/vars.tf @@ -0,0 +1,176 @@ +variable "kind_cluster_name" { + type = string + description = "The name of the cluster." + default = "one-click" +} + +variable "kind_cluster_config_path" { + type = string + description = "The location where this cluster's kubeconfig will be saved to." + default = "~/.kube/config" +} + +variable "kube_config_context" { + type = string + description = "The config context in kubeconfig" + default = "kind-one-click" + } + +variable "ingress_nginx_helm_version" { + type = string + description = "The Helm version for the nginx ingress controller." + default = "4.0.6" +} + +variable "ingress_nginx_namespace" { + type = string + description = "The nginx ingress namespace (it will be created if needed)." + default = "ingress-nginx" +} + + +variable "STAGE" { + description = "Deployment Stage" + default = "dev" +} + +#DRUID +variable "DRUID_CLUSTER_CHART" { + description = "Druid Instance Running Namespace" + default = "../../helm_charts/druid-cluster" +} + +variable "DRUID_NAMESPACE" { + description = "Druid Instance Running Namespace" + default = "druid-raw" +} + +variable "DRUID_OPERATOR_CHART" { + description = "Druid Instance Running Namespace" + default = "../../helm_charts/druid-operator" +} + +variable "DRUID_MIDDLE_MANAGER_WORKER_CAPACITY" { + type = number + default = 2 +} + +variable "DRUID_MIDDLE_MANAGER_PEON_HEAP" { + type = string + default = "256M" +} + +#KAFKA +variable "KAFKA_CHART" { + description = "Kafka Instance Running Namespace" + default = "../../helm_charts/kafka" +} + +variable "KAFKA_NAMESPACE" { + description = "Kafka Instance Running Namespace" + default = "knowlg-db" +} + +#ELASTICSEARCH +variable "ELASTICSEARCH_CHART" { + description = "Elasticsearch Instance Running Namespace" + default = "../../helm_charts/elasticsearch" +} + +variable "ELASTICSEARCH_NAMESPACE" { + description = "Elasticsearch Instance Running Namespace" + default = "knowlg-db" +} + + +#CASSANDRA + +variable "CASSANDRA_CHART" { + description = "Cassandra Instance Running Namespace" + default = "../../helm_charts/cassandra" +} + +variable "CASSANDRA_NAMESPACE" { + description = "CASSANDRA Instance Running Namespace" + default = "knowlg-db" +} + +#NEO4J + +variable "NEO4J_CHART" { + description = "Neo4j Instance Running Namespace" + default = "../../helm_charts/neo4j" +} + +variable "NEO4J_NAMESPACE" { + description = "NEO4J Instance Running Namespace" + default = "knowlg-db" +} + +#REDIS + +variable "REDIS_CHART" { + description = "Redis Instance Running Namespace" + default = "../../helm_charts/redis" +} + +variable "REDIS_NAMESPACE" { + description = "Redis Instance Running Namespace" + default = "knowlg-db" +} + +#LOGSTASH + +variable "LOGSTASH_CHART" { + description = "Redis Instance Running Namespace" + default = "../../helm_charts/logstash" +} + +variable "LOGSTASH_NAMESPACE" { + description = "Logstash Instance Running Namespace" + default = "knowlg-db" +} + +#TAXONOMY +variable "TAXONOMY_CHART" { + description = "Taxonomy Instance Running Namespace" + default = "../../helm_charts/taxonomy" +} + +variable "TAXONOMY_NAMESPACE" { + description = "Taxonomy Instance Running Namespace" + default = "knowlg-api" +} + + +#SUPERSET + +variable "SUPERSET_NAMESPACE" { + type = string + default = "superset" +} + +variable "SUPERSET_ADMIN_USERNAME" { + type = string + default = "admin" +} +variable "SUPERSET_ADMIN_FIRSTNAME" { + type = string + default = "Superset" +} + +variable "SUPERSET_ADMIN_LASTNAME" { + type = string + default = "Admin" +} + +variable "SUPERSET_ADMIN_PASSWORD" { + type = string +default = "admin123" +} + +variable "SUPERSET_ADMIN_EMAIL" { + type = string + default = "admin@superset.com" +} + diff --git a/knowlg-automation/terraform/local/versions.tf b/knowlg-automation/terraform/local/versions.tf new file mode 100644 index 000000000..fb0b06c79 --- /dev/null +++ b/knowlg-automation/terraform/local/versions.tf @@ -0,0 +1,21 @@ +terraform { + required_providers { + kind = { + source = "kyma-incubator/kind" + version = "0.0.11" + } + + kubernetes = { + source = "hashicorp/kubernetes" + version = "2.12.1" + } + + helm = { + source = "hashicorp/helm" + version = "2.6.0" + } + + } + + required_version = ">= 1.0.0" +} diff --git a/knowlg-automation/terraform/terraform_helm_templates/druid_cluster.yaml.tfpl b/knowlg-automation/terraform/terraform_helm_templates/druid_cluster.yaml.tfpl new file mode 100644 index 000000000..b8b0a2179 --- /dev/null +++ b/knowlg-automation/terraform/terraform_helm_templates/druid_cluster.yaml.tfpl @@ -0,0 +1,10 @@ +druid_namespace: ${druid_namespace} +druid_metadata_storage_connector_user: ${druid_user} +druid_metadata_storage_connector_password: ${druid_password} +druid_worker_capacity: ${druid_worker_capacity} +druid_env: ${env} +storageClass: ${kubernetes_storage_class} +druid_deepstorage_type: ${druid_deepstorage_type} +s3_bucket: ${s3_bucket} +s3_access_key: ${s3_access_key} +s3_secret_key: ${s3_secret_key} \ No newline at end of file diff --git a/knowlg-automation/terraform/terraform_helm_templates/flink.yaml.tfpl b/knowlg-automation/terraform/terraform_helm_templates/flink.yaml.tfpl new file mode 100644 index 000000000..6ff845b04 --- /dev/null +++ b/knowlg-automation/terraform/terraform_helm_templates/flink.yaml.tfpl @@ -0,0 +1,3 @@ +checkpoint_store_type: ${checkpoint_store_type} +s3_access_key: ${s3_access_key} +s3_secret_key: ${s3_secret_key} \ No newline at end of file diff --git a/knowlg-automation/terraform/terraform_helm_templates/postgres.yaml.tfpl b/knowlg-automation/terraform/terraform_helm_templates/postgres.yaml.tfpl new file mode 100644 index 000000000..c6cc1ceac --- /dev/null +++ b/knowlg-automation/terraform/terraform_helm_templates/postgres.yaml.tfpl @@ -0,0 +1,23 @@ +auth: + enablePostgresUser: true + postgresPassword: postgres + database: "druid_raw" + username: "druid" + password: "druid" + existingSecret: "" + +primary: + persistence: + size: 1Gi + enabled: true + mountPath: /bitnami/postgresql + + initdb: + user: "postgres" + password: "postgres" + scripts: + 00_create_superset_db.sql: | + CREATE DATABASE superset; + 01_create_superset_user.sql: | + CREATE USER superset WITH ENCRYPTED PASSWORD 'superset$123'; + GRANT ALL PRIVILEGES ON DATABASE superset TO superset; \ No newline at end of file From 017d235cf81015e42b5ea821481a4613da565c6a Mon Sep 17 00:00:00 2001 From: aimansharief <110388201+aimansharief@users.noreply.github.com> Date: Mon, 27 Mar 2023 12:00:26 +0530 Subject: [PATCH 195/490] Issue KN-767 feat: Removed the unused helmcharts --- .../helm_charts/druid-cluster/.helmignore | 22 - .../helm_charts/druid-cluster/Chart.yaml | 19 - .../druid-cluster/charts/zookeeper-10.0.0.tgz | Bin 43731 -> 0 bytes .../helm_charts/druid-cluster/druid-crd.yaml | 4487 ----------------- .../druid-cluster/templates/_helpers.tpl | 51 - .../templates/druid_statefulset.yaml | 476 -- .../templates/historical_hpa.yaml | 29 - .../helm_charts/druid-cluster/values.yaml | 345 -- .../helm_charts/druid-operator/Chart.yaml | 23 - .../druid-operator/templates/NOTES.txt | 1 - .../druid-operator/templates/_helpers.tpl | 62 - .../crds/druid.apache.org_druids.yaml | 4467 ---------------- .../druid-operator/templates/deployment.yaml | 59 - .../druid-operator/templates/role.yaml | 95 - .../templates/role_binding.yaml | 23 - .../templates/serviceaccount.yaml | 13 - .../helm_charts/druid-operator/values.yaml | 63 - .../helm_charts/superset-helm/Chart.lock | 9 - .../helm_charts/superset-helm/Chart.yaml | 37 - .../helm_charts/superset-helm/LICENSE | 21 - .../helm_charts/superset-helm/README.md | 2 - .../superset-helm/charts/redis-12.3.3.tgz | Bin 66002 -> 0 bytes .../superset-helm/templates/_helpers.tpl | 105 - .../templates/configmap-superset.yaml | 32 - .../superset-helm/templates/deployment.yaml | 128 - .../superset-helm/templates/ingress.yaml | 56 - .../superset-helm/templates/init-job.yaml | 80 - .../superset-helm/templates/secret-env.yaml | 39 - .../templates/secret-superset-config.yaml | 40 - .../superset-helm/templates/service.yaml | 42 - .../helm_charts/superset-helm/values.yaml | 312 -- 31 files changed, 11138 deletions(-) delete mode 100644 knowlg-automation/helm_charts/druid-cluster/.helmignore delete mode 100644 knowlg-automation/helm_charts/druid-cluster/Chart.yaml delete mode 100644 knowlg-automation/helm_charts/druid-cluster/charts/zookeeper-10.0.0.tgz delete mode 100644 knowlg-automation/helm_charts/druid-cluster/druid-crd.yaml delete mode 100644 knowlg-automation/helm_charts/druid-cluster/templates/_helpers.tpl delete mode 100644 knowlg-automation/helm_charts/druid-cluster/templates/druid_statefulset.yaml delete mode 100644 knowlg-automation/helm_charts/druid-cluster/templates/historical_hpa.yaml delete mode 100644 knowlg-automation/helm_charts/druid-cluster/values.yaml delete mode 100644 knowlg-automation/helm_charts/druid-operator/Chart.yaml delete mode 100644 knowlg-automation/helm_charts/druid-operator/templates/NOTES.txt delete mode 100644 knowlg-automation/helm_charts/druid-operator/templates/_helpers.tpl delete mode 100644 knowlg-automation/helm_charts/druid-operator/templates/crds/druid.apache.org_druids.yaml delete mode 100644 knowlg-automation/helm_charts/druid-operator/templates/deployment.yaml delete mode 100644 knowlg-automation/helm_charts/druid-operator/templates/role.yaml delete mode 100644 knowlg-automation/helm_charts/druid-operator/templates/role_binding.yaml delete mode 100644 knowlg-automation/helm_charts/druid-operator/templates/serviceaccount.yaml delete mode 100644 knowlg-automation/helm_charts/druid-operator/values.yaml delete mode 100644 knowlg-automation/helm_charts/superset-helm/Chart.lock delete mode 100644 knowlg-automation/helm_charts/superset-helm/Chart.yaml delete mode 100644 knowlg-automation/helm_charts/superset-helm/LICENSE delete mode 100644 knowlg-automation/helm_charts/superset-helm/README.md delete mode 100644 knowlg-automation/helm_charts/superset-helm/charts/redis-12.3.3.tgz delete mode 100644 knowlg-automation/helm_charts/superset-helm/templates/_helpers.tpl delete mode 100644 knowlg-automation/helm_charts/superset-helm/templates/configmap-superset.yaml delete mode 100644 knowlg-automation/helm_charts/superset-helm/templates/deployment.yaml delete mode 100644 knowlg-automation/helm_charts/superset-helm/templates/ingress.yaml delete mode 100644 knowlg-automation/helm_charts/superset-helm/templates/init-job.yaml delete mode 100644 knowlg-automation/helm_charts/superset-helm/templates/secret-env.yaml delete mode 100644 knowlg-automation/helm_charts/superset-helm/templates/secret-superset-config.yaml delete mode 100644 knowlg-automation/helm_charts/superset-helm/templates/service.yaml delete mode 100644 knowlg-automation/helm_charts/superset-helm/values.yaml diff --git a/knowlg-automation/helm_charts/druid-cluster/.helmignore b/knowlg-automation/helm_charts/druid-cluster/.helmignore deleted file mode 100644 index fbe01f88f..000000000 --- a/knowlg-automation/helm_charts/druid-cluster/.helmignore +++ /dev/null @@ -1,22 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*~ -# Various IDEs -.project -.idea/ -*.tmproj -.vscode/ \ No newline at end of file diff --git a/knowlg-automation/helm_charts/druid-cluster/Chart.yaml b/knowlg-automation/helm_charts/druid-cluster/Chart.yaml deleted file mode 100644 index 36bb4b50b..000000000 --- a/knowlg-automation/helm_charts/druid-cluster/Chart.yaml +++ /dev/null @@ -1,19 +0,0 @@ -name: druid-cluster -version: 0.1.0 -appVersion: 0.21.1 -description: Helm chart to deploy a druid cluster on Kubernetes -keywords: -- druid -- zookeeper -- postgres -maintainers: -- name: druid-operator-maintainers -sources: -- https://github.com/druid-io/druid-operator -engine: gotpl -home: https://github.com/druid-io/druid-operator -dependencies: -- name: zookeeper - version: 10.0.0 - repository: https://charts.bitnami.com/bitnami - condition: zookeeper.enabled diff --git a/knowlg-automation/helm_charts/druid-cluster/charts/zookeeper-10.0.0.tgz b/knowlg-automation/helm_charts/druid-cluster/charts/zookeeper-10.0.0.tgz deleted file mode 100644 index d9d1685899284f451a1f9332ca6b775cc7e5a4d5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43731 zcmV)TK(W6ciwG0|00000|0w_~VMtOiV@ORlOnEsqVl!4SWK%V1T2nbTPgYhoO;>Dc zVQyr3R8em|NM&qo0PMYeS0lHQIDG&1r_g$4cOZ+6fq}{OZti&)APG0TEoLTnH#cVx z-LkFFZgnIz;LObLvp+{~-BP!@-8P09a_N%{&@GiprBYR?R4V;J$TdOTUdG?H&ghP%=%=#?=0?5&b&}knW#zP7jr)j`bidrzi?-6B~#C>qH*@8)u z{khfsw%cokC_!1ObVMc$88}kQM+Tk}xN%|2x_v4sQC69dI24`d8+ zw$3;Vu327V9ZyF2Umf|mv$@&3+uVBHP2y4O8clBr4duY(?Mxt!Im9ud)`-2$Mj)k8 zgTAy>wHzuVDGd}4)KoTu8j|1|(N11Hua&*)LOG~iLPzVZeDHhSUbol!(i7;3KSlW; zk>GmSZOoAWy{)a+W%=KGmj93OEL&__vUy`is0BPijQ0T>!_DoT{_yoiaP`;W@XhA- z)tlh2L%8{7b9-m&^~PU!;Lf)jTiZK3;m%fX>-FaF+a37k^GXCkHn#d3Z~DE>?$+y#*W10#?d|_-KZ}M2Pd@)|V3Z>E z5C&lF{NLzp_saAC_4d~D{C|w+#S8HNF%$^<1^f_^D;R+UQaC{zQPz6#!o1v4C192n_J5!W>w5?IzvorXpbSvlPb+K!F*6ltK?g8(#x$V80V4>zNSC zj0g1 zf!i^PEfJX)mL!yBSS~lGYk(1eIS613t}^xu0ggp~K!}G!B*kTp0v|)gs|gfe43j#j zXEXC4mLfL9a1;~9aR4SHL_l*pYD7ivf zrCfe!`{RA=rF{@@@=XG-$ zi0HYHkSszdscRI{5d*v8pM{ze)lFMC9^x4DsVh%&?oo_m306@~FZ-=xN|WVc6i_tG z7Xe~Duh#Bz0pM|CtaCVG>(;E00K1unE4Jj(nWlzWgLvwgPo-KMj!MPFyA?c{q+GI^ zKoCGGz=+`z^C(;`0ZP&+IwKJd%FT;UAd%m^)r^iGBq+ecDbUW2d6c{Y7NVh9T1__v} zbwwTZc^?g78Y$WTYB#!tQ}#+AoDTqI;FS>ljK5j~C}I+8S^RK#Liia)48{DF0_X7D zb*8Mkn5>5+VCyMEv@=R^h}LBbZ2d(-QAZFT$D@vVXC_HLl6_#erJ6dG9z?12ndze* z4?u<`BSu?AW{4*#<6;CRFis_mfFh~x^qkG)YPVU(IAqssJ+p&52?;xyBv=qQXl;j#Z6+J~vOGdKrF`6h7he<1VO`1wbGc;%m2penS-_11`PH^0|dtxEi zs2irw3v?yPstSrShDWhpu2QCqw*`AB0KZn<1ljMK`uD0XXC0Z*ZFGkj$MHx_ZNa6$ zyfO-D1&?Av5d#TDH<+X>@9L^ED;!X|-QQC}(}{PQs!!F^RCVM*r>RCELXgIaO{{s! zot-09^hlM==aZg28Z-H~Dmjp7OT{3YJs}+R!7;o>fTdKqFeLY5rmcFV9&EfU?zQ`6 zp<6Y&ZpSKw!cseEV>U~2l)JZ>#N0uqO8RZuJhSGJ8V=rKl+@+$p3==jcnfrpnYZQYOv%oLbqa=#dVy%_oA?Vn1$8`el_N3O^i|=9vBwd)Y z$1{8b5(nCmtVc>YfrPw-fZ=!)A#ERl37monREBQ$ZGy6{I~;*xh9*~vlrAEDuv5Tt zfS6cj3+5D~eGKpLB%J`JUQ2!x@5^kTiFM5uQi{VUBDcyhqVVPf#Ark~Rz5;!mA2WF z;1yZS4AD5dX!XJC0)58_CX%D%KLWesx2u_m{yV&Z;70@#KoUjNm48g`q|@df6X|~j z7h}u-if_OTRC-YgjZiEtR8dhizCn@n77Q}!Q!EP@hg1q?voSN#&B;c(E$!rA1Gkuu!KH=RcN@J+Ia*tQA{pkw z)(oEsB#gIhqqhb&HrBw#rX2b|I=waUM`uG;{iCz#AlsFkmABCc8x~a}xkqst??%z# z83VhLkMkKNrLia>+p%Y>6oX=JCrFTi#L{(^Zwwut0T_l9F{V7YZbQ~e^fVq3xgP~N z1RL^iog*Wfk^&lWK2;3cfrB=`RTc^1VSa`1e_nCL`ATEXxtVIaeyVdhbP~k3PbJZ z-SZO(^c=wu$B3~rN^X$i^MsNx4TP$Sh!h;spCMRDBiC0gTWA{*WyCG``}<=slyA!D zCzJ$8?Kqlx1!G9C=8LwtyRcZlYAck3KSrFae0O|1)R@0iC zsACi*48R+L1@}!7#IfYJE5vURl6*IkQX~#@p3S6(_?hF^2Of8@{w#L~tdj2Lls&z` zo)P&LnWT_{AV?V}6YvKi-C&qmEC6*!eeu(Q(+8hAE$Q-7yE&a(La%W=>V%knmmxYr zBt#NPRW>4{j%2m(a6BaHUANm++dmF;rw7KIRk%-JqO2o0L4s#soZHE$`%pQb|Z z=x(`Wa5joWQzxev2Yv9f+#TYtnDU>F5x8{fUFL2yOS7u|wgQf&K5z;T;+yx7GO#Nh zpb|yp)nfI(tuw^aq|3%u!*Y+Rrd9d9QIm4J zN(`m5MK#FxwTjzQ8Ut;HC{22jneZMe+9h2ZBg6NMP|mbhtmGxNx|I;htLyWKS(Pu2 z290YDi4f3;I!jjlswoTrI3_S+W7 z0QbR`c=6(eC7=~+8#CYK?0f_B6^QT+5{h*VP@`jY&<&(KP4q@@X}59i4U+u5X|a{hOZI+K~30&2j{O+hte z=%(NrvUFpL&BfFus4e$RW47M@Qe3SCxtFKyZw)xw0W}{#+f}O|&gA7LKpOCI6EF>V zxCy9+{96Fkvj6V;VzcfiJvY33wy|BoXqIs$4b>w@t?w!0)z$UV6KJBen2r=w<}it6 z7d@bgafq_Sxhr~gYXyPav^f`;x4nTqEp%(nRB76l4XDskgoh}Y1`+y^kZYf>Bb$LM z%9RCEP9~6}!opW?4iF09P^@A#o=X4PO_=t};go=)-&4eNXt&(TX=GhmaYX@{B&iIr z(Gi;KjsVs#y9%~k?Po7_gLM>OUSJ06?gUK;oocA^9SVVdxj57yNedvwn;Gz086Yub zRnH{hK}KY?>95f1gU#)oW2~Y}l2rWJo4AXHGZHefOin50Q}LdoJAQxLpJ|Bl6}sc= zFBsw-K1QA5m(GyT4iwexoiZt2WI2seLA8Xo*N%kjmA4Po(1z>>O439+OC3nB%G3b5 zu-uL-f%0i+T|sV!ZMgK_%NB%R0nxLnzQt|x0k9XBHN#K|nWphBlMx{9aMZyCS*6A; zoRIjO5I%P!x}LLmZ@>f@f|?D015FpRw($t#yAg&Au>x<)PXMZKqDBpa1SAfSaviC| z(w!J_?Q30!Vc0=2rx>vgCz5#(4>|%o>)c{KmcMp%OY3q|FUP9j_De{p&jhIR2KrQh z>vj<6H{t6HZK3pTFuGkg{$YuhKFhGK+E~{U(R;EWA)#}u!210`QM1l282qZwCWwzi z6(BCdU%3#&0P0sDk?*?TFyDy1G#@Vkmd@s`+L%M34nXdMOGbwLQfTVSF{I(;T%BXV z7=@{f*F`s2x|r5gB#dN}n1pEkh1{Q#e;aH%Fb+E~<~Vy}`tEeE;5?PI;n+t48`Ki% zaQ?;UQ0iwN2>j$00tr!Z(3g*__}9ToyeOdLY*UuMclk6yUUY%@a_<4sn~wrke(0?l zn`<;x$VhPj#Gjmi3FN`J9+To}x@$D;gSJayw235D zOqGHGC5-8PReiP*PDng5zJX3C4>qzcIPf1%oU8vrGe-o5Y+Yz~@lVI}sG~3vCa=MJ z{YCkmG>o-r(5j?H*%Ug4F&xRDAAL;8?4u3z_M;4zqkyGJLMUf|q6pr}Xco3HZ zoJ6RT0auo-gGd3%GzlR`15P1Fqp7kRtmq(XZ<*;bQtMYk5Yq<7qj&Oc z1t?ucS1tIj+%yS|?crRK>c6xsL7#R$JaK`u)tRIk)>EmV)-*1?3?3aaD!@ z<~tFm_IXY0Bn(5Wihvtk#}n8@LX~X5oJo`6;(L&xK2Q;F0iqlaWrz~90>v(l2BqK; zjhcL6LPzq%SJ!CDIH5>IlBhuRz6mSQUQBr&8EmQzY>*W@sR31Ph|`quWi{mhYdPKY zM7yX@$AWN+jF7Ha?~`5XV5t@xjhJlV4DxXwtc!Noy8B(I!qYgK<_?V6LsN!XEK!2m zDr4^s1j8W;WN=g%+9cNR?IYWqR89v&D+8?%5hV;4ea2I~Fg&Er_F7+^4`n}#FQ$m?2VUxpblXFnndue!=?06HTF6z_CK4!?9RRG;8@ zH;bE=q3P+wilnypSX4}Su|B+e7w6^8QHC_00i%_KS283?qMz>t+W6?Du-il*Y`Ae` z7gQ|40&sAb5P9;>4kpviN!Rb7Un--)vTp|Jy)qOWSAou!a(L#2lhlTm=~5~ztGaqQ zvaDgny1=rAAnM}EN+4R<6v~ehXCY;7P?;yJtQ1(L4^M`w1*K%fNa~~=D*81T6T(!9 zEJXRgw|c!^ud}uJX6Mb{yqy!vVkhh`YetmW>49j z;8w2ablcfZ>WHivDbB=tKr8|X1(c+5*rDV~A4&l4wM$6haCio!-y=qIXLd0A3G3Nm z2N&PI1-8CtYA;Nvs7z%eF4k`lfdBZN?Q)YUj-)hyb`aL?>@1s4hx^A<)h4k{WwA}|D|MAq~oqxnKe(vE2G zN`o_Kx=1v_bs)E<&tW`r_OSd|n^a44Wa5A_Zp3ymc$Td}L{hQJmKld>XNW1fg;BKr zf+EH#4tPg{84~Jryrc2V#!(MrRSzx4>_)Gv{_Cx8zV;E&sh8#PD3B7}aYSPnY1LWE zcVp;i$^ou*Y+~hpp=fwNLQ2iwF>Oc+bqtAQVi)a_lKaXgr-$rb2PavgCtp`Gh3)M< zbd!M@*`VXrVdXxaJ!&mr7p$Dma%mwIgx=s0swwoLk!n9zw_>Pc6UJdq(!eQ2}% zQu*LlEjs9yDo1^cU>G6BTmlrjE86%`7c>S@J+vgfiZC0W5H7uYyXN2>I4mBKHC3`s z>QqJ=0@yzp=p)1mmzZ$5T$`#bRNYepMXB7X*0as=@<0izLTy!N9?JPP!#o?_`qz#D zsNC}ErRGU@Q^)?41w5$J{#d$Y5g#K8pg~p7(LDK6o+w~e!ZWumc~0!WOzeFUzf#H` z=el2QDHRS~Dv|p za+VyYa%|4rLn~(W0E0lfPF&%kvOIK`dDcPmVMjNpT&C*Xu6pHN_ULbB;iv;>PnO`eZcsA8r^9x6bXfh<9^ zK73@-1J)$XG^V@pDc9gFBNfGOF0L2k8pUd}nW@9A%2`$-ohnG;?8v^=Uwi(7J$207 zo>%}6)vPid!>kA*B$7%o$`j}Q^eqFsXNMUl*K{D0kk#FvOHr1`v`SMtb1BPJ8=&Gb zFwL5YRYDP?l;0NYMG#NsjrGg`^Nx0CtNoF@J$C|GAF8t&$s@WRl;7`l1zT&ODaHy- z5v?RhycdxjlWq;Qsd*6K8@Jkx7ZS!j2RWkXn?OLN0U z!pTA`+S4hBL7s!SKoLdYY@Lhi99K3=3`#QWDym{)6W!hbQ%ZMyeD(W*u#CUyaY1_T;MO$b$t5+u10)@ zQF``%uZiXDpljkUvOtV1ddHbBC28Vx=M_^OG6-m9Tr*gK016vQS~jATVLWNP=PaPk zMqplyy)j5;i#&;-jyTL+3@}OhU@{desob?-MSRGDwk#E`*~tJrsRN7yDL1)>l3#xNeKoF?Q}o`H`;Fp}Z@+NjM< z3SEDNtt#s}O!C)+1$^yf3^NyS=eJ)xg?oOP}W;9kFvw1qu zV1Zf2rI7sDrL->JNvzQ+B!zAUJxdhb9DbHSx=9Nhjv9k*lx(_5qgDdyrfu7ashc)o zG8xa+hKgv&?1(J!bXDqUfAVQ_85kR*C{i0GaMTAI8+QKb*V?;NnZp?@n8g_^oX2?% z|M7yEoIxR%Gw@_{2D9@yX9Cij)44F>wP)w%_4a#C&($+z=|tUZe#{WIacs~)*1tKL zFm(*aJBQ*5$KtGmahXBpHsv|91oG6e(seN=x3Lk$QVkDD6snXyA&E_b$r*>66jT+^ zta1fip@`gisM8cyC3zd1w37cE?q`O)tC%uhQq6fM=Y&(w)%3@RQylozyA_T>$prB+ zN|_82pj-t&xvAYI3XrR1^~<7ewE%Kr+?aWLx)wljD7AhA6>1uC0#0Uk!Or+nB&6B~ z{_Do;4U?@+`6r9FUh1_(7AN6D%ZZq%$BMZ@6qzh42U65El+5maAuu5qdUn6-+OSug zzo?LzUrS5R!ceE2bVAGma)T5%d&WA$q;T7+L@om-#3u_VlqXR*O;OuhMD?cq@gwM} z9rmu5RG&XrRF|P}?R8yA`wgs4ohFDvwP=vwj!7)kbuE|ZGBcP(m&)eOo^s+A+Br9( zR%Q+m^bl(lLsjiNE2G;u4R{4o`K>r7&J{hD6SIWjN@(R#Rfz-DolhAEC)IJ*gl7d; zfmN24G0WHpxpr$AideUQNahLsmWg!-Yd|O>Y(3|T4$(whzlz9}xtMmJ1nC6DTpzYc zaCZ{E@NqTHk~TK&mcrB|urNQ>DgQj~1+oHt$;=`x>|FXVO$0(9W%gCN{^}7nRQ9?UOB6)C+NyC~MmyMA&cH0&tZ80}!UcElIkT|yA zG(ZK5WqFYfC9usg8k3k%&5$OAy!|e-maBdquJmqSZ0j}~JJU+;822f-{M~sPRhU7EQ>IG6xuuE<4i;M~m=O!B zutKwSDy)FPL#nU>Rtw6*0>Td@4-2@`R7}A_(^iOOw%YCh7$ISYWftjR8V4x+UPrew z%UM}6`b;?a1TiKf*4+g9OhU>6W%CUWmE>Rg0|oC}+beP>PoA)wOgrY{Nz6c`axuXe zY<5*r;FKx1#soo=#VI;ai z0CR>Sm1J2ADyLI$jnz3R71j|lup-v3S5rV@wA!_(g6847K&J+a4s9v;Mjp#=thp&s;`2TFRdq{ELo9#NC)j>MDvg)M z?;1^2M4B{rD!W+dh5^>C#?5MP9yBkqLvc$%GzV9Ms?%mYvgRD%8RTPdM1m|bO;cqc zKFT!k%775VyH`6^ZHRraP7?0PS+7h&eQr>pl7G#R>i%}ks!`968+wtKM3^6a7wUo? z$br;%A-lOL-&6nx)f3LJpt_znYa_n^%5lz>Vb`-lp~9T=%h#0x@0uc+-Q}Hi)o9Fx z{@n9!kq%VM#yV7*PRG(UbD8%$o0WJd0}?X_PGS00UL033hpJn&rjobKGL+gY3q||> z8w-swpfCDLDB&a^(Oi~*hMZXf|D#|%ltGX;XBh-~eOhM#bQ`PnA~sF*y8k-4<>~dh z0cxVrz4$eiIR4H(ghuza)LgPOLUd^wUAMM~M*pAc;hbNcD?FO3bB*_+>bwT`MRa)y z`$NtE-y{Q2iE1D3G@KE{L99(hb8GkP%vWMJNvuMxj~_cikjqQS zV#2f773XGpwPsocE9m#EfD>rcKC%wBw^s$^&T^lYyan>`_YVzvfu5n!B>cqA8eqY+|U?FGq;F>f&s#`0)Y7Mvwk zof2OAL?<|Dcfl?LFp`LIe1q1q4IdaWl1JZ~Opr#$t1Vsy%o=^8#@o3=zuQ{+=n6JO zaVS~xn{U9G+9|sdb{3ZlZj!-YW_GVW2^qMW>U)}TJdz|4wq3v@FhX3F=ydW; zb0NF*?sqreK>z$1bRqzgr0W`nCQRnLVl|1*uDJ$BrNX5wW- ztT+b$Ed)0>{T^KWcmU2$_x}bu9dHHNxYat0Rd!c(Rj^97p&2`~jLofk`p!plskJ&< zoLvhkr>e6iHFrej#si8uoys7QE*sA(Ow+h4P?VD~Vv1tcW{mQBsfB>P?pua5S|N|# zL|dOfoA?p6X)LDg{>eZEi^%ntm>W2DasuoAef*dmyvmr>Jult{UGVvH*D07Uzq0|; z#n?^IweZ!A?aPeAvcDI^xc0>eNsNkj83URYyJPMm*H|MN`YJ~LgW!bkUarjI@CLXG zes7lb!AnCUF+={i-ixqg!6u_1Ifta2;ZRQ?y>FgRmx>|MYNFDb$#kjY z2x~x3PWKN!9G(@%Q-x$KGN2WtV;u67b+en%#XJ3nA*7Aa+3ER30j2vRBkTk22`JrY^fF>4HWSzHLmiR}R|^4$j{n?s+(G zK}4Ej0Xm($+{cgde<}}Ft_UT62Mnj$d9AuQ9tlnbJ+TfDf<^sM=A9U~c9A3U{`F_; zl7`G;naE}m8_EfQogtyOkcNwpkKF|8jY&AWLw2A2mmq)s4F1C<*~aFZZm-+xZZs#D zvaW4I+c5<5?#guml2#;7MO*IT_*z39#o?{GF{3j7B&pxl#^|9iN^}Tyt0r%c?*q6t%!Qn*Z?a&AGt*v-A zb40BJ)ux-}rN8cHVwbghEB=(LcXya)bbqzF{~a#@_Jk%88l7j?m6%H$^qTPz;Ri=#_B6*BnqJ?*W1l(>vX@^F=xN^E)uS zM0NH9+<+9DMG}f*J_PN57gB>KQIq;FSF2erhiCH23P9~fLpPf@y`)t%T?%RS&UzEl z`eS{Xlh$9qA!$|R2O%v@I?q*J%RUHydrxHgGMSDt}#-`NA9kc&K3LSJ`XniIHEr zKcOl31g>^ohbVCBI@$idt?zN7FvU=9h%DL>0k+a=jqSh){f>={`qsTcq ztFuaeG?rQy5t|{nJW=AFPj1|`Lz2n}-20Fwk3F>F2}^O&#pvk2-k&!-szv%*GxFoCHzIpz_V z=IGMg0<(Jc`v@GU7fnkPm?knu>l_m-ZthOMLVzMpF}e|~Cxhgxl!nK2_Dy{&e7}(l zbm=^^gndY%iu!W{sT~&o5ta7*w71T0JQ($zlLrq$fA^Hd!Xr=y({K#jgEsSwf)*mp zW`I`;)Q&+eaGK5lzoeO2V%{0h2*?A^08RU*GvGedZ$1MSr2qUgpdpwCn*onyG|z9W z%wseceRmZ`$x}0$bIx7FXs$Vy{(ZR0F9EDKvYGvU+@O-|N@2B&D{G#i?N7-={(WzjXK z=`(9!X6GE4L;ZfS`BFB19(PdYwulxRmWA!2FEU^D$RxoL0x*hziSqzB<|Mz4OQ(*j zrFJojGE>>QG7}10#?^Sgz$6i9S@J;;u7zt3?By@p zj~f5w8_vr3=9 z5aOr-U%uB}RNIwI*qyn7-2uDqRR`pW3+1e=6Nh9DIvX!&ebJmwIf z_+~QWT9?-s+Ub7b(RQYE-DubCNYw58`$tS{ovGMx({KsQFdRF^v-BP$uSI$S6V4c{b4{kPE*EkOQ zdC*bI9LMcvH)=OM!q#%q%QrhC*UAz~d(#c*%-QB{T~Vi$tRM?|%fpvMU7f7nE;2ip z0q`a>Z~$6a41gzfLLL(*|Ma3}-$&I- zre^a}#%&H;*)w?h50;=n&|F=KT649h%sC=!%j6qsoH}~y@gV8gjB9Q_oqbiPF0QU! zQ`f}ifUMj1swbOuQU4KK&mfeoN@Jx<7si7QhVszqQdT`hVW^cAoRU zKFU*&lzQmZ4g0ngpw#|}{n@b$JWGTCCh!VJnBx-Qyrx#@UOQrD7}vAY=*c^cjOI&7 zI52uf_Q+Bwiz%KRqbCK&T}%^{Y0E>EIIIPLP9dgmegV5tL~a3&uSgn)_RULxlYC=E zL$MTa{=!kVRY|toW3{!$cMWv^1@Q&wmQ9p3XK%d}rlsirfzCCY*BeqYaXVTVEA!Z6 z=6nE6r?U>`EL?!zxY30eW#hF=B8z4@OK6(7H^hM}#BULbRc6LT`NG_AIb*yFwInTc zcI|r%(>*BmY^nVT9-sY}gjY-2`BTp<|Ifxouay74xAScOJ;o!0&~$$2J((b>|D8-J zVZ@H}%^wCJ_(f+wS{d;IxQ)`L;Ve@zsjv~133^7teavVoBQ)No;Rx}kxaZh#HT-O8 zF5S{B;4GMTg5%vR1B|R^zj3s%zqo0}OBGoK-o1-qp$+Fi_Kidzwb*{-(gp{hv2mMgPy{*0cV9l*gmai_6gB`sngSCNV@@cJ zN8Nx>M5KgY&vu#491!<%5>+g<3rNP+1sf&PV7|H^t;;P+kbD<Io;H|-UauJcB~!vb>;Ff17K!Gy&faTQX?EuRdAa>BwA?magJaA)^3Lim z8)q@WFaB-)3dif}sxlANJtu`ULGTd#@(bt;L0jL1t%|LS8rNvr2EYCa{^LKY%gD@D zHDv;5iu@PgV-#nebCRH#u?QsB0o&LF9erm`2gjAo{L^x~%d^2G6!6KAi0i20x#k?|&}T$MH3qR`%K{Dw=7p^GULvrOQb+ zHuK$-Yc%DAM4*Ewq7T_o{SKo+_J;?6+X|zB{3K&i&VJe(Y;5`qh#rFldK@Bs*5*C{ zto}5(R$Y^|BHvI3n@;ATFX+gt;42vIpM{;W&OI?MX6GrvuOsQj`MbeIEm0+im^5Q^w5jHc&-@5r2p*zgMhWvAgb5WbE*aV`Ptg)1yOLjMpZ-Z=5 zT_H7pPYIWIINEaP9&I>DFN!S`#8{9#SZ|iXuS-I-h9t`p1oJWS-{B1eKcOj*m)bM1 z^7o$xtMbB~2j^*bEnU5|_8jM|Qo5>Gz0$SHR4QFv<~~Zd*i0j(`#B{52|zvjCN0@Tf%rTrK?H0B&F+3HU7X#*OE;7$n`@iT}}2X z>0gA{=Lg5T7kfYcvia+0r$|*lR!-0N!GAhCMmQeGtT2ay3&5&{p3|G0r@x0d zQpqYti0i9YWLmbYld?zc0zw%lTHL6A$eXZ>_q6Mq*uS(B^7;88%lE%-J^TM3<*Cd6;k(j> zuIb%+HbN`Q<=LIU*uGMiX%1ew2QRM5M67MlE@0FKZJksqG;G(Y-3ZLIinl5L6 zuW6E2U^Zj2x=Au?<&tjT>l06Y#F}+{SyF1|of7wuSj}>26s|ycJ{0>Wls=^l@O|fw zap#iNjozwnKh}US465#MMR8zx>RJGJ5}ME7Rt}`LwFT^eFEYalH}gHuu;&@(+kAPH z8D<>rbI&l(`rq&Wk;!3T5Rf!pLJq(g{-2GVo#Oe=Hyb<8`M)0JDNI^D^t*cK^{LJo zS-^DZS>iPpjBYC@PwF%#B@uc;i>_Hoy}r)WSS2(Ie7Wq@V>RICK^&%>OaugXVqB1G z6gx+VitW|Zd*Dn9HS%8{&U$e9|N6~lG5_P6&CTcYe~$lMod0aUZlz zis$xR)RI}SZtTop=1mheDA%q^P&SL60fh;=z!OALE8&^*x(PxRa2CXhBFUZd7Qg(^ zYyR?{6TLyJ3$RzSuBq4Oz7Cc?Jxn*t7(7^K$0R1yRVDyfFPm_d7lwmRW;1w_9!kT( zWNhm+T?Q|>M)2DI9C9>FBZii90Ytt1|E9OK`KFlveS353+5UfwXP(4dHeXa^=NjlD zgFk&%t|#UPdWwdr&LiiF++sdDnL74Z?l4bK%vB~+HP8V{vsTmB z5(kn-MbD&X1|=l)xF+vwrx&1Bm=fO>n{$kb4AnVqPBS%Sd_GGj>(1p?j{H@z5#VL_ zLesGO7BX}O`53ed_w%(^B^R7*Ijgw7RkHBWr#q9GFZ*!V3`RtKRpr>PzD zyi|%7dp>7}XY`XttTT6CtxEIvcVQ*#%%?dNtzRq@t?PSBgtAs`Dh!OelE0UBwiek0 zD|IKYg(q-?&eAASjM3NuGA4}gMi?^WENiltjn%!Eem3;k6r8m(gTo91 zy&o$(@b5q4IEfG~8d=uM`yp#XUk0uTiO6U=NGO8g9*G&J5XaoXyrRf``%%?$-mC6{ zy-Fx16!Ym`1Q`>`ATL?^#^sn#n^g)$DZykjq9P=s+{t%6NRS-V}+C0VhXWHsGrHS>t+0^`uV zOritw^UL{LNJp$#u7iTYn@`T)08Fk!OhG5{90M+t494U(2Ay;71sO$VqiMX$-Z6Q@ zM&ES|6pIeo50s<{`22Y`4)VBpxfJM31P3O33^qV#0`IclW5Pj49^P$tSqu|4CcN#u zjtJxbIV5Q;S^7>;5p?Jy^tRBo!UPt}_9HUd$Fy8D8#Ak^y7G@Oat$$&<5yMo&oYXI zwQ6Z-%d(&nC68=tBDb5jP%qCnHp?BEzjqWdlF|S%w_f(z>-DMwGbe%y)UjCdl?0^H z6M_wkb4(OJlUuhlq*CLFYq!>k$f&x-Mzg8HEmG+zsmb#=I~$daf1X~_t4*s`(2Z25 z!K9gXEP2?MJB=Q2(p2eno!oWZN#SV~Lh+5Cg^&OD!`bQnhm+mog92Yki~4)#qAK$Y zF^a--G%SAspb&jrE+8u_el>pPZkDN}FhYLzOI**Qv~cfn*vm(jS?MdA(@J^oenQg< zdX*D&`*P%!Q7`KdpfYPU2j=DgYDfxC2CP2?F^ZX+8i-9$$*5WwHf7W~UFW{hsY?0w zIAfFNm0H5sl!491sMV?3{uIPmh*67o@Y-4TY7n+_l-7Y`V@Oe0vX6@38s|g3xA2-l1%1kLE-I)xb>Nmq1y5UZS-zP? z>G+?PwTo4NT5z>;iYJY?w*!xJ%uv<0@~HP;J)7J!L}!TMC?<^K;FyG7FJf+`--|^G z4QZH_dpEwWL<|)piD1==;$FBeg4`15EM1q&B(n7X+=Ifh{*qCg8>bnBT}baQtn(`u zo^KB?PIiwEKkOg8efNXUFw-K6#*j|+9n+vagb_n+uwtn!C4WM6m5ydG&#GTExfbO9 z?#1qhqthQg>>r+2qUG_jGyr{edVcZY};^6%K z?$OMS!jzLFrP89IBXnp^!pzm$`N75R;mL<)9Fn&vc;y_4X+Iao3=Rf^!_yNv=}+Iy zZg2wc21q)`&4HRG952)67CG|cyjN0ts@oZN3$@*3yuQljLVh77i?tiv-EToMm-IJ) zRKCx@Ko5DUG(S`-p_Nwl<)HWyq^$7wI)2yzHTxnzKd>KlwDKSiXclm-c;V*x)Srt_gxIpz#Oo zWTP|0sifF!EniO8DM;2x1L}4R&Ae@WaiSG60ygnnd1i@r``&A^6kK0(OqOO&Gl8zg z^XOpr{6w=rGY)WbKsKE%S?>G8qE zj|cAtAC3<$&JXu0P?ZF$S$G$r%;oklXge17{V36VW5;YD-osDBqeha%+D}*1pY5zE z_hPj9_6VR=Wf&5<(;=xJ1JvzyR|!or5utCqgDL$F5d;# zl;D4=NR0a3{<*d?s}tNl?m#^U%`7Omo14{Ejqbp0xSd|Vp>E9x8~EO__qAe<(&l)X>NWJ(J1!5b3VJ$*iFc679DEP5 z?Hzlr8<|0?+Wz=xBKoA}<->PKw?B8Mye|{60Q3%JO!BQHol2k^VkS(Ew~!&VgR3_T zywlbb(Pzbdoyi1(Gh+uHc=4RyYqBVwJn~NX8gXVeU>0q$YD>_-H=8+&!mrvWT+CPF zriYx3jNg|shk&#Bwn;cvp<5BYK`~TU0+Ltsx2!ROB) z4nXI2?phHEU^FI-H<2sg6BtpH06ZbruYUDV$%M(-q?#>R9)~_8K`~|gUI=`nF zanK29Gz6W1#4!qZPLTdA0oIHff0Dk+NNrELd~0|=h$0^nH2IK-#h|Ekjixi|T%)N| zWhPC%D!Lf|nler%M~;YCwm7P5YpFq;FvK&2t6OFn($$6dXBV#p8aI`%&8qz=1Z=ae z{z)Y4m+I^j6tU;7oLIV4s%>qFQkHQ@d79M7Si8hBB&-cDm#=e3QXV7&5?mv`d~vE0 z)qB^X)ss$Ds#vJa$yNiKI7=RenwIX%P_wu!!b22Hg9!ad$TcfkB>LO^dR1uRK0R9I zAx?&Pn$(=%^nU-dCEoyEkD`-r)^c89#&JB#^3_&)V`PCE@>HLo6n}PuVX<;HD8A1R z(QBPU&UtP(H@-)+KaEI!E7IGkif0yg$)+8G2Qs@kWmLqO!&z z3gALVbSrL}GXSfwtUs5l1f}OtZdpENK73c8#-3E;y7C^Mx9i20W3D)vRDA1N5c(^L{g3iXfi&x>L|ISzWF;S=Mtq7Km4#Jn?O? z!jq^07VgkhE$|v9Gc4ebCJCSJW7-EFABv>8`fEnrf?jEKkm6;@8ouT$? zdOGJ=G}EKa(p0D|HkYb-Z1L;}pg~<6vLk3Vw|A;=t5ApcAj|A5Wc7I~79&f(eSbf) z)LRJ)lEu=xOG{tjz??e|dqdH(B4*x7Whr+O&oSdyfu8X3r?(%U{YXE*P;QrCcy^}*-Ql2bL5MyfN{`xTcA&&4$Ny=cbG zMBWtZ@wS0%Fv_`5VQnjqBHcsjVmSd_s3$16N_2&{=)`WRl^2P;{wnb_y2EgZtRrq|J>SqzW?(vo?5 z?vX6H>TxSvd&;K?>VrL%9Om$>1wdea?}HN(qZR;3dKFVi3dr?*&R!Yal{5(;*BQfkd6>PE5-)Y4{)^yvy7G%Qa%GUCxE<+ zR|cqno84p~P+>-c<@j;CRR@;cNFl6zMkD1Qa=Q@=R=w#r>q^{th3uEshW~m`h5fgb zd*977!~WaZdc9Gw|6ad&z4L7UJ;pP?{r9K2!Ts5qtEjjnBko_@H=Hcv(9>=XvWT+HW!23c5g5LO5sU*w+p9GNPe_P_^8hyzcKcT?Y6()FEH?esOrTWCM-_vU0hx zRw7svy(!z)^KGmF8e~_k*ctQ9+rwhTyqXdxp zCe!z!P$sfV$JHF(a-o&5V@ttmJ}B*t%{Sd%x7XchgLZZtwrw#-#$4(`h7pPfcod^> z4^eIvi*aNPgz_ZjD*R<)X87G@O*kr>FqD5W$Jy8K4V&Q%vfW*e>=&}IRSqE6 zE)mAL;fnxv18wcS3}9EX-6wFh^BTp0>+_HciL5B)v&x!GaI)-AXzDF$VOk^ki&w`s zm;5FW{#o)rOa4cd{L7Q@olfV0q`a9N&r<$b%0HHrpJo5&>%Z<8MH4)V2}Mg@W6W6p z^|m**3+unF*PFfP_1|MWFTfe(9MPBoPJkMIa63jZxJq#p;&=oS7+k{jl`86BoSS8-Nf~*6NNhUzh(X^jqDl zKj^yr*Sr{y*2O>OPj(Zp=YXzYaGfS#h$F;W-*nk+()y-*1+QD*bonG{ee?fXFTi_9 zF-aLX+&^HgZbHd#DB!Iw4iQ{eDf*i7`G0<}yMKJporFtoqjvspY`@vsc~hAG zo7+2Yp6CB#JpcV~olXb9Bmj5#{n_~Zh8QrOXW6(HUt?M5(+}hIK@{f zN1?zv!hurW-B#w>}%O(4ByeJ$n4 zkl#Xzq%AJwAZET!cfrLN0g70H0x9-*1_(@0ERaJXz%k%s1julRsDLHllG|88;x2%I zOcEGl#H7pwDHHWXp$P~HVsh^gPm*W~U_1qIIK&ZFBqLO)eL@)$Eq9%>D}|@^;sv-k z`mg=-R_pTeG9WP{5o)~z!UzBqB}85@sPlyYL%U!MDQ8{%T9{BKrHHADW5!_=fyuO^ zZFW#jnsa%1*%AmG#+;HcRr?n$Np;x*xFUozPGKS#=NHc)jxZmmS2hCXXD1}VHKLuY zwN((JBqCEm5P%ZuK2*?86P1em}Wju7p( zTADLdUl<5r46cwEN3H8*`!Gogt&2C^5Z$b=Do)R_7Hpuvt4<7ozy<6n_&7G$idpdD z1vsOKqTf@@Fh{J_>VUkzV57V7*Z*vF6o9SnX16E)I(x5Xs#x(LY81TT+Em!l(NxZX zct|1RG!1x4ksd@j;)YBTsaETPWUL_Gs$rGASKO{xL$1Uwb6&aZe=&Bv7$b&EIzOhV zZ%HzuXkn4ku!yFTK8Wv1F(n;-IfE2VkQkv~S6(F9AFF`rtvw-Qm;{r51$+#-;T{7O zg&<6+8VcDp)l94PH}K6j7dZLmn?5+gj04eY-DRe^_)?NYG^M8ByBJrEU1~n9hbTfE z$$-#cQu+Z|LMvA~_2YZlZeM~j+_N-_l&gbXmf&U()&8LJVgSehi~*z+ ziuLH_FTWPx>I05@5oGKVIQR~%Pg3liWrRDR`qwE`v5|;9S~V3eGX@UOL4GT*^02#Ncua zBe5zp7<&C{x%+J877J+)u}pee(lx79*ceDfjBIQ~ndX?>hc=FH^U^ME4Ui6g0oob9 zgePygEBDSW6*e-?V_%B<0^b~nFMwQ$#jx}rXsx+|ZeWy3nJq*^MwHb#v5|trdLwCL zc^F;3*$qSOAr+_?t)ftpD-C_ldr{ha{9J{N2LMUl#X7*tN>4<4P zzDq~!VNfyH;8?}6wtuzMY0e4CxN2%BfrnSF^-o##W?$+ou}9Fu^-p!7@5Bz)rmxsd>!p|sTDCV{)<076dIPAKE1O<3F1=^!Cd+o7Q#0$oU2k{VHr5G5HDyy2XW6;II;Wz%@g0cTDCXMd+69Lk zqzo}I*c}|L@8c0-ymP$2{m_W3AMco%z2FlNe<**6vYX6;Q9hj zJabppeG3^5v<4U z)?tkK5uRW^Yat=U948R_reu_og2I?Vz41myGV*76sFo1l+1}pTsZc;HhRGgD;|hvjj1h>_ z$(4$au<&MbnMuPuw4m6jD>Nijj!z5|HYV9lB8S%~Rw=N3^(cuVv2vBH((`e-M0^78 z)FI`)_%3E8_k`pkwJn$9ACNdmDMc|i468h&LPtTsa6F2Tws*kFhJG_D5M+6f<5=HEle@yPA zFZCajrO9&1Dx0sX#Nm*nG`iJZR`#<{igFKH*WJxjqV&mHs`7i1QgDPgM-==lRb{q4 zZ*PsGI~1e~EhoeTq&IX8Okz&g08PpDrG@we$I6sA41KHoV;tiNjKJXF{QbfChr@lf z%wLm7kcE^EsEXI%V}vO<+-FvlPjoMDg`jwiIE>^5>S??iMTckQ$+;_;G@m|F1Bn`v zZ|tclrMZ~M2@;Bj#L^oLZy=7ONq=|-V3tI#A>uRaagwBTgt|g%og)r$oQX#z4kv_h zA^Zekjc8*4%A1aqR0{|dbSANkUMwi?67rlB->RW_j=22yFy@Hfz{v2pKwK(x ztUt(^IwmP)YO>yraiC%wWPQLPy9Q&(^n#gFJQ^XoAdmZ{p=K2_Izl%ns*oy2WCSAd zJrgNKbEFFn&d*QJg`H(0V3zZI0+$kU1*`maGx<=?+lcQSmlV*6rnZOF zdZ$yo%=#qy!U|Mgv;%WcJX<8!Ag;Z+=z>s4~F#&9#@;@18Wn0o(*%mjW4)$zNBZq z;J3!x;7{kN3b><4o=>P;-)HnY$(G2=lhBX*louq%WGLS~?^{b1_q(iVE+f&u{^t|8 z+}!wfqqxOgzJ9&JV9hCTaWr^(-PcQ`*%UBIvC0Od?WWafwVoIm1P^@s?YA>1U@~^q zQ6S6t_4FvP<)J`?Z%}rbetC;9udTNVo$`RhwtUD#;l9e}6S(9wMfDWGG3FRX`zV6b zfpRDsSHYqB1VZr*Xm6u5BVD3=y^r95UO61pr9grxCZW@JQ~OMQdXin&PkEcgLjmns zE|cIB(4OfhOoH-$jfVn5h@+IEi!nuPOrl&Md;;IAPk@_mPmTtkz~#G?W+Ub)Q9;-P9tuzdLp)m@X4=p#|D@Dmg*Gfu zph_LqX~P0hzS^fk5*8>>r4GH?@TqiNKNZ@rK!GZC=+}l%iv-Phph_K9X~U;Pg9>d} zpg@&6tkQ;0i~>GwSd;@5>ab25J{2ldXhX&!Vg_TFs912t}4QsWb zLzJ)hsaP926sTMp`nBOx>AHR@w4pJAYv0p4#Jc7lJY<%$%ELPEr;Ue{CeGI#4ba zC@BA@M1ooh3@m1@*fG%*aM=DS^}mt=5gwvo8bs(vLaysJ$IBs2nF+L_0^MNB$pms# zh*;6V!Gr=hWl)i2nvBJ^W-S7xxI6Y3s$j*0zr~`yksr~f+T%f-8U5IO^RN!L|VZqRd=iJzfv)}qy4LWi@Dq9{Fl0Yak|qTT3=nsF|B97+ud9B6?lpMS zd5h}Z^LkG3@j%Y=PlFRGc%YI3G>vyzc3iW_1GT-+s(!)ldY*ZCKz8GV#OH+Y%LOQ) ztA5EY7oG|p7!$^KBMcd;Hirrk6hZ<5Q4c^>@iEX}!c&pSI)R{{9;F6IczZ5Hk%Q2)Cam5e!RHPJ>JizfRDrgGC9Cw~989te| zT?)h`L~i%xU6|!qvJ(=bA`{5#shQ@NI?j$(NwI4wtX;XyVyjCn6BNVbiLNDc49t+f$|5Rv0 z7K~AtMu=ATUe&g8CyO!2>OE%e%=hPZoVDqUi($COF+$gg~= zykvp`@a|mh>6p2$ec!{i?%{6_ zURCG5OL$!z4VHxzC?3i7;8k@RyM$MEBzq~yxd){^cvYQ|F5#6OlwKBITNQZ4Bt-RD z42-i)VjwfA>D1l#e_TOdfmbDu74Z^0ww$L1FMV;#TgZ?~6e*A_I;PI$6>w%UDg-H+ zWP=$Z>Asa0e9noNWg0km>BNq^H6CP{%ZgJXv&zs6JrjlmMr6%ch)k#vVI&a>C`se6 zL&;TjX1Pz`@`S|5$6p1!R7RvCy>>059dV9hE3a7rz&zwCEGiwmf?VEK*LIV_iIFq6 zQyreog-98%D*lQH4Bx^C#z7@=KD>?y41uY)Fm?^`8pr^)a~O{ht8znl@mlmQCU;Tb z;DzouqA`rLUF36?`tUN<0N3`^LKlbRdi%177o5GP@9@3O?gT40`3>d`@v7o`=@O8F zwPqQqf-RH_nG=A7t%*!V-ZDxnF`*Obc*rk&n@JiCF2($4C;nI=4Yx09$f=}SEn7P-~%1ETINYOyR{>eaRI#wNt z36~-7Q#Y2w4I5G$WQgAqdad}p=3JTD9*}+@EM5JGj}ZkBoY(-Yt6S!|yuzc0T)bqy z-~&C!%A5H6Bz~n756<jI4Ag}KvrVg9-}#V` zhYZATF7yj>jp9p_Zju7?$o{@L!47d?GE~vET=&omDycnwcud05&XdAuR~=TIEFqzx%q1$! zik~Uw=rj&cmPgc~QB|7!0*%hqitavC`UEb&{Q;LWcZo)G8fyZ}#c5R7AoOCyDuahU z0TWo1$ILlAUc8~(rQa*2D42JlAx3#(?BW167&Xyq^UT=|N6o^hAP8hWh8uZ3t3{d1 zoHU9uy0n)*E`AN?noT2j0GBjs0xLCgRimb~o`!LT=mbl@1G|TV-5Hr>``YhuLF}9g z?LN<}>tK~UvdCS(q{;fO)uy|_sjtr%OUHziH{@1kRE|S1l1HSpxtQckqXpuO3mnR5 z*4|^Ap?T=yr@(X)i@WB#=61#%wbf{PZ>?Pb&1RR{S~t?_pB*+vk-2%j3eAc;YUf6? z=@qu0z~#nZ>0Q_cMd6AVBAS>aJl?8ub`)ii3No z(TCcmzRxE0L+w%fcBm(cfDII5rWVgAnf(9ky={NnHnKRnulXtF%Gnb;Yf+Y+xVzqy zbN`N$Zhf1^KDL|u_N2WvL_!i`ieLd!j<(7D?DvNOK!Q(tuw^INW4>r(5d;QTRqOjpU!ib|mgm>z+~VZzyBU-1TWp9QLv1A5_Yg#*{Cj|ms)EplOhg3G`4pO)@` zEl`W^^cMjoZ+N@kaFJgHDrrO1(m<_x6}$wdR?*`s#FeGI8!{R2Db*}xIqGUKxmSSt ziN{LRXrlX1aD>J)Yu7SO;mopF%o`L4k|-6y0%Qy+1^6ck6-0gK0M$n56`-(4dqM)& zOADEru8Uf+yVc;^G}&8nm%K9?Q6g#4#g+?H^-`_{C{`-ZWj?kwyMUyo9GAt1Q?^~9 ziiRuPMK2GO?z@r$*Jk_DX+d`p3oai7oXgoSS(W#W7S=w5gRa9f0hSIP#lECMLC;Kidxl=`Z8a$ zfh37A7EzK(5>aVW@#LFuIiRYygDpVmpU+&-J{dt{dC-{B5*Nx8%L6s$F&E`vc6$Ri z1LtKS3FRfM3d&#ZNM4>#ec%HewM0|Z+Y^2BW^O4`B1W|EATw59qV?$ye4xK%F(f%< z`WC3R-B&a{%c4emc+Sy@L-DF{^~kC5>>FdM0@RWmy#n33IeG=CTkxq0pts>u6`({G z)njri!#N&KORiMg@|AX<@qvAP-a9{kePfm4UaC{I+vis7XBEZdUdm3bYY-Fd8CloE z1sYDS-CVprcl;1I=^G{D8%9CZJK&iz@P zTACxZX>#?c;UcVeayOk?0lJ=Cb!rKb@|ug#lwPd_bbo?Z?X1J@BD}*Y#(})c<)?Nh z1X-C{g^w(?*BY=>-Eg)^E`__y<({Ti;Ujl8HJ7>EJsZ)3*MxZsRAD{NISCtLiiDfg zY%dRTH&b)jNPTjRQ*$`US|+#lsr{aDkxf<=kM&ndO;35iquaxC7lM|V+A`wtf9v_& zm6W`8@wk}UT=Cf0J?4qSJ{)C2&^V1|fZlg`ewWi%KUG?`Ozloq?f%5$R$9KZy><)n zxUq|`Ks;`QYc=tBsi}Pd@z}t%Ch_>cZ^1d2lp!_1%ZSHjYHJgZZ$7!3POViuHdFg| z6px#ywvu?dv7n}gl0Rx53C7NCsP@mmz|n43YQFItBS+tF=l}fm5{MXO zm}4;Ia5<*YMBj`@(WMXHa{=@tUi5Bz-sROBItB}`^rVJ_%?@c{IAs8eV? z@@GlK^O#07y# zXJAxt>$xg)3EI^i0ZX?PY?*AD+8|?5+GR12aKd7S+5Om5p@TkQ#r`K8#!n;zL2ga&_OcG(cTb#^z=1w)yK@p+Jm}!W}QY%j7Mx@srnz8*E znM7?A_FwSi$puTEJUM`W@(f|JD6&PM-aFm=y6OC}<#`($a8%0thiN@EF#3m(ri4XO zU#`A4i3njT;ke{O{p;p9O%r+0>xDd!zJb;c_@r0l80(GsbvNa`EDouVDZ$hc*kM=o z(REsGdEPOSw@d|(N72l6(6){T6*q3R{%SeGOs7Yo@stUU`CWau7^CBm%mg+0T?-0@ zkW;;p41t$#qu8*>dy$fnf=XviG~w(7LN**yrG#s@Xy5Z*Xawl1b-SblVtQ?;8+)*M zRh=|!ideDPRPB^PW}gri7wxt|vQdl*ahyjr;06f7pE_L@->O>ehkFh>u>Y zo-TcOF|&kf85$;S=WzMz?Cj+15(YFN7}2N>1oaQ=m}2VS0oISzLjKq(9BzKs^gtz$ z7%q?Be1CEY6DlPcVIkGxA^`kc&Iz`x4+c47R8Xed;W}~r<65&ILIn${S zkiH^5lD_91$2z~Rnv>AEUy#ixbm|Wz*LclYiow8R1@7Z*%%f`qS0R+>+@5)hfKJ@ z{e;at!L zy?pKmYQ{a&f$WKlU%@9B37UZV_v2qZfQAah2e7x}0hB899zb9HR<2ch0M9&tYVFbk z=z9Rwnyv@1?=_XXypPFJA-J%A#90)z&-~e)OdJIkd&-bVa?R+*cnUrJp#D2j#HbG$ z#gDHoqa;Z}QE1SK#WNAp5EMHe%*<(IPok&)&u^z^C#RR#H;W|;k%rZw*27`^MDT

``(ypr_lJi+zdXM1x^QuJc=QT?_R0wtv{v!n3K%&{DI~BFOUKy&Wv^vdW#W-QTo+$gL z98@Rze8Q1+F8)GHlf}7k74z#@eR9pkl{y>#ar9~%4&R+`Bfv@txXK39hjXgMnD<4B zmtQwG3MqaIq~ZKZbt*|dr*DryM;4pBZf@oUb+8ZdOrSC5&7eYWFsaWb1(zaA6cFo5 z&oHu_qL3P zco=?AqJ|r!rcU=-a4aS_LAPYjJJb|p;z6@m?XQ@}UA0r3EtPAoR51=BX4=;o$IAnA z<(SP0$(39mb|o@HzH%*CY6ytMDF>!xUiT0!6igi5-vBAtk9Iuw3J?1gx|_XY5cZv22j(Jb((b$TM@zb=#GO@+&{h3P}v5 zq|gJm z+JItc^gVY|AW-y5SGz{Z-mvM&RfWoeJ?6b(#o#fG5-l96MggBBG-ak}aC%K?oF7eS z!ih;T=E#jWgd8N_&JVK7B3Z$uqwwTe&yYPjB_|ZDieeEsL1IZXGeX?~DwYa82$R*0 z)hbMqbf$D=3nWXe>M7MBW)@N*azK$U36`cbwt{IHDX{rJmZM{i34IU_=$K3y7m7o? zQ#{U4Tl)$WLbF~flPSh*XA4mckGykL^mI}zJm2TiXrA3Wr zIhWS;)+)k6UTeqnxvl?lhtX$tTq@q?h)$YN-dkZq$+qsy8>AgvE7?m0rM(G}DHXl) z*LKc~$}MZ6YuL>7;)C05_4v^C(om($S$By91{dW+Sn2TX)G+4PD1cBB>AkMux1xri z5$?N`gOLXxOSIPNNuf#XJ+h2O0b`CNq0JnTwb;3zcgHlEba8+#lQN@H%M9>yG7?Iu z8JS274*eZ}$KNqKsKKaZHdPqrn!b+CL^ZRk|A&@$8mGqE>!bLqKTHIjGM>r8>xG6! zdv5;NSOFN%v{0K#{iCK{?8~+Aj>%LVi!=s7V=__vTJQ;6;$6TK&61pZ0so0T!!9`d zZJC*~IEXS^v|qJyEF$Cs7x`Cv{$Kqa-(VPT6B3d#wC&C8B|u(;DJ-D)s{@7Z(b^*Q zj3h`UoRGX&UfIloHZiq2cx=ZbE568W)i5_}4HW4{ET+XlC#b0_BJsw~rgd0{~_zhf<_A}gzbZ7GmS zDa8wj%Ef{%VN;6=TYC1T+Cis8XyND5Ul5kMkHs7^u8%wU0I~c1u7Mh#&a2#;OEsZ~592ze%-*eX|jik-l{o8;@`kQY5 zn-0TgvIOb;B;VC;m+7fY4lO-s`KQ)LLu6D-2i9TXw*-BI+WRRvNVRlUxCkuluHuxm z7&_~6undHz?J`gn z@F|@PsDS?7Htg=~?rdxEbGn-gbk_ux0G$I|iNa&O$dsg+?DAnZ9n)?i z_y-!KTgK{9mW|A1lF@XHd}Z=Shxx@BIt*xX)0x4e2^=8?v;ael`vi*vE)tGGiKrRp zSG!jBQ4*29kkV7#(|3=$C?t=qr79PNu$7qsuvF4$s7_TPLaQZOl)XB6?R(z)*OZ7@ zYXlBq*{f;viqK+zmWzXE=Qb+Wa`r(M4+W8_$O1Jf$6wQRq2O`PjDbS`UTz({;PA}f z#oc(Q$lTl-IVZEDn&ifW{F?8YI${79XkbkeN`%JikW9J23srgzJhsAM9&+Avs5U1Z z^4;%BiEK(jWNFa332=(}@2y$Df95~);SEpCmeqC=wQ*4Ywq{4W=AIeG@q^jcSLCQi zqwOcA8aKG}NeYTLqza#C!jfa2YOfcYY}yFvWm^y?7LK_#oO%rtb=`ojF?YMOoA74R zO>k{lRAK*q|34=uKfZc(`s%E^xASbjpkA$8`UEeLeJ)154@{8WK=5lxd*!dTJXaqX zDvkP@%%r^`AXulw7%5e^YOa9-!`_w;NuX_>#RBi@1%5$-t7{@cxk&4REXK6{s>=xv z*>F}Q&@hs!xl%{kcr4v|oYMDxHhwz}zylg-1AyJ>sDGLTkcG~g8fdH6y7oesNSSHr z*;rGo7O3-eNVqJna1JW|Ns4C#X=I(a+!~_!N5zE_bh`nO0SW1IeY1;~e%2gt=NNS_ zi*AX~{;d$2Bjj$-39nf|V@W;lh$l0_M&lIzBjCyG@9GrZwTiaqnRc%BX1p@Yn{9Y& z_Vcd41DmQr$Gqxn{oO;uaA#jy*Yo?-Bncdv!;#wpdZXED^k2CBG>9* zAtPa!Ad9V`eH$bnrq@JJFTZ6eA;?0-oNE|OY9xlv;W-?ici_e0`SJO-_wM-Ohm)T# z;N9Wb+2Nau<5%Z!at22yZ(bf>9G|>7hm-H&@XbHr$Ky9Iw}CP&H$gupLcz`junCGK zzGrRPcJRE!k@iLmAR_T7BO?kUKBWSk;N8nP5{Dl8EgQRQZ94v+9v=NTu_;>K*1~iT z#X#?en`$ir|o;&aWlms~9Vs-$>C7IYG2{XL$0H(VhNs|1}zTe;Xcf7Dj z6?6cH@Y?8_j&h;4F&fa!&EiB~?y(@hS{#&YqP_=kQS$dzoL3)f$k+xmqeJ-Km`~^d z+(w=BSfC)cUAU@3$gkswlTh{|KH@U>Aej(x|W@26!Z&Io7x{%UI zq7(qH3&Si%`=!^lbM+>KS%+8kxjHlt@-qt{6VVd$^7LnBY!lX?x?B8Z(MBCL?zFvh zme2z*dbKyLEcllu-G^rlPaFStT;p#&#sdDo)8F5(@c;eY-3R`EFVDsXRM_Cg2Iy!5 zSDHLJ?j5}}AtWRYArU;ls&2k&|9|8aORPSC+1$L14bT1CvLv3>u>bGl`Qwk?6PU8e0eX5UWmX;f&nF5k!I&Pv zliuggo~N35XEe=(Qd^bI2(EvJlg1LfKmM?$KImv_@*NUVpGjZ!(1Fd61u1l{=&S>s z?@vxT(9!2k2R0)nQ|P=nJnN|c{!eFX3%Z{_dkD@Se{}VT3rA3&fmnIQIDWLvFFk9%cy#=Rgo^Jm>7h2zooUDvwF8!xO~mjZtp-x z@0L4RLA`i-PHYJDcffiBc1$yKe7hXi@SP56Bx$)jlEAe>KdPFqk1^yaL&z`EMhcH^NX-0t>bH%^Kn4{&iet81Z6=>DXH zUW3Ig_yjSFLp9z0PI;Ref{u9#o7(5NK-yNdDI|i$=@2?3N&K>dYW~py{8@Hf%-4o< z_6aX$FVP&V1L%j%owv8Nf??Q}Hu{HQ(g)_KUROwVqR+ z@Q9CQKPq-xM#z{;rIFAT&$0FG#?qXIUHt)Li?YY0Yq`35kyZv@+ z*lai4YD;gl%8>OPZe+(7?#ik~>KIOcYsb+4AFNUZAMwo2urCZ#tljE zL@+ESkYC){@S`yIN*`#i^ySt}7vS`Z{z@w+>10a95uYSPQ0V+;-{0+bcRKZxznu>J zt-M1#y`iIr>`nUvnv#B}jMlccoweaArs*{oS7zeNVaa@Rx+1c^yEU%p$CSos%vcUz z22d7{{HwpBRIpCe1%k&P_+Uw-+1B?-TLUGoEr%-GbbaV4T(g$Mkn_RPNa{bmccLOm zBG-y2$0ck@Ou1j&C#uZUzXXz4JQ7sOB@tv>w;;zJKBn26S56Okt0kIPN&3qWZ1cc> zyY;|L>~t+$_yKG8eRiF_I6NwHgD$zN#DD}V@SEG8_jp-EPqdYujRU|fl zyFN7jph%m?DT&~ehj5ssTm~efVo9uy6S9Ity0K_~_!=vb~IG;Pn{`-|@#!Zn%cJUleGYH589B?s2hesD0; z`D1hXey97lU$>sT|9?OK`sDNHM;+KKp$9%e%FlIv9%Hvx{UG_wqqZ+?EBkkRmazZo z{3o=U&B*9Q}_VOGskGM{ckNeRi?AJ-U16@4L*}s)d?gV%Go%TVW8&R_KW9OH81@R)nn@(wL ziX{l@=G@-~9)+cn3KKF@1Yqi`=z9Az`|Fhn!>nh?3^2v&TEPB36BxSarYJMn%5`L? zf-8mfJqsroCAid4nnbx$=?mo+ve|6XYk%Z2WcIXFoivJxj=c zn8NlB<-ey-pM6)A|N7tUKgfUg@vI>KIaU{)HrymPHl9Q^@x#y!{;va@prsA`^NjPf z@S3yoPdcW$HFWgty%j0iPUG?jxtwMUOEavKcJLhhd`#cIavR&cdyUQQ%i@ESj4G|v zFOEv<+H;y68H{g&?!iK2UBT*8c;50wtJi_^Z#JxS<{Ep3(D}3M{aGHMBV4Ij6)!mL zcC!)oWV@O3;W8#YLtHO0Jv$sDLF98bl{C@o4K|CcH3yiHp-dM7)6Ab{QKY|;)@-vi zhg_4Qd4*_m!MK%f``hjBcDD+uF8in~L*gZ<93j*BT@fXMr*K-OHJ1WST!KqdE{?jn zPhxF^=O%Z5`B%S@5X*mgrJ?ntN}riG$vpwWhOp`SkviRM;d)fja!`S^G%${0L$@)m zqXI_>jP=n!RZ66;zhg9QB@Ww@dk&G;cE;LCy`mH@Anyiv78AL7YZW`ilkLAt3vFAp zJC~UyZEUxO4N|)mNR!Bz2K2*1Cl_-~ zzx6Z9(3=@0=O!j=p0R|Qx8^VN?v#tPcxV2q+9yvPYoya>Ziw{rdi+=hEU&4JE$!Uh zuhlsD*S+I7^#Omv{AZh7K%7kmRBYKyUwQh!<1{Z@h6OM3ERjW`O{{mR^O9mtK5OnI zw!j4!y2eRLN3>?@e5nxU+Bq*wjTrUXUP9Wfo{JMaZa09|7AMz>oZI(<=L>Gan|#n3 zFT}ZkP5OJO^CoYlt$M3=*CIWDv-0{Fa7m$fuNJcO9#^)i2bp&mbKKS;V9mJ$R}oZO z?N~xbFJ-4ThK&Pi1fb-}Zct@AWN}*VZL4@Uj5ta6pH|=7;gD481AbB@)^JkJ*XmBS zOF=4>7+^dD6dzC`*bD?m#oRuy7df_dmH(J zcHM3kFRtqQC*Ad#u6$nTdQML*);P@8em){_n{0Sg)Lj(4-%3 zWe0|IY;(;6>LJ;=j8kP|7zFJ*XOS&cfW$iI6MS#JsgH*j%yD4kDmRk4xxHpBVRtYq ze_@<%n=N`K6x@_j#Om=$#Am)tXy8i|G4Dpe>wSTF`>}-Epihp zeyWxyQz`@tDST35TO7dt9{!U}&a>f=eS}W8F4JoukFb7&$ry}aDwwU;GclR+2Q#NG z1v1h_6EFu#{nE4hXC*tk6Vd;+hKNrfXcCctLgz0X==|2X`J_mWxM3DWGx$9t5gRh4 zm?Q~K*7x2~-8;5V)fftLnY*A%J}K4pEwn}f$SF70t+rs=bXyvRN!T=~dimyDwM9!< zLW3>eJ06x3{pU{h4(?fD;6l8k8@&SFKsz7js$or$v~*%$=CsnSgdo-$EQNPhg?tnn zkAS7=)qyidhwi0(bmp*@eq9;BDixM`)8no}Z1Xkbg+y4?h59CGv0atc)aaDGDhSi& zh*HBCQ*_bJ_~q%Ay3JdY>Bdq|weivyr*mL(H;XgqoEflt&Km zO>b|#l^T0p_m(=0umVe^WQxw_q}`mkMo&mhndU4?S+{89lz1`4M+WG9YV+qWK9sSa zMh2U|ZRN1-ElQ|uNL950w`n`-Fp$gmB)bm7k7a?xWe=DpOjHP>W~ZzkQT|q28>e%B zYbENr2l;Oa`CkVh+}Zta|LM-tn*ZnCcMtiW@8wxR{?D&}H9?qTbI!SG38!>a+6&cl z6W`Kgb}qM3M=Bm9ihtO%UM%EOjmQ-Z$fT_u zmm_~m7x!(hAm$?ke_`>E->xpohMm728L?Vh5I2gaz@)AtU}}>^Gj+OLQ(c13mfEi1 zR4UHyX2-ef`|L7})ec?_*cSvM~G22X|fgYi;TRT42CVzgZNh@|`O>TZ-*qhRIuS9i5@_ilQHL=Yh4{ z4{N!iv*wV%H;&CoHL@H7KV7`RaW!))RUI5W*E!xx8@FNKloQX^c24<=vRh-lM>XP4 zBuQ+bfa7_oV0jKK4(Z2vUp>l!Zg-wsxZP|Qi+5Wd#K{E+nhHjz6zit4A&^K=63%SV z)TMT15i+T7RUG%OQoyTKo6EZpIwoQLTaBUZH^kNMcDA4s(Rh@OJJ8?3HRNgy z(T||4#h3m)*MZDz{u;$Xw?wj!^-4a@GYi4>m1X}Q8`#n|P=9ow#4!1Llw-K#!#fs5kg(tihD^w`D1&UA z0?qymb!Qq>y(Z8_AESa_Dwy>fGt-fw%@72MLq5q7r@PV130-)>c|=KE?&{r`V$^2J zmFfs`HW_ezKet5Jg$1KQ(5bA4Hsl~{#`sxDSXh*>)549lKgn!h?B|POl%ZP-Qufrk z=|aCOHmOc@O;S^3&O}M$K;_-6h_|nf2fcywbErn8nn7(I4}mn0uo??%$rcqh@gTRf zXR-TOnlB4#utvQcR(-cX(uQG*{4GfAd0|yO7LhU!1ct{~bT)UG)Fw#Ll#Oe~Wc4I2 zwxqQgYE;s2%LCZ;;WUqJw^N-mQr#p4@)W}?wyU&2TlAFo>RkBI_Y*g0zGaEa0$kBS zp>#~l2N~V2&YPM&nj_UTY@GC-4`;YFqM5T>D(EsZKE(RoW+&A#YL?GzdsEf5D7yVv zzj$2Y9U7owU|sv&g17MauOB=4!T2bL?BF?cI`;5$~M+p>`+c(dpO~ z;f(v3``WGZQzi#1j5W9Zqalm5m9ZL9E?g7>_Ai}3%U?PWa;kG&8TOVR=KB3FamP+i zk2*ix-gp7Gtfv!9Rm`rXtrN=n2?qO5X&kt<1>AW%afX%PDH@YOM8gtd^9}Xf-9>Ft zKaZ^2<*BnhtGQE}Db9+L7JW8t3Zk)N!RR;;H4v#AfmQ_t6Cm6u`%8UR-TKdS_t<$z|vAql!tMTBr59o+~JkuBThf(yJrD&b%{PN2?2N%%!KOOj_ zgf^K9c(!ckh3192m%&8QqO{@UI2#Tl+PL+anNDru3Q=#eNe#R?T{b)OmsG~YCD?ZN z^lKw#6G#M|GM>r0v_M?3Q!b-5>XwUi#V@9)tthxJ5?1VNyX9l@c1L9WbbLs?_wX!p z|Ce$>M)c0&Kl}atYW)4p$hfRyg(Sy=<9UumtJ=3nOmyXT$2=__L*D7t32ZK`5(=_B0*kV|H{1D zq`%`eP^rmQ#jI@4aJP*Pdiu{m|G1ZDh5fIC`B|*<$sXM3`SRnE{6}2$BH>V@BD!#Z(p_0LI0mWf9}917!XPKpF$Ut+*OSTMYs+{#n>ATYG8o%Sel( zcPRM89+r9q7p~mgi<{h4LjSeWo~m_RSgq7-%r%y?f_|s)yD@vuTsd`|kMZzHPX-^b z!gA$$A#Y|@GasquujBO7ZMdjOwA;O)Q%1EXK8;f*j5`D$!s9>7$Hqv!ANS+VR*YGSM|6dak zvv_oK6=0$Lcc*6mefIRB{`b8+E6D%vG#vmL@GLb2Uj&aLjKU(Ld18M~LZzSdlrY#>55-|3IFowlN^)X zXZr^ooo@8q;n|zxH~%<*vtsrT(v$`%4PjG>&3Tq|4mhqJ;$Sd!#%{XKezvj+d)``EYF)Zf-QjRV$?X=rR zF>6nGxYo<8$ur&09n*`EY|tE$#kX82wEFN;YkdcLzO85E zvdDFG&`Rh_gGN*IV7jb@Rh9ZPE`UpQq}x_{2nRn)DozC*vX4u3b46#q{;1NE^%!)0 z2@Bn*h+n9TS$1nfv4RUNI6SJx{a^;#Y780Pv2NLy1|d2rWjRu)=GiB!#;aAlB5Tua zb%|^s-p(Sx3OiW+U9*2>G~u#x+KoYz>s}p5hG&e1PExG zHfCjDqj73YZd_C%{^l~bgfL1VzAfIZ zN4#F|m2mDJ1y+K{Wd)NK_^aQ2?j#nhd3BdHEob@7gPZ5Wv#S0#A%c-`u$n1wf&Ta1 z{%$q@^`2mS9}o}1`@KPe)3`C{$bpCzuZM)$K5ZPolR`uTQRpB>|FbiM|ZuB7qV z?@f0kOUE}>`YL@jt9(QRW0uk&%|zp+S`{Ftd9|l39k;8Fm44Jgt3GH^8_zgTTU1Bk zyy}!jZRkfGzt+vf=_4~gFZ_P7`a1vnGnzy!KoV)2PQ69F*RND1uC7w6T3l+_t{NK} z>8d4Ovvyp9pgbD{(j}Ce1me~Ru1P~KJ4U({)=DsJ)g9c|ZG^Cxt|NJ^W{9f4y*qtf zK*;4j9cM3e<{+VEQ?*lHL;EF&u=wf@(9{Iee2o2XG2|?B0Cxn$Fdxxar|Gu*27A0ChRdhI{`ou_+yHT%#0 zL;T0RJU6ocC?a_I;y(l({eAsLAVXnaj}ge7ZM!8%gMCX|kUP{J8HDQSUCkh5zF&($ zsN7eJL1?}ir`$!08E2tUsWPc{qte%A5PC^cG9a@0eQ18ZFN4xKwQ(MGQ<8qSu&2W; z9lt7Q#px%fixx8!{`{()%vQ0NxKJ$6Meo4+rEL%{2w$aH%&@xu1!gh*fxF3WGiu(? zVq=`5tjd%tu-v^(u5zM%RW>ANNxl@zqJeB0zU7Fk5MeUN^yHb#?1YP9+^{iI^ zclT-kA^!7To*UW!X6OI?di{pKQYDEmZ}!7U-l^voj&Mh2zA7?TGxFirYccV;J>Agb zs|3uA{k_~?9=yFCyuIogKJMuM1xBqe?dw(IoL`ky=?i;$X##xk^ZGKjriEwT`YcWB zI`|jEd=m`|JN>q8OgM$5SNo>+rN%yPYhA)lzjE8s(ngGS(d{lx7`+q>!#0&_Me2ybR_6%MxX`$U;X`>{b%p#L;T698|QqolVFY>uwA~%?Mt}I^=Y5%QP?zJvEw$a_us2@2X#mdsW=t z{wfPY$AU2&U1^SYQS)exh84ej*nB~2O1)VlX4}>Ldh>R@wqb9qDaA}3in4jdV8~g> zm8j5N#bWi!c7%o@s_QWa*{d29<`U;pD{!~wh50l4nhXs$6Q4sGu#o08?x+GxortDb zkb!gFnO7&aOj6%gI4b4jzM``(A9n4#dFNVd(R|D-ZtDJ9Xmtf%78|JT5msU1t%u6| z8I;B8iW1XOQ>n3)Gmb-^no_Vr{=%{6cgw2SHi6zBT92Ex3Cusfmo`nodn0 z)mz4`q+L6Onn%0?PT~ryH)ADbarYah;>P)`kGr{uUXjyPdtK1gn)#hOT5@xi*Qncm z!{pLBrv)U_LJ~=3o4fQduicliAFpWt6*Ock7lK&e|F^%lvs3l|+k2@0ct4MK69e#> zqJp~#|J4Nc)!2R!oOZJ>zP_EsSC4Qv#@-q-mov-i&z7^uwmRIEI=P}V=_lB>(h-)N zAcFmihHt5mEhf5BXU-@7RDaXawUF9v*r}2*#SAtJ=Qk{gRGCMo$%$fX;aBw!=gGdXHRDrrAGg%Zbf;gX$w{Ahx|XC>#PsZ!#xz=F%)xnw zI@a2xkoIH0eVO$)?wZX<{n3~PS2DkaL3|_p*yCeqF)Fn@XBoKOldx%I2KcY%(0{tM z^hz{}9k-zW#{L{*mwWP9rd~e2Hdm0jw)9!pX_39u0H!SH4bP4$wDZLeHf$T--LZi>Jso;`ig|L^0uiTZEUVbul|{Q<4(`dW6! z_PDW+cs$(MQOsqtD)Qw;fAObIo35WbGZ+!6yR&1&m9A1kG71+*Cy%dvd4|M zX_`X41af5=AaM?RJIiJx$Hmx`wNAN}L=ftDWV&LpBcb>+GKosjj)k(2H0&)OW@)OxFB)GrGjlV*gd`=+L$v!nEFsqyN0qJ^?dR69lESo@dL)ZS zHFcq&VLxB!R=vp7$Zsewy_|TPQ<*I}KXS{wI@J;d3%BHC)^*z!(y;T-hfQ<8 zo2H^_)Ov?*d3v@f7Ql7p=NOXJ3=`BqhW;v>vb=YyHp`n9YA<$NTX3ZosqMrZ8&#*{ z+MWDPq5N#wAWY|%`fc~LcF+B2O7e(NwH?e+R%$Y$=}{&GjSHh{t`E6I(l#oxmr1c z0e`E-(W`6$(A_`&vwU18l;}jLP~nNo!}ole<}kW(%v^@m1x`lXRi)l{`AV9 zgliaM{{G+Z@9sUV?EhzbyAS*SUY-qj!P1yanB5Jp*+3|%xN5~ zaxpsq3%D7?jOhn_(lh^7gW-q+BFEk%P~by8@C>p<$*FlOgo`BXtsQsT_pVqR9)Qx| z+2|*dcoUiuB|R&UHNXujlX@ZjVHQQ&7ZPu@?N^hT7LB0u1C1gM@3@G2Vq4^&J9i}wm3B>f;tOsOD zu-4R2TmLP4NWg@|WJHDUc`r)KSsDO2{`()Z0VPQyi{7rxFSs08gqA@}1P^`2dt0#i;>XjoEgxPYl%W2yw?HII`2-c3i`VB6P?54B3rI@kwxauy zLMG0L$BH!7pK87{o(UjH?j~mT!Kt8VOOPz3((}45xP8C>x4(E@J>NZl*WdYz{zfUe zR3Su>1cf1wB7Us~O35Iicz>*~NGvIkDPa-5Gn${-%7#YJ|0UoFMa?i|!y!efUDBWc zbpsBELl(1?DL_9dsMImeLK-U!Pyz1q30_he2!?7REd6|fS057^DC{1`NtQ1D!28sl z|15gHuFpPspWxDLd6Hwwk9nwcS3XQH;S-$EG!ropP(OfrXOKG63NULz*C)7i{j0w} zIq4Wfi?nvf&Wppdj{5Kabha*GE+}In!V)Ozhx-O4;h{xJ4J!!`EhIhl?>J>mkmaBR ze3I~(#%T^vVI;aNlS(Tn8Ywlb+#M*mp^ivG1}s`~OEqkk8||^>pQhcoc*EU(E3IPy zceMit&%R>x=1jk=PS>oy;*cp3irYKv*j_Jh>H{-~nBtK({)i31x*Iqn0F!8hkTl)$ z6`lB(`dkA;7|;r5Ab9+N5Bx#|xom=yB#GdOU})0Ko8pmUd$_^6uK;IpkXJ!n7EY&! zJA_lFB&EYFlC%owvT(XR+#Q@O#vq+rth?=DHDs;|rx=i+0mv2LJbQ6?1cFMQiGZ#G z?uz>;YzA{_IFHU=-T;}a!kO@h1+yklmxWWEWHiVGOJ}CDyMQyM=`|Nur*&|bh12w~ z8jLH$NfLHW#gql~rhMt}^w{=rhwDC0$Svvf2QJvZc$|_5PI(B2S;}QVqDA7vs{GR! z2bDHb2G+tUwOwopqy1>j#Id1Q3N!W83vd%#?Oq{l*ZZ)e@*ch z-6dKLv{A!{;~`uY7oeKNFRdu4FBX{@EgcJf4I~Bx5DgFbyG(hax1oj@2&(dap{rB} zsJgJ&7mfo<{!@Po%BqdojmHx*TCB?4mu=w2&!RnYUnYecKQ84mToe7jMoChgD^!!t zFpDCwT4usU8bDHah&#R8iX@CVlovoFfkc#$b?GR3~3^0KvEhWr~?Yh zLkJp&RGbzA0gGiyNw`&+dcIj&XW!p$f493;;HI4An5vNJHEt`eSh3$ZO>|^nJ;m;e=`C_@su{CQLG0fxT0}**{}rg597N9#BNMO z@?gFK8mo<|FP_w~&-FGwJUz}O<<*wm?pmpp73)>Uc_;tvl%x0DfQXJ^#%jp-7BtHi z)z~plSx~tc(H1bD@!lZfqu%aL|8KpW?|S{e_DI6I&eyWb;%>j&-|snWeHN$WV}7j= zPzg;)wfe;=NyitngkCxmJQXZXr45TQ3PxfYTA#Mue}RT;*h>v6$5;N$L#kQmqlieU zPWH<-q_RrGwIcW51gQZP=}-DP^o;(VQJEHURSl`R9-zMJnp;#Lam-T_x1{#T#p`pB zY!sVz_C`_P%+b_d+u8l&yYr9FMf%9KPAvf#(kfps|u zaA|9Im-;IlDv2_AGg)%p&NSNR{JnY0J^2l4T`NBG8Otph$~UNAt@yaRK}QL=e9=+A zs{GJ|6wF^6D@xV!o;OHS)ubPW`Kj~kJR4$gc4son|LNL7&=~fr^!=}!8@5AfQj0X0 z#nwT6HS}H4S$>g|qtC>ST+tOS=x&9+JT)?^uU`^J^JeIvuebuxLj7d}`tpz`E|`v>eN))h@sk_Q?}Szbho!fYa{d} z%nNr&a%=0_rM-r*VSTE<$XFBtji*fTc%q|$1;c0r6wtLx1kobHitb>VWSbHQta8ZL zr%?$&&duehx(gjcBJ+q4E0gGi2rJXV0>Q=3&ismlbnTDjE7s~XDxvRML`@4`Fcf@} z8;34|4?fT!H4DO0sgvoGbiC3uu1eWh^k1t8UyalFk0UB6qZp8k09o8*S2=2Q5UMO=&WC!9E#FS(=%M_ZIQ^}1}^?z-d*i0 zKHKm6xk2c|mzNyA^!Q)V<1&^7`9<8z=Or@c+$GVK$jd%VVBFH>Z^PftzRWy zlr&eL81w6rQ=5k^uFy}amzOUp$?9*utaa(kz}YQ!-JHCwm&M-6Jt^!4(k3(1l?GJ% z&MSD(1jkb2AtZsvCSBBwXBq*Sq+p;hT9ds2)M&aReza))N@=lVZc|=y{jem1o z{V>JV7hXLD!&kI+P1R#BS6(}(e(+iru)cOq!OJhZfKKh2ds)Q=>sKiVal^gml>fNZ z`qkG>Cv*R@OXj4KzMnm}#JUAUN)m^Hl-6$5bvv@Xtch>d*Dr_j3$I}g$9=8iqBD=j zBYu0CMfLLX#Ts@V%v(eQ?3X_JG!*xH_@OE09T72$M~m$+OW0g)WfoazNLWH?zm;C^ zQuVdLiZ6H+v3PVrM(X4@$O1CpS!!|wnHG?e5iX5}F4%a!W*yc{IRUMbaBt#rzfII;DkxY_^ z9zcj+`HXv>lU8cMP-l5dB;+D;%I(>tpX$XU7Ju}-l#CX2tNTgG$nB=@_xJsNH>3kb z;%@fxjrH{9D zF$YOgY<@g^!_!kiC5_X^8Xn^)RsckVATtkurBo;9oe!zaEgQ{1TA*Xy3GiV7z75ja zEDB(k;+cHU3+W(p&^^$VGHR&NbFtCO$gYsPMeGV42o-hHkEtNQ;vp9k9Vwv`ok@3* zbPWwf+lR4s^NRy&IvdafZzCs5m*O@`nas-cvZ3(bvicj~EI+s_;cbAPx`D{#6Waji zzPQ*~rasC-JlUcmx;;3z&Ay8Sg-6dbcj9>6y@)pdG3HX|Aua$+;VqA{2`yioZ^QA> z(q5u*H<*I0(=lnSLM9cQ62h56XrPm(NyZgpD?!}pc07*+0hRJ69@0hN9%4(F z@UW=E*$Q`QpfgIscY>w#1Q$TEe=QvBoc)WgFv4H|!8|V^>3C7UrzZLJpjgPT)Ki`} zZyq^Y($R~)QCbd-*9~wGTS34EAO;ZV4ZLY`hRtcV>;+bkEoR(XFiST%wRIk|zJTsz zCo|Lg6`lP=l7z*`HcEY&-}AhNx=$koT+x~F^KEd(RxerH284!RU%t&nwoWmagSr7O zup0omRH+)-ic>$*S&mADOPsI*EK{87ory%pMB^Z7EE(o_#l46Urkkmc#rb9DHb_39 zkg^Gts)K71d4N z!OG2ZGJ*-E&?gj0lI1_uOL&ry-!q!$b}Uxok3SrL3M2S`g2Db%8V9yAJthcuCmkkH zF=OBNP4;aXPY*mRN!mP_r-$d~?@rEMdWDcVwHLn!o-2E5RAL>rVfoD*bh)0kdH(b^ zFVEI;g^x@^b@#cVv$-Q)ISJdB9nfAt=k@+Re@6Uy8*Vm#L~CV7l{f#Q-2^2gyxCxH zPA*=Z`{_r`rve^J9?{#>P1)JG-^wXF{TtLyS+aP_!g_hPuA+i#b39ZWX~;#OPEIcK z`yPGV=?of*XJMucN;NAZ5f!N{Et^RaYvMI9$tX-_rWgWdkgqJ&^G;{Gg3afh4$}PN zO4B7%)K>?1WViZns*6h6`u5cOUcT?>$ApU%oNk^!+PumJG)N=-*p{;B7I7&yu&1B~ z=yLcVd91*D-uc522?~#?o(E+V>1BtMq?u4%g`rM8tEalBWhm@L?L;H= zqFYZsl$imrRMKdu)G8wK04s~KS0}H1&wKLZ9r`>Pz^|#Gz)WIgYKoy$k??;%dE$9p zIJN%g=4YwVVUrN+`C^1!StPlHO&jE;&@h<6fTv^q(JoGJ&*I+cNUf~SLXFce_qki| zQzr%n9!Ik+AKw2!1)ZP4+h*=oBtQ~oCZ(Opwova4NUUfogb7D1722*g1S#xZy6MWa z*PhpfpA{Cg9AvT!U)Ern?)fFKhUE9&hH2eJ;JSv&7eJ#1yeJcs4me1({%fV}Cx}=) z@1I~HfFffw7sfj^O2%YBQx@QcLJ@b&CwxR>s#gA*%u1$Kn^)S&QLRYqND*pCg^9|> z3{ORBC)^|~CnHT<$HvrPBxjqcsnS+E={cbg7(b#3!KU0c`(_*a)9$zN;}nL9aglfi zOv;SP<%iTVy4gjBPGvx2A{ft8n&$Zch%A2{aw^sEmSNf5NaC4V#{!IE7TQm~yw}{R z={=*smh<2)Bg32|f)1&$Z0bE94jCF9v02E2OldZ_YV-HbboaOE-ft5&(o5Xi$p6{R zB<%)7Qn_VziFp-|^y1k$q#UMuidcS@beKgDvG_`ATCgLl5lhMI(=6qJkx2Fgl{}i# zt^#fs|GU>sxJrQvODL4Pz>)~nkngujgn&c5r zX1Y4=e*`?4{au}2yLPvDo@p0C7Kf&`>tRBIF||ExgUxBU>+is(YS1ySI$MAD6unHy z3`s9TpCcyqj;0?2n&6HP_#}y#R-5f%VY=|Wf0_>XASJlT6_oaLGp&H6T1rs(IOz4R zudjWAQ2JbqdXa7=d#{g=UcEVg)!kLc_Rn!dr7RA}!OZ4)$7Jl}8Wi^!397j|i{VWmO!}IVwJP*&e T_x%3>00960Bk5W907wJ?Xvi)O diff --git a/knowlg-automation/helm_charts/druid-cluster/druid-crd.yaml b/knowlg-automation/helm_charts/druid-cluster/druid-crd.yaml deleted file mode 100644 index a05f02026..000000000 --- a/knowlg-automation/helm_charts/druid-cluster/druid-crd.yaml +++ /dev/null @@ -1,4487 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.6.0-beta.0 - meta.helm.sh/release-name: druid-operator - meta.helm.sh/release-namespace: druid-raw - creationTimestamp: "2022-06-21T10:47:15Z" - generation: 1 - labels: - app.kubernetes.io/managed-by: Helm - name: druids.druid.apache.org - resourceVersion: "934" - uid: c464c8d8-e136-46c1-8458-d4968d18350c -spec: - conversion: - strategy: None - group: druid.apache.org - names: - kind: Druid - listKind: DruidList - plural: druids - singular: druid - scope: Namespaced - versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - affinity: - properties: - nodeAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: - properties: - preference: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - weight: - format: int32 - type: integer - required: - - preference - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - properties: - nodeSelectorTerms: - items: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - type: array - required: - - nodeSelectorTerms - type: object - type: object - podAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: - properties: - podAffinityTerm: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - weight: - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - items: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - type: array - type: object - podAntiAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: - properties: - podAffinityTerm: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - weight: - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - items: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - type: array - type: object - type: object - common.runtime.properties: - type: string - commonConfigMountPath: - type: string - containerSecurityContext: - properties: - allowPrivilegeEscalation: - type: boolean - capabilities: - properties: - add: - items: - type: string - type: array - drop: - items: - type: string - type: array - type: object - privileged: - type: boolean - procMount: - type: string - readOnlyRootFilesystem: - type: boolean - runAsGroup: - format: int64 - type: integer - runAsNonRoot: - type: boolean - runAsUser: - format: int64 - type: integer - seLinuxOptions: - properties: - level: - type: string - role: - type: string - type: - type: string - user: - type: string - type: object - seccompProfile: - properties: - localhostProfile: - type: string - type: - type: string - required: - - type - type: object - windowsOptions: - properties: - gmsaCredentialSpec: - type: string - gmsaCredentialSpecName: - type: string - runAsUserName: - type: string - type: object - type: object - deepStorage: - properties: - spec: - format: byte - type: string - type: - type: string - required: - - spec - - type - type: object - deleteOrphanPvc: - type: boolean - disablePVCDeletionFinalizer: - type: boolean - env: - items: - properties: - name: - type: string - value: - type: string - valueFrom: - properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - optional: - type: boolean - required: - - key - type: object - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - resourceFieldRef: - properties: - containerName: - type: string - divisor: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - type: string - required: - - resource - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - optional: - type: boolean - required: - - key - type: object - type: object - required: - - name - type: object - type: array - envFrom: - items: - properties: - configMapRef: - properties: - name: - type: string - optional: - type: boolean - type: object - prefix: - type: string - secretRef: - properties: - name: - type: string - optional: - type: boolean - type: object - type: object - type: array - forceDeleteStsPodOnError: - type: boolean - ignored: - type: boolean - image: - type: string - imagePullPolicy: - type: string - imagePullSecrets: - items: - properties: - name: - type: string - type: object - type: array - jvm.options: - type: string - livenessProbe: - properties: - exec: - properties: - command: - items: - type: string - type: array - type: object - failureThreshold: - format: int32 - type: integer - httpGet: - properties: - host: - type: string - httpHeaders: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - path: - type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - scheme: - type: string - required: - - port - type: object - initialDelaySeconds: - format: int32 - type: integer - periodSeconds: - format: int32 - type: integer - successThreshold: - format: int32 - type: integer - tcpSocket: - properties: - host: - type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - required: - - port - type: object - timeoutSeconds: - format: int32 - type: integer - type: object - log4j.config: - type: string - metadataStore: - properties: - spec: - format: byte - type: string - type: - type: string - required: - - spec - - type - type: object - nodeSelector: - additionalProperties: - type: string - type: object - nodes: - additionalProperties: - properties: - affinity: - properties: - nodeAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: - properties: - preference: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - weight: - format: int32 - type: integer - required: - - preference - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - properties: - nodeSelectorTerms: - items: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - type: array - required: - - nodeSelectorTerms - type: object - type: object - podAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: - properties: - podAffinityTerm: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - weight: - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - items: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - type: array - type: object - podAntiAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: - properties: - podAffinityTerm: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - weight: - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - items: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - type: array - type: object - type: object - containerSecurityContext: - properties: - allowPrivilegeEscalation: - type: boolean - capabilities: - properties: - add: - items: - type: string - type: array - drop: - items: - type: string - type: array - type: object - privileged: - type: boolean - procMount: - type: string - readOnlyRootFilesystem: - type: boolean - runAsGroup: - format: int64 - type: integer - runAsNonRoot: - type: boolean - runAsUser: - format: int64 - type: integer - seLinuxOptions: - properties: - level: - type: string - role: - type: string - type: - type: string - user: - type: string - type: object - seccompProfile: - properties: - localhostProfile: - type: string - type: - type: string - required: - - type - type: object - windowsOptions: - properties: - gmsaCredentialSpec: - type: string - gmsaCredentialSpecName: - type: string - runAsUserName: - type: string - type: object - type: object - druid.port: - format: int32 - type: integer - env: - items: - properties: - name: - type: string - value: - type: string - valueFrom: - properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - optional: - type: boolean - required: - - key - type: object - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - resourceFieldRef: - properties: - containerName: - type: string - divisor: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - type: string - required: - - resource - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - optional: - type: boolean - required: - - key - type: object - type: object - required: - - name - type: object - type: array - envFrom: - items: - properties: - configMapRef: - properties: - name: - type: string - optional: - type: boolean - type: object - prefix: - type: string - secretRef: - properties: - name: - type: string - optional: - type: boolean - type: object - type: object - type: array - extra.jvm.options: - type: string - hpAutoscaler: - properties: - behavior: - properties: - scaleDown: - properties: - policies: - items: - properties: - periodSeconds: - format: int32 - type: integer - type: - type: string - value: - format: int32 - type: integer - required: - - periodSeconds - - type - - value - type: object - type: array - selectPolicy: - type: string - stabilizationWindowSeconds: - format: int32 - type: integer - type: object - scaleUp: - properties: - policies: - items: - properties: - periodSeconds: - format: int32 - type: integer - type: - type: string - value: - format: int32 - type: integer - required: - - periodSeconds - - type - - value - type: object - type: array - selectPolicy: - type: string - stabilizationWindowSeconds: - format: int32 - type: integer - type: object - type: object - maxReplicas: - format: int32 - type: integer - metrics: - items: - properties: - containerResource: - properties: - container: - type: string - name: - type: string - target: - properties: - averageUtilization: - format: int32 - type: integer - averageValue: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: - type: string - value: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - required: - - type - type: object - required: - - container - - name - - target - type: object - external: - properties: - metric: - properties: - name: - type: string - selector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - required: - - name - type: object - target: - properties: - averageUtilization: - format: int32 - type: integer - averageValue: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: - type: string - value: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - required: - - type - type: object - required: - - metric - - target - type: object - object: - properties: - describedObject: - properties: - apiVersion: - type: string - kind: - type: string - name: - type: string - required: - - kind - - name - type: object - metric: - properties: - name: - type: string - selector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - required: - - name - type: object - target: - properties: - averageUtilization: - format: int32 - type: integer - averageValue: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: - type: string - value: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - required: - - type - type: object - required: - - describedObject - - metric - - target - type: object - pods: - properties: - metric: - properties: - name: - type: string - selector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - required: - - name - type: object - target: - properties: - averageUtilization: - format: int32 - type: integer - averageValue: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: - type: string - value: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - required: - - type - type: object - required: - - metric - - target - type: object - resource: - properties: - name: - type: string - target: - properties: - averageUtilization: - format: int32 - type: integer - averageValue: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: - type: string - value: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - required: - - type - type: object - required: - - name - - target - type: object - type: - type: string - required: - - type - type: object - type: array - minReplicas: - format: int32 - type: integer - scaleTargetRef: - properties: - apiVersion: - type: string - kind: - type: string - name: - type: string - required: - - kind - - name - type: object - required: - - maxReplicas - - scaleTargetRef - type: object - image: - type: string - imagePullPolicy: - type: string - imagePullSecrets: - items: - properties: - name: - type: string - type: object - type: array - ingress: - properties: - defaultBackend: - properties: - resource: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - required: - - kind - - name - type: object - service: - properties: - name: - type: string - port: - properties: - name: - type: string - number: - format: int32 - type: integer - type: object - required: - - name - type: object - type: object - ingressClassName: - type: string - rules: - items: - properties: - host: - type: string - http: - properties: - paths: - items: - properties: - backend: - properties: - resource: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - required: - - kind - - name - type: object - service: - properties: - name: - type: string - port: - properties: - name: - type: string - number: - format: int32 - type: integer - type: object - required: - - name - type: object - type: object - path: - type: string - pathType: - type: string - required: - - backend - type: object - type: array - x-kubernetes-list-type: atomic - required: - - paths - type: object - type: object - type: array - x-kubernetes-list-type: atomic - tls: - items: - properties: - hosts: - items: - type: string - type: array - x-kubernetes-list-type: atomic - secretName: - type: string - type: object - type: array - x-kubernetes-list-type: atomic - type: object - ingressAnnotations: - additionalProperties: - type: string - type: object - jvm.options: - type: string - kind: - type: string - lifecycle: - properties: - postStart: - properties: - exec: - properties: - command: - items: - type: string - type: array - type: object - httpGet: - properties: - host: - type: string - httpHeaders: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - path: - type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - scheme: - type: string - required: - - port - type: object - tcpSocket: - properties: - host: - type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - required: - - port - type: object - type: object - preStop: - properties: - exec: - properties: - command: - items: - type: string - type: array - type: object - httpGet: - properties: - host: - type: string - httpHeaders: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - path: - type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - scheme: - type: string - required: - - port - type: object - tcpSocket: - properties: - host: - type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - required: - - port - type: object - type: object - type: object - livenessProbe: - properties: - exec: - properties: - command: - items: - type: string - type: array - type: object - failureThreshold: - format: int32 - type: integer - httpGet: - properties: - host: - type: string - httpHeaders: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - path: - type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - scheme: - type: string - required: - - port - type: object - initialDelaySeconds: - format: int32 - type: integer - periodSeconds: - format: int32 - type: integer - successThreshold: - format: int32 - type: integer - tcpSocket: - properties: - host: - type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - required: - - port - type: object - timeoutSeconds: - format: int32 - type: integer - type: object - log4j.config: - type: string - maxSurge: - format: int32 - type: integer - maxUnavailable: - format: int32 - type: integer - nodeConfigMountPath: - type: string - nodeType: - type: string - persistentVolumeClaim: - items: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - properties: - annotations: - additionalProperties: - type: string - type: object - finalizers: - items: - type: string - type: array - labels: - additionalProperties: - type: string - type: object - name: - type: string - namespace: - type: string - type: object - spec: - properties: - accessModes: - items: - type: string - type: array - dataSource: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - required: - - kind - - name - type: object - resources: - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - selector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - storageClassName: - type: string - volumeMode: - type: string - volumeName: - type: string - type: object - status: - properties: - accessModes: - items: - type: string - type: array - capacity: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - conditions: - items: - properties: - lastProbeTime: - format: date-time - type: string - lastTransitionTime: - format: date-time - type: string - message: - type: string - reason: - type: string - status: - type: string - type: - type: string - required: - - status - - type - type: object - type: array - phase: - type: string - type: object - type: object - type: array - podAnnotations: - additionalProperties: - type: string - type: object - podDisruptionBudgetSpec: - properties: - maxUnavailable: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - minAvailable: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - selector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - type: object - podLabels: - additionalProperties: - type: string - type: object - podManagementPolicy: - type: string - ports: - items: - properties: - containerPort: - format: int32 - type: integer - hostIP: - type: string - hostPort: - format: int32 - type: integer - name: - type: string - protocol: - default: TCP - type: string - required: - - containerPort - type: object - type: array - readinessProbe: - properties: - exec: - properties: - command: - items: - type: string - type: array - type: object - failureThreshold: - format: int32 - type: integer - httpGet: - properties: - host: - type: string - httpHeaders: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - path: - type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - scheme: - type: string - required: - - port - type: object - initialDelaySeconds: - format: int32 - type: integer - periodSeconds: - format: int32 - type: integer - successThreshold: - format: int32 - type: integer - tcpSocket: - properties: - host: - type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - required: - - port - type: object - timeoutSeconds: - format: int32 - type: integer - type: object - replicas: - format: int32 - minimum: 0 - type: integer - resources: - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - runtime.properties: - type: string - securityContext: - properties: - fsGroup: - format: int64 - type: integer - fsGroupChangePolicy: - type: string - runAsGroup: - format: int64 - type: integer - runAsNonRoot: - type: boolean - runAsUser: - format: int64 - type: integer - seLinuxOptions: - properties: - level: - type: string - role: - type: string - type: - type: string - user: - type: string - type: object - seccompProfile: - properties: - localhostProfile: - type: string - type: - type: string - required: - - type - type: object - supplementalGroups: - items: - format: int64 - type: integer - type: array - sysctls: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - windowsOptions: - properties: - gmsaCredentialSpec: - type: string - gmsaCredentialSpecName: - type: string - runAsUserName: - type: string - type: object - type: object - services: - items: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - properties: - annotations: - additionalProperties: - type: string - type: object - finalizers: - items: - type: string - type: array - labels: - additionalProperties: - type: string - type: object - name: - type: string - namespace: - type: string - type: object - spec: - properties: - allocateLoadBalancerNodePorts: - type: boolean - clusterIP: - type: string - clusterIPs: - items: - type: string - type: array - x-kubernetes-list-type: atomic - externalIPs: - items: - type: string - type: array - externalName: - type: string - externalTrafficPolicy: - type: string - healthCheckNodePort: - format: int32 - type: integer - ipFamilies: - items: - type: string - type: array - x-kubernetes-list-type: atomic - ipFamilyPolicy: - type: string - loadBalancerIP: - type: string - loadBalancerSourceRanges: - items: - type: string - type: array - ports: - items: - properties: - appProtocol: - type: string - name: - type: string - nodePort: - format: int32 - type: integer - port: - format: int32 - type: integer - protocol: - default: TCP - type: string - targetPort: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - required: - - port - type: object - type: array - x-kubernetes-list-map-keys: - - port - - protocol - x-kubernetes-list-type: map - publishNotReadyAddresses: - type: boolean - selector: - additionalProperties: - type: string - type: object - sessionAffinity: - type: string - sessionAffinityConfig: - properties: - clientIP: - properties: - timeoutSeconds: - format: int32 - type: integer - type: object - type: object - topologyKeys: - items: - type: string - type: array - type: - type: string - type: object - status: - properties: - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - maxLength: 32768 - type: string - observedGeneration: - format: int64 - minimum: 0 - type: integer - reason: - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - enum: - - "True" - - "False" - - Unknown - type: string - type: - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - loadBalancer: - properties: - ingress: - items: - properties: - hostname: - type: string - ip: - type: string - ports: - items: - properties: - error: - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - port: - format: int32 - type: integer - protocol: - default: TCP - type: string - required: - - port - - protocol - type: object - type: array - x-kubernetes-list-type: atomic - type: object - type: array - type: object - type: object - type: object - type: array - startUpProbes: - properties: - exec: - properties: - command: - items: - type: string - type: array - type: object - failureThreshold: - format: int32 - type: integer - httpGet: - properties: - host: - type: string - httpHeaders: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - path: - type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - scheme: - type: string - required: - - port - type: object - initialDelaySeconds: - format: int32 - type: integer - periodSeconds: - format: int32 - type: integer - successThreshold: - format: int32 - type: integer - tcpSocket: - properties: - host: - type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - required: - - port - type: object - timeoutSeconds: - format: int32 - type: integer - type: object - terminationGracePeriodSeconds: - format: int64 - type: integer - tolerations: - items: - properties: - effect: - type: string - key: - type: string - operator: - type: string - tolerationSeconds: - format: int64 - type: integer - value: - type: string - type: object - type: array - topologySpreadConstraints: - items: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - maxSkew: - format: int32 - type: integer - topologyKey: - type: string - whenUnsatisfiable: - type: string - required: - - maxSkew - - topologyKey - - whenUnsatisfiable - type: object - type: array - updateStrategy: - properties: - rollingUpdate: - properties: - partition: - format: int32 - type: integer - type: object - type: - type: string - type: object - volumeClaimTemplates: - items: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - properties: - annotations: - additionalProperties: - type: string - type: object - finalizers: - items: - type: string - type: array - labels: - additionalProperties: - type: string - type: object - name: - type: string - namespace: - type: string - type: object - spec: - properties: - accessModes: - items: - type: string - type: array - dataSource: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - required: - - kind - - name - type: object - resources: - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - selector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - storageClassName: - type: string - volumeMode: - type: string - volumeName: - type: string - type: object - status: - properties: - accessModes: - items: - type: string - type: array - capacity: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - conditions: - items: - properties: - lastProbeTime: - format: date-time - type: string - lastTransitionTime: - format: date-time - type: string - message: - type: string - reason: - type: string - status: - type: string - type: - type: string - required: - - status - - type - type: object - type: array - phase: - type: string - type: object - type: object - type: array - volumeMounts: - items: - properties: - mountPath: - type: string - mountPropagation: - type: string - name: - type: string - readOnly: - type: boolean - subPath: - type: string - subPathExpr: - type: string - required: - - mountPath - - name - type: object - type: array - volumes: - items: - properties: - awsElasticBlockStore: - properties: - fsType: - type: string - partition: - format: int32 - type: integer - readOnly: - type: boolean - volumeID: - type: string - required: - - volumeID - type: object - azureDisk: - properties: - cachingMode: - type: string - diskName: - type: string - diskURI: - type: string - fsType: - type: string - kind: - type: string - readOnly: - type: boolean - required: - - diskName - - diskURI - type: object - azureFile: - properties: - readOnly: - type: boolean - secretName: - type: string - shareName: - type: string - required: - - secretName - - shareName - type: object - cephfs: - properties: - monitors: - items: - type: string - type: array - path: - type: string - readOnly: - type: boolean - secretFile: - type: string - secretRef: - properties: - name: - type: string - type: object - user: - type: string - required: - - monitors - type: object - cinder: - properties: - fsType: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - volumeID: - type: string - required: - - volumeID - type: object - configMap: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - name: - type: string - optional: - type: boolean - type: object - csi: - properties: - driver: - type: string - fsType: - type: string - nodePublishSecretRef: - properties: - name: - type: string - type: object - readOnly: - type: boolean - volumeAttributes: - additionalProperties: - type: string - type: object - required: - - driver - type: object - downwardAPI: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - mode: - format: int32 - type: integer - path: - type: string - resourceFieldRef: - properties: - containerName: - type: string - divisor: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - type: string - required: - - resource - type: object - required: - - path - type: object - type: array - type: object - emptyDir: - properties: - medium: - type: string - sizeLimit: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - ephemeral: - properties: - readOnly: - type: boolean - volumeClaimTemplate: - properties: - metadata: - properties: - annotations: - additionalProperties: - type: string - type: object - finalizers: - items: - type: string - type: array - labels: - additionalProperties: - type: string - type: object - name: - type: string - namespace: - type: string - type: object - spec: - properties: - accessModes: - items: - type: string - type: array - dataSource: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - required: - - kind - - name - type: object - resources: - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - selector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - storageClassName: - type: string - volumeMode: - type: string - volumeName: - type: string - type: object - required: - - spec - type: object - type: object - fc: - properties: - fsType: - type: string - lun: - format: int32 - type: integer - readOnly: - type: boolean - targetWWNs: - items: - type: string - type: array - wwids: - items: - type: string - type: array - type: object - flexVolume: - properties: - driver: - type: string - fsType: - type: string - options: - additionalProperties: - type: string - type: object - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - required: - - driver - type: object - flocker: - properties: - datasetName: - type: string - datasetUUID: - type: string - type: object - gcePersistentDisk: - properties: - fsType: - type: string - partition: - format: int32 - type: integer - pdName: - type: string - readOnly: - type: boolean - required: - - pdName - type: object - gitRepo: - properties: - directory: - type: string - repository: - type: string - revision: - type: string - required: - - repository - type: object - glusterfs: - properties: - endpoints: - type: string - path: - type: string - readOnly: - type: boolean - required: - - endpoints - - path - type: object - hostPath: - properties: - path: - type: string - type: - type: string - required: - - path - type: object - iscsi: - properties: - chapAuthDiscovery: - type: boolean - chapAuthSession: - type: boolean - fsType: - type: string - initiatorName: - type: string - iqn: - type: string - iscsiInterface: - type: string - lun: - format: int32 - type: integer - portals: - items: - type: string - type: array - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - targetPortal: - type: string - required: - - iqn - - lun - - targetPortal - type: object - name: - type: string - nfs: - properties: - path: - type: string - readOnly: - type: boolean - server: - type: string - required: - - path - - server - type: object - persistentVolumeClaim: - properties: - claimName: - type: string - readOnly: - type: boolean - required: - - claimName - type: object - photonPersistentDisk: - properties: - fsType: - type: string - pdID: - type: string - required: - - pdID - type: object - portworxVolume: - properties: - fsType: - type: string - readOnly: - type: boolean - volumeID: - type: string - required: - - volumeID - type: object - projected: - properties: - defaultMode: - format: int32 - type: integer - sources: - items: - properties: - configMap: - properties: - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - name: - type: string - optional: - type: boolean - type: object - downwardAPI: - properties: - items: - items: - properties: - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - mode: - format: int32 - type: integer - path: - type: string - resourceFieldRef: - properties: - containerName: - type: string - divisor: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - type: string - required: - - resource - type: object - required: - - path - type: object - type: array - type: object - secret: - properties: - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - name: - type: string - optional: - type: boolean - type: object - serviceAccountToken: - properties: - audience: - type: string - expirationSeconds: - format: int64 - type: integer - path: - type: string - required: - - path - type: object - type: object - type: array - type: object - quobyte: - properties: - group: - type: string - readOnly: - type: boolean - registry: - type: string - tenant: - type: string - user: - type: string - volume: - type: string - required: - - registry - - volume - type: object - rbd: - properties: - fsType: - type: string - image: - type: string - keyring: - type: string - monitors: - items: - type: string - type: array - pool: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - user: - type: string - required: - - image - - monitors - type: object - scaleIO: - properties: - fsType: - type: string - gateway: - type: string - protectionDomain: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - sslEnabled: - type: boolean - storageMode: - type: string - storagePool: - type: string - system: - type: string - volumeName: - type: string - required: - - gateway - - secretRef - - system - type: object - secret: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - optional: - type: boolean - secretName: - type: string - type: object - storageos: - properties: - fsType: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - volumeName: - type: string - volumeNamespace: - type: string - type: object - vsphereVolume: - properties: - fsType: - type: string - storagePolicyID: - type: string - storagePolicyName: - type: string - volumePath: - type: string - required: - - volumePath - type: object - required: - - name - type: object - type: array - required: - - druid.port - - nodeConfigMountPath - - nodeType - - replicas - - runtime.properties - type: object - type: object - podAnnotations: - additionalProperties: - type: string - type: object - podLabels: - additionalProperties: - type: string - type: object - podManagementPolicy: - type: string - readinessProbe: - properties: - exec: - properties: - command: - items: - type: string - type: array - type: object - failureThreshold: - format: int32 - type: integer - httpGet: - properties: - host: - type: string - httpHeaders: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - path: - type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - scheme: - type: string - required: - - port - type: object - initialDelaySeconds: - format: int32 - type: integer - periodSeconds: - format: int32 - type: integer - successThreshold: - format: int32 - type: integer - tcpSocket: - properties: - host: - type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - required: - - port - type: object - timeoutSeconds: - format: int32 - type: integer - type: object - rollingDeploy: - type: boolean - securityContext: - properties: - fsGroup: - format: int64 - type: integer - fsGroupChangePolicy: - type: string - runAsGroup: - format: int64 - type: integer - runAsNonRoot: - type: boolean - runAsUser: - format: int64 - type: integer - seLinuxOptions: - properties: - level: - type: string - role: - type: string - type: - type: string - user: - type: string - type: object - seccompProfile: - properties: - localhostProfile: - type: string - type: - type: string - required: - - type - type: object - supplementalGroups: - items: - format: int64 - type: integer - type: array - sysctls: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - windowsOptions: - properties: - gmsaCredentialSpec: - type: string - gmsaCredentialSpecName: - type: string - runAsUserName: - type: string - type: object - type: object - serviceAccount: - type: string - services: - items: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - properties: - annotations: - additionalProperties: - type: string - type: object - finalizers: - items: - type: string - type: array - labels: - additionalProperties: - type: string - type: object - name: - type: string - namespace: - type: string - type: object - spec: - properties: - allocateLoadBalancerNodePorts: - type: boolean - clusterIP: - type: string - clusterIPs: - items: - type: string - type: array - x-kubernetes-list-type: atomic - externalIPs: - items: - type: string - type: array - externalName: - type: string - externalTrafficPolicy: - type: string - healthCheckNodePort: - format: int32 - type: integer - ipFamilies: - items: - type: string - type: array - x-kubernetes-list-type: atomic - ipFamilyPolicy: - type: string - loadBalancerIP: - type: string - loadBalancerSourceRanges: - items: - type: string - type: array - ports: - items: - properties: - appProtocol: - type: string - name: - type: string - nodePort: - format: int32 - type: integer - port: - format: int32 - type: integer - protocol: - default: TCP - type: string - targetPort: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - required: - - port - type: object - type: array - x-kubernetes-list-map-keys: - - port - - protocol - x-kubernetes-list-type: map - publishNotReadyAddresses: - type: boolean - selector: - additionalProperties: - type: string - type: object - sessionAffinity: - type: string - sessionAffinityConfig: - properties: - clientIP: - properties: - timeoutSeconds: - format: int32 - type: integer - type: object - type: object - topologyKeys: - items: - type: string - type: array - type: - type: string - type: object - status: - properties: - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - maxLength: 32768 - type: string - observedGeneration: - format: int64 - minimum: 0 - type: integer - reason: - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - enum: - - "True" - - "False" - - Unknown - type: string - type: - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - loadBalancer: - properties: - ingress: - items: - properties: - hostname: - type: string - ip: - type: string - ports: - items: - properties: - error: - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - port: - format: int32 - type: integer - protocol: - default: TCP - type: string - required: - - port - - protocol - type: object - type: array - x-kubernetes-list-type: atomic - type: object - type: array - type: object - type: object - type: object - type: array - startScript: - type: string - startUpProbes: - properties: - exec: - properties: - command: - items: - type: string - type: array - type: object - failureThreshold: - format: int32 - type: integer - httpGet: - properties: - host: - type: string - httpHeaders: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - path: - type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - scheme: - type: string - required: - - port - type: object - initialDelaySeconds: - format: int32 - type: integer - periodSeconds: - format: int32 - type: integer - successThreshold: - format: int32 - type: integer - tcpSocket: - properties: - host: - type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - required: - - port - type: object - timeoutSeconds: - format: int32 - type: integer - type: object - tolerations: - items: - properties: - effect: - type: string - key: - type: string - operator: - type: string - tolerationSeconds: - format: int64 - type: integer - value: - type: string - type: object - type: array - updateStrategy: - properties: - rollingUpdate: - properties: - partition: - format: int32 - type: integer - type: object - type: - type: string - type: object - volumeClaimTemplates: - items: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - properties: - annotations: - additionalProperties: - type: string - type: object - finalizers: - items: - type: string - type: array - labels: - additionalProperties: - type: string - type: object - name: - type: string - namespace: - type: string - type: object - spec: - properties: - accessModes: - items: - type: string - type: array - dataSource: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - required: - - kind - - name - type: object - resources: - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - selector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - storageClassName: - type: string - volumeMode: - type: string - volumeName: - type: string - type: object - status: - properties: - accessModes: - items: - type: string - type: array - capacity: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - conditions: - items: - properties: - lastProbeTime: - format: date-time - type: string - lastTransitionTime: - format: date-time - type: string - message: - type: string - reason: - type: string - status: - type: string - type: - type: string - required: - - status - - type - type: object - type: array - phase: - type: string - type: object - type: object - type: array - volumeMounts: - items: - properties: - mountPath: - type: string - mountPropagation: - type: string - name: - type: string - readOnly: - type: boolean - subPath: - type: string - subPathExpr: - type: string - required: - - mountPath - - name - type: object - type: array - volumes: - items: - properties: - awsElasticBlockStore: - properties: - fsType: - type: string - partition: - format: int32 - type: integer - readOnly: - type: boolean - volumeID: - type: string - required: - - volumeID - type: object - azureDisk: - properties: - cachingMode: - type: string - diskName: - type: string - diskURI: - type: string - fsType: - type: string - kind: - type: string - readOnly: - type: boolean - required: - - diskName - - diskURI - type: object - azureFile: - properties: - readOnly: - type: boolean - secretName: - type: string - shareName: - type: string - required: - - secretName - - shareName - type: object - cephfs: - properties: - monitors: - items: - type: string - type: array - path: - type: string - readOnly: - type: boolean - secretFile: - type: string - secretRef: - properties: - name: - type: string - type: object - user: - type: string - required: - - monitors - type: object - cinder: - properties: - fsType: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - volumeID: - type: string - required: - - volumeID - type: object - configMap: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - name: - type: string - optional: - type: boolean - type: object - csi: - properties: - driver: - type: string - fsType: - type: string - nodePublishSecretRef: - properties: - name: - type: string - type: object - readOnly: - type: boolean - volumeAttributes: - additionalProperties: - type: string - type: object - required: - - driver - type: object - downwardAPI: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - mode: - format: int32 - type: integer - path: - type: string - resourceFieldRef: - properties: - containerName: - type: string - divisor: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - type: string - required: - - resource - type: object - required: - - path - type: object - type: array - type: object - emptyDir: - properties: - medium: - type: string - sizeLimit: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - ephemeral: - properties: - readOnly: - type: boolean - volumeClaimTemplate: - properties: - metadata: - properties: - annotations: - additionalProperties: - type: string - type: object - finalizers: - items: - type: string - type: array - labels: - additionalProperties: - type: string - type: object - name: - type: string - namespace: - type: string - type: object - spec: - properties: - accessModes: - items: - type: string - type: array - dataSource: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - required: - - kind - - name - type: object - resources: - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - selector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - storageClassName: - type: string - volumeMode: - type: string - volumeName: - type: string - type: object - required: - - spec - type: object - type: object - fc: - properties: - fsType: - type: string - lun: - format: int32 - type: integer - readOnly: - type: boolean - targetWWNs: - items: - type: string - type: array - wwids: - items: - type: string - type: array - type: object - flexVolume: - properties: - driver: - type: string - fsType: - type: string - options: - additionalProperties: - type: string - type: object - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - required: - - driver - type: object - flocker: - properties: - datasetName: - type: string - datasetUUID: - type: string - type: object - gcePersistentDisk: - properties: - fsType: - type: string - partition: - format: int32 - type: integer - pdName: - type: string - readOnly: - type: boolean - required: - - pdName - type: object - gitRepo: - properties: - directory: - type: string - repository: - type: string - revision: - type: string - required: - - repository - type: object - glusterfs: - properties: - endpoints: - type: string - path: - type: string - readOnly: - type: boolean - required: - - endpoints - - path - type: object - hostPath: - properties: - path: - type: string - type: - type: string - required: - - path - type: object - iscsi: - properties: - chapAuthDiscovery: - type: boolean - chapAuthSession: - type: boolean - fsType: - type: string - initiatorName: - type: string - iqn: - type: string - iscsiInterface: - type: string - lun: - format: int32 - type: integer - portals: - items: - type: string - type: array - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - targetPortal: - type: string - required: - - iqn - - lun - - targetPortal - type: object - name: - type: string - nfs: - properties: - path: - type: string - readOnly: - type: boolean - server: - type: string - required: - - path - - server - type: object - persistentVolumeClaim: - properties: - claimName: - type: string - readOnly: - type: boolean - required: - - claimName - type: object - photonPersistentDisk: - properties: - fsType: - type: string - pdID: - type: string - required: - - pdID - type: object - portworxVolume: - properties: - fsType: - type: string - readOnly: - type: boolean - volumeID: - type: string - required: - - volumeID - type: object - projected: - properties: - defaultMode: - format: int32 - type: integer - sources: - items: - properties: - configMap: - properties: - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - name: - type: string - optional: - type: boolean - type: object - downwardAPI: - properties: - items: - items: - properties: - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - mode: - format: int32 - type: integer - path: - type: string - resourceFieldRef: - properties: - containerName: - type: string - divisor: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - type: string - required: - - resource - type: object - required: - - path - type: object - type: array - type: object - secret: - properties: - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - name: - type: string - optional: - type: boolean - type: object - serviceAccountToken: - properties: - audience: - type: string - expirationSeconds: - format: int64 - type: integer - path: - type: string - required: - - path - type: object - type: object - type: array - type: object - quobyte: - properties: - group: - type: string - readOnly: - type: boolean - registry: - type: string - tenant: - type: string - user: - type: string - volume: - type: string - required: - - registry - - volume - type: object - rbd: - properties: - fsType: - type: string - image: - type: string - keyring: - type: string - monitors: - items: - type: string - type: array - pool: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - user: - type: string - required: - - image - - monitors - type: object - scaleIO: - properties: - fsType: - type: string - gateway: - type: string - protectionDomain: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - sslEnabled: - type: boolean - storageMode: - type: string - storagePool: - type: string - system: - type: string - volumeName: - type: string - required: - - gateway - - secretRef - - system - type: object - secret: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - optional: - type: boolean - secretName: - type: string - type: object - storageos: - properties: - fsType: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - volumeName: - type: string - volumeNamespace: - type: string - type: object - vsphereVolume: - properties: - fsType: - type: string - storagePolicyID: - type: string - storagePolicyName: - type: string - volumePath: - type: string - required: - - volumePath - type: object - required: - - name - type: object - type: array - zookeeper: - properties: - spec: - format: byte - type: string - type: - type: string - required: - - spec - - type - type: object - required: - - common.runtime.properties - - commonConfigMountPath - - nodes - - startScript - type: object - status: - properties: - configMaps: - items: - type: string - type: array - deployments: - items: - type: string - type: array - druidNodeStatus: - properties: - druidNode: - type: string - druidNodeConditionStatus: - type: string - druidNodeConditionType: - type: string - reason: - type: string - type: object - hpAutoscalers: - items: - type: string - type: array - ingress: - items: - type: string - type: array - persistentVolumeClaims: - items: - type: string - type: array - podDisruptionBudgets: - items: - type: string - type: array - pods: - items: - type: string - type: array - services: - items: - type: string - type: array - statefulSets: - items: - type: string - type: array - type: object - required: - - spec - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: Druid - listKind: DruidList - plural: druids - singular: druid - conditions: - - lastTransitionTime: "2022-06-21T10:47:15Z" - message: no conflicts found - reason: NoConflicts - status: "True" - type: NamesAccepted - - lastTransitionTime: "2022-06-21T10:47:16Z" - message: the initial names have been accepted - reason: InitialNamesAccepted - status: "True" - type: Established - storedVersions: - - v1alpha1 diff --git a/knowlg-automation/helm_charts/druid-cluster/templates/_helpers.tpl b/knowlg-automation/helm_charts/druid-cluster/templates/_helpers.tpl deleted file mode 100644 index f05bab676..000000000 --- a/knowlg-automation/helm_charts/druid-cluster/templates/_helpers.tpl +++ /dev/null @@ -1,51 +0,0 @@ -{{/* vim: set filetype=mustache: */}} -{{/* -Expand the name of the chart. -*/}} -{{- define "druid-cluster.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{- define "name" -}} -{{- default .Release.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -*/}} -{{- define "druid-cluster.fullname" -}} -{{- if .Values.fullnameOverride -}} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- $name := default .Chart.Name .Values.nameOverride -}} -{{- if contains $name .Release.Name -}} -{{- .Release.Name | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} -{{- end -}} -{{- end -}} -{{- end -}} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "druid-cluster.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -App config names -*/}} -{{- define "druid-cluster.appConfigName" -}} -{{- $.Release.Name -}} -{{- end -}} - -{{/* -Selector labels -*/}} -{{- define "druid-cluster.selectorLabels" -}} -app.kubernetes.io/name: {{ include "druid-cluster.name" . }} -app.kubernetes.io/instance: {{ .Release.Name }} -{{- end -}} diff --git a/knowlg-automation/helm_charts/druid-cluster/templates/druid_statefulset.yaml b/knowlg-automation/helm_charts/druid-cluster/templates/druid_statefulset.yaml deleted file mode 100644 index 8ac2f43f2..000000000 --- a/knowlg-automation/helm_charts/druid-cluster/templates/druid_statefulset.yaml +++ /dev/null @@ -1,476 +0,0 @@ -# This spec only works on a single node kubernetes cluster(e.g. typical k8s cluster setup for dev using kind/minikube or single node AWS EKS cluster etc) -# as it uses local disk as "deep storage". -# -apiVersion: "druid.apache.org/v1alpha1" -kind: "Druid" -metadata: - name: "{{ .Values.druid_cluster_type }}-{{ .Values.druid_env }}" -spec: - image: "{{ .Values.druid_image }}" - startScript: /druid.sh - podLabels: - environment: {{ .Values.druid_env }} - release: alpha - podAnnotations: - dummykey: dummyval - readinessProbe: - httpGet: - path: /status/health - port: 8088 - securityContext: - fsGroup: 1000 - runAsUser: 1000 - runAsGroup: 1000 - services: - - spec: - type: ClusterIP - clusterIP: None - commonConfigMountPath: "/opt/druid/conf/druid/cluster/_common" - jvm.options: |- - -server - -XX:+UseG1GC - -XX:+ExitOnOutOfMemoryError - -XX:MaxDirectMemorySize=10240g - -Duser.timezone=UTC - -Dfile.encoding=UTF-8 - -Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager - -Djava.io.tmpdir=/var/tmp - log4j.config: |- - - - - - - - - - - - - - - common.runtime.properties: | - druid.extensions.loadList=[{{ .Values.druid_extensions_loadList }}] - # druid.extensions.directory=/opt/druid/extensions - # Logging - # Log all runtime properties on startup. Disable to avoid logging properties on startup: - druid.startup.logging.logProperties=true - # Zookeeper - druid.zk.service.host={{ .Release.Name }}-zookeeper-headless.{{ .Values.druid_namespace }}.svc.cluster.local - druid.zk.paths.base=/druid - # Metadata storage - # For PostgreSQL: - druid.metadata.storage.type={{ .Values.druid_metadata_storage_type }} - druid.metadata.storage.connector.connectURI={{ .Values.druid_metadata_storage_connector_connectURI }} - druid.metadata.storage.connector.user={{ .Values.druid_metadata_storage_connector_user }} - druid.metadata.storage.connector.password={{ .Values.druid_metadata_storage_connector_password }} - - # Deep storage - druid.storage.type={{ .Values.druid_deepstorage_type }} - # AWS - druid.s3.accessKey = {{ .Values.s3_access_key }} - druid.s3.secretKey = {{ .Values.s3_secret_key }} - druid.storage.bucket={{ .Values.s3_bucket }} - druid.storage.baseKey=druid/segments - # Azure - druid.azure.account = {{ .Values.azure_storage_account_name }} - druid.azure.key = {{ .Values.azure_storage_account_key }} - druid.azure.container = {{ .Values.azure_storage_container }} - #druid.storage.storageDirectory = {{ .Values.druid_storage_directory }} - #druid.storage.disableAcl=true - - # # Indexing service logs - # # For local disk (only viable in a cluster if this is a network mount): - druid.indexer.logs.type={{ .Values.druid_indexer_logs_type }} - druid.indexer.logs.container={{ .Values.druid_indexer_logs_container }} - druid.indexer.logs.prefix={{ .Values.druid_indexer_logs_prefix }} - # - # - # Service discovery - druid.selectors.indexing.serviceName={{ .Values.druid_selectors_indexing_serviceName }} - druid.selectors.coordinator.serviceName={{ .Values.druid_selectors_coordinator_serviceName }} - # Monitoring - # druid.monitoring.monitors=[{{ .Values.druid_monitoring_monitors }}] - # druid.emitter=composing - # druid.emitter.composing.emitters=[{{ .Values.druid_emitter_composing_emitters }}] - # druid.emitter.logging.logLevel={{ .Values.druid_emitter_logging_logLevel }} - {{- if .Values.druid_monitoring -}} - druid.emitter.graphite.port={{ .Values.druid_emitter_graphite_port }} - druid.emitter.graphite.hostname={{ .Values.druid_emitter_graphite_hostname }} - druid.emitter.graphite.protocol={{ .Values.druid_emitter_graphite_protocol }} - druid.emitter.graphite.eventConverter={{ .Values.druid_emitter_graphite_eventConverter }} - {{- end -}} - # Storage type of double columns - # ommiting this will lead to index double as float at the storage layer - druid.indexing.doubleStorage={{ .Values.druid_indexing_doubleStorage }} - #Writing query logs into file - druid.request.logging.type={{ .Values.druid_request_logging_type }} - druid.request.logging.dir={{ .Values.druid_request_logging_dir }} - druid.javascript.enabled=true - druid.sql.enable={{ .Values.druid_sql_enable }} - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - nodes: - brokers: - # Optionally specify for running broker as Deployment - kind: Deployment - nodeType: "broker" - druid.port: {{ .Values.druid_broker_port }} - nodeConfigMountPath: "/opt/druid/conf/druid/cluster/query/broker" - replicas: {{ .Values.druid_broker_replicas }} - runtime.properties: | - druid.service={{ .Values.druid_broker_service }} - # HTTP server threads - druid.broker.http.numConnections={{ .Values.druid_broker_http_numConnections }} - druid.server.http.numThreads={{ .Values.druid_server_http_numThreads }} - # Processing threads and buffers - druid.processing.buffer.sizeBytes={{ .Values.druid_broker_processing_buffer_sizeBytes }} - druid.processing.numThreads={{ .Values.druid_broker_processing_numThreads }} - druid.processing.numMergeBuffers={{ .Values.druid_broker_processing_numMergeBuffers }} - druid.javascript.enabled=true - druid.sql.enable={{ .Values.druid_sql_enable }} - extra.jvm.options: |+ - -Xms{{ .Values.druid_broker_min_heap_size }} - -Xmx{{ .Values.druid_broker_max_heap_size }} - -XX:MaxDirectMemorySize={{ .Values.druid_broker_max_direct_size }} - readinessProbe: - httpGet: - path: /status/health - port: {{ .Values.druid_broker_port }} - - # hpAutoscaler: - # maxReplicas: 2 - # minReplicas: 1 - # scaleTargetRef: - # apiVersion: apps/v1 - # kind: StatefulSet - # name: druid-tiny-cluster-brokers - # metrics: - # - type: Resource - # resource: - # name: cpu - # target: - # type: Utilization - # averageUtilization: 50 - - resources: - requests: - memory: {{ .Values.druid_broker_pod_memory_request }} - cpu: {{ .Values.druid_broker_pod_cpu_request }} - limits: - memory: {{ .Values.druid_broker_pod_memory_limit }} - cpu: {{ .Values.druid_broker_pod_cpu_limit }} - - coordinators: - # Optionally specify for running coordinator as Deployment - kind: Deployment - nodeType: "coordinator" - druid.port: {{ .Values.druid_coordinator_port }} - nodeConfigMountPath: "/opt/druid/conf/druid/cluster/master/coordinator-overlord" - replicas: {{ .Values.druid_coordinator_replicas }} - runtime.properties: | - druid.service={{ .Values.druid_coordinator_service }} - druid.coordinator.startDelay={{ .Values.druid_coordinator_startDelay }} - druid.coordinator.period={{ .Values.druid_coordinator_period }} - druid.coordinator.balancer.strategy={{ .Values.druid_coordinator_balancer_strategy }} - druid.coordinator.asOverlord.enabled=false - extra.jvm.options: |- - -Xms{{ .Values.druid_coordinator_min_heap_size }} - -Xmx{{ .Values.druid_coordinator_max_heap_size }} - - readinessProbe: - httpGet: - path: /status/health - port: {{ .Values.druid_coordinator_port }} - - resources: - requests: - memory: {{ .Values.druid_coordinator_pod_memory_request }} - cpu: {{ .Values.druid_coordinator_pod_cpu_request }} - limits: - memory: {{ .Values.druid_coordinator_pod_memory_limit }} - cpu: {{ .Values.druid_coordinator_pod_cpu_limit }} - - overlords: - # Optionally specify for running coordinator as Deployment - kind: Deployment - nodeType: "overlord" - druid.port: {{ .Values.druid_overlord_port }} - nodeConfigMountPath: "/opt/druid/conf/druid/cluster/master/coordinator-overlord" - replicas: {{ .Values.druid_overlord_replicas }} - runtime.properties: | - druid.service={{ .Values.druid_overlord_service }} - druid.indexer.queue.startDelay={{ .Values.druid_indexer_queue_startDelay }} - druid.indexer.runner.type={{ .Values.druid_indexer_runner_type }} - druid.indexer.storage.type={{ .Values.druid_indexer_storage_type }} - # Additional parameters for minor compaction - druid.indexer.tasklock.forceTimeChunkLock={{ .Values.druid_indexer_tasklock_forceTimeChunkLock }} - extra.jvm.options: |- - -Xms{{ .Values.druid_overlord_min_heap_size }} - -Xmx{{ .Values.druid_overlord_max_heap_size }} - - readinessProbe: - httpGet: - path: /status/health - port: {{ .Values.druid_overlord_port }} - - resources: - requests: - memory: {{ .Values.druid_overlord_pod_memory_request }} - cpu: {{ .Values.druid_overlord_pod_cpu_request }} - limits: - memory: {{ .Values.druid_overlord_pod_memory_limit }} - cpu: {{ .Values.druid_overlord_pod_cpu_limit }} - - historicals: - nodeType: "historical" - druid.port: {{ .Values.druid_historical_port }} - nodeConfigMountPath: "/opt/druid/conf/druid/cluster/data/historical" - replicas: {{ .Values.druid_historical_replicas }} - readinessProbe: - httpGet: - path: /status/health - port: {{ .Values.druid_historical_port }} - runtime.properties: | - druid.service={{ .Values.druid_historical_service }} - # HTTP server threads - druid.server.http.numThreads={{ .Values.druid_server_http_numThreads }} - # Processing threads and buffers - druid.processing.buffer.sizeBytes={{ .Values.druid_historical_processing_buffer_sizeBytes }} - druid.processing.numThreads={{ .Values.druid_historical_processing_numThreads }} - druid.processing.numMergeBuffers={{ .Values.druid_historical_processing_numMergeBuffers }} - # Segmentstorage - druid.segmentCache.locations=[{{ .Values.druid_segmentCache_locations }}] - druid.segmentCache.numLoadingThreads={{ .Values.druid_segmentCache_numLoadingThreads }} - # Caching - druid.historical.cache.useCache={{ .Values.druid_historical_cache_useCache }} - druid.historical.cache.populateCache={{ .Values.druid_historical_cache_populateCache }} - druid.historical.cache.unCacheable=[{{ .Values.druid_historical_cache_unCacheable }}] - druid.cache.type={{ .Values.druid_cache_type }} - druid.cache.sizeInBytes={{ .Values.druid_historical_cache_size }} - # druid.cache.expireAfter={{ .Values.druid_historical_cache_expiry }} - extra.jvm.options: |- - -Xms{{ .Values.druid_historical_min_heap_size }} - -Xmx{{ .Values.druid_historical_max_heap_size }} - -XX:MaxDirectMemorySize={{ .Values.druid_historical_max_direct_size }} - securityContext: - fsGroup: 0 - runAsUser: 0 - runAsGroup: 0 - volumeMounts: - - mountPath: {{ .Values.mount_path }} - name: historical-volume - volumeClaimTemplates: - - metadata: - name: historical-volume - spec: - storageClassName: "{{- .Values.storageClass }}" - accessModes: - - ReadWriteOnce - resources: - requests: - storage: {{ .Values.druid_historical_persistent_volume_size }} - - resources: - requests: - memory: {{ .Values.druid_historical_pod_memory_request }} - cpu: {{ .Values.druid_historical_pod_cpu_request }} - limits: - memory: {{ .Values.druid_historical_pod_memory_limit }} - cpu: {{ .Values.druid_historical_pod_cpu_limit }} - - # middlemanagers: - # nodeType: "middleManager" - # druid.port: {{ .Values.druid_middlemanager_port }} - # nodeConfigMountPath: "/opt/druid/conf/druid/cluster/data/middleManager" - # replicas: {{ .Values.druid_middlemanager_replicas }} - # runtime.properties: | - # druid.service={{ .Values.druid_middlemanager_service }} - # # Number of tasks per middleManager - # druid.worker.capacity={{ .Values.druid_worker_capacity }} - # # Task launch parameters - # druid.indexer.runner.javaOpts={{ .Values.druid_indexer_runner_javaOpts }} - # druid.indexer.task.baseTaskDir={{ .Values.druid_indexer_task_baseTaskDir }} - # # Peon properties - # druid.indexer.fork.property.druid.processing.buffer.sizeBytes={{ .Values.druid_indexer_fork_property_druid_processing_buffer_sizeBytes }} - # druid.indexer.fork.property.druid.processing.numThreads={{ .Values.druid_indexer_fork_property_druid_processing_numThreads }} - # druid.indexer.fork.property.druid.server.http.numThreads={{ .Values.druid_indexer_fork_property_druid_server_http_numThreads }} - # #Additional Parameters - # druid.indexer.task.restoreTasksOnRestart={{ .Values.druid_indexer_task_restoreTasksOnRestart }} - # druid.indexer.task.defaultHadoopCoordinates=[\"org.apache.hadoop:hadoop-client:2.8.3\"] - # extra.jvm.options: |+ - # -Xmx{{ .Values.druid_middlemanager_heap_size }} - # -Xms{{ .Values.druid_middlemanager_heap_size }} - # # services: - # # - spec: - # # clusterIP: None - # # ports: - # # - name: middlemanager-port - # # port: {{ .Values.druid_middlemanager_port }} - # # targetPort: {{ .Values.druid_middlemanager_port }} - # # type: ClusterIP - # readinessProbe: - # initialDelaySeconds: 30 - # httpGet: - # path: /status/health - # port: {{ .Values.druid_middlemanager_port }} - - # securityContext: - # fsGroup: 0 - # runAsUser: 0 - # runAsGroup: 0 - # volumeMounts: - # - mountPath: {{ .Values.mount_path }} - # name: middlemanager-volume - # volumeClaimTemplates: - # - metadata: - # name: middlemanager-volume - # spec: - # storageClassName: "{{- .Values.storageClass }}" - # accessModes: - # - ReadWriteOnce - # resources: - # requests: - # storage: {{ .Values.druid_middlemanager_persistent_volume_size }} - - # resources: - # requests: - # memory: 1Gi - # cpu: 256m - # limits: - # memory: 2Gi - # cpu: 1 - - indexers: - nodeType: "indexer" - druid.port: {{ .Values.druid_indexer_port }} - nodeConfigMountPath: "/opt/druid/conf/druid/cluster/data/indexer" - replicas: {{ .Values.druid_indexer_replicas }} - runtime.properties: | - druid.service={{ .Values.druid_indexer_service }} - # Number of tasks per indexer - druid.worker.capacity={{ .Values.druid_indexer_worker_capacity }} - # Task launch parameters - druid.indexer.runner.javaOpts={{ .Values.druid_indexer_runner_javaOpts }} - druid.indexer.task.baseTaskDir={{ .Values.druid_indexer_task_baseTaskDir }} - # Merge jobs - druid.worker.numConcurrentMerges={{ .Values.druid_indexer_numConcurrentMerges }} - # Processing resource properties - # testing auto for sizeBytes - druid.processing.buffer.sizeBytes={{ .Values.druid_indexer_fork_property_druid_processing_buffer_sizeBytes }} - druid.processing.numThreads={{ .Values.druid_indexer_fork_property_druid_processing_numThreads }} - druid.processing.numMergeBuffers={{ .Values.druid_indexer_fork_property_druid_processing_numMergeBuffers }} - druid.server.http.numThreads={{ .Values.druid_indexer_fork_property_druid_server_http_numThreads }} - #Additional Parameters - druid.indexer.task.restoreTasksOnRestart={{ .Values.druid_indexer_task_restoreTasksOnRestart }} - druid.indexer.task.defaultHadoopCoordinates=[\"org.apache.hadoop:hadoop-client:2.8.3\"] - extra.jvm.options: |- - -Xms{{ .Values.druid_indexer_min_heap_size }} - -Xmx{{ .Values.druid_indexer_max_heap_size }} - -XX:MaxDirectMemorySize={{ .Values.druid_indexer_max_direct_size }} - readinessProbe: - initialDelaySeconds: {{ .Values.indexer_intial_delay }} - httpGet: - path: /status/health - port: {{ .Values.druid_indexer_port }} - - securityContext: - fsGroup: 0 - runAsUser: 0 - runAsGroup: 0 - volumeMounts: - - mountPath: {{ .Values.mount_path }} - name: indexer-volume - volumeClaimTemplates: - - metadata: - name: indexer-volume - spec: - storageClassName: "{{- .Values.storageClass }}" - accessModes: - - ReadWriteOnce - resources: - requests: - storage: {{ .Values.druid_indexer_persistent_volume_size }} - resources: - requests: - memory: {{ .Values.druid_indexer_pod_memory_request }} - cpu: {{ .Values.druid_indexer_pod_cpu_request }} - limits: - memory: {{ .Values.druid_indexer_pod_memory_limit }} - cpu: {{ .Values.druid_indexer_pod_cpu_limit }} - - routers: - nodeType: "router" - druid.port: {{ .Values.druid_router_plaintextPort }} - nodeConfigMountPath: "/opt/druid/conf/druid/cluster/query/router" - replicas: {{ .Values.druid_router_replicas }} - services: - - spec: - type: ClusterIP - ports: - - name: druidrouterport - port: 8888 - targetPort: 8888 - protocol: TCP - # services: - # - spec: - # type: ClusterIP - # ports: - # - name: druidrouterport - # port: 80 - # targetPort: 8888 - # protocol: TCP - # ingressAnnotations: - # name: router-ingress - # nginx.ingress.kubernetes.io/rewrite-target: /$1 - # ingress: - # ingressClassName: nginx - # rules: - # - host: "*.nip.io" - # http: - # paths: - # - path: / - # pathType: Prefix - # backend: - # service: - # name: druid-{{ .Values.druid_cluster_type }}-{{ .Values.druid_env }}-routers - # port: - # name: druidrouterport - - runtime.properties: | - druid.service={{ .Values.druid_router_service }} - # druid.plaintextPort={{ .Values.druid_router_plaintextPort }} - # HTTP proxy - druid.router.http.numConnections={{ .Values.druid_router_http_numConnections }} - druid.router.http.readTimeout={{ .Values.druid_router_http_readTimeout }} - druid.router.http.numMaxThreads={{ .Values.druid_router_http_numMaxThreads }} - druid.server.http.numThreads={{ .Values.druid_server_http_numThreads }} - # Service discovery - druid.router.defaultBrokerServiceName={{ .Values.druid_broker_service }} - druid.router.coordinatorServiceName={{ .Values.druid_coordinator_service }} - # Management proxy to coordinator / overlord: required for unified web console. - druid.router.managementProxy.enabled={{ .Values.druid_router_managementProxy_enabled }} - extra.jvm.options: |- - -Xms{{ .Values.druid_router_min_heap_size }} - -Xmx{{ .Values.druid_router_max_heap_size }} - readinessProbe: - httpGet: - path: /status/health - port: {{ .Values.druid_router_plaintextPort }} - - resources: - requests: - memory: {{ .Values.druid_router_pod_memory_request }} - cpu: {{ .Values.druid_router_pod_cpu_request }} - limits: - memory: {{ .Values.druid_router_pod_memory_limit }} - cpu: {{ .Values.druid_router_pod_cpu_limit }} - diff --git a/knowlg-automation/helm_charts/druid-cluster/templates/historical_hpa.yaml b/knowlg-automation/helm_charts/druid-cluster/templates/historical_hpa.yaml deleted file mode 100644 index 1a8ca602e..000000000 --- a/knowlg-automation/helm_charts/druid-cluster/templates/historical_hpa.yaml +++ /dev/null @@ -1,29 +0,0 @@ -# apiVersion: autoscaling/v2beta2 -# kind: HorizontalPodAutoscaler -# metadata: -# name: "{{ .Release.Namespace }}-historicals" -# namespace: {{ .Values.namespace }} -# spec: -# scaleTargetRef: -# apiVersion: apps/v1 -# kind: StatefulSet -# name: "{{ .Release.Namespace }}-{{ .Values.druid_env }}-historicals" -# # behavior: -# # scaleUp: -# # stabilizationWindowSeconds: 10 -# # scaleDown: -# # stabilizationWindowSeconds: 20 -# minReplicas: 1 -# maxReplicas: 2 -# metrics: -# - type: Object -# object: -# metric: -# name: pv_used_percentage -# describedObject: -# apiVersion: v1 -# kind: persistentvolumeclaim -# name: "historical-volume-{{ .Release.Namespace }}-{{ .Values.druid_env }}-historicals-0" -# target: -# type: Utilization -# averageValue: 20 diff --git a/knowlg-automation/helm_charts/druid-cluster/values.yaml b/knowlg-automation/helm_charts/druid-cluster/values.yaml deleted file mode 100644 index 3e4f4afbc..000000000 --- a/knowlg-automation/helm_charts/druid-cluster/values.yaml +++ /dev/null @@ -1,345 +0,0 @@ -druid_env: "dev" -druid_cluster_type: "raw" -druid_image: "apache/druid:25.0.0" -druid_namespace: "druid" -dp_vault_pgdb_admin_password: postgres -druid_monitoring: False -mount_path: /druid/data -storageClass: "default" - -######################### Druid common variables ######################## -druid_directory: "/opt/druid" -druid_extensions_loadList: '"postgresql-metadata-storage", "druid-kafka-indexing-service"' - -# Logging -# Log all runtime properties on startup. Disable to avoid logging properties on startup: -druid.startup.logging.logProperties: true - -# Druid Metadata Store -druid_metadata_storage_type: "postgresql" -druid_metadata_storage_connector_connectURI: "jdbc:postgresql://postgresql-hl.postgresql.svc.cluster.local:5432/druid_raw" -druid_metadata_storage_connector_user: "druid" -druid_metadata_storage_connector_password: "druid" - -# Druid Storage Type -druid_deepstorage_type: "local" -druid_storage_directory: "/data" - -# AWS S3 Details -s3_access_key: "" -s3_secret_key: "" -s3_bucket: "" - -# Azure Container Details -azure_storage_account_name: "" -azure_storage_account_key: "" -azure_storage_container: "" - -# Indexing service logs -# For local disk (only viable in a cluster_type if this is a network mount): -druid_indexer_logs_type: "file" -druid_indexer_logs_prefix: "druid-task-logs" -druid_indexer_logs_container: "" - -# Service discovery -druid_selectors_indexing_serviceName: druid/overlord -druid_selectors_coordinator_serviceName: druid/coordinator - -# Monitoring -druid_monitoring_monitors: '"com.metamx.metrics.JvmMonitor","org.apache.druid.java.util.metrics.JvmMonitor"' -druid_emitter: composing -druid_emitter_composing_emitters: '"logging"' -druid_emitter_logging_logLevel: INFO - - -# Storage type of double columns -# ommiting this will lead to index double as float at the storage layer -druid_indexing_doubleStorage: double - -#Writing query logs into file -druid_request_logging_type: composing -druid_request_logging_dir: "/data/druid/log" - -druid_javascript_enabled: true -druid_sql_enable: true - -####################### Druid Broker Variables ########################## - -druid_broker_service: druid/broker -druid_broker_port: 8082 -druid_broker_min_heap_size: 256M -druid_broker_max_heap_size: 512M -druid_broker_max_direct_size: 192M - -# Processing threads and buffers -druid_broker_processing_buffer_sizeBytes: 64MiB -druid_broker_processing_numThreads: 1 -druid_broker_processing_numMergeBuffers: 1 - -druid_broker_pod_memory_request: 256Mi -druid_broker_pod_memory_limit: 750Mi -druid_broker_pod_cpu_request: 250m -druid_broker_pod_cpu_limit: 500m - -# HTTP server threads -druid_broker_http_numConnections: 5 -druid_server_http_numThreads: 25 - -druid_javascript.enabled: true -druid_sql_enable: true - -druid_broker_replicas: 1 - -##################### Druid Coordinator Variables ####################### - -druid_coordinator_service: druid/coordinator -druid_coordinator_port: 8081 - -druid_coordinator_min_heap_size: 256M -druid_coordinator_max_heap_size: 512M - -druid_coordinator_pod_memory_request: 256Mi -druid_coordinator_pod_memory_limit: 512Mi -druid_coordinator_pod_cpu_request: 250m -druid_coordinator_pod_cpu_limit: 500m - -druid_coordinator_startDelay: PT30S -druid_coordinator_period: PT30S -druid_coordinator_balancer_strategy: diskNormalized - -druid_coordinator_replicas: 1 - -####################### Druid Overlord Variables ######################## - -druid_overlord_service: druid/overlord -druid_overlord_port: 8090 -druid_overlord_min_heap_size: 256M -druid_overlord_max_heap_size: 512M - -druid_overlord_pod_memory_request: 256Mi -druid_overlord_pod_memory_limit: 512Mi -druid_overlord_pod_cpu_request: 250m -druid_overlord_pod_cpu_limit: 500m - -druid_indexer_queue_startDelay: PT30S - -druid_indexer_runner_type: remote -druid_indexer_storage_type: metadata - -# Additional parameters for minor compaction -druid_indexer_tasklock_forceTimeChunkLock: false - -druid_overlord_replicas: 1 - -###################### Druid Historical Variables ####################### - -druid_historical_service: druid/historical -druid_historical_port: 8084 -druid_historical_min_heap_size: 256M -druid_historical_max_heap_size: 512M -druid_historical_max_direct_size: 320M - -druid_historical_pod_memory_request: 512Mi -druid_historical_pod_memory_limit: 850Mi -druid_historical_pod_cpu_request: 500m -druid_historical_pod_cpu_limit: 1 - -# HTTP server threads -druid_server_http_numThreads: 25 - -# Processing threads and buffers -druid_historical_processing_buffer_sizeBytes: 64MiB -druid_historical_processing_numThreads: 2 -druid_historical_processing_numMergeBuffers: 2 - -druid_query_ondiskstorage_enabled: True -druid_query_groupBy_maxMergingDictionarySize: 100000000 -druid_query_groupBy_maxOnDiskStorage: 524288000 - -# Segmentstorage (default 1 gb storage) -druid_segmentCache_locations: '{"path": "/druid/data/store", "maxSize": 1073741824}' -druid_historical_persistent_volume_size: 1G - -druid_segmentCache_numLoadingThreads: 4 - -# Caching -druid_historical_cache_useCache: False -druid_historical_cache_populateCache: true -druid_historical_cache_unCacheable: '"select", "scan"' -druid_cache_type: caffeine -druid_historical_cache_size: 128MiB -druid_historical_cache_expiry: 3600000 - -druid_historical_replicas: 1 - -#################### Druid Middlemanager Variables ###################### - -druid_middlemanager_service: druid/middlemanager -druid_middlemanager_port: 8091 -druid_middlemanager_heap_size: 128M - -druid_middlemanager_persistent_volume_size: 1G - -# Number of tasks per middleManager -druid_worker_capacity: 2 -druid_middlemanager_peon_heap_size: 256M - -# Task launch parameters -druid_indexer_runner_javaOpts: "-server -Xms512m -Xmx512m -XX:+UseG1GC -XX:MaxGCPauseMillis=100" -druid_indexer_task_baseTaskDir: "/var/task" - -# Peon properties -druid_indexer_fork_property_druid_processing_buffer_sizeBytes: 25MiB -druid_indexer_fork_property_druid_processing_numThreads: 2 -druid_indexer_fork_property_druid_server_http_numThreads: 25 - -#Additional Parameters -druid_indexer_task_restoreTasksOnRestart: true - -druid_middlemanager_replicas: 1 - - -#################### Druid Indexer Variables ###################### - -druid_indexer_service: druid/indexer -druid_indexer_port: 8091 -druid_indexer_min_heap_size: 256M -druid_indexer_max_heap_size: 512M -druid_indexer_max_direct_size: 256M - -# Peon properties -druid_indexer_fork_property_druid_processing_buffer_sizeBytes: 64M -druid_indexer_fork_property_druid_processing_numThreads: 2 # Number of cores - 1 (or 1) -druid_indexer_fork_property_druid_processing_numMergeBuffers: 1 -druid_indexer_fork_property_druid_server_http_numThreads: 25 # max(10, (Number of cores * 17) / 16 + 2) + 30 - -druid_indexer_pod_memory_request: 512Mi -druid_indexer_pod_memory_limit: 768Mi -druid_indexer_pod_cpu_request: 500m -druid_indexer_pod_cpu_limit: 1 -druid_indexer_numConcurrentMerges: 1 - -druid_indexer_persistent_volume_size: 1Gi - -indexer_intial_delay: 30 - -# Number of tasks per indexer -druid_indexer_worker_capacity: 2 #(Number of available processors - 1) - -# Task launch parameters -# druid_indexer_runner_javaOpts: "-server -Xms512m -Xmx1024m -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:MaxDirectMemorySize=512m" -druid_indexer_task_baseTaskDir: "/druid/data/task" - - - -#Additional Parameters -druid_indexer_task_restoreTasksOnRestart: true - -druid_indexer_replicas: 1 - -######################## Druid Router Variables ######################### - -druid_router_service: druid/router -druid_router_plaintextPort: 8888 - -druid_router_min_heap_size: 256M -druid_router_max_heap_size: 512M - -druid_router_pod_memory_request: 256Mi -druid_router_pod_memory_limit: 512Mi -druid_router_pod_cpu_request: 250m -druid_router_pod_cpu_limit: 512m - -# HTTP proxy -druid_router_http_numConnections: 25 -druid_router_http_readTimeout: PT5M -druid_router_http_numMaxThreads: 25 -druid_server_http_numThreads: 25 - -# Service discovery -druid_router_defaultBrokerServiceName: druid/broker -druid_router_coordinatorServiceName: druid/coordinator - -# Management proxy to coordinator / overlord: required for unified web console. -druid_router_managementProxy_enabled: True - -druid_router_replicas: 1 - -#################### Zookeeper Variables ###################### - -zookeeper: - image: - registry: docker.io - repository: bitnami/zookeeper - tag: 3.6-debian-10 - pullPolicy: Always - - persistence: - size: 1Gi - - heapSize: 128 - replicaCount: 1 - - resources: - requests: - memory: 128Mi - cpu: 512m - - tickTime: 2000 - initLimit: 10 - syncLimit: 5 - preAllocSize: 65536 - snapCount: 100000 - maxClientCnxns: 60 - fourlwCommandsWhitelist: srvr, mntr, ruok - listenOnAllIPs: false - allowAnonymousLogin: true - autopurge: - snapRetainCount: 3 - purgeInterval: 0 - maxSessionTimeout: 40000 - - allowAnonymousLogin: true - - minServerId: 1 - - securityContext: - enabled: true - fsGroup: 1001 - runAsUser: 1001 - - livenessProbe: - enabled: true - initialDelaySeconds: 30 - periodSeconds: 10 - timeoutSeconds: 5 - failureThreshold: 6 - successThreshold: 1 - probeCommandTimeout: 2 - - readinessProbe: - enabled: true - initialDelaySeconds: 5 - periodSeconds: 10 - timeoutSeconds: 5 - failureThreshold: 6 - successThreshold: 1 - probeCommandTimeout: 2 - - service: - type: ClusterIP - port: 2181 - followerPort: 2888 - electionPort: 3888 - nodePorts: - client: "" - clientTls: "" - publishNotReadyAddresses: true - tlsClientPort: 3181 - disableBaseClientPort: false - annotations: {} - headless: - annotations: {} - - - \ No newline at end of file diff --git a/knowlg-automation/helm_charts/druid-operator/Chart.yaml b/knowlg-automation/helm_charts/druid-operator/Chart.yaml deleted file mode 100644 index 6588f1e73..000000000 --- a/knowlg-automation/helm_charts/druid-operator/Chart.yaml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: v2 -name: druid-operator -description: Druid Kubernetes Operator - -# A chart can be either an 'application' or a 'library' chart. -# -# Application charts are a collection of templates that can be packaged into versioned archives -# to be deployed. -# -# Library charts provide useful utilities or functions for the chart developer. They're included as -# a dependency of application charts to inject those utilities and functions into the rendering -# pipeline. Library charts do not define any templates and therefore cannot be deployed. -type: application - -# This is the chart version. This version number should be incremented each time you make changes -# to the chart and its templates, including the app version. -# Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.1.1 - -# This is the version number of the application being deployed. This version number should be -# incremented each time you make changes to the application. Versions are not expected to -# follow Semantic Versioning. They should reflect the version the application is using. -appVersion: 0.0.7 diff --git a/knowlg-automation/helm_charts/druid-operator/templates/NOTES.txt b/knowlg-automation/helm_charts/druid-operator/templates/NOTES.txt deleted file mode 100644 index 88a89946c..000000000 --- a/knowlg-automation/helm_charts/druid-operator/templates/NOTES.txt +++ /dev/null @@ -1 +0,0 @@ -Refer to https://github.com/druid-io/druid-operator/blob/master/docs/README.md to get started. diff --git a/knowlg-automation/helm_charts/druid-operator/templates/_helpers.tpl b/knowlg-automation/helm_charts/druid-operator/templates/_helpers.tpl deleted file mode 100644 index 628079bfe..000000000 --- a/knowlg-automation/helm_charts/druid-operator/templates/_helpers.tpl +++ /dev/null @@ -1,62 +0,0 @@ -{{/* -Expand the name of the chart. -*/}} -{{- define "druid-operator.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -*/}} -{{- define "druid-operator.fullname" -}} -{{- if .Values.fullnameOverride }} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- $name := default .Chart.Name .Values.nameOverride }} -{{- if contains $name .Release.Name }} -{{- .Release.Name | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} -{{- end }} -{{- end }} -{{- end }} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "druid-operator.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{/* -Common labels -*/}} -{{- define "druid-operator.labels" -}} -helm.sh/chart: {{ include "druid-operator.chart" . }} -{{ include "druid-operator.selectorLabels" . }} -{{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -{{- end }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- end }} - -{{/* -Selector labels -*/}} -{{- define "druid-operator.selectorLabels" -}} -app.kubernetes.io/name: {{ include "druid-operator.name" . }} -app.kubernetes.io/instance: {{ .Release.Name }} -{{- end }} - -{{/* -Create the name of the service account to use -*/}} -{{- define "druid-operator.serviceAccountName" -}} -{{- if .Values.serviceAccount.create }} -{{- default (include "druid-operator.fullname" .) .Values.serviceAccount.name }} -{{- else }} -{{- default "default" .Values.serviceAccount.name }} -{{- end }} -{{- end }} diff --git a/knowlg-automation/helm_charts/druid-operator/templates/crds/druid.apache.org_druids.yaml b/knowlg-automation/helm_charts/druid-operator/templates/crds/druid.apache.org_druids.yaml deleted file mode 100644 index ed176311c..000000000 --- a/knowlg-automation/helm_charts/druid-operator/templates/crds/druid.apache.org_druids.yaml +++ /dev/null @@ -1,4467 +0,0 @@ - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.6.0-beta.0 - creationTimestamp: null - name: druids.druid.apache.org -spec: - group: druid.apache.org - names: - kind: Druid - listKind: DruidList - plural: druids - singular: druid - scope: Namespaced - versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - properties: - affinity: - properties: - nodeAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: - properties: - preference: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - weight: - format: int32 - type: integer - required: - - preference - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - properties: - nodeSelectorTerms: - items: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - type: array - required: - - nodeSelectorTerms - type: object - type: object - podAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: - properties: - podAffinityTerm: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - weight: - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - items: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - type: array - type: object - podAntiAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: - properties: - podAffinityTerm: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - weight: - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - items: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - type: array - type: object - type: object - common.runtime.properties: - type: string - commonConfigMountPath: - type: string - containerSecurityContext: - properties: - allowPrivilegeEscalation: - type: boolean - capabilities: - properties: - add: - items: - type: string - type: array - drop: - items: - type: string - type: array - type: object - privileged: - type: boolean - procMount: - type: string - readOnlyRootFilesystem: - type: boolean - runAsGroup: - format: int64 - type: integer - runAsNonRoot: - type: boolean - runAsUser: - format: int64 - type: integer - seLinuxOptions: - properties: - level: - type: string - role: - type: string - type: - type: string - user: - type: string - type: object - seccompProfile: - properties: - localhostProfile: - type: string - type: - type: string - required: - - type - type: object - windowsOptions: - properties: - gmsaCredentialSpec: - type: string - gmsaCredentialSpecName: - type: string - runAsUserName: - type: string - type: object - type: object - deepStorage: - properties: - spec: - format: byte - type: string - type: - type: string - required: - - spec - - type - type: object - deleteOrphanPvc: - type: boolean - disablePVCDeletionFinalizer: - type: boolean - env: - items: - properties: - name: - type: string - value: - type: string - valueFrom: - properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - optional: - type: boolean - required: - - key - type: object - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - resourceFieldRef: - properties: - containerName: - type: string - divisor: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - type: string - required: - - resource - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - optional: - type: boolean - required: - - key - type: object - type: object - required: - - name - type: object - type: array - envFrom: - items: - properties: - configMapRef: - properties: - name: - type: string - optional: - type: boolean - type: object - prefix: - type: string - secretRef: - properties: - name: - type: string - optional: - type: boolean - type: object - type: object - type: array - forceDeleteStsPodOnError: - type: boolean - ignored: - type: boolean - image: - type: string - imagePullPolicy: - type: string - imagePullSecrets: - items: - properties: - name: - type: string - type: object - type: array - jvm.options: - type: string - livenessProbe: - properties: - exec: - properties: - command: - items: - type: string - type: array - type: object - failureThreshold: - format: int32 - type: integer - httpGet: - properties: - host: - type: string - httpHeaders: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - path: - type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - scheme: - type: string - required: - - port - type: object - initialDelaySeconds: - format: int32 - type: integer - periodSeconds: - format: int32 - type: integer - successThreshold: - format: int32 - type: integer - tcpSocket: - properties: - host: - type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - required: - - port - type: object - timeoutSeconds: - format: int32 - type: integer - type: object - log4j.config: - type: string - metadataStore: - properties: - spec: - format: byte - type: string - type: - type: string - required: - - spec - - type - type: object - nodeSelector: - additionalProperties: - type: string - type: object - nodes: - additionalProperties: - properties: - affinity: - properties: - nodeAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: - properties: - preference: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - weight: - format: int32 - type: integer - required: - - preference - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - properties: - nodeSelectorTerms: - items: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - type: array - required: - - nodeSelectorTerms - type: object - type: object - podAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: - properties: - podAffinityTerm: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - weight: - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - items: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - type: array - type: object - podAntiAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: - properties: - podAffinityTerm: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - weight: - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - items: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - type: array - type: object - type: object - containerSecurityContext: - properties: - allowPrivilegeEscalation: - type: boolean - capabilities: - properties: - add: - items: - type: string - type: array - drop: - items: - type: string - type: array - type: object - privileged: - type: boolean - procMount: - type: string - readOnlyRootFilesystem: - type: boolean - runAsGroup: - format: int64 - type: integer - runAsNonRoot: - type: boolean - runAsUser: - format: int64 - type: integer - seLinuxOptions: - properties: - level: - type: string - role: - type: string - type: - type: string - user: - type: string - type: object - seccompProfile: - properties: - localhostProfile: - type: string - type: - type: string - required: - - type - type: object - windowsOptions: - properties: - gmsaCredentialSpec: - type: string - gmsaCredentialSpecName: - type: string - runAsUserName: - type: string - type: object - type: object - druid.port: - format: int32 - type: integer - env: - items: - properties: - name: - type: string - value: - type: string - valueFrom: - properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - optional: - type: boolean - required: - - key - type: object - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - resourceFieldRef: - properties: - containerName: - type: string - divisor: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - type: string - required: - - resource - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - optional: - type: boolean - required: - - key - type: object - type: object - required: - - name - type: object - type: array - envFrom: - items: - properties: - configMapRef: - properties: - name: - type: string - optional: - type: boolean - type: object - prefix: - type: string - secretRef: - properties: - name: - type: string - optional: - type: boolean - type: object - type: object - type: array - extra.jvm.options: - type: string - hpAutoscaler: - properties: - behavior: - properties: - scaleDown: - properties: - policies: - items: - properties: - periodSeconds: - format: int32 - type: integer - type: - type: string - value: - format: int32 - type: integer - required: - - periodSeconds - - type - - value - type: object - type: array - selectPolicy: - type: string - stabilizationWindowSeconds: - format: int32 - type: integer - type: object - scaleUp: - properties: - policies: - items: - properties: - periodSeconds: - format: int32 - type: integer - type: - type: string - value: - format: int32 - type: integer - required: - - periodSeconds - - type - - value - type: object - type: array - selectPolicy: - type: string - stabilizationWindowSeconds: - format: int32 - type: integer - type: object - type: object - maxReplicas: - format: int32 - type: integer - metrics: - items: - properties: - containerResource: - properties: - container: - type: string - name: - type: string - target: - properties: - averageUtilization: - format: int32 - type: integer - averageValue: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: - type: string - value: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - required: - - type - type: object - required: - - container - - name - - target - type: object - external: - properties: - metric: - properties: - name: - type: string - selector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - required: - - name - type: object - target: - properties: - averageUtilization: - format: int32 - type: integer - averageValue: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: - type: string - value: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - required: - - type - type: object - required: - - metric - - target - type: object - object: - properties: - describedObject: - properties: - apiVersion: - type: string - kind: - type: string - name: - type: string - required: - - kind - - name - type: object - metric: - properties: - name: - type: string - selector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - required: - - name - type: object - target: - properties: - averageUtilization: - format: int32 - type: integer - averageValue: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: - type: string - value: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - required: - - type - type: object - required: - - describedObject - - metric - - target - type: object - pods: - properties: - metric: - properties: - name: - type: string - selector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - required: - - name - type: object - target: - properties: - averageUtilization: - format: int32 - type: integer - averageValue: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: - type: string - value: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - required: - - type - type: object - required: - - metric - - target - type: object - resource: - properties: - name: - type: string - target: - properties: - averageUtilization: - format: int32 - type: integer - averageValue: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: - type: string - value: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - required: - - type - type: object - required: - - name - - target - type: object - type: - type: string - required: - - type - type: object - type: array - minReplicas: - format: int32 - type: integer - scaleTargetRef: - properties: - apiVersion: - type: string - kind: - type: string - name: - type: string - required: - - kind - - name - type: object - required: - - maxReplicas - - scaleTargetRef - type: object - image: - type: string - imagePullPolicy: - type: string - imagePullSecrets: - items: - properties: - name: - type: string - type: object - type: array - ingress: - properties: - defaultBackend: - properties: - resource: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - required: - - kind - - name - type: object - service: - properties: - name: - type: string - port: - properties: - name: - type: string - number: - format: int32 - type: integer - type: object - required: - - name - type: object - type: object - ingressClassName: - type: string - rules: - items: - properties: - host: - type: string - http: - properties: - paths: - items: - properties: - backend: - properties: - resource: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - required: - - kind - - name - type: object - service: - properties: - name: - type: string - port: - properties: - name: - type: string - number: - format: int32 - type: integer - type: object - required: - - name - type: object - type: object - path: - type: string - pathType: - type: string - required: - - backend - type: object - type: array - x-kubernetes-list-type: atomic - required: - - paths - type: object - type: object - type: array - x-kubernetes-list-type: atomic - tls: - items: - properties: - hosts: - items: - type: string - type: array - x-kubernetes-list-type: atomic - secretName: - type: string - type: object - type: array - x-kubernetes-list-type: atomic - type: object - ingressAnnotations: - additionalProperties: - type: string - type: object - jvm.options: - type: string - kind: - type: string - lifecycle: - properties: - postStart: - properties: - exec: - properties: - command: - items: - type: string - type: array - type: object - httpGet: - properties: - host: - type: string - httpHeaders: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - path: - type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - scheme: - type: string - required: - - port - type: object - tcpSocket: - properties: - host: - type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - required: - - port - type: object - type: object - preStop: - properties: - exec: - properties: - command: - items: - type: string - type: array - type: object - httpGet: - properties: - host: - type: string - httpHeaders: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - path: - type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - scheme: - type: string - required: - - port - type: object - tcpSocket: - properties: - host: - type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - required: - - port - type: object - type: object - type: object - livenessProbe: - properties: - exec: - properties: - command: - items: - type: string - type: array - type: object - failureThreshold: - format: int32 - type: integer - httpGet: - properties: - host: - type: string - httpHeaders: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - path: - type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - scheme: - type: string - required: - - port - type: object - initialDelaySeconds: - format: int32 - type: integer - periodSeconds: - format: int32 - type: integer - successThreshold: - format: int32 - type: integer - tcpSocket: - properties: - host: - type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - required: - - port - type: object - timeoutSeconds: - format: int32 - type: integer - type: object - log4j.config: - type: string - maxSurge: - format: int32 - type: integer - maxUnavailable: - format: int32 - type: integer - nodeConfigMountPath: - type: string - nodeType: - type: string - persistentVolumeClaim: - items: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - properties: - annotations: - additionalProperties: - type: string - type: object - finalizers: - items: - type: string - type: array - labels: - additionalProperties: - type: string - type: object - name: - type: string - namespace: - type: string - type: object - spec: - properties: - accessModes: - items: - type: string - type: array - dataSource: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - required: - - kind - - name - type: object - resources: - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - selector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - storageClassName: - type: string - volumeMode: - type: string - volumeName: - type: string - type: object - status: - properties: - accessModes: - items: - type: string - type: array - capacity: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - conditions: - items: - properties: - lastProbeTime: - format: date-time - type: string - lastTransitionTime: - format: date-time - type: string - message: - type: string - reason: - type: string - status: - type: string - type: - type: string - required: - - status - - type - type: object - type: array - phase: - type: string - type: object - type: object - type: array - podAnnotations: - additionalProperties: - type: string - type: object - podDisruptionBudgetSpec: - properties: - maxUnavailable: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - minAvailable: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - selector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - type: object - podLabels: - additionalProperties: - type: string - type: object - podManagementPolicy: - type: string - ports: - items: - properties: - containerPort: - format: int32 - type: integer - hostIP: - type: string - hostPort: - format: int32 - type: integer - name: - type: string - protocol: - default: TCP - type: string - required: - - containerPort - type: object - type: array - readinessProbe: - properties: - exec: - properties: - command: - items: - type: string - type: array - type: object - failureThreshold: - format: int32 - type: integer - httpGet: - properties: - host: - type: string - httpHeaders: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - path: - type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - scheme: - type: string - required: - - port - type: object - initialDelaySeconds: - format: int32 - type: integer - periodSeconds: - format: int32 - type: integer - successThreshold: - format: int32 - type: integer - tcpSocket: - properties: - host: - type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - required: - - port - type: object - timeoutSeconds: - format: int32 - type: integer - type: object - replicas: - format: int32 - minimum: 0 - type: integer - resources: - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - runtime.properties: - type: string - securityContext: - properties: - fsGroup: - format: int64 - type: integer - fsGroupChangePolicy: - type: string - runAsGroup: - format: int64 - type: integer - runAsNonRoot: - type: boolean - runAsUser: - format: int64 - type: integer - seLinuxOptions: - properties: - level: - type: string - role: - type: string - type: - type: string - user: - type: string - type: object - seccompProfile: - properties: - localhostProfile: - type: string - type: - type: string - required: - - type - type: object - supplementalGroups: - items: - format: int64 - type: integer - type: array - sysctls: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - windowsOptions: - properties: - gmsaCredentialSpec: - type: string - gmsaCredentialSpecName: - type: string - runAsUserName: - type: string - type: object - type: object - services: - items: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - properties: - annotations: - additionalProperties: - type: string - type: object - finalizers: - items: - type: string - type: array - labels: - additionalProperties: - type: string - type: object - name: - type: string - namespace: - type: string - type: object - spec: - properties: - allocateLoadBalancerNodePorts: - type: boolean - clusterIP: - type: string - clusterIPs: - items: - type: string - type: array - x-kubernetes-list-type: atomic - externalIPs: - items: - type: string - type: array - externalName: - type: string - externalTrafficPolicy: - type: string - healthCheckNodePort: - format: int32 - type: integer - ipFamilies: - items: - type: string - type: array - x-kubernetes-list-type: atomic - ipFamilyPolicy: - type: string - loadBalancerIP: - type: string - loadBalancerSourceRanges: - items: - type: string - type: array - ports: - items: - properties: - appProtocol: - type: string - name: - type: string - nodePort: - format: int32 - type: integer - port: - format: int32 - type: integer - protocol: - default: TCP - type: string - targetPort: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - required: - - port - type: object - type: array - x-kubernetes-list-map-keys: - - port - - protocol - x-kubernetes-list-type: map - publishNotReadyAddresses: - type: boolean - selector: - additionalProperties: - type: string - type: object - sessionAffinity: - type: string - sessionAffinityConfig: - properties: - clientIP: - properties: - timeoutSeconds: - format: int32 - type: integer - type: object - type: object - topologyKeys: - items: - type: string - type: array - type: - type: string - type: object - status: - properties: - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - maxLength: 32768 - type: string - observedGeneration: - format: int64 - minimum: 0 - type: integer - reason: - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - enum: - - "True" - - "False" - - Unknown - type: string - type: - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - loadBalancer: - properties: - ingress: - items: - properties: - hostname: - type: string - ip: - type: string - ports: - items: - properties: - error: - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - port: - format: int32 - type: integer - protocol: - default: TCP - type: string - required: - - port - - protocol - type: object - type: array - x-kubernetes-list-type: atomic - type: object - type: array - type: object - type: object - type: object - type: array - startUpProbes: - properties: - exec: - properties: - command: - items: - type: string - type: array - type: object - failureThreshold: - format: int32 - type: integer - httpGet: - properties: - host: - type: string - httpHeaders: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - path: - type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - scheme: - type: string - required: - - port - type: object - initialDelaySeconds: - format: int32 - type: integer - periodSeconds: - format: int32 - type: integer - successThreshold: - format: int32 - type: integer - tcpSocket: - properties: - host: - type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - required: - - port - type: object - timeoutSeconds: - format: int32 - type: integer - type: object - terminationGracePeriodSeconds: - format: int64 - type: integer - tolerations: - items: - properties: - effect: - type: string - key: - type: string - operator: - type: string - tolerationSeconds: - format: int64 - type: integer - value: - type: string - type: object - type: array - topologySpreadConstraints: - items: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - maxSkew: - format: int32 - type: integer - topologyKey: - type: string - whenUnsatisfiable: - type: string - required: - - maxSkew - - topologyKey - - whenUnsatisfiable - type: object - type: array - updateStrategy: - properties: - rollingUpdate: - properties: - partition: - format: int32 - type: integer - type: object - type: - type: string - type: object - volumeClaimTemplates: - items: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - properties: - annotations: - additionalProperties: - type: string - type: object - finalizers: - items: - type: string - type: array - labels: - additionalProperties: - type: string - type: object - name: - type: string - namespace: - type: string - type: object - spec: - properties: - accessModes: - items: - type: string - type: array - dataSource: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - required: - - kind - - name - type: object - resources: - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - selector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - storageClassName: - type: string - volumeMode: - type: string - volumeName: - type: string - type: object - status: - properties: - accessModes: - items: - type: string - type: array - capacity: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - conditions: - items: - properties: - lastProbeTime: - format: date-time - type: string - lastTransitionTime: - format: date-time - type: string - message: - type: string - reason: - type: string - status: - type: string - type: - type: string - required: - - status - - type - type: object - type: array - phase: - type: string - type: object - type: object - type: array - volumeMounts: - items: - properties: - mountPath: - type: string - mountPropagation: - type: string - name: - type: string - readOnly: - type: boolean - subPath: - type: string - subPathExpr: - type: string - required: - - mountPath - - name - type: object - type: array - volumes: - items: - properties: - awsElasticBlockStore: - properties: - fsType: - type: string - partition: - format: int32 - type: integer - readOnly: - type: boolean - volumeID: - type: string - required: - - volumeID - type: object - azureDisk: - properties: - cachingMode: - type: string - diskName: - type: string - diskURI: - type: string - fsType: - type: string - kind: - type: string - readOnly: - type: boolean - required: - - diskName - - diskURI - type: object - azureFile: - properties: - readOnly: - type: boolean - secretName: - type: string - shareName: - type: string - required: - - secretName - - shareName - type: object - cephfs: - properties: - monitors: - items: - type: string - type: array - path: - type: string - readOnly: - type: boolean - secretFile: - type: string - secretRef: - properties: - name: - type: string - type: object - user: - type: string - required: - - monitors - type: object - cinder: - properties: - fsType: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - volumeID: - type: string - required: - - volumeID - type: object - configMap: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - name: - type: string - optional: - type: boolean - type: object - csi: - properties: - driver: - type: string - fsType: - type: string - nodePublishSecretRef: - properties: - name: - type: string - type: object - readOnly: - type: boolean - volumeAttributes: - additionalProperties: - type: string - type: object - required: - - driver - type: object - downwardAPI: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - mode: - format: int32 - type: integer - path: - type: string - resourceFieldRef: - properties: - containerName: - type: string - divisor: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - type: string - required: - - resource - type: object - required: - - path - type: object - type: array - type: object - emptyDir: - properties: - medium: - type: string - sizeLimit: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - ephemeral: - properties: - readOnly: - type: boolean - volumeClaimTemplate: - properties: - metadata: - properties: - annotations: - additionalProperties: - type: string - type: object - finalizers: - items: - type: string - type: array - labels: - additionalProperties: - type: string - type: object - name: - type: string - namespace: - type: string - type: object - spec: - properties: - accessModes: - items: - type: string - type: array - dataSource: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - required: - - kind - - name - type: object - resources: - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - selector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - storageClassName: - type: string - volumeMode: - type: string - volumeName: - type: string - type: object - required: - - spec - type: object - type: object - fc: - properties: - fsType: - type: string - lun: - format: int32 - type: integer - readOnly: - type: boolean - targetWWNs: - items: - type: string - type: array - wwids: - items: - type: string - type: array - type: object - flexVolume: - properties: - driver: - type: string - fsType: - type: string - options: - additionalProperties: - type: string - type: object - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - required: - - driver - type: object - flocker: - properties: - datasetName: - type: string - datasetUUID: - type: string - type: object - gcePersistentDisk: - properties: - fsType: - type: string - partition: - format: int32 - type: integer - pdName: - type: string - readOnly: - type: boolean - required: - - pdName - type: object - gitRepo: - properties: - directory: - type: string - repository: - type: string - revision: - type: string - required: - - repository - type: object - glusterfs: - properties: - endpoints: - type: string - path: - type: string - readOnly: - type: boolean - required: - - endpoints - - path - type: object - hostPath: - properties: - path: - type: string - type: - type: string - required: - - path - type: object - iscsi: - properties: - chapAuthDiscovery: - type: boolean - chapAuthSession: - type: boolean - fsType: - type: string - initiatorName: - type: string - iqn: - type: string - iscsiInterface: - type: string - lun: - format: int32 - type: integer - portals: - items: - type: string - type: array - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - targetPortal: - type: string - required: - - iqn - - lun - - targetPortal - type: object - name: - type: string - nfs: - properties: - path: - type: string - readOnly: - type: boolean - server: - type: string - required: - - path - - server - type: object - persistentVolumeClaim: - properties: - claimName: - type: string - readOnly: - type: boolean - required: - - claimName - type: object - photonPersistentDisk: - properties: - fsType: - type: string - pdID: - type: string - required: - - pdID - type: object - portworxVolume: - properties: - fsType: - type: string - readOnly: - type: boolean - volumeID: - type: string - required: - - volumeID - type: object - projected: - properties: - defaultMode: - format: int32 - type: integer - sources: - items: - properties: - configMap: - properties: - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - name: - type: string - optional: - type: boolean - type: object - downwardAPI: - properties: - items: - items: - properties: - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - mode: - format: int32 - type: integer - path: - type: string - resourceFieldRef: - properties: - containerName: - type: string - divisor: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - type: string - required: - - resource - type: object - required: - - path - type: object - type: array - type: object - secret: - properties: - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - name: - type: string - optional: - type: boolean - type: object - serviceAccountToken: - properties: - audience: - type: string - expirationSeconds: - format: int64 - type: integer - path: - type: string - required: - - path - type: object - type: object - type: array - type: object - quobyte: - properties: - group: - type: string - readOnly: - type: boolean - registry: - type: string - tenant: - type: string - user: - type: string - volume: - type: string - required: - - registry - - volume - type: object - rbd: - properties: - fsType: - type: string - image: - type: string - keyring: - type: string - monitors: - items: - type: string - type: array - pool: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - user: - type: string - required: - - image - - monitors - type: object - scaleIO: - properties: - fsType: - type: string - gateway: - type: string - protectionDomain: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - sslEnabled: - type: boolean - storageMode: - type: string - storagePool: - type: string - system: - type: string - volumeName: - type: string - required: - - gateway - - secretRef - - system - type: object - secret: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - optional: - type: boolean - secretName: - type: string - type: object - storageos: - properties: - fsType: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - volumeName: - type: string - volumeNamespace: - type: string - type: object - vsphereVolume: - properties: - fsType: - type: string - storagePolicyID: - type: string - storagePolicyName: - type: string - volumePath: - type: string - required: - - volumePath - type: object - required: - - name - type: object - type: array - required: - - druid.port - - nodeConfigMountPath - - nodeType - - replicas - - runtime.properties - type: object - type: object - podAnnotations: - additionalProperties: - type: string - type: object - podLabels: - additionalProperties: - type: string - type: object - podManagementPolicy: - type: string - readinessProbe: - properties: - exec: - properties: - command: - items: - type: string - type: array - type: object - failureThreshold: - format: int32 - type: integer - httpGet: - properties: - host: - type: string - httpHeaders: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - path: - type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - scheme: - type: string - required: - - port - type: object - initialDelaySeconds: - format: int32 - type: integer - periodSeconds: - format: int32 - type: integer - successThreshold: - format: int32 - type: integer - tcpSocket: - properties: - host: - type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - required: - - port - type: object - timeoutSeconds: - format: int32 - type: integer - type: object - rollingDeploy: - type: boolean - securityContext: - properties: - fsGroup: - format: int64 - type: integer - fsGroupChangePolicy: - type: string - runAsGroup: - format: int64 - type: integer - runAsNonRoot: - type: boolean - runAsUser: - format: int64 - type: integer - seLinuxOptions: - properties: - level: - type: string - role: - type: string - type: - type: string - user: - type: string - type: object - seccompProfile: - properties: - localhostProfile: - type: string - type: - type: string - required: - - type - type: object - supplementalGroups: - items: - format: int64 - type: integer - type: array - sysctls: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - windowsOptions: - properties: - gmsaCredentialSpec: - type: string - gmsaCredentialSpecName: - type: string - runAsUserName: - type: string - type: object - type: object - serviceAccount: - type: string - services: - items: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - properties: - annotations: - additionalProperties: - type: string - type: object - finalizers: - items: - type: string - type: array - labels: - additionalProperties: - type: string - type: object - name: - type: string - namespace: - type: string - type: object - spec: - properties: - allocateLoadBalancerNodePorts: - type: boolean - clusterIP: - type: string - clusterIPs: - items: - type: string - type: array - x-kubernetes-list-type: atomic - externalIPs: - items: - type: string - type: array - externalName: - type: string - externalTrafficPolicy: - type: string - healthCheckNodePort: - format: int32 - type: integer - ipFamilies: - items: - type: string - type: array - x-kubernetes-list-type: atomic - ipFamilyPolicy: - type: string - loadBalancerIP: - type: string - loadBalancerSourceRanges: - items: - type: string - type: array - ports: - items: - properties: - appProtocol: - type: string - name: - type: string - nodePort: - format: int32 - type: integer - port: - format: int32 - type: integer - protocol: - default: TCP - type: string - targetPort: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - required: - - port - type: object - type: array - x-kubernetes-list-map-keys: - - port - - protocol - x-kubernetes-list-type: map - publishNotReadyAddresses: - type: boolean - selector: - additionalProperties: - type: string - type: object - sessionAffinity: - type: string - sessionAffinityConfig: - properties: - clientIP: - properties: - timeoutSeconds: - format: int32 - type: integer - type: object - type: object - topologyKeys: - items: - type: string - type: array - type: - type: string - type: object - status: - properties: - conditions: - items: - properties: - lastTransitionTime: - format: date-time - type: string - message: - maxLength: 32768 - type: string - observedGeneration: - format: int64 - minimum: 0 - type: integer - reason: - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - enum: - - "True" - - "False" - - Unknown - type: string - type: - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - loadBalancer: - properties: - ingress: - items: - properties: - hostname: - type: string - ip: - type: string - ports: - items: - properties: - error: - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - port: - format: int32 - type: integer - protocol: - default: TCP - type: string - required: - - port - - protocol - type: object - type: array - x-kubernetes-list-type: atomic - type: object - type: array - type: object - type: object - type: object - type: array - startScript: - type: string - startUpProbes: - properties: - exec: - properties: - command: - items: - type: string - type: array - type: object - failureThreshold: - format: int32 - type: integer - httpGet: - properties: - host: - type: string - httpHeaders: - items: - properties: - name: - type: string - value: - type: string - required: - - name - - value - type: object - type: array - path: - type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - scheme: - type: string - required: - - port - type: object - initialDelaySeconds: - format: int32 - type: integer - periodSeconds: - format: int32 - type: integer - successThreshold: - format: int32 - type: integer - tcpSocket: - properties: - host: - type: string - port: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - required: - - port - type: object - timeoutSeconds: - format: int32 - type: integer - type: object - tolerations: - items: - properties: - effect: - type: string - key: - type: string - operator: - type: string - tolerationSeconds: - format: int64 - type: integer - value: - type: string - type: object - type: array - updateStrategy: - properties: - rollingUpdate: - properties: - partition: - format: int32 - type: integer - type: object - type: - type: string - type: object - volumeClaimTemplates: - items: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - properties: - annotations: - additionalProperties: - type: string - type: object - finalizers: - items: - type: string - type: array - labels: - additionalProperties: - type: string - type: object - name: - type: string - namespace: - type: string - type: object - spec: - properties: - accessModes: - items: - type: string - type: array - dataSource: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - required: - - kind - - name - type: object - resources: - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - selector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - storageClassName: - type: string - volumeMode: - type: string - volumeName: - type: string - type: object - status: - properties: - accessModes: - items: - type: string - type: array - capacity: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - conditions: - items: - properties: - lastProbeTime: - format: date-time - type: string - lastTransitionTime: - format: date-time - type: string - message: - type: string - reason: - type: string - status: - type: string - type: - type: string - required: - - status - - type - type: object - type: array - phase: - type: string - type: object - type: object - type: array - volumeMounts: - items: - properties: - mountPath: - type: string - mountPropagation: - type: string - name: - type: string - readOnly: - type: boolean - subPath: - type: string - subPathExpr: - type: string - required: - - mountPath - - name - type: object - type: array - volumes: - items: - properties: - awsElasticBlockStore: - properties: - fsType: - type: string - partition: - format: int32 - type: integer - readOnly: - type: boolean - volumeID: - type: string - required: - - volumeID - type: object - azureDisk: - properties: - cachingMode: - type: string - diskName: - type: string - diskURI: - type: string - fsType: - type: string - kind: - type: string - readOnly: - type: boolean - required: - - diskName - - diskURI - type: object - azureFile: - properties: - readOnly: - type: boolean - secretName: - type: string - shareName: - type: string - required: - - secretName - - shareName - type: object - cephfs: - properties: - monitors: - items: - type: string - type: array - path: - type: string - readOnly: - type: boolean - secretFile: - type: string - secretRef: - properties: - name: - type: string - type: object - user: - type: string - required: - - monitors - type: object - cinder: - properties: - fsType: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - volumeID: - type: string - required: - - volumeID - type: object - configMap: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - name: - type: string - optional: - type: boolean - type: object - csi: - properties: - driver: - type: string - fsType: - type: string - nodePublishSecretRef: - properties: - name: - type: string - type: object - readOnly: - type: boolean - volumeAttributes: - additionalProperties: - type: string - type: object - required: - - driver - type: object - downwardAPI: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - mode: - format: int32 - type: integer - path: - type: string - resourceFieldRef: - properties: - containerName: - type: string - divisor: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - type: string - required: - - resource - type: object - required: - - path - type: object - type: array - type: object - emptyDir: - properties: - medium: - type: string - sizeLimit: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - ephemeral: - properties: - readOnly: - type: boolean - volumeClaimTemplate: - properties: - metadata: - properties: - annotations: - additionalProperties: - type: string - type: object - finalizers: - items: - type: string - type: array - labels: - additionalProperties: - type: string - type: object - name: - type: string - namespace: - type: string - type: object - spec: - properties: - accessModes: - items: - type: string - type: array - dataSource: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - required: - - kind - - name - type: object - resources: - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - selector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - storageClassName: - type: string - volumeMode: - type: string - volumeName: - type: string - type: object - required: - - spec - type: object - type: object - fc: - properties: - fsType: - type: string - lun: - format: int32 - type: integer - readOnly: - type: boolean - targetWWNs: - items: - type: string - type: array - wwids: - items: - type: string - type: array - type: object - flexVolume: - properties: - driver: - type: string - fsType: - type: string - options: - additionalProperties: - type: string - type: object - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - required: - - driver - type: object - flocker: - properties: - datasetName: - type: string - datasetUUID: - type: string - type: object - gcePersistentDisk: - properties: - fsType: - type: string - partition: - format: int32 - type: integer - pdName: - type: string - readOnly: - type: boolean - required: - - pdName - type: object - gitRepo: - properties: - directory: - type: string - repository: - type: string - revision: - type: string - required: - - repository - type: object - glusterfs: - properties: - endpoints: - type: string - path: - type: string - readOnly: - type: boolean - required: - - endpoints - - path - type: object - hostPath: - properties: - path: - type: string - type: - type: string - required: - - path - type: object - iscsi: - properties: - chapAuthDiscovery: - type: boolean - chapAuthSession: - type: boolean - fsType: - type: string - initiatorName: - type: string - iqn: - type: string - iscsiInterface: - type: string - lun: - format: int32 - type: integer - portals: - items: - type: string - type: array - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - targetPortal: - type: string - required: - - iqn - - lun - - targetPortal - type: object - name: - type: string - nfs: - properties: - path: - type: string - readOnly: - type: boolean - server: - type: string - required: - - path - - server - type: object - persistentVolumeClaim: - properties: - claimName: - type: string - readOnly: - type: boolean - required: - - claimName - type: object - photonPersistentDisk: - properties: - fsType: - type: string - pdID: - type: string - required: - - pdID - type: object - portworxVolume: - properties: - fsType: - type: string - readOnly: - type: boolean - volumeID: - type: string - required: - - volumeID - type: object - projected: - properties: - defaultMode: - format: int32 - type: integer - sources: - items: - properties: - configMap: - properties: - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - name: - type: string - optional: - type: boolean - type: object - downwardAPI: - properties: - items: - items: - properties: - fieldRef: - properties: - apiVersion: - type: string - fieldPath: - type: string - required: - - fieldPath - type: object - mode: - format: int32 - type: integer - path: - type: string - resourceFieldRef: - properties: - containerName: - type: string - divisor: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - type: string - required: - - resource - type: object - required: - - path - type: object - type: array - type: object - secret: - properties: - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - name: - type: string - optional: - type: boolean - type: object - serviceAccountToken: - properties: - audience: - type: string - expirationSeconds: - format: int64 - type: integer - path: - type: string - required: - - path - type: object - type: object - type: array - type: object - quobyte: - properties: - group: - type: string - readOnly: - type: boolean - registry: - type: string - tenant: - type: string - user: - type: string - volume: - type: string - required: - - registry - - volume - type: object - rbd: - properties: - fsType: - type: string - image: - type: string - keyring: - type: string - monitors: - items: - type: string - type: array - pool: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - user: - type: string - required: - - image - - monitors - type: object - scaleIO: - properties: - fsType: - type: string - gateway: - type: string - protectionDomain: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - sslEnabled: - type: boolean - storageMode: - type: string - storagePool: - type: string - system: - type: string - volumeName: - type: string - required: - - gateway - - secretRef - - system - type: object - secret: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: - type: string - required: - - key - - path - type: object - type: array - optional: - type: boolean - secretName: - type: string - type: object - storageos: - properties: - fsType: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - volumeName: - type: string - volumeNamespace: - type: string - type: object - vsphereVolume: - properties: - fsType: - type: string - storagePolicyID: - type: string - storagePolicyName: - type: string - volumePath: - type: string - required: - - volumePath - type: object - required: - - name - type: object - type: array - zookeeper: - properties: - spec: - format: byte - type: string - type: - type: string - required: - - spec - - type - type: object - required: - - common.runtime.properties - - commonConfigMountPath - - nodes - - startScript - type: object - status: - properties: - configMaps: - items: - type: string - type: array - deployments: - items: - type: string - type: array - druidNodeStatus: - properties: - druidNode: - type: string - druidNodeConditionStatus: - type: string - druidNodeConditionType: - type: string - reason: - type: string - type: object - hpAutoscalers: - items: - type: string - type: array - ingress: - items: - type: string - type: array - persistentVolumeClaims: - items: - type: string - type: array - podDisruptionBudgets: - items: - type: string - type: array - pods: - items: - type: string - type: array - services: - items: - type: string - type: array - statefulSets: - items: - type: string - type: array - type: object - required: - - spec - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/knowlg-automation/helm_charts/druid-operator/templates/deployment.yaml b/knowlg-automation/helm_charts/druid-operator/templates/deployment.yaml deleted file mode 100644 index f4bdc5ae8..000000000 --- a/knowlg-automation/helm_charts/druid-operator/templates/deployment.yaml +++ /dev/null @@ -1,59 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - namespace: {{ .Release.Namespace }} - name: {{ include "druid-operator.fullname" . }} - labels: - {{- include "druid-operator.labels" . | nindent 4 }} -spec: - replicas: {{ .Values.replicaCount }} - selector: - matchLabels: - {{- include "druid-operator.selectorLabels" . | nindent 6 }} - template: - metadata: - {{- with .Values.podAnnotations }} - annotations: - {{- toYaml . | nindent 8 }} - {{- end }} - labels: - {{- include "druid-operator.selectorLabels" . | nindent 8 }} - spec: - {{- with .Values.imagePullSecrets }} - imagePullSecrets: - {{- toYaml . | nindent 8 }} - {{- end }} - serviceAccountName: {{ include "druid-operator.serviceAccountName" . }} - securityContext: - {{- toYaml .Values.podSecurityContext | nindent 8 }} - containers: - - name: {{ .Chart.Name }} - securityContext: - {{- toYaml .Values.securityContext | nindent 12 }} - image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" - command: - - /manager - imagePullPolicy: {{ .Values.image.pullPolicy }} - env: - {{- range $key, $value := .Values.env }} - - name: {{ $key }} - value: {{ tpl $value $ | quote }} - {{- end }} - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - resources: - {{- toYaml .Values.resources | nindent 12 }} - {{- with .Values.nodeSelector }} - nodeSelector: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.affinity }} - affinity: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.tolerations }} - tolerations: - {{- toYaml . | nindent 8 }} - {{- end }} diff --git a/knowlg-automation/helm_charts/druid-operator/templates/role.yaml b/knowlg-automation/helm_charts/druid-operator/templates/role.yaml deleted file mode 100644 index b12ed0d85..000000000 --- a/knowlg-automation/helm_charts/druid-operator/templates/role.yaml +++ /dev/null @@ -1,95 +0,0 @@ -{{- if .Values.rbac.create }} -apiVersion: rbac.authorization.k8s.io/v1 -{{- if .Values.env.WATCH_NAMESPACE }} -kind: Role -{{- else }} -kind: ClusterRole -{{- end }} -metadata: -{{- if .Values.env.WATCH_NAMESPACE }} - namespace: {{ .Values.env.WATCH_NAMESPACE }} -{{- end }} - name: {{ template "druid-operator.fullname" . }} - labels: - {{- include "druid-operator.labels" . | nindent 4 }} -rules: - - apiGroups: - - "" - resources: - - pods - - configmaps - - services - - persistentvolumeclaims - verbs: - - list - - watch - - create - - update - - delete - - apiGroups: - - "" - resources: - - events - verbs: - - create - - patch - - apiGroups: - - apps - resources: - - statefulsets - - deployments - verbs: - - list - - watch - - create - - update - - delete - - apiGroups: - - autoscaling - resources: - - horizontalpodautoscalers - verbs: - - list - - watch - - create - - update - - patch - - apiGroups: - - policy - resources: - - poddisruptionbudgets - verbs: - - list - - watch - - create - - update - - apiGroups: - - networking.k8s.io - resources: - - ingresses - verbs: - - list - - watch - - create - - update - - apiGroups: - - druid.apache.org - resources: - - druids - verbs: - - get - - list - - watch - - create - - update - - patch - - delete - - apiGroups: - - druid.apache.org - resources: - - druids/status - verbs: - - get - - update - - patch -{{- end }} diff --git a/knowlg-automation/helm_charts/druid-operator/templates/role_binding.yaml b/knowlg-automation/helm_charts/druid-operator/templates/role_binding.yaml deleted file mode 100644 index 2587fe604..000000000 --- a/knowlg-automation/helm_charts/druid-operator/templates/role_binding.yaml +++ /dev/null @@ -1,23 +0,0 @@ -{{- if .Values.rbac.create }} -apiVersion: rbac.authorization.k8s.io/v1 -{{- if .Values.env.WATCH_NAMESPACE }} -kind: RoleBinding -{{- else }} -kind: ClusterRoleBinding -{{- end }} -metadata: -{{- if .Values.env.WATCH_NAMESPACE }} - namespace: {{ .Values.env.WATCH_NAMESPACE }} -{{- end }} - name: {{ template "druid-operator.fullname" . }} - labels: - {{- include "druid-operator.labels" . | nindent 4 }} -subjects: -- kind: ServiceAccount - name: {{ include "druid-operator.serviceAccountName" . }} - namespace: {{ .Release.Namespace }} -roleRef: - kind: {{ if .Values.env.WATCH_NAMESPACE }} Role {{ else }} ClusterRole {{ end }} - name: {{ template "druid-operator.fullname" . }} - apiGroup: rbac.authorization.k8s.io -{{- end }} diff --git a/knowlg-automation/helm_charts/druid-operator/templates/serviceaccount.yaml b/knowlg-automation/helm_charts/druid-operator/templates/serviceaccount.yaml deleted file mode 100644 index d1af85fe9..000000000 --- a/knowlg-automation/helm_charts/druid-operator/templates/serviceaccount.yaml +++ /dev/null @@ -1,13 +0,0 @@ -{{- if .Values.serviceAccount.create -}} -apiVersion: v1 -kind: ServiceAccount -metadata: - namespace: {{ .Release.Namespace }} - name: {{ include "druid-operator.serviceAccountName" . }} - labels: - {{- include "druid-operator.labels" . | nindent 4 }} - {{- with .Values.serviceAccount.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -{{- end }} diff --git a/knowlg-automation/helm_charts/druid-operator/values.yaml b/knowlg-automation/helm_charts/druid-operator/values.yaml deleted file mode 100644 index 75ab618ec..000000000 --- a/knowlg-automation/helm_charts/druid-operator/values.yaml +++ /dev/null @@ -1,63 +0,0 @@ -# Default values for druid-operator. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. - -env: - DENY_LIST: "default,kube-system" # Comma-separated list of namespaces to ignore - RECONCILE_WAIT: "10s" # Reconciliation delay - WATCH_NAMESPACE: "" # Namespace to watch or empty string to watch all namespaces - -replicaCount: 1 - -image: - repository: druidio/druid-operator - pullPolicy: IfNotPresent - # Overrides the image tag whose default is the chart appVersion. - tag: "" - -imagePullSecrets: [] -nameOverride: "" -fullnameOverride: "" - -rbac: - create: true - -serviceAccount: - # Specifies whether a service account should be created - create: true - # Annotations to add to the service account - annotations: {} - # The name of the service account to use. - # If not set and create is true, a name is generated using the fullname template - name: "" - -podAnnotations: {} - -podSecurityContext: {} - # fsGroup: 2000 - -securityContext: {} - # capabilities: - # drop: - # - ALL - # readOnlyRootFilesystem: true - # runAsNonRoot: true - # runAsUser: 1000 - -resources: {} - # We usually recommend not to specify default resources and to leave this as a conscious - # choice for the user. This also increases chances charts run on environments with little - # resources, such as Minikube. If you do want to specify resources, uncomment the following - # lines, adjust them as necessary, and remove the curly braces after 'resources:'. - # limits: - # cpu: 100m - # memory: 128Mi - # requests: - # cpu: 100m - # memory: 128Mi - -nodeSelector: {} - -tolerations: [] - -affinity: {} diff --git a/knowlg-automation/helm_charts/superset-helm/Chart.lock b/knowlg-automation/helm_charts/superset-helm/Chart.lock deleted file mode 100644 index bfd0d0e68..000000000 --- a/knowlg-automation/helm_charts/superset-helm/Chart.lock +++ /dev/null @@ -1,9 +0,0 @@ -dependencies: -- name: redis - repository: https://charts.bitnami.com/bitnami - version: 12.3.3 -- name: postgresql - repository: https://charts.bitnami.com/bitnami - version: 10.2.0 -digest: sha256:51635f1352f64f364f1454e0c26ce3b22f7d85e5a7d3e940f8f8d0aed0f82534 -generated: "2021-08-17T15:20:27.029281275+05:30" diff --git a/knowlg-automation/helm_charts/superset-helm/Chart.yaml b/knowlg-automation/helm_charts/superset-helm/Chart.yaml deleted file mode 100644 index 177da04a3..000000000 --- a/knowlg-automation/helm_charts/superset-helm/Chart.yaml +++ /dev/null @@ -1,37 +0,0 @@ -# apiVersion: v2 -# name: superset -# description: A Helm chart for Apache Superset - -# # A chart can be either an 'application' or a 'library' chart. -# # -# # Application charts are a collection of templates that can be packaged into versioned archives -# # to be deployed. -# # -# # Library charts provide useful utilities or functions for the chart developer. They're included as -# # a dependency of application charts to inject those utilities and functions into the rendering -# # pipeline. Library charts do not define any templates and therefore cannot be deployed. -# type: application - -# # This is the chart version. This version number should be incremented each time you make changes -# # to the chart and its templates, including the app version. -# version: 1.0.0 - -# # This is the version number of the application being deployed. This version number should be -# # incremented each time you make changes to the application. -# appVersion: 1.20 - - -apiVersion: v2 -appVersion: "1.0" -description: Apache Superset is a modern, enterprise-ready business intelligence web application -name: superset -maintainers: - - name: craig-rueda - email: craig@craigrueda.com - url: https://github.com/craig-rueda -version: 0.3.5 -dependencies: -- name: redis - version: 12.3.3 - repository: https://charts.bitnami.com/bitnami - condition: redis.enabled diff --git a/knowlg-automation/helm_charts/superset-helm/LICENSE b/knowlg-automation/helm_charts/superset-helm/LICENSE deleted file mode 100644 index abbe4f211..000000000 --- a/knowlg-automation/helm_charts/superset-helm/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2021 Anand Parthasarathy - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/knowlg-automation/helm_charts/superset-helm/README.md b/knowlg-automation/helm_charts/superset-helm/README.md deleted file mode 100644 index af705a8e4..000000000 --- a/knowlg-automation/helm_charts/superset-helm/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# superset-helm -Helm chart for Apache Superset diff --git a/knowlg-automation/helm_charts/superset-helm/charts/redis-12.3.3.tgz b/knowlg-automation/helm_charts/superset-helm/charts/redis-12.3.3.tgz deleted file mode 100644 index e98483df9b9557e8595b627fc48657500838506c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 66002 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

~JsQ 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 DPb|^@ diff --git a/knowlg-automation/helm_charts/superset-helm/templates/_helpers.tpl b/knowlg-automation/helm_charts/superset-helm/templates/_helpers.tpl deleted file mode 100644 index 78840f449..000000000 --- a/knowlg-automation/helm_charts/superset-helm/templates/_helpers.tpl +++ /dev/null @@ -1,105 +0,0 @@ -{{/* - - Licensed to the Apache Software Foundation (ASF) under one or more - contributor license agreements. See the NOTICE file distributed with - this work for additional information regarding copyright ownership. - The ASF licenses this file to You under the Apache License, Version 2.0 - (the "License"); you may not use this file except in compliance with - the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/}} -{{/* vim: set filetype=mustache: */}} -{{/* -Expand the name of the chart. -*/}} -{{- define "superset.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -*/}} -{{- define "superset.fullname" -}} -{{- if .Values.fullnameOverride -}} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- $name := default .Chart.Name .Values.nameOverride -}} -{{- if contains $name .Release.Name -}} -{{- .Release.Name | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} -{{- end -}} -{{- end -}} -{{- end -}} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "superset.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{- define "superset-config" }} -import os -from cachelib.redis import RedisCache - -def env(key, default=None): - return os.getenv(key, default) - -MAPBOX_API_KEY = env('MAPBOX_API_KEY', '') -CACHE_CONFIG = { - 'CACHE_TYPE': 'redis', - 'CACHE_DEFAULT_TIMEOUT': 300, - 'CACHE_KEY_PREFIX': 'superset_', - 'CACHE_REDIS_HOST': env('REDIS_HOST'), - 'CACHE_REDIS_PORT': env('REDIS_PORT'), - 'CACHE_REDIS_PASSWORD': env('REDIS_PASSWORD'), - 'CACHE_REDIS_DB': env('REDIS_DB', 1), -} -DATA_CACHE_CONFIG = CACHE_CONFIG - -SQLALCHEMY_DATABASE_URI = f"postgresql+psycopg2://{env('DB_USER')}:{env('DB_PASS')}@{env('DB_HOST')}:{env('DB_PORT')}/{env('DB_NAME')}" -SQLALCHEMY_TRACK_MODIFICATIONS = True -SECRET_KEY = env('SECRET_KEY', 'thisISaSECRET_1234') - -# Flask-WTF flag for CSRF -WTF_CSRF_ENABLED = True -# Add endpoints that need to be exempt from CSRF protection -WTF_CSRF_EXEMPT_LIST = [] -# A CSRF token that expires in 1 year -WTF_CSRF_TIME_LIMIT = 60 * 60 * 24 * 365 -class CeleryConfig(object): - BROKER_URL = f"redis://{env('REDIS_HOST')}:{env('REDIS_PORT')}/0" - CELERY_IMPORTS = ('superset.sql_lab', ) - CELERY_RESULT_BACKEND = f"redis://{env('REDIS_HOST')}:{env('REDIS_PORT')}/0" - CELERY_ANNOTATIONS = {'tasks.add': {'rate_limit': '10/s'}} - -CELERY_CONFIG = CeleryConfig -RESULTS_BACKEND = RedisCache( - host=env('REDIS_HOST'), - port=env('REDIS_PORT'), - key_prefix='superset_results' -) - -{{ if .Values.configOverrides }} -{{- $oauth_enabled := .Values.oauth_enabled -}} -# Overrides -{{- range $key, $value := .Values.configOverrides }} -{{- if or (ne $key "oauth") (default $oauth_enabled false) }} -# {{ $key }} -{{ tpl $value $ }} -{{- end }} -{{- end }} -{{- end }} - -{{- end }} diff --git a/knowlg-automation/helm_charts/superset-helm/templates/configmap-superset.yaml b/knowlg-automation/helm_charts/superset-helm/templates/configmap-superset.yaml deleted file mode 100644 index a7d7b0933..000000000 --- a/knowlg-automation/helm_charts/superset-helm/templates/configmap-superset.yaml +++ /dev/null @@ -1,32 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -{{- if .Values.extraConfigs }} -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ template "superset.fullname" . }}-extra-config - labels: - app: {{ template "superset.name" . }} - chart: {{ template "superset.chart" . }} - release: {{ .Release.Name }} - heritage: {{ .Release.Service }} -data: -{{- range $path, $config := .Values.extraConfigs }} - {{ $path }}: | -{{- tpl $config $ | nindent 4 -}} -{{- end -}} -{{- end -}} diff --git a/knowlg-automation/helm_charts/superset-helm/templates/deployment.yaml b/knowlg-automation/helm_charts/superset-helm/templates/deployment.yaml deleted file mode 100644 index d1c08f02c..000000000 --- a/knowlg-automation/helm_charts/superset-helm/templates/deployment.yaml +++ /dev/null @@ -1,128 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ template "superset.fullname" . }} - labels: - app: {{ template "superset.name" . }} - chart: {{ template "superset.chart" . }} - release: {{ .Release.Name }} - heritage: {{ .Release.Service }} -{{- if .Values.supersetNode.deploymentAnnotations }} - annotations: - {{ toYaml .Values.supersetNode.deploymentAnnotations | nindent 4 }} -{{- end }} -spec: - replicas: {{ .Values.replicaCount }} - selector: - matchLabels: - app: {{ template "superset.name" . }} - release: {{ .Release.Name }} - template: - metadata: - annotations: - # Force reload on config changes - checksum/superset_config.py: {{ include "superset-config" . | sha256sum }} - checksum/superset_init.sh: {{ tpl .Values.init.initscript . | sha256sum }} - checksum/connections: {{ .Values.supersetNode.connections | toYaml | sha256sum }} - checksum/extraConfigs: {{ .Values.extraConfigs | toYaml | sha256sum }} - checksum/extraSecrets: {{ .Values.extraSecrets | toYaml | sha256sum }} - checksum/extraSecretEnv: {{ .Values.extraSecretEnv | toYaml | sha256sum }} - checksum/configOverrides: {{ .Values.configOverrides | toYaml | sha256sum }} - {{- if .Values.supersetNode.forceReload }} - # Optionally force the thing to reload - force-reload: {{ randAlphaNum 5 | quote }} - {{- end }} - {{- if .Values.supersetNode.podAnnotations }} - {{ toYaml .Values.supersetNode.podAnnotations | nindent 8 }} - {{- end }} - labels: - app: {{ template "superset.name" . }} - release: {{ .Release.Name }} - spec: - {{- if .Values.serviceAccountName }} - serviceAccountName: {{ .Values.serviceAccountName }} - {{- end }} - securityContext: - runAsUser: {{ .Values.runAsUser }} - {{- if .Values.supersetNode.initContainers }} - initContainers: - {{- tpl (toYaml .Values.supersetNode.initContainers) . | nindent 6 }} - {{- end }} - containers: - - name: {{ .Chart.Name }} - image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" - imagePullPolicy: {{ .Values.image.pullPolicy }} - command: {{ tpl (toJson .Values.supersetNode.command) . }} - env: - - name: "SUPERSET_PORT" - value: {{ .Values.service.port | quote}} - {{- if .Values.extraEnv }} - {{- range $key, $value := .Values.extraEnv }} - - name: {{ $key | quote}} - value: {{ $value | quote }} - {{- end }} - {{- end }} - envFrom: - - secretRef: - name: {{ tpl .Values.envFromSecret . | quote }} - {{- range .Values.envFromSecrets }} - - secretRef: - name: {{ tpl . $ | quote }} - {{- end }} - volumeMounts: - - name: superset-config - mountPath: {{ .Values.configMountPath | quote }} - readOnly: true - {{- if .Values.extraConfigs }} - - name: superset-extra-config - mountPath: {{ .Values.extraConfigMountPath | quote }} - readOnly: true - {{- end }} - ports: - - name: http - containerPort: {{ .Values.service.port }} - protocol: TCP - resources: -{{ toYaml .Values.resources | indent 12 }} - {{- with .Values.nodeSelector }} - nodeSelector: -{{ toYaml . | indent 8 }} - {{- end }} - {{- with .Values.affinity }} - affinity: -{{ toYaml . | indent 8 }} - {{- end }} - {{- with .Values.tolerations }} - tolerations: -{{ toYaml . | indent 8 }} - {{- end }} -{{- if .Values.imagePullSecrets }} - imagePullSecrets: -{{ toYaml .Values.imagePullSecrets | indent 8 }} - {{- end }} - - volumes: - - name: superset-config - secret: - secretName: {{ tpl .Values.configFromSecret . }} - {{- if .Values.extraConfigs }} - - name: superset-extra-config - configMap: - name: {{ template "superset.fullname" . }}-extra-config - {{- end }} diff --git a/knowlg-automation/helm_charts/superset-helm/templates/ingress.yaml b/knowlg-automation/helm_charts/superset-helm/templates/ingress.yaml deleted file mode 100644 index 7c1dd72f0..000000000 --- a/knowlg-automation/helm_charts/superset-helm/templates/ingress.yaml +++ /dev/null @@ -1,56 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -{{ if .Values.ingress.enabled -}} -{{- $fullName := include "superset.fullname" . -}} -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: {{ $fullName }} - labels: - app: {{ template "superset.name" . }} - chart: {{ template "superset.chart" . }} - release: {{ .Release.Name }} - heritage: {{ .Release.Service }} -{{- with .Values.ingress.annotations }} - annotations: -{{ toYaml . | indent 4 }} -{{- end }} -spec: -{{- if .Values.ingress.tls }} - tls: - {{- range .Values.ingress.tls }} - - hosts: - {{- range .hosts }} - - {{ . }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} -{{- end }} - rules: - {{- range .Values.ingress.hosts }} - - host: {{ . }} - http: - paths: - - path: {{ $.Values.ingress.path }} - pathType: {{ $.Values.ingress.pathType }} - backend: - service: - name: {{ $fullName }} - port: - name: http - {{- end }} -{{- end }} diff --git a/knowlg-automation/helm_charts/superset-helm/templates/init-job.yaml b/knowlg-automation/helm_charts/superset-helm/templates/init-job.yaml deleted file mode 100644 index 45ab2235b..000000000 --- a/knowlg-automation/helm_charts/superset-helm/templates/init-job.yaml +++ /dev/null @@ -1,80 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -{{- if .Values.init.enabled }} -apiVersion: batch/v1 -kind: Job -metadata: - name: {{ template "superset.name" . }}-init-db - annotations: - "helm.sh/hook": post-install,post-upgrade - "helm.sh/hook-delete-policy": "before-hook-creation" -spec: - template: - metadata: - name: {{ template "superset.name" . }}-init-db - spec: - securityContext: - runAsUser: {{ .Values.runAsUser }} - {{- if .Values.init.initContainers }} - initContainers: - {{- tpl (toYaml .Values.init.initContainers) . | nindent 6 }} - {{- end }} - containers: - - name: {{ template "superset.name" . }}-init-db - image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" - {{ if .Values.extraEnv }} - env: - {{- range $key, $value := .Values.extraEnv }} - - name: {{ $key | quote }} - value: {{ $value | quote }} - {{- end }} - {{- end }} - envFrom: - - secretRef: - name: {{ tpl .Values.envFromSecret . }} - {{- range .Values.envFromSecrets }} - - secretRef: - name: {{ tpl . $ }} - {{- end }} - imagePullPolicy: {{ .Values.image.pullPolicy }} - volumeMounts: - - name: superset-config - mountPath: {{ .Values.configMountPath | quote }} - readOnly: true - {{- if .Values.extraConfigs }} - - name: superset-extra-config - mountPath: {{ .Values.extraConfigMountPath | quote }} - readOnly: true - {{- end }} - command: {{ tpl (toJson .Values.init.command) . }} - resources: -{{ toYaml .Values.init.resources | indent 10 }} -{{- if .Values.imagePullSecrets }} - imagePullSecrets: -{{ toYaml .Values.imagePullSecrets | indent 8 }} - {{- end }} - volumes: - - name: superset-config - secret: - secretName: {{ tpl .Values.configFromSecret . }} - {{- if .Values.extraConfigs }} - - name: superset-extra-config - configMap: - name: {{ template "superset.fullname" . }}-extra-config - {{- end }} - restartPolicy: Never -{{- end }} diff --git a/knowlg-automation/helm_charts/superset-helm/templates/secret-env.yaml b/knowlg-automation/helm_charts/superset-helm/templates/secret-env.yaml deleted file mode 100644 index 278ecc401..000000000 --- a/knowlg-automation/helm_charts/superset-helm/templates/secret-env.yaml +++ /dev/null @@ -1,39 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -apiVersion: v1 -kind: Secret -metadata: - name: {{ template "superset.fullname" . }}-env - labels: - app: {{ template "superset.fullname" . }} - chart: {{ template "superset.chart" . }} - release: "{{ .Release.Name }}" - heritage: "{{ .Release.Service }}" -type: Opaque -stringData: - REDIS_HOST: {{ tpl .Values.supersetNode.connections.redis_host . | quote }} - REDIS_PORT: {{ .Values.supersetNode.connections.redis_port | quote }} - DB_HOST: {{ tpl .Values.supersetNode.connections.db_host . | quote }} - DB_PORT: {{ .Values.supersetNode.connections.db_port | quote }} - DB_USER: {{ .Values.supersetNode.connections.db_user | quote }} - DB_PASS: {{ .Values.supersetNode.connections.db_pass | quote }} - DB_NAME: {{ .Values.supersetNode.connections.db_name | quote }} - {{- if .Values.extraSecretEnv }} - {{- range $key, $value := .Values.extraSecretEnv }} - {{ $key }}: {{ $value | quote }} - {{- end }} - {{- end }} diff --git a/knowlg-automation/helm_charts/superset-helm/templates/secret-superset-config.yaml b/knowlg-automation/helm_charts/superset-helm/templates/secret-superset-config.yaml deleted file mode 100644 index ddf0befcd..000000000 --- a/knowlg-automation/helm_charts/superset-helm/templates/secret-superset-config.yaml +++ /dev/null @@ -1,40 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -apiVersion: v1 -kind: Secret -metadata: - name: {{ template "superset.fullname" . }}-config - labels: - app: {{ template "superset.fullname" . }} - chart: {{ template "superset.chart" . }} - release: "{{ .Release.Name }}" - heritage: "{{ .Release.Service }}" -type: Opaque -stringData: - superset_config.py: | -{{- include "superset-config" . | nindent 4 }} - superset_init.sh: | -{{- tpl .Values.init.initscript . | nindent 4 }} - superset_bootstrap.sh: | -{{- tpl .Values.bootstrapScript . | nindent 4 }} - -{{- if .Values.extraSecrets }} -{{- range $path, $config := .Values.extraSecrets }} - {{ $path }}: | -{{- tpl $config $ | nindent 4 -}} -{{- end -}} -{{- end -}} diff --git a/knowlg-automation/helm_charts/superset-helm/templates/service.yaml b/knowlg-automation/helm_charts/superset-helm/templates/service.yaml deleted file mode 100644 index 0124ad2a9..000000000 --- a/knowlg-automation/helm_charts/superset-helm/templates/service.yaml +++ /dev/null @@ -1,42 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -apiVersion: v1 -kind: Service -metadata: - name: {{ template "superset.fullname" . }} - labels: - app: {{ template "superset.name" . }} - chart: {{ template "superset.chart" . }} - release: {{ .Release.Name }} - heritage: {{ .Release.Service }} -{{- with .Values.service.annotations }} - annotations: -{{- toYaml . | nindent 4 }} -{{- end }} -spec: - type: {{ .Values.service.type }} - ports: - - port: {{ .Values.service.port }} - targetPort: http - protocol: TCP - name: http - selector: - app: {{ template "superset.name" . }} - release: {{ .Release.Name }} - {{- if .Values.service.loadBalancerIP }} - loadBalancerIP: {{ .Values.service.loadBalancerIP }} - {{- end }} diff --git a/knowlg-automation/helm_charts/superset-helm/values.yaml b/knowlg-automation/helm_charts/superset-helm/values.yaml deleted file mode 100644 index ab652a449..000000000 --- a/knowlg-automation/helm_charts/superset-helm/values.yaml +++ /dev/null @@ -1,312 +0,0 @@ -# global: -postgres: - adminUser: "postgres" - adminPassword: "postgres" - db_port: 5432 - superset: - db_name: "superset" - db_username: "superset" - db_password: "superset$123" - -replicaCount: 1 -oauth_enabled: False - -adminUser: - username: "admin" - firstname: "Superset" - lastname: "Admin" - email: "admin@superset.com" - password: "admin123" - -oauth: - enabled: false - client_id: "client_id" - client_secret: "client_secret" - email_whitelist_regex: "" - whitelist_domain: "" - user_registration_role: "Gamma" - -runAsUser: 1000 - -# Install additional packages and do any other bootstrap configuration in this script -# For production clusters it's recommended to build own image with this step done in CI -bootstrapScript: | - #!/bin/bash - #rm -rf /var/lib/apt/lists/* && \ - pip install \ - sqlalchemy==1.3.24 \ - psycopg2==2.8.5 \ - redis==3.2.1 && \ - if [ ! -f ~/bootstrap ]; then echo "Running Superset with uid {{ .Values.runAsUser }}" > ~/bootstrap; fi - -## The name of the secret which we will use to generate a superset_config.py file -## Note: this secret must have the key superset_config.py in it and can include other files as well -## -configFromSecret: '{{ template "superset.fullname" . }}-config' - -## The name of the secret which we will use to populate env vars in deployed pods -## This can be useful for secret keys, etc. -## -envFromSecret: '{{ template "superset.fullname" . }}-env' -## This can be a list of template strings -envFromSecrets: [] - -## Extra environment variables that will be passed into pods -## -extraEnv: {} - # Extend timeout to allow long running queries. - # GUNICORN_TIMEOUT: 300 - - - # OAUTH_HOME_DOMAIN: .. - # # If a whitelist is not set, any address that can use your OAuth2 endpoint will be able to login. - # # this includes any random Gmail address if your OAuth2 Web App is set to External. - # OAUTH_WHITELIST_REGEX: ... - -## Extra environment variables to pass as secrets -## -extraSecretEnv: {} - # MAPBOX_API_KEY: ... - # # Google API Keys: https://console.cloud.google.com/apis/credentials - # GOOGLE_KEY: ... - # GOOGLE_SECRET: ... - -extraConfigs: {} - # datasources-init.yaml: | - # databases: - # - allow_csv_upload: true - # allow_ctas: true - # allow_cvas: true - # database_name: example-db - # extra: "{\r\n \"metadata_params\": {},\r\n \"engine_params\": {},\r\n \"\ - # metadata_cache_timeout\": {},\r\n \"schemas_allowed_for_csv_upload\": []\r\n\ - # }" - # sqlalchemy_uri: example://example-db.local - # tables: [] - -extraSecrets: {} - -# A dictionary of overrides to append at the end of superset_config.py - the name does not matter -# WARNING: the order is not guaranteed -configOverrides: - enable_feature_flags: | - FEATURE_FLAGS = { - "DASHBOARD_NATIVE_FILTERS": True, - "DASHBOARD_CROSS_FILTERS": True, - "DASHBOARD_NATIVE_FILTERS_SET": True, - "ENABLE_TEMPLATE_PROCESSING": True, - } - - data_cache_config: | - DATA_CACHE_CONFIG = { - 'CACHE_TYPE': 'redis', - 'CACHE_DEFAULT_TIMEOUT': 600, - 'CACHE_KEY_PREFIX': 'superset_', - 'CACHE_REDIS_URL': 'redis://{{ template "superset.fullname" . }}-redis-headless:6379/1' - } - - sql_alchemy_config: | - SQLALCHEMY_DATABASE_URI = 'postgresql://{{ tpl .Values.postgres.superset.db_username . }}:{{ tpl .Values.postgres.superset.db_password . }}@postgresql-hl.postgresql.svc.cluster.local:5432/{{ tpl .Values.postgres.superset.db_name . }}' - SQLALCHEMY_TRACK_MODIFICATIONS = True - SECRET_KEY = 'thisISaSECRET_1234' - - #map_box_key: | - # MAPBOX_API_KEY='' - - oauth: | - from flask_appbuilder.security.manager import (AUTH_DB, AUTH_OAUTH) - AUTH_TYPE = AUTH_OAUTH - - OAUTH_PROVIDERS = [ - { - "name": "google", - "whitelist": [ "{{ .Values.oauth.email_whitelist_regex }}" ], - "icon": "fa-google", - "token_key": "access_token", - "remote_app": { - "client_id": "{{ .Values.oauth.client_id }}", - "client_secret": "{{ .Values.oauth.client_secret }}", - "api_base_url": "https://www.googleapis.com/oauth2/v2/", - "client_kwargs": {"scope": "email profile"}, - "request_token_url": None, - "access_token_url": "https://accounts.google.com/o/oauth2/token", - "authorize_url": "https://accounts.google.com/o/oauth2/auth", - "authorize_params": {"hd": "{{ .Values.oauth.whitelist_domain }}"} - } - } - ] - # Map Authlib roles to superset roles - AUTH_ROLE_ADMIN = 'Admin' - AUTH_ROLE_PUBLIC = 'Public' - # Will allow user self registration, allowing to create Flask users from Authorized User - AUTH_USER_REGISTRATION = True - # The default user self registration role - AUTH_USER_REGISTRATION_ROLE = "{{ oauth.user_registration_role }}" - -configMountPath: "/etc/superset" - -extraConfigMountPath: "/app/configs" - -image: - repository: amancevice/superset - tag: 1.5.0 - pullPolicy: Always - -imagePullSecrets: [] - - -service: - type: ClusterIP - port: 8088 - annotations: {} - -ingress: - enabled: false - annotations: {} - # kubernetes.io/ingress.class: nginx - # kubernetes.io/tls-acme: "true" - ## Extend timeout to allow long running queries. - # nginx.ingress.kubernetes.io/proxy-connect-timeout: "300" - # nginx.ingress.kubernetes.io/proxy-read-timeout: "300" - # nginx.ingress.kubernetes.io/proxy-send-timeout: "300" - path: / - pathType: ImplementationSpecific - hosts: - - chart-example.local - tls: [] - # - secretName: chart-example-tls - # hosts: - # - chart-example.local - -resources: {} - # We usually recommend not to specify default resources and to leave this as a conscious - # choice for the user. This also increases chances charts run on environments with little - # resources, such as Minikube. If you do want to specify resources, uncomment the following - # lines, adjust them as necessary, and remove the curly braces after 'resources:'. - # limits: - # cpu: 100m - # memory: 128Mi - # requests: - # cpu: 100m - # memory: 128Mi - -## -## Superset node configuration -supersetNode: - connections: - redis_host: '{{ template "superset.fullname" . }}-redis-headless' - redis_port: "6379" - db_host: '{{ template "superset.fullname" . }}-postgres-headless' - db_port: "{{ .Values.postgres.db_port }}" - db_user: "{{ .Values.postgres.superset.db_username }}" - db_pass: "{{ .Values.postgres.superset.db_password }}" - db_name: "{{ .Values.postgres.superset.db_name }}" - forceReload: false # If true, forces deployment to reload on each upgrade - # initContainers: - # - name: wait-for-postgres - # image: busybox:latest - # imagePullPolicy: IfNotPresent - # envFrom: - # - secretRef: - # name: '{{ tpl .Values.envFromSecret . }}' - # command: [ "/bin/sh", "-c", "until nc -zv $DB_HOST $DB_PORT -w1; do echo 'waiting for db'; sleep 1; done" ] - ## Annotations to be added to supersetNode deployment - deploymentAnnotations: {} - ## Annotations to be added to supersetNode pods - podAnnotations: {} - -## -## Init job configuration -init: - # Configure resources - # Warning: fab command consumes a lot of ram and can - # cause the process to be killed due to OOM if it exceeds limit - resources: {} - # limits: - # cpu: - # memory: - # requests: - # cpu: - # memory: - command: - - "/bin/sh" - - "-c" - - ". {{ .Values.configMountPath }}/superset_bootstrap.sh; . {{ .Values.configMountPath }}/superset_init.sh" - enabled: true - createAdmin: true - # initContainers: - # - name: wait-for-postgres - # image: busybox:latest - # imagePullPolicy: IfNotPresent - # envFrom: - # - secretRef: - # name: '{{ tpl .Values.envFromSecret . }}' - # command: [ "/bin/sh", "-c", "until nc -zv $DB_HOST $DB_PORT -w1; do echo 'waiting for db'; sleep 1; done" ] - initscript: |- - #!/bin/sh - echo "Upgrading DB schema..." - superset db upgrade - echo "Initializing roles..." - superset init - {{ if .Values.init.createAdmin }} - echo "Creating admin user..." - superset fab create-admin \ - --username {{ .Values.adminUser.username }} \ - --firstname {{ .Values.adminUser.firstname }} \ - --lastname {{ .Values.adminUser.lastname }} \ - --email {{ .Values.adminUser.email }} \ - --password {{ .Values.adminUser.password }} \ - || true - {{ end }} - if [ -f "{{ .Values.extraConfigMountPath }}/import_datasources.yaml" ]; then - echo "Importing database connections.... " - superset import_datasources -p {{ .Values.extraConfigMountPath }}/import_datasources.yaml - fi - -## Configuration values for the Redis dependency. -## ref: https://github.com/kubernetes/charts/blob/master/stable/redis/README.md -redis: - ## - ## Use the redis chart dependency. - ## Set to false if bringing your own redis. - enabled: false - usePassword: false - ## - ## If you are bringing your own redis, you can set the host in redisHost. - ## redisHost: - ## - ## Redis password - ## - ## password: superset - ## - ## Master configuration - master: - ## - ## Image configuration - # image: - ## - ## docker registry secret names (list) - # pullSecrets: nil - ## - ## Configure persistance - persistence: - ## - ## Use a PVC to persist data. - enabled: false - ## - ## Persistant class - # storageClass: classname - ## - ## Access mode: - accessModes: - - ReadWriteOnce - ## - ## Disable cluster management by default. - cluster: - enabled: false - -nodeSelector: {} - -tolerations: [] - -affinity: {} From 8884c37bd7dddee9ed422a797eba982594f22118 Mon Sep 17 00:00:00 2001 From: aimansharief <110388201+aimansharief@users.noreply.github.com> Date: Mon, 27 Mar 2023 12:07:54 +0530 Subject: [PATCH 196/490] Issue KN-767 feat: Updated Readme file . --- knowlg-automation/README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/knowlg-automation/README.md b/knowlg-automation/README.md index a7608b22c..aa52eb34d 100644 --- a/knowlg-automation/README.md +++ b/knowlg-automation/README.md @@ -23,7 +23,11 @@ Pre-requisites: For local provision, kind provider is used to provision the cluster. Steps: Run shell script and provide option as "local" - +Create infra on local +Steps: +cd terraform +sh create --auto-approve Destroy infra on local Steps: -Run destroy shell script and provide option as "local" append --auto-approve to continue without confirmation. +cd terraform +sh destroy --auto-approve From 38d52691b74d4e502fed443705d24fb5c8b9a7c8 Mon Sep 17 00:00:00 2001 From: aimansharief <110388201+aimansharief@users.noreply.github.com> Date: Mon, 27 Mar 2023 12:14:09 +0530 Subject: [PATCH 197/490] Issue KN-767 feat: Updated Readme file . --- knowlg-automation/README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/knowlg-automation/README.md b/knowlg-automation/README.md index aa52eb34d..0045039ca 100644 --- a/knowlg-automation/README.md +++ b/knowlg-automation/README.md @@ -20,14 +20,14 @@ Run destroy shell script with option as "azure" and append --auto-approve to con Infra provision on local Pre-requisites: -For local provision, kind provider is used to provision the cluster. -Steps: -Run shell script and provide option as "local" -Create infra on local -Steps: -cd terraform -sh create --auto-approve -Destroy infra on local -Steps: -cd terraform +For local provision, kind provider is used to provision the cluster. +Steps:Run shell script and provide option as "local" +Create infra on local +Steps: +cd terraform +sh create --auto-approve + +Destroy infra on local +Steps: +cd terraform sh destroy --auto-approve From b7936ef55e78b0e98c5129e85aec204fc95c3a36 Mon Sep 17 00:00:00 2001 From: aimansharief <110388201+aimansharief@users.noreply.github.com> Date: Tue, 28 Mar 2023 10:30:26 +0530 Subject: [PATCH 198/490] Issue KN-767 feat: Removed Unused files . --- .../taxonomy/templates/deployment.yaml | 3 +- .../helm_charts/taxonomy/values.yaml | 6 +- knowlg-automation/terraform/aws/data.tf | 1 - .../terraform/aws/druid_cluster_vars.tf | 1 - .../terraform/aws/druid_operator.tf | 1 - .../terraform/aws/druid_operator_vars.tf | 1 - knowlg-automation/terraform/aws/druid_raw.tf | 1 - knowlg-automation/terraform/aws/eks_addons.tf | 7 - knowlg-automation/terraform/aws/eks_master.tf | 20 - .../aws/eks_master_policy_attachment.tf | 4 - .../terraform/aws/eks_master_role.tf | 23 - knowlg-automation/terraform/aws/eks_nodes.tf | 29 - .../aws/eks_nodes_policy_attachment.tf | 19 - .../terraform/aws/eks_nodes_role.tf | 19 - knowlg-automation/terraform/aws/flink.tf | 1 - knowlg-automation/terraform/aws/flink_vars.tf | 1 - .../terraform/aws/iam_s3_user.tf | 9 - .../terraform/aws/internet_gateway.tf | 10 - knowlg-automation/terraform/aws/kafka.tf | 1 - knowlg-automation/terraform/aws/kafka_vars.tf | 1 - knowlg-automation/terraform/aws/local.tf | 45 -- knowlg-automation/terraform/aws/local_file.tf | 4 - knowlg-automation/terraform/aws/monitoring.tf | 1 - .../terraform/aws/monitoring_vars.tf | 1 - knowlg-automation/terraform/aws/output.tf | 15 - knowlg-automation/terraform/aws/postgresql.tf | 1 - .../terraform/aws/postgresql_vars.tf | 1 - knowlg-automation/terraform/aws/provider.tf | 15 - .../terraform/aws/route_table.tf | 15 - .../terraform/aws/route_table_association.tf | 5 - knowlg-automation/terraform/aws/s3_bucket.tf | 10 - .../terraform/aws/s3_bucket_object.tf | 7 - .../terraform/aws/s3_bucket_policy.tf | 20 - knowlg-automation/terraform/aws/subnets.tf | 14 - knowlg-automation/terraform/aws/superset.tf | 1 - .../terraform/aws/superset_vars.tf | 1 - .../terraform/aws/terraform_backend.conf | 2 - .../terraform/aws/terraform_backend.tf | 5 - knowlg-automation/terraform/aws/variables.tf | 155 ----- knowlg-automation/terraform/aws/vpc.tf | 10 - .../terraform/azure/druid-raw.tf | 60 -- .../terraform/azure/flink-provision.tf | 23 - .../terraform/azure/kafka-client.yaml | 13 - .../terraform/azure/kafka-provision.tf | 63 -- .../terraform/azure/obsrv-aks-cluster.tf | 43 -- .../terraform/azure/obsrv-infra.tf | 39 -- .../terraform/azure/obsrv-storage.tf | 35 -- .../terraform/azure/postgres_provision.tf | 12 - .../azure/postgresql/custom-values.yaml | 24 - .../terraform/azure/superset-provision.tf | 32 - knowlg-automation/terraform/azure/vars.tf | 181 ------ .../terraform/helm/druid_cluster_vars.tf | 59 -- .../terraform/helm/druid_operator.tf | 8 - .../terraform/helm/druid_operator_vars.tf | 35 -- knowlg-automation/terraform/helm/druid_raw.tf | 25 - knowlg-automation/terraform/helm/flink.tf | 18 - .../terraform/helm/flink_vars.tf | 47 -- knowlg-automation/terraform/helm/kafka.tf | 19 - .../terraform/helm/kafka_vars.tf | 77 --- .../terraform/helm/monitoring.tf | 10 - .../terraform/helm/monitoring_vars.tf | 53 -- knowlg-automation/terraform/helm/postgres.tf | 14 - .../terraform/helm/postgresql_vars.tf | 53 -- knowlg-automation/terraform/helm/superset.tf | 10 - .../terraform/helm/superset_vars.tf | 41 -- knowlg-automation/terraform/local/main.tf | 27 + .../local/postgresql/custom-values.yaml | 24 - .../terraform/local/terraform.tfstate | 588 ++++++++++++++++++ .../terraform/local/terraform.tfstate.backup | 588 ++++++++++++++++++ knowlg-automation/terraform/local/vars.tf | 59 +- knowlg-automation/terraform/local/versions.tf | 4 + .../druid_cluster.yaml.tfpl | 10 - .../terraform_helm_templates/flink.yaml.tfpl | 3 - .../postgres.yaml.tfpl | 23 - 74 files changed, 1213 insertions(+), 1593 deletions(-) delete mode 100644 knowlg-automation/terraform/aws/data.tf delete mode 120000 knowlg-automation/terraform/aws/druid_cluster_vars.tf delete mode 120000 knowlg-automation/terraform/aws/druid_operator.tf delete mode 120000 knowlg-automation/terraform/aws/druid_operator_vars.tf delete mode 120000 knowlg-automation/terraform/aws/druid_raw.tf delete mode 100644 knowlg-automation/terraform/aws/eks_addons.tf delete mode 100644 knowlg-automation/terraform/aws/eks_master.tf delete mode 100644 knowlg-automation/terraform/aws/eks_master_policy_attachment.tf delete mode 100644 knowlg-automation/terraform/aws/eks_master_role.tf delete mode 100644 knowlg-automation/terraform/aws/eks_nodes.tf delete mode 100644 knowlg-automation/terraform/aws/eks_nodes_policy_attachment.tf delete mode 100644 knowlg-automation/terraform/aws/eks_nodes_role.tf delete mode 120000 knowlg-automation/terraform/aws/flink.tf delete mode 120000 knowlg-automation/terraform/aws/flink_vars.tf delete mode 100644 knowlg-automation/terraform/aws/iam_s3_user.tf delete mode 100644 knowlg-automation/terraform/aws/internet_gateway.tf delete mode 120000 knowlg-automation/terraform/aws/kafka.tf delete mode 120000 knowlg-automation/terraform/aws/kafka_vars.tf delete mode 100644 knowlg-automation/terraform/aws/local.tf delete mode 100644 knowlg-automation/terraform/aws/local_file.tf delete mode 120000 knowlg-automation/terraform/aws/monitoring.tf delete mode 120000 knowlg-automation/terraform/aws/monitoring_vars.tf delete mode 100644 knowlg-automation/terraform/aws/output.tf delete mode 120000 knowlg-automation/terraform/aws/postgresql.tf delete mode 120000 knowlg-automation/terraform/aws/postgresql_vars.tf delete mode 100644 knowlg-automation/terraform/aws/provider.tf delete mode 100644 knowlg-automation/terraform/aws/route_table.tf delete mode 100644 knowlg-automation/terraform/aws/route_table_association.tf delete mode 100644 knowlg-automation/terraform/aws/s3_bucket.tf delete mode 100644 knowlg-automation/terraform/aws/s3_bucket_object.tf delete mode 100644 knowlg-automation/terraform/aws/s3_bucket_policy.tf delete mode 100644 knowlg-automation/terraform/aws/subnets.tf delete mode 120000 knowlg-automation/terraform/aws/superset.tf delete mode 120000 knowlg-automation/terraform/aws/superset_vars.tf delete mode 100644 knowlg-automation/terraform/aws/terraform_backend.conf delete mode 100644 knowlg-automation/terraform/aws/terraform_backend.tf delete mode 100644 knowlg-automation/terraform/aws/variables.tf delete mode 100644 knowlg-automation/terraform/aws/vpc.tf delete mode 100644 knowlg-automation/terraform/azure/druid-raw.tf delete mode 100644 knowlg-automation/terraform/azure/flink-provision.tf delete mode 100644 knowlg-automation/terraform/azure/kafka-client.yaml delete mode 100644 knowlg-automation/terraform/azure/kafka-provision.tf delete mode 100644 knowlg-automation/terraform/azure/obsrv-aks-cluster.tf delete mode 100644 knowlg-automation/terraform/azure/obsrv-infra.tf delete mode 100644 knowlg-automation/terraform/azure/obsrv-storage.tf delete mode 100644 knowlg-automation/terraform/azure/postgres_provision.tf delete mode 100644 knowlg-automation/terraform/azure/postgresql/custom-values.yaml delete mode 100644 knowlg-automation/terraform/azure/superset-provision.tf delete mode 100644 knowlg-automation/terraform/azure/vars.tf delete mode 100644 knowlg-automation/terraform/helm/druid_cluster_vars.tf delete mode 100644 knowlg-automation/terraform/helm/druid_operator.tf delete mode 100644 knowlg-automation/terraform/helm/druid_operator_vars.tf delete mode 100644 knowlg-automation/terraform/helm/druid_raw.tf delete mode 100644 knowlg-automation/terraform/helm/flink.tf delete mode 100644 knowlg-automation/terraform/helm/flink_vars.tf delete mode 100644 knowlg-automation/terraform/helm/kafka.tf delete mode 100644 knowlg-automation/terraform/helm/kafka_vars.tf delete mode 100644 knowlg-automation/terraform/helm/monitoring.tf delete mode 100644 knowlg-automation/terraform/helm/monitoring_vars.tf delete mode 100644 knowlg-automation/terraform/helm/postgres.tf delete mode 100644 knowlg-automation/terraform/helm/postgresql_vars.tf delete mode 100644 knowlg-automation/terraform/helm/superset.tf delete mode 100644 knowlg-automation/terraform/helm/superset_vars.tf delete mode 100644 knowlg-automation/terraform/local/postgresql/custom-values.yaml create mode 100644 knowlg-automation/terraform/local/terraform.tfstate create mode 100644 knowlg-automation/terraform/local/terraform.tfstate.backup delete mode 100644 knowlg-automation/terraform/terraform_helm_templates/druid_cluster.yaml.tfpl delete mode 100644 knowlg-automation/terraform/terraform_helm_templates/flink.yaml.tfpl delete mode 100644 knowlg-automation/terraform/terraform_helm_templates/postgres.yaml.tfpl diff --git a/knowlg-automation/helm_charts/taxonomy/templates/deployment.yaml b/knowlg-automation/helm_charts/taxonomy/templates/deployment.yaml index 064f43fb2..35972b8cb 100644 --- a/knowlg-automation/helm_charts/taxonomy/templates/deployment.yaml +++ b/knowlg-automation/helm_charts/taxonomy/templates/deployment.yaml @@ -30,7 +30,7 @@ spec: containers: - name: {{ .Chart.Name }} image: "{{ .Values.repository }}:{{ .Values.image_tag }}" - imagePullPolicy: Never + imagePullPolicy: Always env: - name: JAVA_OPTIONS value: {{ .Values.env.javaoptions | quote }} @@ -66,6 +66,7 @@ metadata: labels: app: {{ .Chart.Name }} spec: + type: LoadBalancer ports: - name: http-{{ .Chart.Name }} protocol: TCP diff --git a/knowlg-automation/helm_charts/taxonomy/values.yaml b/knowlg-automation/helm_charts/taxonomy/values.yaml index 76033d47e..b8ee17780 100644 --- a/knowlg-automation/helm_charts/taxonomy/values.yaml +++ b/knowlg-automation/helm_charts/taxonomy/values.yaml @@ -1,12 +1,12 @@ ### Default variable file for taxonomy-service ### -namespace: knowlg-api +namespace: knowlg-db env: javaoptions: -Xmx600m replicaCount: 1 -repository: taxonomy-service +repository: docker.io/aimansharief/taxonomy-service image_tag: R5.2.0 resources: requests: @@ -22,7 +22,7 @@ strategy: type: RollingUpdate maxsurge: 25% maxunavailable: 25% - +serviceType: LoadBalancer diff --git a/knowlg-automation/terraform/aws/data.tf b/knowlg-automation/terraform/aws/data.tf deleted file mode 100644 index 8fc4b38cc..000000000 --- a/knowlg-automation/terraform/aws/data.tf +++ /dev/null @@ -1 +0,0 @@ -data "aws_caller_identity" "current" {} diff --git a/knowlg-automation/terraform/aws/druid_cluster_vars.tf b/knowlg-automation/terraform/aws/druid_cluster_vars.tf deleted file mode 120000 index ac4ac492d..000000000 --- a/knowlg-automation/terraform/aws/druid_cluster_vars.tf +++ /dev/null @@ -1 +0,0 @@ -../helm/druid_cluster_vars.tf \ No newline at end of file diff --git a/knowlg-automation/terraform/aws/druid_operator.tf b/knowlg-automation/terraform/aws/druid_operator.tf deleted file mode 120000 index 9f33d5a27..000000000 --- a/knowlg-automation/terraform/aws/druid_operator.tf +++ /dev/null @@ -1 +0,0 @@ -../helm/druid_operator.tf \ No newline at end of file diff --git a/knowlg-automation/terraform/aws/druid_operator_vars.tf b/knowlg-automation/terraform/aws/druid_operator_vars.tf deleted file mode 120000 index dfb76fa2f..000000000 --- a/knowlg-automation/terraform/aws/druid_operator_vars.tf +++ /dev/null @@ -1 +0,0 @@ -../helm/druid_operator_vars.tf \ No newline at end of file diff --git a/knowlg-automation/terraform/aws/druid_raw.tf b/knowlg-automation/terraform/aws/druid_raw.tf deleted file mode 120000 index 24b2b90ea..000000000 --- a/knowlg-automation/terraform/aws/druid_raw.tf +++ /dev/null @@ -1 +0,0 @@ -../helm/druid_raw.tf \ No newline at end of file diff --git a/knowlg-automation/terraform/aws/eks_addons.tf b/knowlg-automation/terraform/aws/eks_addons.tf deleted file mode 100644 index 3c99e4f94..000000000 --- a/knowlg-automation/terraform/aws/eks_addons.tf +++ /dev/null @@ -1,7 +0,0 @@ -resource "aws_eks_addon" "addons" { - for_each = { for addon in var.eks_addons : addon.name => addon } - cluster_name = aws_eks_cluster.eks_master.id - addon_name = each.value.name - addon_version = each.value.version - resolve_conflicts = "OVERWRITE" -} \ No newline at end of file diff --git a/knowlg-automation/terraform/aws/eks_master.tf b/knowlg-automation/terraform/aws/eks_master.tf deleted file mode 100644 index f63b5ea4e..000000000 --- a/knowlg-automation/terraform/aws/eks_master.tf +++ /dev/null @@ -1,20 +0,0 @@ -resource "aws_eks_cluster" "eks_master" { - name = "${var.building_block}-${var.env}-eks" - role_arn = aws_iam_role.eks_master_role.arn - version = var.eks_version - - vpc_config { - subnet_ids = aws_subnet.public_subnets[*].id - } - - tags = merge( - { - Name = "${var.building_block}-${var.env}-eks" - }, - local.common_tags, - var.additional_tags) - - depends_on = [ - aws_iam_role_policy_attachment.AmazonEKSClusterPolicy - ] -} \ No newline at end of file diff --git a/knowlg-automation/terraform/aws/eks_master_policy_attachment.tf b/knowlg-automation/terraform/aws/eks_master_policy_attachment.tf deleted file mode 100644 index 0a0f30020..000000000 --- a/knowlg-automation/terraform/aws/eks_master_policy_attachment.tf +++ /dev/null @@ -1,4 +0,0 @@ -resource "aws_iam_role_policy_attachment" "AmazonEKSClusterPolicy" { - policy_arn = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy" - role = aws_iam_role.eks_master_role.name -} \ No newline at end of file diff --git a/knowlg-automation/terraform/aws/eks_master_role.tf b/knowlg-automation/terraform/aws/eks_master_role.tf deleted file mode 100644 index 3aa2924a3..000000000 --- a/knowlg-automation/terraform/aws/eks_master_role.tf +++ /dev/null @@ -1,23 +0,0 @@ -resource "aws_iam_role" "eks_master_role" { - name = var.eks_master_role - assume_role_policy = < Date: Thu, 30 Mar 2023 10:50:34 +0530 Subject: [PATCH 199/490] Issue KN-767 feat: Added gitignore. --- knowlg-automation/.gitignore | 39 ++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 knowlg-automation/.gitignore diff --git a/knowlg-automation/.gitignore b/knowlg-automation/.gitignore new file mode 100644 index 000000000..537b9c22a --- /dev/null +++ b/knowlg-automation/.gitignore @@ -0,0 +1,39 @@ +# Local .terraform directories +**/.terraform/* + +# .tfstate files +*.tfstate +*.tfstate.* +*.terraform.lock.hcl + +# Crash log files +crash.log +crash.*.log + +# Exclude all .tfvars files, which are likely to contain sensitive data, such as +# password, private keys, and other secrets. These should not be part of version +# control as they are data points which are potentially sensitive and subject +# to change depending on the environment. +*.tfvars +*.tfvars.json + +# Ignore override files as they are usually used to override resources locally and so +# are not checked in +override.tf +override.tf.json +*_override.tf +*_override.tf.json + +# Include override files you do wish to add to version control using negated pattern +# !example_override.tf + +# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan +# example: *tfplan* + +# Ignore CLI configuration files +.terraformrc +terraform.rc + +**/.DS_Store + +*kubeconfig.yaml \ No newline at end of file From 62940171f05e5f904b0511709463ca6b21f5aa15 Mon Sep 17 00:00:00 2001 From: aimansharief <110388201+aimansharief@users.noreply.github.com> Date: Thu, 30 Mar 2023 10:54:58 +0530 Subject: [PATCH 200/490] Issue KN-767 feat: Added Helm chart and terraform file for content-service and added few changes to taxonomy-service. --- .../helm_charts/content/Chart.yaml | 5 + .../content/content-service_application.conf | 657 ++++++++++++++++++ .../content/content-service_logback.xml | 30 + .../content/templates/deployment.yaml | 75 ++ .../helm_charts/content/values.yaml | 23 + .../elasticsearch/charts/kibana-10.2.16.tgz | Bin 42078 -> 0 bytes .../taxonomy/templates/deployment.yaml | 2 +- .../helm_charts/taxonomy/values.yaml | 6 +- .../terraform/local/content-provision.tf | 11 + .../terraform/local/kafka-provision.tf | 40 +- knowlg-automation/terraform/local/main.tf | 31 - knowlg-automation/terraform/local/vars.tf | 12 +- knowlg-automation/terraform/local/versions.tf | 4 - 13 files changed, 836 insertions(+), 60 deletions(-) create mode 100644 knowlg-automation/helm_charts/content/Chart.yaml create mode 100644 knowlg-automation/helm_charts/content/content-service_application.conf create mode 100644 knowlg-automation/helm_charts/content/content-service_logback.xml create mode 100644 knowlg-automation/helm_charts/content/templates/deployment.yaml create mode 100644 knowlg-automation/helm_charts/content/values.yaml delete mode 100644 knowlg-automation/helm_charts/elasticsearch/charts/kibana-10.2.16.tgz create mode 100644 knowlg-automation/terraform/local/content-provision.tf diff --git a/knowlg-automation/helm_charts/content/Chart.yaml b/knowlg-automation/helm_charts/content/Chart.yaml new file mode 100644 index 000000000..dbf9d485c --- /dev/null +++ b/knowlg-automation/helm_charts/content/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: content +version: 0.1.0 diff --git a/knowlg-automation/helm_charts/content/content-service_application.conf b/knowlg-automation/helm_charts/content/content-service_application.conf new file mode 100644 index 000000000..34f18a850 --- /dev/null +++ b/knowlg-automation/helm_charts/content/content-service_application.conf @@ -0,0 +1,657 @@ +# This is the main configuration file for the application. +# https://www.playframework.com/documentation/latest/ConfigFile +# ~~~~~ +# Play uses HOCON as its configuration file format. HOCON has a number +# of advantages over other config formats, but there are two things that +# can be used when modifying settings. +# +# You can include other configuration files in this main application.conf file: +#include "extra-config.conf" +# +# You can declare variables and substitute for them: +#mykey = ${some.value} +# +# And if an environment variable exists when there is no other substitution, then +# HOCON will fall back to substituting environment variable: +#mykey = ${JAVA_HOME} + +## Akka +# https://www.playframework.com/documentation/latest/ScalaAkka#Configuration +# https://www.playframework.com/documentation/latest/JavaAkka#Configuration +# ~~~~~ +# Play uses Akka internally and exposes Akka Streams and actors in Websockets and +# other streaming HTTP responses. +akka { + # "akka.log-config-on-start" is extraordinarly useful because it log the complete + # configuration at INFO level, including defaults and overrides, so it s worth + # putting at the very top. + # + # Put the following in your conf/logback.xml file: + # + # + # + # And then uncomment this line to debug the configuration. + # + #log-config-on-start = true + default-dispatcher { + # This will be used if you have set "executor = "fork-join-executor"" + fork-join-executor { + # Min number of threads to cap factor-based parallelism number to + parallelism-min = 8 + + # The parallelism factor is used to determine thread pool size using the + # following formula: ceil(available processors * factor). Resulting size + # is then bounded by the parallelism-min and parallelism-max values. + parallelism-factor = 32.0 + + # Max number of threads to cap factor-based parallelism number to + parallelism-max = 64 + + # Setting to "FIFO" to use queue like peeking mode which "poll" or "LIFO" to use stack + # like peeking mode which "pop". + task-peeking-mode = "FIFO" + } + } + actors-dispatcher { + type = "Dispatcher" + executor = "fork-join-executor" + fork-join-executor { + parallelism-min = 8 + parallelism-factor = 32.0 + parallelism-max = 64 + } + # Throughput for default Dispatcher, set to 1 for as fair as possible + throughput = 1 + } + actor { + deployment { + /contentActor + { + router = smallest-mailbox-pool + nr-of-instances = 10 + dispatcher = actors-dispatcher + } + /channelActor + { + router = smallest-mailbox-pool + nr-of-instances = 10 + dispatcher = actors-dispatcher + } + /collectionActor + { + router = smallest-mailbox-pool + nr-of-instances = 10 + dispatcher = actors-dispatcher + } + /healthActor + { + router = smallest-mailbox-pool + nr-of-instances = 5 + dispatcher = actors-dispatcher + } + /licenseActor + { + router = smallest-mailbox-pool + nr-of-instances = 2 + dispatcher = actors-dispatcher + } + } + } +} + +## Secret key +# http://www.playframework.com/documentation/latest/ApplicationSecret +# ~~~~~ +# The secret key is used to sign Play's session cookie. +# This must be changed for production, but we don't recommend you change it in this file. +play.http.secret.key="jd5ECm/o0BXwQCe8PfZY1NoUkB9HN41QjA80p22MKyRIcP5RW4qHw8sZztCzv87M" + +## Modules +# https://www.playframework.com/documentation/latest/Modules +# ~~~~~ +# Control which modules are loaded when Play starts. Note that modules are +# the replacement for "GlobalSettings", which are deprecated in 2.5.x. +# Please see https://www.playframework.com/documentation/latest/GlobalSettings +# for more information. +# +# You can also extend Play functionality by using one of the publically available +# Play modules: https://playframework.com/documentation/latest/ModuleDirectory +play.modules { + # By default, Play will load any class called Module that is defined + # in the root package (the "app" directory), or you can define them + # explicitly below. + # If there are any built-in modules that you want to enable, you can list them here. + enabled += modules.ContentModule + + # If there are any built-in modules that you want to disable, you can list them here. + #disabled += "" +} + +## IDE +# https://www.playframework.com/documentation/latest/IDE +# ~~~~~ +# Depending on your IDE, you can add a hyperlink for errors that will jump you +# directly to the code location in the IDE in dev mode. The following line makes +# use of the IntelliJ IDEA REST interface: +#play.editor="http://localhost:63342/api/file/?file=%s&line=%s" + +## Internationalisation +# https://www.playframework.com/documentation/latest/JavaI18N +# https://www.playframework.com/documentation/latest/ScalaI18N +# ~~~~~ +# Play comes with its own i18n settings, which allow the user's preferred language +# to map through to internal messages, or allow the language to be stored in a cookie. +play.i18n { + # The application languages + langs = [ "en" ] + + # Whether the language cookie should be secure or not + #langCookieSecure = true + + # Whether the HTTP only attribute of the cookie should be set to true + #langCookieHttpOnly = true +} + +## Play HTTP settings +# ~~~~~ +play.http { + ## Router + # https://www.playframework.com/documentation/latest/JavaRouting + # https://www.playframework.com/documentation/latest/ScalaRouting + # ~~~~~ + # Define the Router object to use for this application. + # This router will be looked up first when the application is starting up, + # so make sure this is the entry point. + # Furthermore, it's assumed your route file is named properly. + # So for an application router like `my.application.Router`, + # you may need to define a router file `conf/my.application.routes`. + # Default to Routes in the root package (aka "apps" folder) (and conf/routes) + #router = my.application.Router + + ## Action Creator + # https://www.playframework.com/documentation/latest/JavaActionCreator + # ~~~~~ + #actionCreator = null + + ## ErrorHandler + # https://www.playframework.com/documentation/latest/JavaRouting + # https://www.playframework.com/documentation/latest/ScalaRouting + # ~~~~~ + # If null, will attempt to load a class called ErrorHandler in the root package, + #errorHandler = null + + ## Session & Flash + # https://www.playframework.com/documentation/latest/JavaSessionFlash + # https://www.playframework.com/documentation/latest/ScalaSessionFlash + # ~~~~~ + session { + # Sets the cookie to be sent only over HTTPS. + #secure = true + + # Sets the cookie to be accessed only by the server. + #httpOnly = true + + # Sets the max-age field of the cookie to 5 minutes. + # NOTE: this only sets when the browser will discard the cookie. Play will consider any + # cookie value with a valid signature to be a valid session forever. To implement a server side session timeout, + # you need to put a timestamp in the session and check it at regular intervals to possibly expire it. + #maxAge = 300 + + # Sets the domain on the session cookie. + #domain = "example.com" + } + + flash { + # Sets the cookie to be sent only over HTTPS. + #secure = true + + # Sets the cookie to be accessed only by the server. + #httpOnly = true + } +} + +play.server.http.idleTimeout = 60s +play.http.parser.maxDiskBuffer = 100MB +parsers.anyContent.maxLength = 100MB + +## Netty Provider +# https://www.playframework.com/documentation/latest/SettingsNetty +# ~~~~~ +play.server.netty { + # Whether the Netty wire should be logged + log.wire = true + + # If you run Play on Linux, you can use Netty's native socket transport + # for higher performance with less garbage. + transport = "native" +} + +## WS (HTTP Client) +# https://www.playframework.com/documentation/latest/ScalaWS#Configuring-WS +# ~~~~~ +# The HTTP client primarily used for REST APIs. The default client can be +# configured directly, but you can also create different client instances +# with customized settings. You must enable this by adding to build.sbt: +# +# libraryDependencies += ws // or javaWs if using java +# +play.ws { + # Sets HTTP requests not to follow 302 requests + #followRedirects = false + + # Sets the maximum number of open HTTP connections for the client. + #ahc.maxConnectionsTotal = 50 + + ## WS SSL + # https://www.playframework.com/documentation/latest/WsSSL + # ~~~~~ + ssl { + # Configuring HTTPS with Play WS does not require programming. You can + # set up both trustManager and keyManager for mutual authentication, and + # turn on JSSE debugging in development with a reload. + #debug.handshake = true + #trustManager = { + # stores = [ + # { type = "JKS", path = "exampletrust.jks" } + # ] + #} + } +} + +## Cache +# https://www.playframework.com/documentation/latest/JavaCache +# https://www.playframework.com/documentation/latest/ScalaCache +# ~~~~~ +# Play comes with an integrated cache API that can reduce the operational +# overhead of repeated requests. You must enable this by adding to build.sbt: +# +# libraryDependencies += cache +# +play.cache { +# If you want to bind several caches, you can bind the individually +#bindCaches = ["db-cache", "user-cache", "session-cache"] +} + +## Filter Configuration +# https://www.playframework.com/documentation/latest/Filters +# ~~~~~ +# There are a number of built-in filters that can be enabled and configured +# to give Play greater security. +# +play.filters { + + # Enabled filters are run automatically against Play. + # CSRFFilter, AllowedHostFilters, and SecurityHeadersFilters are enabled by default. + enabled = [filters.AccessLogFilter] + + # Disabled filters remove elements from the enabled list. + # disabled += filters.CSRFFilter + + + ## CORS filter configuration + # https://www.playframework.com/documentation/latest/CorsFilter + # ~~~~~ + # CORS is a protocol that allows web applications to make requests from the browser + # across different domains. + # NOTE: You MUST apply the CORS configuration before the CSRF filter, as CSRF has + # dependencies on CORS settings. + cors { + # Filter paths by a whitelist of path prefixes + #pathPrefixes = ["/some/path", ...] + + # The allowed origins. If null, all origins are allowed. + #allowedOrigins = ["http://www.example.com"] + + # The allowed HTTP methods. If null, all methods are allowed + #allowedHttpMethods = ["GET", "POST"] + } + + ## Security headers filter configuration + # https://www.playframework.com/documentation/latest/SecurityHeaders + # ~~~~~ + # Defines security headers that prevent XSS attacks. + # If enabled, then all options are set to the below configuration by default: + headers { + # The X-Frame-Options header. If null, the header is not set. + #frameOptions = "DENY" + + # The X-XSS-Protection header. If null, the header is not set. + #xssProtection = "1; mode=block" + + # The X-Content-Type-Options header. If null, the header is not set. + #contentTypeOptions = "nosniff" + + # The X-Permitted-Cross-Domain-Policies header. If null, the header is not set. + #permittedCrossDomainPolicies = "master-only" + + # The Content-Security-Policy header. If null, the header is not set. + #contentSecurityPolicy = "default-src 'self'" + } + + ## Allowed hosts filter configuration + # https://www.playframework.com/documentation/latest/AllowedHostsFilter + # ~~~~~ + # Play provides a filter that lets you configure which hosts can access your application. + # This is useful to prevent cache poisoning attacks. + hosts { + # Allow requests to example.com, its subdomains, and localhost:9000. + #allowed = [".example.com", "localhost:9000"] + } +} + +play.http.parser.maxMemoryBuffer = 50MB +akka.http.parsing.max-content-length = 50MB +schema.base_path="https://sunbirddevbbpublic.blob.core.windows.net/sunbird-content-staging-knowlg/schemas/local" + +# Cassandra Configuration +cassandra { + lp { + connection: "cassandra-db.knowlg-db.svc.cluster.local:9042" + } + lpa { + connection: "cassandra-db.knowlg-db.svc.cluster.local:9042" + } +} + +# Consistency Level for Multi Node Cassandra cluster +cassandra.lp.consistency.level=QUORUM + +collection { + keyspace: "dev_hierarchy_store" + cache.enable: true + image.migration.enabled: true +} + +content { + keyspace: "dev_content_store" + cache { + ttl: 86400 + enable: true + } + hierarchy { + removed_props_for_leafNodes: ["collections", "children", "usedByContent", "item_sets", "methods", "libraries", "editorState"] + } + # Added for supporting backward compatibility - remove in release-2.7.0 + tagging { + backward_enable: true + property: "subject,medium" + } + h5p { + library { + path: "https://sunbirdpublic.blob.core.windows.net/installation/h5p-standalone-1.3.4.zip" + } + } + copy { + invalid_statusList: ["Flagged","FlaggedDraft","FraggedReview","Retired", "Processing"] + origin_data: ["name", "author", "license", "organisation"] + props_to_remove: ["downloadUrl", "artifactUrl", "variants", "createdOn", "collections", "children", "lastUpdatedOn", "SYS_INTERNAL_LAST_UPDATED_ON", "versionKey", "s3Key", "status", "pkgVersion", "toc_url", "mimeTypesCount", "contentTypesCount", "leafNodesCount", "childNodes", "prevState", "lastPublishedOn", "flagReasons", "compatibilityLevel", "size", "publishChecklist", "publishComment", "LastPublishedBy", "rejectReasons", "rejectComment", "gradeLevel", "subject", "medium", "board", "topic", "purpose", "subtopic", "contentCredits", "owner", "collaborators", "creators", "contributors", "badgeAssertions", "dialcodes", "concepts", "keywords", "reservedDialcodes", "dialcodeRequired", "leafNodes", "sYS_INTERNAL_LAST_UPDATED_ON", "prevStatus", "lastPublishedBy", "streamingUrl", "boardIds", "gradeLevelIds", "subjectIds", "mediumIds", "topicsIds", "targetFWIds", "targetBoardIds", "targetGradeLevelIds", "targetSubjectIds", "targetMediumIds", "targetTopicIds", "se_boards", "se_subjects", "se_mediums", "se_gradeLevels", "se_topics", "se_FWIds", "se_boardIds", "se_subjectIds", "se_mediumIds", "se_gradeLevelIds", "se_topicIds"] + } + # Need to depreacte + media { + base.url: "https://dev.knowlg.sunbird.org" + } +} + +# Redis Configuration +redis { + host: "redis-db.knowlg-db.svc.cluster.local" + port: 6379 + maxConnections: 128 +} + + +#--Maximum Content Package File Size Limit in Bytes (50 MB) +MAX_CONTENT_PACKAGE_FILE_SIZE_LIMIT=157286400 + +#--Maximum Asset File Size Limit in Bytes (50 MB) +MAX_ASSET_FILE_SIZE_LIMIT=157286400 + +#--No of Retry While File Download Fails +RETRY_ASSET_DOWNLOAD_COUNT=1 + +#Current environment - need this property post cloud-store implementation +cloud_storage { + env: staging + content.folder: "content" + asset.folder: "assets" + artefact.folder: "artifact" + bundle.folder: "bundle" + media.folder: "media" + ecar.folder: "ecar_files" + upload.url.ttl: "54000" +} + +# Configuration +akka.request_timeout: 30 +environment.id: 20000000 +graph { + dir: "/data/graphDB" + ids: ["domain"] + passport.key.base: "jd5ECm/o0BXwQCe8PfZY1NoUkB9HN41QjA80p22MKyRIcP5RW4qHw8sZztCzv87M" +} +route { + domain: "bolt://neo4j-db.knowlg-db.svc.cluster.local:7687" + all: "bolt://neo4j-db.knowlg-db.svc.cluster.local:8687" + bolt { + write { + domain: "bolt://neo4j-db.knowlg-db.svc.cluster.local:7687" + all: "bolt://neo4j-db.knowlg-db.svc.cluster.local:8687" + }, + read { + domain: "bolt://neo4j-db.knowlg-db.svc.cluster.local:7687" + all: "bolt://neo4j-db.knowlg-db.svc.cluster.local:8687" + } + } +} + +shard.id: 1 +platform { + auth.check.enabled: false + cache.ttl: 3600000 + language.codes: ["as","bn","en","gu","hi","hoc","jun","ka","mai","mr","unx","or","san","sat","ta","te","urd", "pj"] +} + +#Top N Config for Search Telemetry +telemetry_env: staging + +installation.id: ekstep + +channel { + default: "in.ekstep" + fetch.suggested_frameworks: true +} + +languageCode { + assamese : "as" + bengali : "bn" + english : "en" + gujarati : "gu" + hindi : "hi" + kannada : "ka" + marathi : "mr" + odia : "or" + tamil : "ta" + telugu : "te" +} +# Need to depreacte +composite { + search { + url : "http://search-service.knowlg.svc.cluster.local:9000/v3/search" + } +} + +cloud_storage_type: "azure" +cloud_storage_key: "" +cloud_storage_secret: "" +cloud_storage_endpoint: "" +cloud_storage_container: "" + +# Google Drive APIKEY +learning_content_drive_apiKey = "" + +#Youtube Standard Licence Validation +learning.content.youtube.application.name="" +learning_content_youtube_apikey="" +youtube.license.regex.pattern=["\\?vi?=([^&]*)", "watch\\?.*v=([^&]*)", "(?:embed|vi?)/([^/?]*)","^([A-Za-z0-9\\-\\_]*)"] +learning.valid_license=["creativeCommon"] + +kafka { + urls : "kafka.knowlg-db.svc.cluster.local:9092" + topic.send.enable : true + topics.instruction : "" + publish.request.topic : "" +} + +# Need to depreacte +# DIAL Link Config +dial_service { + api { + base_url : "http://dial-service.knowlg.svc.cluster.local:9000" + auth_key : "" + search : "/dialcode/v3/search" + generate : "/dialcode/v3/generate" + } +} +# Need to depreacte +reserve_dialcode { + mimeType : ["application/vnd.ekstep.content-collection"] + max_count : 250 +} + +content.link_dialcode.validation=true +content.link_dialcode.max_limit=10 + +# Content Import API Config +import { + request_size_limit : 1000 + output_topic_name : "sunbirdstaging.knowlg.auto.creation.job.request" + required_props : ["name","code","mimeType","primaryCategory","artifactUrl","framework"] + remove_props : ["downloadUrl","variants","previewUrl","streamingUrl","itemSets","level1Name","level1Concept","level2Name","level2Concept","level3Name","level3Concept","me_totalPlaySessionCount","me_totalTimeSpentInSec","me_totalSessionsCount","me_totalTimespent","me_totalInteractions","me_creationSessions","me_creationTimespent","me_averageInteractionsPerMin","me_averageSessionsPerDevice","me_totalDevices","me_averageTimespentPerSession","me_averageRating","me_totalDownloads","me_totalSideloads","me_totalRatings","me_totalComments","me_totalDialcode","me_totalDialcodeLinkedToContent","me_totalDialcodeAttached","me_hierarchyLevel","origin","originData","contentPolicyCheck","questions"] +} + +# Need to depreacte +contentTypeToPrimaryCategory { + ClassroomTeachingVideo: "Explanation Content" + ConceptMap: "Learning Resource" + Course: "Course" + CuriosityQuestionSet: "Practice Question Set" + eTextBook: "eTextbook" + ExperientialResource: "Learning Resource" + ExplanationResource: "Explanation Content" + ExplanationVideo: "Explanation Content" + FocusSpot: "Teacher Resource" + LearningOutcomeDefinition: "Teacher Resource" + MarkingSchemeRubric: "Teacher Resource" + PedagogyFlow: "Teacher Resource" + PracticeQuestionSet: "Practice Question Set" + PracticeResource: "Practice Question Set" + SelfAssess: "Course Assessment" + TeachingMethod: "Teacher Resource" + TextBook: "Digital Textbook" + Collection: "Content Playlist" + ExplanationReadingMaterial: "Learning Resource" + LearningActivity: "Learning Resource" + LessonPlan: "Content Playlist" + LessonPlanResource: "Teacher Resource" + PreviousBoardExamPapers: "Learning Resource" + TVLesson: "Explanation Content" + OnboardingResource: "Learning Resource" + ReadingMaterial: "Learning Resource" + Template: "Template" + Asset: "Asset" + Plugin: "Plugin" + LessonPlanUnit: "Lesson Plan Unit" + CourseUnit: "Course Unit" + TextBookUnit: "Textbook Unit" + Asset: "Certificate Template" +} +# Need to depreacte +resourceTypeToPrimaryCategory { + Learn: "Learning Resource" + Read: "Learning Resource" + Practice: "Learning Resource" + Teach: "Teacher Resource" + Test: "Learning Resource" + Experiment: "Learning Resource" + LessonPlan: "Teacher Resource" +} +# Need to depreacte +mimeTypeToPrimaryCategory { + "application/vnd.ekstep.h5p-archive": ["Learning Resource"] + "application/vnd.ekstep.html-archive": ["Learning Resource"] + "application/vnd.android.package-archive": ["Learning Resource"] + "video/webm": ["Explanation Content"] + "video/x-youtube": ["Explanation Content"] + "video/mp4": ["Explanation Content"] + "application/pdf": ["Learning Resource", "Teacher Resource"] + "application/epub": ["Learning Resource", "Teacher Resource"] + "application/vnd.ekstep.ecml-archive": ["Learning Resource", "Teacher Resource"] + "text/x-url": ["Learnin Resource", "Teacher Resource"] +} + +objectcategorydefinition.keyspace="dev_category_store" + +# Need to depreacte +#Default objectCategory mapping for channel +channel { + content{ + primarycategories=["Course Assessment", "eTextbook", "Explanation Content", "Learning Resource", "Practice Question Set", "Teacher Resource", "Exam Question"] + additionalcategories= ["Classroom Teaching Video", "Concept Map", "Curiosity Question Set", "Experiential Resource", "Explanation Video", "Focus Spot", "Learning Outcome Definition", "Lesson Plan", "Marking Scheme Rubric", "Pedagogy Flow", "Previous Board Exam Papers", "TV Lesson", "Textbook"] + } + collection { + primarycategories=["Content Playlist", "Course", "Digital Textbook", "Question paper"] + additionalcategories=["Textbook", "Lesson Plan"] + } + asset { + primarycategories=["Asset", "CertAsset", "Certificate Template"] + additionalcategories=[] + } +} +master.category.validation.enabled="Yes" + +# Need to depreacte +#Collection CSV +sunbird_dialcode_search_api="http://dial-service.knowlg.svc.cluster.local:9000/dialcode/v3/list" +framework_read_api_url="http://localhost:8080/learning-service/framework/v3/read" +sunbird_link_dial_code_api="http://content-service.knowlg.svc.cluster.local:9000/collection/v4/dialcode/link" + +# Need to depreacte +collection { + csv { + maxRows = 6500 + allowedContentTypes = ["TextBook","Collection","Course"] + maxFirstLevelUnits=30 + ttl = 86400 + maxUnitFieldLength=120 + maxDescFieldLength=1500 + contentTypeToUnitType = {"TextBook": "TextBookUnit", "Course": "CourseUnit", "Collection":"Collection"} + headers { + folderIdentifier = ["Folder Identifier"] + hierarchy = ["Level 1 Folder","Level 2 Folder","Level 3 Folder","Level 4 Folder"] + QR = ["QR Code Required?","QR Code"] + topics = ["Mapped Topics"] + collectionName = ["Collection Name"] + linkedContents = ["Linked Content 1","Linked Content 2","Linked Content 3","Linked Content 4","Linked Content 5","Linked Content 6","Linked Content 7","Linked Content 8","Linked Content 9","Linked Content 10","Linked Content 11","Linked Content 12","Linked Content 13","Linked Content 14","Linked Content 15","Linked Content 16","Linked Content 17","Linked Content 18","Linked Content 19","Linked Content 20","Linked Content 21","Linked Content 22","Linked Content 23","Linked Content 24","Linked Content 25","Linked Content 26","Linked Content 27","Linked Content 28","Linked Content 29","Linked Content 30"] + output = ["Collection Name","Folder Identifier","Level 1 Folder","Level 2 Folder","Level 3 Folder","Level 4 Folder","Description","Mapped Topics","Keywords","QR Code Required?","QR Code","Linked Content 1","Linked Content 2","Linked Content 3","Linked Content 4","Linked Content 5","Linked Content 6","Linked Content 7","Linked Content 8","Linked Content 9","Linked Content 10","Linked Content 11","Linked Content 12","Linked Content 13","Linked Content 14","Linked Content 15","Linked Content 16","Linked Content 17","Linked Content 18","Linked Content 19","Linked Content 20","Linked Content 21","Linked Content 22","Linked Content 23","Linked Content 24","Linked Content 25","Linked Content 26","Linked Content 27","Linked Content 28","Linked Content 29","Linked Content 30"] + sequence { + create = {"Level 1 Folder":0,"Level 2 Folder":1,"Level 3 Folder":2,"Level 4 Folder":3,"Description":4} + update = {"Collection Name":0,"Folder Identifier":1,"Level 1 Folder":2,"Level 2 Folder":3,"Level 3 Folder":4,"Level 4 Folder":5,"Description":6,"Mapped Topics":7,"Keywords":8,"QR Code Required?":9,"QR Code":10,"Linked Content 1":11,"Linked Content 2":12,"Linked Content 3":13,"Linked Content 4":14,"Linked Content 5":15,"Linked Content 6":16,"Linked Content 7":17,"Linked Content 8":18,"Linked Content 9":19,"Linked Content 10":20,"Linked Content 11":21,"Linked Content 12":22,"Linked Content 13":23,"Linked Content 14":24,"Linked Content 15":25,"Linked Content 16":26,"Linked Content 17":27,"Linked Content 18":28,"Linked Content 19":29,"Linked Content 20":30,"Linked Content 21":31,"Linked Content 22":32,"Linked Content 23":33,"Linked Content 24":34,"Linked Content 25":35,"Linked Content 26":36,"Linked Content 27":37,"Linked Content 28":38,"Linked Content 29":39,"Linked Content 30":40} + linkedContents = {"Linked Content 1":0,"Linked Content 2":1,"Linked Content 3":2,"Linked Content 4":3,"Linked Content 5":4,"Linked Content 6":5,"Linked Content 7":6,"Linked Content 8":7,"Linked Content 9":8,"Linked Content 10":9,"Linked Content 11":10,"Linked Content 12":11,"Linked Content 13":12,"Linked Content 14":13,"Linked Content 15":14,"Linked Content 16":15,"Linked Content 17":16,"Linked Content 18":17,"Linked Content 19":18,"Linked Content 20":19,"Linked Content 21":20,"Linked Content 22":21,"Linked Content 23":22,"Linked Content 24":23,"Linked Content 25":24,"Linked Content 26":25,"Linked Content 27":26,"Linked Content 28":27,"Linked Content 29":28,"Linked Content 30":29} + } + } + mandatory { + create = ["Level 1 Folder"] + update = ["Collection Name","Folder Identifier"] + } + } +} + +plugin.media.base.url="https://dev.knowlg.sunbird.org" + +cloudstorage { + metadata.replace_absolute_path=true + relative_path_prefix=CONTENT_STORAGE_BASE_PATH + metadata.list=["appIcon", "artifactUrl", "posterImage", "previewUrl", "thumbnail", "assetsMap", "certTemplate", "itemSetPreviewUrl", "grayScaleAppIcon", "sourceURL", "variants", "downloadUrl", "streamingUrl", "toc_url", "data", "question", "solutions", "editorState", "media", "pdfUrl", "transcripts"] + read_base_path="https://sunbirddevbbpublic.blob.core.windows.net" + write_base_path=["https://sunbirdstagingpublic.blob.core.windows.net", "https://sunbirddevbbpublic.blob.core.windows.net"] +} \ No newline at end of file diff --git a/knowlg-automation/helm_charts/content/content-service_logback.xml b/knowlg-automation/helm_charts/content/content-service_logback.xml new file mode 100644 index 000000000..3b3a26ecc --- /dev/null +++ b/knowlg-automation/helm_charts/content/content-service_logback.xml @@ -0,0 +1,30 @@ + + + + + + + + + %d %msg%n + + + + + + + + + + + + + + + + + + + + + diff --git a/knowlg-automation/helm_charts/content/templates/deployment.yaml b/knowlg-automation/helm_charts/content/templates/deployment.yaml new file mode 100644 index 000000000..b9763875c --- /dev/null +++ b/knowlg-automation/helm_charts/content/templates/deployment.yaml @@ -0,0 +1,75 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Chart.Name }} + namespace: {{ .Values.namespace }} + annotations: + reloader.stakater.com/auto: "true" +spec: + replicas: {{ .Values.replicaCount }} + strategy: + rollingUpdate: + maxSurge: {{ .Values.strategy.maxsurge }} + maxUnavailable: {{ .Values.strategy.maxunavailable }} + selector: + matchLabels: + app: {{ .Chart.Name }} + template: + metadata: + labels: + app: {{ .Chart.Name }} + spec: + volumes: + - name: {{ .Chart.Name }}-config + configMap: + name: {{ .Chart.Name }}-config + - name: {{ .Chart.Name }}-xml-config + configMap: + name: {{ .Chart.Name }}-xml-config + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.repository }}:{{ .Values.image_tag }}" + imagePullPolicy: Always + env: + - name: JAVA_OPTIONS + value: {{ .Values.env.javaoptions | quote }} + - name: _JAVA_OPTIONS + value: -Dlog4j2.formatMsgNoLookups=true + envFrom: + - configMapRef: + name: {{ .Chart.Name }}-config + resources: +{{ toYaml .Values.resources | indent 10 }} + ports: + - containerPort: {{ .Values.network.port }} + {{- if .Values.healthcheck }} + livenessProbe: +{{ toYaml .Values.livenessProbe | indent 10 }} + readinessProbe: +{{ toYaml .Values.readinessProbe | indent 10 }} + {{- end }} + volumeMounts: + - name: {{ .Chart.Name }}-config + mountPath: /home/sunbird/content-service-1.0-SNAPSHOT/config/application.conf + subPath: content-service_application.conf + - name: {{ .Chart.Name }}-xml-config + mountPath: /home/sunbird/content-service-1.0-SNAPSHOT/config/logback.xml + subPath: content-service_logback.xml + +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ .Chart.Name }}-service + namespace: {{ .Values.namespace }} + labels: + app: {{ .Chart.Name }} +spec: + type: {{ .Values.serviceType }} + ports: + - name: http-{{ .Chart.Name }} + protocol: TCP + port: {{ .Values.network.targetport }} + selector: + app: {{ .Chart.Name }} diff --git a/knowlg-automation/helm_charts/content/values.yaml b/knowlg-automation/helm_charts/content/values.yaml new file mode 100644 index 000000000..6064b56a6 --- /dev/null +++ b/knowlg-automation/helm_charts/content/values.yaml @@ -0,0 +1,23 @@ +namespace: knowlg-api + +env: + javaoptions: -Xmx600m + +replicaCount: 1 +repository: docker.io/aimansharief/content-service-modified +image_tag: R5.2.0 +resources: + requests: + cpu: 100m + memory: 100Mi + limits: + cpu: 1 + memory: 1024Mi +network: + port: 9000 + targetport: 9000 +strategy: + type: RollingUpdate + maxsurge: 25% + maxunavailable: 25% +serviceType: ClusterIP \ No newline at end of file diff --git a/knowlg-automation/helm_charts/elasticsearch/charts/kibana-10.2.16.tgz b/knowlg-automation/helm_charts/elasticsearch/charts/kibana-10.2.16.tgz deleted file mode 100644 index 313e3d0240367593658eb44da6ea7d65e714a854..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 42078 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 z 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 diff --git a/knowlg-automation/helm_charts/taxonomy/templates/deployment.yaml b/knowlg-automation/helm_charts/taxonomy/templates/deployment.yaml index 35972b8cb..598850f0d 100644 --- a/knowlg-automation/helm_charts/taxonomy/templates/deployment.yaml +++ b/knowlg-automation/helm_charts/taxonomy/templates/deployment.yaml @@ -66,7 +66,7 @@ metadata: labels: app: {{ .Chart.Name }} spec: - type: LoadBalancer + type: {{ .Values.serviceType }} ports: - name: http-{{ .Chart.Name }} protocol: TCP diff --git a/knowlg-automation/helm_charts/taxonomy/values.yaml b/knowlg-automation/helm_charts/taxonomy/values.yaml index b8ee17780..a0bdb1a8d 100644 --- a/knowlg-automation/helm_charts/taxonomy/values.yaml +++ b/knowlg-automation/helm_charts/taxonomy/values.yaml @@ -1,12 +1,12 @@ ### Default variable file for taxonomy-service ### -namespace: knowlg-db +namespace: knowlg-api env: javaoptions: -Xmx600m replicaCount: 1 -repository: docker.io/aimansharief/taxonomy-service +repository: docker.io/aimansharief/taxonomy-service-modified image_tag: R5.2.0 resources: requests: @@ -22,7 +22,7 @@ strategy: type: RollingUpdate maxsurge: 25% maxunavailable: 25% -serviceType: LoadBalancer +serviceType: ClusterIP diff --git a/knowlg-automation/terraform/local/content-provision.tf b/knowlg-automation/terraform/local/content-provision.tf new file mode 100644 index 000000000..6d4ead915 --- /dev/null +++ b/knowlg-automation/terraform/local/content-provision.tf @@ -0,0 +1,11 @@ +resource "helm_release" "content" { + name = "content" + chart = var.CONTENT_CHART + namespace = var.CONTENT_NAMESPACE + create_namespace = true + dependency_update = true + depends_on = [kind_cluster.one-click] + wait_for_jobs = true + +} + \ No newline at end of file diff --git a/knowlg-automation/terraform/local/kafka-provision.tf b/knowlg-automation/terraform/local/kafka-provision.tf index dfd5691b7..9fe395b1d 100644 --- a/knowlg-automation/terraform/local/kafka-provision.tf +++ b/knowlg-automation/terraform/local/kafka-provision.tf @@ -42,26 +42,26 @@ resource "helm_release" "kafka" { ] } -# # data "kubernetes_service" "kafka" { -# # metadata { -# # namespace = "kafka" -# # name = "kafka" -# # } -# # depends_on = [kind_cluster.one-click, helm_release.kafka] -# # } +# data "kubernetes_service" "kafka" { +# metadata { +# namespace = "kafka" +# name = "kafka" +# } +# depends_on = [kind_cluster.one-click, helm_release.kafka] +# } -# # data "kubernetes_service" "zookeeper" { -# # metadata { -# # namespace = "kafka" -# # name = "kafka-zookeeper" -# # } -# # depends_on = [kind_cluster.one-click, helm_release.kafka] -# # } +# data "kubernetes_service" "zookeeper" { +# metadata { +# namespace = "kafka" +# name = "kafka-zookeeper" +# } +# depends_on = [kind_cluster.one-click, helm_release.kafka] +# } -# # output "kafka-service-ip" { -# # value = data.kubernetes_service.kafka.spec.0.cluster_ip -# # } +# output "kafka-service-ip" { +# value = data.kubernetes_service.kafka.spec.0.cluster_ip +# } -# # output "zookeeper-service-ip" { -# # value = data.kubernetes_service.zookeeper.spec.0.cluster_ip -# # } +# output "zookeeper-service-ip" { +# value = data.kubernetes_service.zookeeper.spec.0.cluster_ip +# } diff --git a/knowlg-automation/terraform/local/main.tf b/knowlg-automation/terraform/local/main.tf index 9b04e13a6..c9d4368c1 100644 --- a/knowlg-automation/terraform/local/main.tf +++ b/knowlg-automation/terraform/local/main.tf @@ -1,17 +1,3 @@ -# provider "docker" { -# host = "unix:///var/run/docker.sock" -# } - - -# # Create a docker image resource -# resource "docker_image" "taxonomy-service" { -# name = "taxonomy-service" -# build { -# tag = ["taxonomy-service:R5.2.0"] -# path = "../taxonomy-service" -# dockerfile = "../taxonomy-service" -# } -# } provider "kind" { } @@ -63,20 +49,3 @@ resource "kind_cluster" "one-click" { } } - - - -# resource "docker_image" "taxonomy-service" { -# name = "taxonomy-service" -# build { -# context = "https://github.com/Sunbird-Knowlg/knowledge-platform.git#master:build/taxonomy-service/Dockerfile" -# tag = ["taxonomy-service:R5.2.0"] -# build_arg = { -# foo : "taxonomy-service" -# } -# label = { -# author : "taxonomy-service" -# } -# } -# } - diff --git a/knowlg-automation/terraform/local/vars.tf b/knowlg-automation/terraform/local/vars.tf index 8632d2ad5..ac1b36f1d 100644 --- a/knowlg-automation/terraform/local/vars.tf +++ b/knowlg-automation/terraform/local/vars.tf @@ -113,7 +113,17 @@ variable "TAXONOMY_CHART" { variable "TAXONOMY_NAMESPACE" { description = "Taxonomy Instance Running Namespace" - default = "knowlg-db" + default = "knowlg-api" +} + +#CONTENT +variable "CONTENT_CHART" { + description = "Content Instance Running Namespace" + default = "../../helm_charts/content" } +variable "CONTENT_NAMESPACE" { + description = "Content Instance Running Namespace" + default = "knowlg-api" +} diff --git a/knowlg-automation/terraform/local/versions.tf b/knowlg-automation/terraform/local/versions.tf index 20ae80c83..fb0b06c79 100644 --- a/knowlg-automation/terraform/local/versions.tf +++ b/knowlg-automation/terraform/local/versions.tf @@ -14,10 +14,6 @@ terraform { source = "hashicorp/helm" version = "2.6.0" } - # docker = { - # source = "kreuzwerker/docker" - # version = "2.22.0" - # } } From 3db4a6a484e3d227a7aad00db7c182a743948267 Mon Sep 17 00:00:00 2001 From: aimansharief <110388201+aimansharief@users.noreply.github.com> Date: Fri, 31 Mar 2023 12:52:07 +0530 Subject: [PATCH 201/490] Issue KN-767 feat: Added Helm chart and terraform file for search-service --- .../helm_charts/search/Chart.yaml | 5 + .../search/search-service_application.conf | 315 ++++++++++++++++++ .../search/search-service_logback.xml | 28 ++ .../search/templates/deployment.yaml | 75 +++++ .../helm_charts/search/values.yaml | 29 ++ .../helm_charts/taxonomy/values.yaml | 2 +- knowlg-automation/terraform/aks.yaml | 8 +- .../terraform/local/search-provision.tf | 10 + knowlg-automation/terraform/local/vars.tf | 11 + 9 files changed, 478 insertions(+), 5 deletions(-) create mode 100644 knowlg-automation/helm_charts/search/Chart.yaml create mode 100644 knowlg-automation/helm_charts/search/search-service_application.conf create mode 100644 knowlg-automation/helm_charts/search/search-service_logback.xml create mode 100644 knowlg-automation/helm_charts/search/templates/deployment.yaml create mode 100644 knowlg-automation/helm_charts/search/values.yaml create mode 100644 knowlg-automation/terraform/local/search-provision.tf diff --git a/knowlg-automation/helm_charts/search/Chart.yaml b/knowlg-automation/helm_charts/search/Chart.yaml new file mode 100644 index 000000000..a70e56598 --- /dev/null +++ b/knowlg-automation/helm_charts/search/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: search +version: 0.1.0 diff --git a/knowlg-automation/helm_charts/search/search-service_application.conf b/knowlg-automation/helm_charts/search/search-service_application.conf new file mode 100644 index 000000000..e6f0753e5 --- /dev/null +++ b/knowlg-automation/helm_charts/search/search-service_application.conf @@ -0,0 +1,315 @@ +# This is the main configuration file for the application. +# https://www.playframework.com/documentation/latest/ConfigFile +# ~~~~~ +# Play uses HOCON as its configuration file format. HOCON has a number +# of advantages over other config formats, but there are two things that +# can be used when modifying settings. +# +# You can include other configuration files in this main application.conf file: +#include "extra-config.conf" +# +# You can declare variables and substitute for them: +#mykey = ${some.value} +# +# And if an environment variable exists when there is no other substitution, then +# HOCON will fall back to substituting environment variable: +#mykey = ${JAVA_HOME} + +## Akka +# https://www.playframework.com/documentation/latest/ScalaAkka#Configuration +# https://www.playframework.com/documentation/latest/JavaAkka#Configuration +# ~~~~~ +# Play uses Akka internally and exposes Akka Streams and actors in Websockets and +# other streaming HTTP responses. +akka { + # "akka.log-config-on-start" is extraordinarly useful because it log the complete + # configuration at INFO level, including defaults and overrides, so it s worth + # putting at the very top. + # + # Put the following in your conf/logback.xml file: + # + # + # + # And then uncomment this line to debug the configuration. + # + #log-config-on-start = true +} + +## Secret key +# http://www.playframework.com/documentation/latest/ApplicationSecret +# ~~~~~ +# The secret key is used to sign Play's session cookie. +# This must be changed for production, but we don't recommend you change it in this file. +play.http.secret.key = a-long-secret-to-calm-the-rage-of-the-entropy-gods + +## Modules +# https://www.playframework.com/documentation/latest/Modules +# ~~~~~ +# Control which modules are loaded when Play starts. Note that modules are +# the replacement for "GlobalSettings", which are deprecated in 2.5.x. +# Please see https://www.playframework.com/documentation/latest/GlobalSettings +# for more information. +# +# You can also extend Play functionality by using one of the publically available +# Play modules: https://playframework.com/documentation/latest/ModuleDirectory +play.modules { + # By default, Play will load any class called Module that is defined + # in the root package (the "app" directory), or you can define them + # explicitly below. + # If there are any built-in modules that you want to enable, you can list them here. + #enabled += my.application.Module + + # If there are any built-in modules that you want to disable, you can list them here. + #disabled += "" + enabled += modules.SearchModule +} + +## IDE +# https://www.playframework.com/documentation/latest/IDE +# ~~~~~ +# Depending on your IDE, you can add a hyperlink for errors that will jump you +# directly to the code location in the IDE in dev mode. The following line makes +# use of the IntelliJ IDEA REST interface: +#play.editor="http://localhost:63342/api/file/?file=%s&line=%s" + +## Internationalisation +# https://www.playframework.com/documentation/latest/JavaI18N +# https://www.playframework.com/documentation/latest/ScalaI18N +# ~~~~~ +# Play comes with its own i18n settings, which allow the user's preferred language +# to map through to internal messages, or allow the language to be stored in a cookie. +play.i18n { + # The application languages + langs = [ "en" ] + + # Whether the language cookie should be secure or not + #langCookieSecure = true + + # Whether the HTTP only attribute of the cookie should be set to true + #langCookieHttpOnly = true +} + +## Play HTTP settings +# ~~~~~ +play.http { + ## Router + # https://www.playframework.com/documentation/latest/JavaRouting + # https://www.playframework.com/documentation/latest/ScalaRouting + # ~~~~~ + # Define the Router object to use for this application. + # This router will be looked up first when the application is starting up, + # so make sure this is the entry point. + # Furthermore, it's assumed your route file is named properly. + # So for an application router like `my.application.Router`, + # you may need to define a router file `conf/my.application.routes`. + # Default to Routes in the root package (aka "apps" folder) (and conf/routes) + #router = my.application.Router + + ## Action Creator + # https://www.playframework.com/documentation/latest/JavaActionCreator + # ~~~~~ + #actionCreator = null + + ## ErrorHandler + # https://www.playframework.com/documentation/latest/JavaRouting + # https://www.playframework.com/documentation/latest/ScalaRouting + # ~~~~~ + # If null, will attempt to load a class called ErrorHandler in the root package, + #errorHandler = null + + ## Session & Flash + # https://www.playframework.com/documentation/latest/JavaSessionFlash + # https://www.playframework.com/documentation/latest/ScalaSessionFlash + # ~~~~~ + session { + # Sets the cookie to be sent only over HTTPS. + #secure = true + + # Sets the cookie to be accessed only by the server. + #httpOnly = true + + # Sets the max-age field of the cookie to 5 minutes. + # NOTE: this only sets when the browser will discard the cookie. Play will consider any + # cookie value with a valid signature to be a valid session forever. To implement a server side session timeout, + # you need to put a timestamp in the session and check it at regular intervals to possibly expire it. + #maxAge = 300 + + # Sets the domain on the session cookie. + #domain = "example.com" + } + + flash { + # Sets the cookie to be sent only over HTTPS. + #secure = true + + # Sets the cookie to be accessed only by the server. + #httpOnly = true + } +} + +play.http.parser.maxDiskBuffer = 10MB +parsers.anyContent.maxLength = 10MB + +play.server.provider = play.core.server.NettyServerProvider + +## Netty Provider +# https://www.playframework.com/documentation/latest/SettingsNetty +# ~~~~~ +play.server.netty { + # Whether the Netty wire should be logged + log.wire = true + + # If you run Play on Linux, you can use Netty's native socket transport + # for higher performance with less garbage. + transport = "native" +} + +## WS (HTTP Client) +# https://www.playframework.com/documentation/latest/ScalaWS#Configuring-WS +# ~~~~~ +# The HTTP client primarily used for REST APIs. The default client can be +# configured directly, but you can also create different client instances +# with customized settings. You must enable this by adding to build.sbt: +# +# libraryDependencies += ws // or javaWs if using java +# +play.ws { + # Sets HTTP requests not to follow 302 requests + #followRedirects = false + + # Sets the maximum number of open HTTP connections for the client. + #ahc.maxConnectionsTotal = 50 + + ## WS SSL + # https://www.playframework.com/documentation/latest/WsSSL + # ~~~~~ + ssl { + # Configuring HTTPS with Play WS does not require programming. You can + # set up both trustManager and keyManager for mutual authentication, and + # turn on JSSE debugging in development with a reload. + #debug.handshake = true + #trustManager = { + # stores = [ + # { type = "JKS", path = "exampletrust.jks" } + # ] + #} + } +} + +## Cache +# https://www.playframework.com/documentation/latest/JavaCache +# https://www.playframework.com/documentation/latest/ScalaCache +# ~~~~~ +# Play comes with an integrated cache API that can reduce the operational +# overhead of repeated requests. You must enable this by adding to build.sbt: +# +# libraryDependencies += cache +# +play.cache { + # If you want to bind several caches, you can bind the individually + #bindCaches = ["db-cache", "user-cache", "session-cache"] +} + +## Filter Configuration +# https://www.playframework.com/documentation/latest/Filters +# ~~~~~ +# There are a number of built-in filters that can be enabled and configured +# to give Play greater security. +# +play.filters { + + # Enabled filters are run automatically against Play. + # CSRFFilter, AllowedHostFilters, and SecurityHeadersFilters are enabled by default. + enabled = [filters.AccessLogFilter] + + # Disabled filters remove elements from the enabled list. + # disabled += filters.CSRFFilter + + + ## CORS filter configuration + # https://www.playframework.com/documentation/latest/CorsFilter + # ~~~~~ + # CORS is a protocol that allows web applications to make requests from the browser + # across different domains. + # NOTE: You MUST apply the CORS configuration before the CSRF filter, as CSRF has + # dependencies on CORS settings. + cors { + # Filter paths by a whitelist of path prefixes + #pathPrefixes = ["/some/path", ...] + + # The allowed origins. If null, all origins are allowed. + #allowedOrigins = ["http://www.example.com"] + + # The allowed HTTP methods. If null, all methods are allowed + #allowedHttpMethods = ["GET", "POST"] + } + + ## Security headers filter configuration + # https://www.playframework.com/documentation/latest/SecurityHeaders + # ~~~~~ + # Defines security headers that prevent XSS attacks. + # If enabled, then all options are set to the below configuration by default: + headers { + # The X-Frame-Options header. If null, the header is not set. + #frameOptions = "DENY" + + # The X-XSS-Protection header. If null, the header is not set. + #xssProtection = "1; mode=block" + + # The X-Content-Type-Options header. If null, the header is not set. + #contentTypeOptions = "nosniff" + + # The X-Permitted-Cross-Domain-Policies header. If null, the header is not set. + #permittedCrossDomainPolicies = "master-only" + + # The Content-Security-Policy header. If null, the header is not set. + #contentSecurityPolicy = "default-src 'self'" + } + + ## Allowed hosts filter configuration + # https://www.playframework.com/documentation/latest/AllowedHostsFilter + # ~~~~~ + # Play provides a filter that lets you configure which hosts can access your application. + # This is useful to prevent cache poisoning attacks. + hosts { + # Allow requests to example.com, its subdomains, and localhost:9000. + #allowed = [".example.com", "localhost:9000"] + } +} + +play.http.parser.maxMemoryBuffer = 50MB +akka.http.parsing.max-content-length = 50MB + +schema.base_path = "../../schemas/" + + +content.relatedBoards.properties=["channel","board","subject","medium"] + + +# Path for elasticsearch.properties file +search.config.path=/home/learning/platform/search + +# Cache-Manager Configuration +cache.type="redis" + +search.es_conn_info="elasticsearch.knowlg-db.svc.cluster.local:9200" +search.fields.query=["name^100","title^100","lemma^100","code^100","domain","subject","description^10","keywords^100","ageGroup^10","filter^10","theme^10","genre^10","objects^25","contentType^100","language^200","teachingMode^25","skills^10","learningObjective^10","curriculum^100","gradeLevel^100","developer^100","attributions^10","identifier^100","IL_UNIQUE_ID^100","owner^50","board^100","relatedBoards^100","creator^100", "dialcodes^100","text","words","releaseNotes"] +search.fields.date=["lastUpdatedOn","createdOn","versionDate","lastSubmittedOn","lastPublishedOn"] +search.fields.mode_collection=["identifier","name","objectType","contentType","mimeType","size","childNodes","board","subject","medium","gradeLevel","appIcon", "origin", "originData"] +search.batch.size=500 +search.connection.timeout=30 + +platform-api-url="http://localhost:8080/learning-service" +language.map={"Hindi":"hi", "English":"en", "Telugu":"te", "Kannada":"ka", "Tamil":"ta", "Assamese":"as", "Bengali":"bn", "Bodo":"bo", "Gujarati":"gu", "Konkani":"ko", "Malayalam":"ml", "Marathi":"mr", "Nepali":"ne", "Odia":"or", "Punjabi":"pj", "Sanskrit":"sk"} +#Top N Config for Search Telemetry +telemetry.search.topn=5 +telemetry_env=dev +installation.id=ekstep + +# Configuration for default channel ID +channel.default="in.ekstep" +ekstepPlatformApiUserId="search-service" + + +content.tagging.property=["subject","medium"] +search.payload.log_enable=true \ No newline at end of file diff --git a/knowlg-automation/helm_charts/search/search-service_logback.xml b/knowlg-automation/helm_charts/search/search-service_logback.xml new file mode 100644 index 000000000..73529d622 --- /dev/null +++ b/knowlg-automation/helm_charts/search/search-service_logback.xml @@ -0,0 +1,28 @@ + + + + + + + + + + %d %msg%n + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/knowlg-automation/helm_charts/search/templates/deployment.yaml b/knowlg-automation/helm_charts/search/templates/deployment.yaml new file mode 100644 index 000000000..dad5e8642 --- /dev/null +++ b/knowlg-automation/helm_charts/search/templates/deployment.yaml @@ -0,0 +1,75 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Chart.Name }} + namespace: {{ .Values.namespace }} + annotations: + reloader.stakater.com/auto: "true" +spec: + replicas: {{ .Values.replicaCount }} + strategy: + rollingUpdate: + maxSurge: {{ .Values.strategy.maxsurge }} + maxUnavailable: {{ .Values.strategy.maxunavailable }} + selector: + matchLabels: + app: {{ .Chart.Name }} + template: + metadata: + labels: + app: {{ .Chart.Name }} + spec: + volumes: + - name: {{ .Chart.Name }}-config + configMap: + name: {{ .Chart.Name }}-config + - name: {{ .Chart.Name }}-xml-config + configMap: + name: {{ .Chart.Name }}-xml-config + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.repository }}:{{ .Values.image_tag }}" + imagePullPolicy: Always + env: + - name: JAVA_OPTIONS + value: {{ .Values.env.javaoptions | quote }} + - name: _JAVA_OPTIONS + value: -Dlog4j2.formatMsgNoLookups=true + envFrom: + - configMapRef: + name: {{ .Chart.Name }}-config + resources: +{{ toYaml .Values.resources | indent 10 }} + ports: + - containerPort: {{ .Values.network.port }} + {{- if .Values.healthcheck }} + livenessProbe: +{{ toYaml .Values.livenessProbe | indent 10 }} + readinessProbe: +{{ toYaml .Values.readinessProbe | indent 10 }} + {{- end }} + volumeMounts: + - name: {{ .Chart.Name }}-config + mountPath: /home/sunbird/search-service-1.0-SNAPSHOT/config/application.conf + subPath: search-service_application.conf + - name: {{ .Chart.Name }}-xml-config + mountPath: /home/sunbird/search-service-1.0-SNAPSHOT/config/logback.xml + subPath: search-service_logback.xml + +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ .Chart.Name }}-service + namespace: {{ .Values.namespace }} + labels: + app: {{ .Chart.Name }} +spec: + type: {{ .Values.serviceType }} + ports: + - name: http-{{ .Chart.Name }} + protocol: TCP + port: {{ .Values.network.targetport }} + selector: + app: {{ .Chart.Name }} diff --git a/knowlg-automation/helm_charts/search/values.yaml b/knowlg-automation/helm_charts/search/values.yaml new file mode 100644 index 000000000..07c4c6b77 --- /dev/null +++ b/knowlg-automation/helm_charts/search/values.yaml @@ -0,0 +1,29 @@ +### Default variable file for search-service ### + +namespace: knowlg-api + +env: + javaoptions: -Xmx600m + +replicaCount: 1 +repository: docker.io/aimansharief/search-service-modified +image_tag: R5.2.0 +resources: + requests: + cpu: 100m + memory: 100Mi + limits: + cpu: 1 + memory: 1024Mi +network: + port: 9006 + targetport: 9000 +strategy: + type: RollingUpdate + maxsurge: 25% + maxunavailable: 25% +serviceType: ClusterIP + + + + diff --git a/knowlg-automation/helm_charts/taxonomy/values.yaml b/knowlg-automation/helm_charts/taxonomy/values.yaml index a0bdb1a8d..194f61992 100644 --- a/knowlg-automation/helm_charts/taxonomy/values.yaml +++ b/knowlg-automation/helm_charts/taxonomy/values.yaml @@ -6,7 +6,7 @@ env: javaoptions: -Xmx600m replicaCount: 1 -repository: docker.io/aimansharief/taxonomy-service-modified +repository: docker.io/aimansharief/taxonomy-service image_tag: R5.2.0 resources: requests: diff --git a/knowlg-automation/terraform/aks.yaml b/knowlg-automation/terraform/aks.yaml index 2a07d48b4..5758313ce 100644 --- a/knowlg-automation/terraform/aks.yaml +++ b/knowlg-automation/terraform/aks.yaml @@ -1,8 +1,8 @@ ╷ │ Warning: No outputs found │  -│ The state file either has no outputs defined, or all the defined outputs are empty. Please define an -│ output in your configuration with the `output` keyword and run `terraform refresh` for it to become -│ available. If you are using interpolation, please verify the interpolated value is not empty. You -│ can use the `terraform console` command to assist. +│ The state file either has no outputs defined, or all the defined outputs are empty. Please define an output +│ in your configuration with the `output` keyword and run `terraform refresh` for it to become available. If +│ you are using interpolation, please verify the interpolated value is not empty. You can use the `terraform +│ console` command to assist. ╵ diff --git a/knowlg-automation/terraform/local/search-provision.tf b/knowlg-automation/terraform/local/search-provision.tf new file mode 100644 index 000000000..4dbed7228 --- /dev/null +++ b/knowlg-automation/terraform/local/search-provision.tf @@ -0,0 +1,10 @@ +# resource "helm_release" "search" { +# name = "search" +# chart = var.SEARCH_CHART +# namespace = var.SEARCH_NAMESPACE +# create_namespace = true +# dependency_update = true +# depends_on = [kind_cluster.one-click] +# wait_for_jobs = true + +# } \ No newline at end of file diff --git a/knowlg-automation/terraform/local/vars.tf b/knowlg-automation/terraform/local/vars.tf index ac1b36f1d..f1f6e1e14 100644 --- a/knowlg-automation/terraform/local/vars.tf +++ b/knowlg-automation/terraform/local/vars.tf @@ -127,3 +127,14 @@ variable "CONTENT_NAMESPACE" { default = "knowlg-api" } +#SEARCH +variable "SEARCH_CHART" { + description = "Search Instance Running Namespace" + default = "../../helm_charts/search" +} + +variable "SEARCH_NAMESPACE" { + description = "Search Instance Running Namespace" + default = "knowlg-api" +} + From 72ffcbdba249e1cc2bff9a49d623fe9480884f7f Mon Sep 17 00:00:00 2001 From: aimansharief <110388201+aimansharief@users.noreply.github.com> Date: Thu, 6 Apr 2023 20:19:58 +0530 Subject: [PATCH 202/490] Issue KN-767 feat: Added Helm chart and terraform file for learning-service ,added configmaps and updated changes in the helmcharts. --- .../cassandra/templates/deployment.yaml | 4 +- .../helm_charts/cassandra/values.yaml | 2 +- .../content/content-service_application.conf | 4 +- .../content/templates/content-configmap.yaml | 705 ++++++++++++++++++ .../content/templates/deployment.yaml | 4 +- .../helm_charts/content/values.yaml | 23 +- .../templates/metrics/prometheusrule.yaml | 2 +- .../templates/metrics/servicemonitor.yaml | 2 +- .../helm_charts/elasticsearch/values.yaml | 10 +- .../zookeeper/templates/prometheusrule.yaml | 4 +- .../zookeeper/templates/servicemonitor.yaml | 4 +- .../kafka/charts/zookeeper/values.yaml | 4 +- .../kafka/templates/prometheusrule.yaml | 2 +- .../templates/servicemonitor-metrics.yaml | 4 +- .../helm_charts/kafka/values.yaml | 4 +- .../helm_charts/learning/Chart.yaml | 5 + .../learning-service_application.conf | 315 ++++++++ .../learning/templates/deployment.yaml | 64 ++ .../templates/learning-configmap.yaml | 323 ++++++++ .../helm_charts/learning/values.yaml | 54 ++ .../helm_charts/logstash/values.yaml | 2 +- .../neo4j/templates/deployment.yaml | 4 +- .../helm_charts/neo4j/values.yaml | 2 +- .../redis/templates/deployment.yaml | 4 +- .../helm_charts/redis/values.yaml | 2 +- .../search/templates/deployment.yaml | 4 +- .../search/templates/search-configmap.yaml | 359 +++++++++ .../helm_charts/search/values.yaml | 5 +- .../taxonomy-service_application.conf | 6 +- .../taxonomy/templates/deployment.yaml | 4 +- .../templates/taxonomy-configmap.yaml | 446 +++++++++++ .../helm_charts/taxonomy/values.yaml | 11 +- knowlg-automation/terraform/aks.yaml | 7 +- knowlg-automation/terraform/local/vars.tf | 11 + 34 files changed, 2354 insertions(+), 52 deletions(-) create mode 100644 knowlg-automation/helm_charts/content/templates/content-configmap.yaml create mode 100644 knowlg-automation/helm_charts/learning/Chart.yaml create mode 100644 knowlg-automation/helm_charts/learning/learning-service_application.conf create mode 100644 knowlg-automation/helm_charts/learning/templates/deployment.yaml create mode 100644 knowlg-automation/helm_charts/learning/templates/learning-configmap.yaml create mode 100644 knowlg-automation/helm_charts/learning/values.yaml create mode 100644 knowlg-automation/helm_charts/search/templates/search-configmap.yaml create mode 100644 knowlg-automation/helm_charts/taxonomy/templates/taxonomy-configmap.yaml diff --git a/knowlg-automation/helm_charts/cassandra/templates/deployment.yaml b/knowlg-automation/helm_charts/cassandra/templates/deployment.yaml index 4d00c736b..349f869d9 100644 --- a/knowlg-automation/helm_charts/cassandra/templates/deployment.yaml +++ b/knowlg-automation/helm_charts/cassandra/templates/deployment.yaml @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: {{ .Chart.Name }} - namespace: {{ .Values.namespace }} + namespace: {{ .Values.db_namespace }} spec: replicas: {{ .Values.replicaCount }} selector: @@ -23,7 +23,7 @@ apiVersion: v1 kind: Service metadata: name: {{ .Chart.Name }}-db - namespace: {{ .Values.namespace }} + namespace: {{ .Values.db_namespace }} labels: app: {{ .Chart.Name }} spec: diff --git a/knowlg-automation/helm_charts/cassandra/values.yaml b/knowlg-automation/helm_charts/cassandra/values.yaml index ef6b4aad2..b1eb5b3af 100644 --- a/knowlg-automation/helm_charts/cassandra/values.yaml +++ b/knowlg-automation/helm_charts/cassandra/values.yaml @@ -1,4 +1,4 @@ -namespace: knowlg-db +db_namespace: knowlg-db replicaCount: 1 network: port: 9042 \ No newline at end of file diff --git a/knowlg-automation/helm_charts/content/content-service_application.conf b/knowlg-automation/helm_charts/content/content-service_application.conf index 34f18a850..d73b3cc59 100644 --- a/knowlg-automation/helm_charts/content/content-service_application.conf +++ b/knowlg-automation/helm_charts/content/content-service_application.conf @@ -104,7 +104,7 @@ akka { # ~~~~~ # The secret key is used to sign Play's session cookie. # This must be changed for production, but we don't recommend you change it in this file. -play.http.secret.key="jd5ECm/o0BXwQCe8PfZY1NoUkB9HN41QjA80p22MKyRIcP5RW4qHw8sZztCzv87M" +play.http.secret.key="" ## Modules # https://www.playframework.com/documentation/latest/Modules @@ -427,7 +427,7 @@ environment.id: 20000000 graph { dir: "/data/graphDB" ids: ["domain"] - passport.key.base: "jd5ECm/o0BXwQCe8PfZY1NoUkB9HN41QjA80p22MKyRIcP5RW4qHw8sZztCzv87M" + passport.key.base: "" } route { domain: "bolt://neo4j-db.knowlg-db.svc.cluster.local:7687" diff --git a/knowlg-automation/helm_charts/content/templates/content-configmap.yaml b/knowlg-automation/helm_charts/content/templates/content-configmap.yaml new file mode 100644 index 000000000..d48a960fd --- /dev/null +++ b/knowlg-automation/helm_charts/content/templates/content-configmap.yaml @@ -0,0 +1,705 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: content-xml-config + namespace: knowlg-api +data: + content-service_logback.xml: | + + + + + + + + + + %d %msg%n + + + + + + + + + + + + + + + + + + + + + + + +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: content-config + namespace: knowlg-api +data: + content-service_application.conf: | + # This is the main configuration file for the application. + # https://www.playframework.com/documentation/latest/ConfigFile + # ~~~~~ + # Play uses HOCON as its configuration file format. HOCON has a number + # of advantages over other config formats, but there are two things that + # can be used when modifying settings. + # + # You can include other configuration files in this main application.conf file: + #include "extra-config.conf" + # + # You can declare variables and substitute for them: + #mykey = ${some.value} + # + # And if an environment variable exists when there is no other substitution, then + # HOCON will fall back to substituting environment variable: + #mykey = ${JAVA_HOME} + + ## Akka + # https://www.playframework.com/documentation/latest/ScalaAkka#Configuration + # https://www.playframework.com/documentation/latest/JavaAkka#Configuration + # ~~~~~ + # Play uses Akka internally and exposes Akka Streams and actors in Websockets and + # other streaming HTTP responses. + akka { + # "akka.log-config-on-start" is extraordinarly useful because it log the complete + # configuration at INFO level, including defaults and overrides, so it s worth + # putting at the very top. + # + # Put the following in your conf/logback.xml file: + # + # + # + # And then uncomment this line to debug the configuration. + # + #log-config-on-start = true + default-dispatcher { + # This will be used if you have set "executor = "fork-join-executor"" + fork-join-executor { + # Min number of threads to cap factor-based parallelism number to + parallelism-min = 8 + + # The parallelism factor is used to determine thread pool size using the + # following formula: ceil(available processors * factor). Resulting size + # is then bounded by the parallelism-min and parallelism-max values. + parallelism-factor = 32.0 + + # Max number of threads to cap factor-based parallelism number to + parallelism-max = 64 + + # Setting to "FIFO" to use queue like peeking mode which "poll" or "LIFO" to use stack + # like peeking mode which "pop". + task-peeking-mode = "FIFO" + } + } + actors-dispatcher { + type = "Dispatcher" + executor = "fork-join-executor" + fork-join-executor { + parallelism-min = 8 + parallelism-factor = 32.0 + parallelism-max = 64 + } + # Throughput for default Dispatcher, set to 1 for as fair as possible + throughput = 1 + } + actor { + deployment { + /contentActor + { + router = smallest-mailbox-pool + nr-of-instances = 10 + dispatcher = actors-dispatcher + } + /channelActor + { + router = smallest-mailbox-pool + nr-of-instances = 10 + dispatcher = actors-dispatcher + } + /collectionActor + { + router = smallest-mailbox-pool + nr-of-instances = 10 + dispatcher = actors-dispatcher + } + /healthActor + { + router = smallest-mailbox-pool + nr-of-instances = 5 + dispatcher = actors-dispatcher + } + /licenseActor + { + router = smallest-mailbox-pool + nr-of-instances = 2 + dispatcher = actors-dispatcher + } + } + } + } + + ## Secret key + # http://www.playframework.com/documentation/latest/ApplicationSecret + # ~~~~~ + # The secret key is used to sign Play's session cookie. + # This must be changed for production, but we don't recommend you change it in this file. + play.http.secret.key="jd5ECm/o0BXwQCe8PfZY1NoUkB9HN41QjA80p22MKyRIcP5RW4qHw8sZztCzv87M" + + ## Modules + # https://www.playframework.com/documentation/latest/Modules + # ~~~~~ + # Control which modules are loaded when Play starts. Note that modules are + # the replacement for "GlobalSettings", which are deprecated in 2.5.x. + # Please see https://www.playframework.com/documentation/latest/GlobalSettings + # for more information. + # + # You can also extend Play functionality by using one of the publically available + # Play modules: https://playframework.com/documentation/latest/ModuleDirectory + play.modules { + # By default, Play will load any class called Module that is defined + # in the root package (the "app" directory), or you can define them + # explicitly below. + # If there are any built-in modules that you want to enable, you can list them here. + enabled += modules.ContentModule + + # If there are any built-in modules that you want to disable, you can list them here. + #disabled += "" + } + + ## IDE + # https://www.playframework.com/documentation/latest/IDE + # ~~~~~ + # Depending on your IDE, you can add a hyperlink for errors that will jump you + # directly to the code location in the IDE in dev mode. The following line makes + # use of the IntelliJ IDEA REST interface: + #play.editor="http://localhost:63342/api/file/?file=%s&line=%s" + + ## Internationalisation + # https://www.playframework.com/documentation/latest/JavaI18N + # https://www.playframework.com/documentation/latest/ScalaI18N + # ~~~~~ + # Play comes with its own i18n settings, which allow the user's preferred language + # to map through to internal messages, or allow the language to be stored in a cookie. + play.i18n { + # The application languages + langs = [ "en" ] + + # Whether the language cookie should be secure or not + #langCookieSecure = true + + # Whether the HTTP only attribute of the cookie should be set to true + #langCookieHttpOnly = true + } + + ## Play HTTP settings + # ~~~~~ + play.http { + ## Router + # https://www.playframework.com/documentation/latest/JavaRouting + # https://www.playframework.com/documentation/latest/ScalaRouting + # ~~~~~ + # Define the Router object to use for this application. + # This router will be looked up first when the application is starting up, + # so make sure this is the entry point. + # Furthermore, it's assumed your route file is named properly. + # So for an application router like `my.application.Router`, + # you may need to define a router file `conf/my.application.routes`. + # Default to Routes in the root package (aka "apps" folder) (and conf/routes) + #router = my.application.Router + + ## Action Creator + # https://www.playframework.com/documentation/latest/JavaActionCreator + # ~~~~~ + #actionCreator = null + + ## ErrorHandler + # https://www.playframework.com/documentation/latest/JavaRouting + # https://www.playframework.com/documentation/latest/ScalaRouting + # ~~~~~ + # If null, will attempt to load a class called ErrorHandler in the root package, + #errorHandler = null + + ## Session & Flash + # https://www.playframework.com/documentation/latest/JavaSessionFlash + # https://www.playframework.com/documentation/latest/ScalaSessionFlash + # ~~~~~ + session { + # Sets the cookie to be sent only over HTTPS. + #secure = true + + # Sets the cookie to be accessed only by the server. + #httpOnly = true + + # Sets the max-age field of the cookie to 5 minutes. + # NOTE: this only sets when the browser will discard the cookie. Play will consider any + # cookie value with a valid signature to be a valid session forever. To implement a server side session timeout, + # you need to put a timestamp in the session and check it at regular intervals to possibly expire it. + #maxAge = 300 + + # Sets the domain on the session cookie. + #domain = "example.com" + } + + flash { + # Sets the cookie to be sent only over HTTPS. + #secure = true + + # Sets the cookie to be accessed only by the server. + #httpOnly = true + } + } + + play.server.http.idleTimeout = 60s + play.http.parser.maxDiskBuffer = 100MB + parsers.anyContent.maxLength = 100MB + + ## Netty Provider + # https://www.playframework.com/documentation/latest/SettingsNetty + # ~~~~~ + play.server.netty { + # Whether the Netty wire should be logged + log.wire = true + + # If you run Play on Linux, you can use Netty's native socket transport + # for higher performance with less garbage. + transport = "native" + } + + ## WS (HTTP Client) + # https://www.playframework.com/documentation/latest/ScalaWS#Configuring-WS + # ~~~~~ + # The HTTP client primarily used for REST APIs. The default client can be + # configured directly, but you can also create different client instances + # with customized settings. You must enable this by adding to build.sbt: + # + # libraryDependencies += ws // or javaWs if using java + # + play.ws { + # Sets HTTP requests not to follow 302 requests + #followRedirects = false + + # Sets the maximum number of open HTTP connections for the client. + #ahc.maxConnectionsTotal = 50 + + ## WS SSL + # https://www.playframework.com/documentation/latest/WsSSL + # ~~~~~ + ssl { + # Configuring HTTPS with Play WS does not require programming. You can + # set up both trustManager and keyManager for mutual authentication, and + # turn on JSSE debugging in development with a reload. + #debug.handshake = true + #trustManager = { + # stores = [ + # { type = "JKS", path = "exampletrust.jks" } + # ] + #} + } + } + + ## Cache + # https://www.playframework.com/documentation/latest/JavaCache + # https://www.playframework.com/documentation/latest/ScalaCache + # ~~~~~ + # Play comes with an integrated cache API that can reduce the operational + # overhead of repeated requests. You must enable this by adding to build.sbt: + # + # libraryDependencies += cache + # + play.cache { + # If you want to bind several caches, you can bind the individually + #bindCaches = ["db-cache", "user-cache", "session-cache"] + } + + ## Filter Configuration + # https://www.playframework.com/documentation/latest/Filters + # ~~~~~ + # There are a number of built-in filters that can be enabled and configured + # to give Play greater security. + # + play.filters { + + # Enabled filters are run automatically against Play. + # CSRFFilter, AllowedHostFilters, and SecurityHeadersFilters are enabled by default. + enabled = [filters.AccessLogFilter] + + # Disabled filters remove elements from the enabled list. + # disabled += filters.CSRFFilter + + + ## CORS filter configuration + # https://www.playframework.com/documentation/latest/CorsFilter + # ~~~~~ + # CORS is a protocol that allows web applications to make requests from the browser + # across different domains. + # NOTE: You MUST apply the CORS configuration before the CSRF filter, as CSRF has + # dependencies on CORS settings. + cors { + # Filter paths by a whitelist of path prefixes + #pathPrefixes = ["/some/path", ...] + + # The allowed origins. If null, all origins are allowed. + #allowedOrigins = ["http://www.example.com"] + + # The allowed HTTP methods. If null, all methods are allowed + #allowedHttpMethods = ["GET", "POST"] + } + + ## Security headers filter configuration + # https://www.playframework.com/documentation/latest/SecurityHeaders + # ~~~~~ + # Defines security headers that prevent XSS attacks. + # If enabled, then all options are set to the below configuration by default: + headers { + # The X-Frame-Options header. If null, the header is not set. + #frameOptions = "DENY" + + # The X-XSS-Protection header. If null, the header is not set. + #xssProtection = "1; mode=block" + + # The X-Content-Type-Options header. If null, the header is not set. + #contentTypeOptions = "nosniff" + + # The X-Permitted-Cross-Domain-Policies header. If null, the header is not set. + #permittedCrossDomainPolicies = "master-only" + + # The Content-Security-Policy header. If null, the header is not set. + #contentSecurityPolicy = "default-src 'self'" + } + + ## Allowed hosts filter configuration + # https://www.playframework.com/documentation/latest/AllowedHostsFilter + # ~~~~~ + # Play provides a filter that lets you configure which hosts can access your application. + # This is useful to prevent cache poisoning attacks. + hosts { + # Allow requests to example.com, its subdomains, and localhost:9000. + #allowed = [".example.com", "localhost:9000"] + } + } + + play.http.parser.maxMemoryBuffer = 50MB + akka.http.parsing.max-content-length = 50MB + schema.base_path="https://sunbirddevbbpublic.blob.core.windows.net/sunbird-content-staging-knowlg/schemas/local" + + # Cassandra Configuration + cassandra { + lp { + connection: "{{ .Values.cassandra_connection }}" + } + lpa { + connection: "{{ .Values.cassandra_connection }}" + } + } + + # Consistency Level for Multi Node Cassandra cluster + cassandra.lp.consistency.level=QUORUM + + collection { + keyspace: "{{ .Values.collection_keyspace }}" + cache.enable: true + image.migration.enabled: true + } + + content { + keyspace: "{{ .Values.content_keyspace }}" + cache { + ttl: 86400 + enable: true + } + hierarchy { + removed_props_for_leafNodes: ["collections", "children", "usedByContent", "item_sets", "methods", "libraries", "editorState"] + } + # Added for supporting backward compatibility - remove in release-2.7.0 + tagging { + backward_enable: true + property: "subject,medium" + } + h5p { + library { + path: "https://sunbirdpublic.blob.core.windows.net/installation/h5p-standalone-1.3.4.zip" + } + } + copy { + invalid_statusList: ["Flagged","FlaggedDraft","FraggedReview","Retired", "Processing"] + origin_data: ["name", "author", "license", "organisation"] + props_to_remove: ["downloadUrl", "artifactUrl", "variants", "createdOn", "collections", "children", "lastUpdatedOn", "SYS_INTERNAL_LAST_UPDATED_ON", "versionKey", "s3Key", "status", "pkgVersion", "toc_url", "mimeTypesCount", "contentTypesCount", "leafNodesCount", "childNodes", "prevState", "lastPublishedOn", "flagReasons", "compatibilityLevel", "size", "publishChecklist", "publishComment", "LastPublishedBy", "rejectReasons", "rejectComment", "gradeLevel", "subject", "medium", "board", "topic", "purpose", "subtopic", "contentCredits", "owner", "collaborators", "creators", "contributors", "badgeAssertions", "dialcodes", "concepts", "keywords", "reservedDialcodes", "dialcodeRequired", "leafNodes", "sYS_INTERNAL_LAST_UPDATED_ON", "prevStatus", "lastPublishedBy", "streamingUrl", "boardIds", "gradeLevelIds", "subjectIds", "mediumIds", "topicsIds", "targetFWIds", "targetBoardIds", "targetGradeLevelIds", "targetSubjectIds", "targetMediumIds", "targetTopicIds", "se_boards", "se_subjects", "se_mediums", "se_gradeLevels", "se_topics", "se_FWIds", "se_boardIds", "se_subjectIds", "se_mediumIds", "se_gradeLevelIds", "se_topicIds"] + } + # Need to depreacte + media { + base.url: "https://dev.knowlg.sunbird.org" + } + } + + # Redis Configuration + redis { + host: "{{ .Values.redis_connection }}" + port: 6379 + maxConnections: 128 + } + + + #--Maximum Content Package File Size Limit in Bytes (50 MB) + MAX_CONTENT_PACKAGE_FILE_SIZE_LIMIT=157286400 + + #--Maximum Asset File Size Limit in Bytes (50 MB) + MAX_ASSET_FILE_SIZE_LIMIT=157286400 + + #--No of Retry While File Download Fails + RETRY_ASSET_DOWNLOAD_COUNT=1 + + #Current environment - need this property post cloud-store implementation + cloud_storage { + env: {{ .Values.cloud_storage_env }} + content.folder: "{{ .Values.cloud_storage_content_folder }}" + asset.folder: "{{ .Values.cloud_storage_asset_folder }}" + artefact.folder: "{{ .Values.cloud_storage_artefact_folder }}" + bundle.folder: "{{ .Values.cloud_storage_bundle_folder }}" + media.folder: "{{ .Values.cloud_storage_media_folder }}" + ecar.folder: "{{ .Values.cloud_storage_ecar_folder }}" + upload.url.ttl: "{{ .Values.cloud_storage_upload_url_ttl }}" + } + + # Configuration + akka.request_timeout: 30 + environment.id: 20000000 + graph { + dir: "/data/graphDB" + ids: ["domain"] + passport.key.base: "jd5ECm/o0BXwQCe8PfZY1NoUkB9HN41QjA80p22MKyRIcP5RW4qHw8sZztCzv87M" + } + route { + domain: "bolt://{{ .Values.neo4j_domain_connection }}" + all: "bolt://{{ .Values.neo4j_all_connection }}" + bolt { + write { + domain: "bolt://{{ .Values.neo4j_domain_connection }}" + all: "bolt://{{ .Values.neo4j_all_connection }}" + }, + read { + domain: "bolt://{{ .Values.neo4j_domain_connection }}" + all: "bolt://{{ .Values.neo4j_all_connection }}" + } + } + } + + shard.id: 1 + platform { + auth.check.enabled: false + cache.ttl: 3600000 + language.codes: ["as","bn","en","gu","hi","hoc","jun","ka","mai","mr","unx","or","san","sat","ta","te","urd", "pj"] + } + + #Top N Config for Search Telemetry + telemetry_env: staging + + installation.id: ekstep + + channel { + default: "in.ekstep" + fetch.suggested_frameworks: true + } + + languageCode { + assamese : "as" + bengali : "bn" + english : "en" + gujarati : "gu" + hindi : "hi" + kannada : "ka" + marathi : "mr" + odia : "or" + tamil : "ta" + telugu : "te" + } + # Need to depreacte + composite { + search { + url : "http://search-service.knowlg.svc.cluster.local:9000/v3/search" + } + } + + cloud_storage_type: "azure" + cloud_storage_key: "" + cloud_storage_secret: "" + cloud_storage_endpoint: "" + cloud_storage_container: "" + + # Google Drive APIKEY + learning_content_drive_apiKey = "" + + #Youtube Standard Licence Validation + learning.content.youtube.application.name="" + learning_content_youtube_apikey="" + youtube.license.regex.pattern=["\\?vi?=([^&]*)", "watch\\?.*v=([^&]*)", "(?:embed|vi?)/([^/?]*)","^([A-Za-z0-9\\-\\_]*)"] + learning.valid_license=["creativeCommon"] + + kafka { + urls : "{{ .Values.kafka_connection }}" + topic.send.enable : true + topics.instruction : "" + publish.request.topic : "" + } + + # Need to depreacte + # DIAL Link Config + dial_service { + api { + base_url : "http://dial-service.knowlg.svc.cluster.local:9000" + auth_key : "" + search : "/dialcode/v3/search" + generate : "/dialcode/v3/generate" + } + } + # Need to depreacte + reserve_dialcode { + mimeType : ["application/vnd.ekstep.content-collection"] + max_count : 250 + } + + content.link_dialcode.validation=true + content.link_dialcode.max_limit=10 + + # Content Import API Config + import { + request_size_limit : 1000 + output_topic_name : "sunbirdstaging.knowlg.auto.creation.job.request" + required_props : ["name","code","mimeType","primaryCategory","artifactUrl","framework"] + remove_props : ["downloadUrl","variants","previewUrl","streamingUrl","itemSets","level1Name","level1Concept","level2Name","level2Concept","level3Name","level3Concept","me_totalPlaySessionCount","me_totalTimeSpentInSec","me_totalSessionsCount","me_totalTimespent","me_totalInteractions","me_creationSessions","me_creationTimespent","me_averageInteractionsPerMin","me_averageSessionsPerDevice","me_totalDevices","me_averageTimespentPerSession","me_averageRating","me_totalDownloads","me_totalSideloads","me_totalRatings","me_totalComments","me_totalDialcode","me_totalDialcodeLinkedToContent","me_totalDialcodeAttached","me_hierarchyLevel","origin","originData","contentPolicyCheck","questions"] + } + + # Need to depreacte + contentTypeToPrimaryCategory { + ClassroomTeachingVideo: "Explanation Content" + ConceptMap: "Learning Resource" + Course: "Course" + CuriosityQuestionSet: "Practice Question Set" + eTextBook: "eTextbook" + ExperientialResource: "Learning Resource" + ExplanationResource: "Explanation Content" + ExplanationVideo: "Explanation Content" + FocusSpot: "Teacher Resource" + LearningOutcomeDefinition: "Teacher Resource" + MarkingSchemeRubric: "Teacher Resource" + PedagogyFlow: "Teacher Resource" + PracticeQuestionSet: "Practice Question Set" + PracticeResource: "Practice Question Set" + SelfAssess: "Course Assessment" + TeachingMethod: "Teacher Resource" + TextBook: "Digital Textbook" + Collection: "Content Playlist" + ExplanationReadingMaterial: "Learning Resource" + LearningActivity: "Learning Resource" + LessonPlan: "Content Playlist" + LessonPlanResource: "Teacher Resource" + PreviousBoardExamPapers: "Learning Resource" + TVLesson: "Explanation Content" + OnboardingResource: "Learning Resource" + ReadingMaterial: "Learning Resource" + Template: "Template" + Asset: "Asset" + Plugin: "Plugin" + LessonPlanUnit: "Lesson Plan Unit" + CourseUnit: "Course Unit" + TextBookUnit: "Textbook Unit" + Asset: "Certificate Template" + } + # Need to depreacte + resourceTypeToPrimaryCategory { + Learn: "Learning Resource" + Read: "Learning Resource" + Practice: "Learning Resource" + Teach: "Teacher Resource" + Test: "Learning Resource" + Experiment: "Learning Resource" + LessonPlan: "Teacher Resource" + } + # Need to depreacte + mimeTypeToPrimaryCategory { + "application/vnd.ekstep.h5p-archive": ["Learning Resource"] + "application/vnd.ekstep.html-archive": ["Learning Resource"] + "application/vnd.android.package-archive": ["Learning Resource"] + "video/webm": ["Explanation Content"] + "video/x-youtube": ["Explanation Content"] + "video/mp4": ["Explanation Content"] + "application/pdf": ["Learning Resource", "Teacher Resource"] + "application/epub": ["Learning Resource", "Teacher Resource"] + "application/vnd.ekstep.ecml-archive": ["Learning Resource", "Teacher Resource"] + "text/x-url": ["Learnin Resource", "Teacher Resource"] + } + + objectcategorydefinition.keyspace="dev_category_store" + + # Need to depreacte + #Default objectCategory mapping for channel + channel { + content{ + primarycategories=["Course Assessment", "eTextbook", "Explanation Content", "Learning Resource", "Practice Question Set", "Teacher Resource", "Exam Question"] + additionalcategories= ["Classroom Teaching Video", "Concept Map", "Curiosity Question Set", "Experiential Resource", "Explanation Video", "Focus Spot", "Learning Outcome Definition", "Lesson Plan", "Marking Scheme Rubric", "Pedagogy Flow", "Previous Board Exam Papers", "TV Lesson", "Textbook"] + } + collection { + primarycategories=["Content Playlist", "Course", "Digital Textbook", "Question paper"] + additionalcategories=["Textbook", "Lesson Plan"] + } + asset { + primarycategories=["Asset", "CertAsset", "Certificate Template"] + additionalcategories=[] + } + } + master.category.validation.enabled="Yes" + + # Need to depreacte + #Collection CSV + sunbird_dialcode_search_api="http://dial-service.knowlg.svc.cluster.local:9000/dialcode/v3/list" + framework_read_api_url="http://localhost:8080/learning-service/framework/v3/read" + sunbird_link_dial_code_api="http://content-service.knowlg.svc.cluster.local:9000/collection/v4/dialcode/link" + + # Need to depreacte + collection { + csv { + maxRows = 6500 + allowedContentTypes = ["TextBook","Collection","Course"] + maxFirstLevelUnits=30 + ttl = 86400 + maxUnitFieldLength=120 + maxDescFieldLength=1500 + contentTypeToUnitType = {"TextBook": "TextBookUnit", "Course": "CourseUnit", "Collection":"Collection"} + headers { + folderIdentifier = ["Folder Identifier"] + hierarchy = ["Level 1 Folder","Level 2 Folder","Level 3 Folder","Level 4 Folder"] + QR = ["QR Code Required?","QR Code"] + topics = ["Mapped Topics"] + collectionName = ["Collection Name"] + linkedContents = ["Linked Content 1","Linked Content 2","Linked Content 3","Linked Content 4","Linked Content 5","Linked Content 6","Linked Content 7","Linked Content 8","Linked Content 9","Linked Content 10","Linked Content 11","Linked Content 12","Linked Content 13","Linked Content 14","Linked Content 15","Linked Content 16","Linked Content 17","Linked Content 18","Linked Content 19","Linked Content 20","Linked Content 21","Linked Content 22","Linked Content 23","Linked Content 24","Linked Content 25","Linked Content 26","Linked Content 27","Linked Content 28","Linked Content 29","Linked Content 30"] + output = ["Collection Name","Folder Identifier","Level 1 Folder","Level 2 Folder","Level 3 Folder","Level 4 Folder","Description","Mapped Topics","Keywords","QR Code Required?","QR Code","Linked Content 1","Linked Content 2","Linked Content 3","Linked Content 4","Linked Content 5","Linked Content 6","Linked Content 7","Linked Content 8","Linked Content 9","Linked Content 10","Linked Content 11","Linked Content 12","Linked Content 13","Linked Content 14","Linked Content 15","Linked Content 16","Linked Content 17","Linked Content 18","Linked Content 19","Linked Content 20","Linked Content 21","Linked Content 22","Linked Content 23","Linked Content 24","Linked Content 25","Linked Content 26","Linked Content 27","Linked Content 28","Linked Content 29","Linked Content 30"] + sequence { + create = {"Level 1 Folder":0,"Level 2 Folder":1,"Level 3 Folder":2,"Level 4 Folder":3,"Description":4} + update = {"Collection Name":0,"Folder Identifier":1,"Level 1 Folder":2,"Level 2 Folder":3,"Level 3 Folder":4,"Level 4 Folder":5,"Description":6,"Mapped Topics":7,"Keywords":8,"QR Code Required?":9,"QR Code":10,"Linked Content 1":11,"Linked Content 2":12,"Linked Content 3":13,"Linked Content 4":14,"Linked Content 5":15,"Linked Content 6":16,"Linked Content 7":17,"Linked Content 8":18,"Linked Content 9":19,"Linked Content 10":20,"Linked Content 11":21,"Linked Content 12":22,"Linked Content 13":23,"Linked Content 14":24,"Linked Content 15":25,"Linked Content 16":26,"Linked Content 17":27,"Linked Content 18":28,"Linked Content 19":29,"Linked Content 20":30,"Linked Content 21":31,"Linked Content 22":32,"Linked Content 23":33,"Linked Content 24":34,"Linked Content 25":35,"Linked Content 26":36,"Linked Content 27":37,"Linked Content 28":38,"Linked Content 29":39,"Linked Content 30":40} + linkedContents = {"Linked Content 1":0,"Linked Content 2":1,"Linked Content 3":2,"Linked Content 4":3,"Linked Content 5":4,"Linked Content 6":5,"Linked Content 7":6,"Linked Content 8":7,"Linked Content 9":8,"Linked Content 10":9,"Linked Content 11":10,"Linked Content 12":11,"Linked Content 13":12,"Linked Content 14":13,"Linked Content 15":14,"Linked Content 16":15,"Linked Content 17":16,"Linked Content 18":17,"Linked Content 19":18,"Linked Content 20":19,"Linked Content 21":20,"Linked Content 22":21,"Linked Content 23":22,"Linked Content 24":23,"Linked Content 25":24,"Linked Content 26":25,"Linked Content 27":26,"Linked Content 28":27,"Linked Content 29":28,"Linked Content 30":29} + } + } + mandatory { + create = ["Level 1 Folder"] + update = ["Collection Name","Folder Identifier"] + } + } + } + + plugin.media.base.url="https://dev.knowlg.sunbird.org" + + cloudstorage { + metadata.replace_absolute_path=true + relative_path_prefix="{{ .Values.cloudstorage_relative_path_prefix_content }}" + metadata.list="{{ .Values.cloudstorage_metadata_list }}" + read_base_path="{{ .Values.cloudstorage_read_base_path }}" + write_base_path="{{ .Values.cloudstorage_write_base_path }}" + } diff --git a/knowlg-automation/helm_charts/content/templates/deployment.yaml b/knowlg-automation/helm_charts/content/templates/deployment.yaml index b9763875c..e9bc81077 100644 --- a/knowlg-automation/helm_charts/content/templates/deployment.yaml +++ b/knowlg-automation/helm_charts/content/templates/deployment.yaml @@ -3,7 +3,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: {{ .Chart.Name }} - namespace: {{ .Values.namespace }} + namespace: {{ .Values.api_namespace }} annotations: reloader.stakater.com/auto: "true" spec: @@ -62,7 +62,7 @@ apiVersion: v1 kind: Service metadata: name: {{ .Chart.Name }}-service - namespace: {{ .Values.namespace }} + namespace: {{ .Values.api_namespace }} labels: app: {{ .Chart.Name }} spec: diff --git a/knowlg-automation/helm_charts/content/values.yaml b/knowlg-automation/helm_charts/content/values.yaml index 6064b56a6..6b7f5c0ab 100644 --- a/knowlg-automation/helm_charts/content/values.yaml +++ b/knowlg-automation/helm_charts/content/values.yaml @@ -1,4 +1,4 @@ -namespace: knowlg-api +api_namespace: knowlg-api env: javaoptions: -Xmx600m @@ -20,4 +20,23 @@ strategy: type: RollingUpdate maxsurge: 25% maxunavailable: 25% -serviceType: ClusterIP \ No newline at end of file +serviceType: ClusterIP +cassandra_connection: cassandra-db.knowlg-db.svc.cluster.local:9042 +redis_connection: redis-db.knowlg-db.svc.cluster.local +neo4j_domain_connection: neo4j-db.knowlg-db.svc.cluster.local:7687 +neo4j_all_connection: neo4j-db.knowlg-db.svc.cluster.local:8687 +kafka_connection: kafka.knowlg-db.svc.cluster.local:9092 +collection_keyspace: dev_hierarchy_store +content_keyspace: dev_content_store +cloud_storage_env: staging +cloud_storage_content_folder: "content" +cloud_storage_asset_folder: "assets" +cloud_storage_artefact_folder: "artifact" +cloud_storage_bundle_folder: "bundle" +cloud_storage_media_folder: "media" +cloud_storage_ecar_folder: "ecar_files" +cloud_storage_upload_url_ttl: "54000" +cloudstorage_relative_path_prefix_content: CONTENT_STORAGE_BASE_PATH +cloudstorage_metadata_list: ["appIcon", "artifactUrl", "posterImage", "previewUrl", "thumbnail", "assetsMap", "certTemplate", "itemSetPreviewUrl", "grayScaleAppIcon", "sourceURL", "variants", "downloadUrl", "streamingUrl", "toc_url", "data", "question", "solutions", "editorState", "media", "pdfUrl", "transcripts"] +cloudstorage_read_base_path: "https://sunbirddevbbpublic.blob.core.windows.net" +cloudstorage_write_base_path: ["https://sunbirdstagingpublic.blob.core.windows.net", "https://sunbirddevbbpublic.blob.core.windows.net"] \ No newline at end of file diff --git a/knowlg-automation/helm_charts/elasticsearch/templates/metrics/prometheusrule.yaml b/knowlg-automation/helm_charts/elasticsearch/templates/metrics/prometheusrule.yaml index c235dab4f..7c25edd7c 100644 --- a/knowlg-automation/helm_charts/elasticsearch/templates/metrics/prometheusrule.yaml +++ b/knowlg-automation/helm_charts/elasticsearch/templates/metrics/prometheusrule.yaml @@ -3,7 +3,7 @@ apiVersion: monitoring.coreos.com/v1 kind: PrometheusRule metadata: name: {{ include "common.names.fullname" . }} - namespace: {{ default .Release.Namespace .Values.metrics.prometheusRule.namespace | quote }} + namespace: {{ default .Release.Namespace .Values.metrics.prometheusRule.db_namespace | quote }} labels: {{- include "common.labels.standard" . | nindent 4 }} app.kubernetes.io/component: metrics {{- if .Values.commonLabels }} diff --git a/knowlg-automation/helm_charts/elasticsearch/templates/metrics/servicemonitor.yaml b/knowlg-automation/helm_charts/elasticsearch/templates/metrics/servicemonitor.yaml index 7d0f394ee..e64de5e34 100644 --- a/knowlg-automation/helm_charts/elasticsearch/templates/metrics/servicemonitor.yaml +++ b/knowlg-automation/helm_charts/elasticsearch/templates/metrics/servicemonitor.yaml @@ -3,7 +3,7 @@ apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: name: {{ include "elasticsearch.metrics.fullname" . }} - namespace: {{ default (include "common.names.namespace" .) .Values.metrics.serviceMonitor.namespace | quote }} + namespace: {{ default (include "common.names.namespace" .) .Values.metrics.serviceMonitor.db_namespace | quote }} labels: {{- include "common.labels.standard" . | nindent 4 }} app.kubernetes.io/component: metrics {{- if .Values.commonLabels }} diff --git a/knowlg-automation/helm_charts/elasticsearch/values.yaml b/knowlg-automation/helm_charts/elasticsearch/values.yaml index 02d04d174..190288d45 100644 --- a/knowlg-automation/helm_charts/elasticsearch/values.yaml +++ b/knowlg-automation/helm_charts/elasticsearch/values.yaml @@ -2022,11 +2022,11 @@ metrics: ## @param metrics.serviceMonitor.enabled Create ServiceMonitor Resource for scraping metrics using PrometheusOperator ## enabled: false - ## @param metrics.serviceMonitor.namespace Namespace which Prometheus is running in + ## @param metrics.serviceMonitor.db-namespace db-namespace which Prometheus is running in ## e.g: - ## namespace: monitoring + ## db-namespace: monitoring ## - namespace: knowlg-db + db_namespace: knowlg-db ## @param metrics.serviceMonitor.jobLabel The name of the label on the target service to use as the job name in prometheus. ## jobLabel: "" @@ -2065,9 +2065,9 @@ metrics: ## @param metrics.prometheusRule.enabled Creates a Prometheus Operator PrometheusRule (also requires `metrics.enabled` to be `true` and `metrics.prometheusRule.rules`) ## enabled: false - ## @param metrics.prometheusRule.namespace Namespace for the PrometheusRule Resource (defaults to the Release Namespace) + ## @param metrics.prometheusRule.db-namespace db-namespace for the PrometheusRule Resource (defaults to the Release db-namespace) ## - namespace: knowlg-db + db_namespace: knowlg-db ## @param metrics.prometheusRule.additionalLabels Additional labels that can be used so PrometheusRule will be discovered by Prometheus ## additionalLabels: {} diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/prometheusrule.yaml b/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/prometheusrule.yaml index 87dcd3565..d76682339 100644 --- a/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/prometheusrule.yaml +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/prometheusrule.yaml @@ -3,8 +3,8 @@ apiVersion: monitoring.coreos.com/v1 kind: PrometheusRule metadata: name: {{ include "common.names.fullname" . }} - {{- if .Values.metrics.prometheusRule.namespace }} - namespace: {{ .Values.metrics.prometheusRule.namespace }} + {{- if .Values.metrics.prometheusRule.db_namespace }} + namespace: {{ .Values.metrics.prometheusRule.db_namespace }} {{- else }} namespace: {{ .Release.Namespace }} {{- end }} diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/servicemonitor.yaml b/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/servicemonitor.yaml index 2c8af3350..e9ef58507 100644 --- a/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/servicemonitor.yaml +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/templates/servicemonitor.yaml @@ -3,8 +3,8 @@ apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: name: {{ template "common.names.fullname" . }} - {{- if .Values.metrics.serviceMonitor.namespace }} - namespace: {{ .Values.metrics.serviceMonitor.namespace }} + {{- if .Values.metrics.serviceMonitor.db_namespace }} + namespace: {{ .Values.metrics.serviceMonitor.db_namespace }} {{- else }} namespace: {{ .Release.Namespace }} {{- end }} diff --git a/knowlg-automation/helm_charts/kafka/charts/zookeeper/values.yaml b/knowlg-automation/helm_charts/kafka/charts/zookeeper/values.yaml index 934383269..50f7587a6 100644 --- a/knowlg-automation/helm_charts/kafka/charts/zookeeper/values.yaml +++ b/knowlg-automation/helm_charts/kafka/charts/zookeeper/values.yaml @@ -722,7 +722,7 @@ metrics: enabled: false ## @param metrics.serviceMonitor.namespace Namespace for the ServiceMonitor Resource (defaults to the Release Namespace) ## - namespace: "" + db_namespace: "knowlg-db" ## @param metrics.serviceMonitor.interval Interval at which metrics should be scraped. ## ref: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#endpoint ## @@ -758,7 +758,7 @@ metrics: enabled: false ## @param metrics.prometheusRule.namespace Namespace for the PrometheusRule Resource (defaults to the Release Namespace) ## - namespace: "" + db_namespace: "knowlg-db" ## @param metrics.prometheusRule.additionalLabels Additional labels that can be used so PrometheusRule will be discovered by Prometheus ## additionalLabels: {} diff --git a/knowlg-automation/helm_charts/kafka/templates/prometheusrule.yaml b/knowlg-automation/helm_charts/kafka/templates/prometheusrule.yaml index bce728a37..f3744d517 100644 --- a/knowlg-automation/helm_charts/kafka/templates/prometheusrule.yaml +++ b/knowlg-automation/helm_charts/kafka/templates/prometheusrule.yaml @@ -3,7 +3,7 @@ apiVersion: monitoring.coreos.com/v1 kind: PrometheusRule metadata: name: {{ include "common.names.fullname" . }} - namespace: {{ default (include "common.names.namespace" .) .Values.metrics.prometheusRule.namespace }} + namespace: {{ default (include "common.names.namespace" .) .Values.metrics.prometheusRule.db_namespace }} labels: {{- include "common.labels.standard" . | nindent 4 }} app.kubernetes.io/component: metrics {{- if .Values.commonLabels }} diff --git a/knowlg-automation/helm_charts/kafka/templates/servicemonitor-metrics.yaml b/knowlg-automation/helm_charts/kafka/templates/servicemonitor-metrics.yaml index 343194667..edca22304 100644 --- a/knowlg-automation/helm_charts/kafka/templates/servicemonitor-metrics.yaml +++ b/knowlg-automation/helm_charts/kafka/templates/servicemonitor-metrics.yaml @@ -3,8 +3,8 @@ apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: name: {{ printf "%s-metrics" (include "common.names.fullname" .) }} - {{- if .Values.metrics.serviceMonitor.namespace }} - namespace: {{ .Values.metrics.serviceMonitor.namespace }} + {{- if .Values.metrics.serviceMonitor.db_namespace }} + namespace: {{ .Values.metrics.serviceMonitor.db_namespace }} {{- else }} namespace: {{ .Release.Namespace | quote }} {{- end }} diff --git a/knowlg-automation/helm_charts/kafka/values.yaml b/knowlg-automation/helm_charts/kafka/values.yaml index cbf1e8fb6..244d96423 100644 --- a/knowlg-automation/helm_charts/kafka/values.yaml +++ b/knowlg-automation/helm_charts/kafka/values.yaml @@ -1426,7 +1426,7 @@ metrics: enabled: false ## @param metrics.serviceMonitor.namespace Namespace in which Prometheus is running ## - namespace: "" + db_namespace: "knowlg-db" ## @param metrics.serviceMonitor.interval Interval at which metrics should be scraped ## ref: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#endpoint ## @@ -1461,7 +1461,7 @@ metrics: enabled: false ## @param metrics.prometheusRule.namespace Namespace in which Prometheus is running ## - namespace: "" + db_namespace: "knowlg-db" ## @param metrics.prometheusRule.labels Additional labels that can be used so PrometheusRule will be discovered by Prometheus ## labels: {} diff --git a/knowlg-automation/helm_charts/learning/Chart.yaml b/knowlg-automation/helm_charts/learning/Chart.yaml new file mode 100644 index 000000000..e1b159391 --- /dev/null +++ b/knowlg-automation/helm_charts/learning/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: learning +version: 0.1.0 \ No newline at end of file diff --git a/knowlg-automation/helm_charts/learning/learning-service_application.conf b/knowlg-automation/helm_charts/learning/learning-service_application.conf new file mode 100644 index 000000000..cb6887435 --- /dev/null +++ b/knowlg-automation/helm_charts/learning/learning-service_application.conf @@ -0,0 +1,315 @@ +#Platform Configuration +platform.services=["learning", "language", "config"] + +# Learning-Service Configuration +learning.graph_ids=["domain"] +content.metadata.visibility.parent=["textbookunit", "courseunit", "lessonplanunit"] + +# Language-Service Configuration +language.graph_ids=["as","bn","en","gu","hi","hoc","jun","ka","mai","mr","unx","or","san","sat","ta","te","urd","pj"] + +# Redis Configuration +redis.host="redis-db.knowlg-db.svc.cluster.local" +redis.port=6379 +redis.maxConnections=128 + +# Cassandra Configuration +content.keyspace.name="content_store" +hierarchy.keyspace.name="hierarchy_store" +content.hierarchy.table="content_hierarchy" +framework.hierarchy.table="framework_hierarchy" + +dialcode.keyspace.name="dialcode_store" +dialcode.keyspace.table="dial_code" +dialcode.max_count=1000 + +system.config.keyspace.name="dialcode_store" +system.config.table="system_config" +publisher.keyspace.name="dialcode_store" +publisher.keyspace.table="publisher" + +#DIAL Code Generator Configuration +dialcode.strip.chars="0" +dialcode.length=6.0 +dialcode.large.prime_number=1679979167 + +dialcode.es_conn_info="{{ .Values.es_connection }}" +dialcode.search.limit=1000 + +#DIAL Code ElasticSearch Configuration +dialcode_store.dial_code.index=true +dialcode_store.dial_code.object_type="DialCode" + +audit.es_conn_info="{{ .Values.es_connection }}" + +#Assessment Item Configuration +assessment.keyspace.name="sunbird_content_store" +assessment.keyspace.table="question_data" + +# Actor System Configuration +LearningActorSystem { + akka { + actor { + default-dispatcher { + type = "Dispatcher" + executor = "fork-join-executor" + fork-join-executor { + parallelism-min = 1 + parallelism-factor = 2.0 + parallelism-max = 4 + } + # Throughput for default Dispatcher, set to 1 for as fair as possible + throughput = 1 + } + deployment { + /HealthCheckManager + { + router = smallest-mailbox-pool + nr-of-instances = 5 + } + } + } + } +} + +env= "" + +#Current environment +cloud_storage.env = "sunbird" + +#Folder configuration +cloud_storage.content.folder = "content" +cloud_storage.asset.folder = "assets" +cloud_storage.artefact.folder = "artifact" +cloud_storage.bundle.folder = "bundle" +cloud_storage.media.folder = "media" +cloud_storage.ecar.folder = "ecar_files" +cloud_storage.itemset.folder = "itemset" + +cloud_storage.upload.url.ttl = 600 + + +# Media download configuration +content.media.base.url="https://dev.knowlg.sunbird.org" +plugin.media.base.url="https://dev.knowlg.sunbird.org" + + +# Content Extraction Configuration + +#directory location where store unzip file +dist.directory = "/data/tmp/dist/" +output.zipfile = "/data/tmp/story.zip" +source.folder = "/data/tmp/temp2/" +save.directory = "/data/tmp/temp/" + +# FOR CONTENT WORKFLOW PIPELINE (CWP) + +#--Content Workflow Pipeline Mode +OPERATION_MODE = "TEST" + +#--Maximum Content Package File Size Limit in Bytes (50 MB) +MAX_CONTENT_PACKAGE_FILE_SIZE_LIMIT = 52428800 + +#--Maximum Asset File Size Limit in Bytes (20 MB - 20971520) +MAX_ASSET_FILE_SIZE_LIMIT = 52428800 + +#--No of Retry While File Download Fails +RETRY_ASSET_DOWNLOAD_COUNT = 1 + +# H5P Library Path +content.h5p.library.path="learning_content_h5p_library_path" + +# ElasticSearch Configuration +search.es_conn_info="{{ .Values.es_connection }}" +search.fields.query=["name^100","title^100","lemma^100","code^100","domain","subject","description^10","keywords^100","ageGroup^10","filter^10","theme^10","genre^10","objects^25","contentType^100","language^200","teachingMode^25","skills^10","learningObjective^10","curriculum^100","gradeLevel^100","developer^100","attributions^10","identifier^100","IL_UNIQUE_ID^100","owner^50","board^100", "creator^100", "dialcodes^100","text","words","releaseNotes"] +search.fields.date=["lastUpdatedOn","createdOn","versionDate","lastSubmittedOn","lastPublishedOn"] +search.batch.size=500 +search.connection.timeout=30 + +platform-api-url="http://localhost:8080/learning-service" + +# Language Index Configuration +ignoreStartWordsList=[" + + + + + + + + + %d %msg%n + + + + + + + + + + + + + + + + + + + +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: search-config + namespace: knowlg-api +data: + search-service_application.conf: | + # This is the main configuration file for the application. + # https://www.playframework.com/documentation/latest/ConfigFile + # ~~~~~ + # Play uses HOCON as its configuration file format. HOCON has a number + # of advantages over other config formats, but there are two things that + # can be used when modifying settings. + # + # You can include other configuration files in this main application.conf file: + #include "extra-config.conf" + # + # You can declare variables and substitute for them: + #mykey = ${some.value} + # + # And if an environment variable exists when there is no other substitution, then + # HOCON will fall back to substituting environment variable: + #mykey = ${JAVA_HOME} + + ## Akka + # https://www.playframework.com/documentation/latest/ScalaAkka#Configuration + # https://www.playframework.com/documentation/latest/JavaAkka#Configuration + # ~~~~~ + # Play uses Akka internally and exposes Akka Streams and actors in Websockets and + # other streaming HTTP responses. + akka { + # "akka.log-config-on-start" is extraordinarly useful because it log the complete + # configuration at INFO level, including defaults and overrides, so it s worth + # putting at the very top. + # + # Put the following in your conf/logback.xml file: + # + # + # + # And then uncomment this line to debug the configuration. + # + #log-config-on-start = true + } + + ## Secret key + # http://www.playframework.com/documentation/latest/ApplicationSecret + # ~~~~~ + # The secret key is used to sign Play's session cookie. + # This must be changed for production, but we don't recommend you change it in this file. + play.http.secret.key = a-long-secret-to-calm-the-rage-of-the-entropy-gods + + ## Modules + # https://www.playframework.com/documentation/latest/Modules + # ~~~~~ + # Control which modules are loaded when Play starts. Note that modules are + # the replacement for "GlobalSettings", which are deprecated in 2.5.x. + # Please see https://www.playframework.com/documentation/latest/GlobalSettings + # for more information. + # + # You can also extend Play functionality by using one of the publically available + # Play modules: https://playframework.com/documentation/latest/ModuleDirectory + play.modules { + # By default, Play will load any class called Module that is defined + # in the root package (the "app" directory), or you can define them + # explicitly below. + # If there are any built-in modules that you want to enable, you can list them here. + #enabled += my.application.Module + + # If there are any built-in modules that you want to disable, you can list them here. + #disabled += "" + enabled += modules.SearchModule + } + + ## IDE + # https://www.playframework.com/documentation/latest/IDE + # ~~~~~ + # Depending on your IDE, you can add a hyperlink for errors that will jump you + # directly to the code location in the IDE in dev mode. The following line makes + # use of the IntelliJ IDEA REST interface: + #play.editor="http://localhost:63342/api/file/?file=%s&line=%s" + + ## Internationalisation + # https://www.playframework.com/documentation/latest/JavaI18N + # https://www.playframework.com/documentation/latest/ScalaI18N + # ~~~~~ + # Play comes with its own i18n settings, which allow the user's preferred language + # to map through to internal messages, or allow the language to be stored in a cookie. + play.i18n { + # The application languages + langs = [ "en" ] + + # Whether the language cookie should be secure or not + #langCookieSecure = true + + # Whether the HTTP only attribute of the cookie should be set to true + #langCookieHttpOnly = true + } + + ## Play HTTP settings + # ~~~~~ + play.http { + ## Router + # https://www.playframework.com/documentation/latest/JavaRouting + # https://www.playframework.com/documentation/latest/ScalaRouting + # ~~~~~ + # Define the Router object to use for this application. + # This router will be looked up first when the application is starting up, + # so make sure this is the entry point. + # Furthermore, it's assumed your route file is named properly. + # So for an application router like `my.application.Router`, + # you may need to define a router file `conf/my.application.routes`. + # Default to Routes in the root package (aka "apps" folder) (and conf/routes) + #router = my.application.Router + + ## Action Creator + # https://www.playframework.com/documentation/latest/JavaActionCreator + # ~~~~~ + #actionCreator = null + + ## ErrorHandler + # https://www.playframework.com/documentation/latest/JavaRouting + # https://www.playframework.com/documentation/latest/ScalaRouting + # ~~~~~ + # If null, will attempt to load a class called ErrorHandler in the root package, + #errorHandler = null + + ## Session & Flash + # https://www.playframework.com/documentation/latest/JavaSessionFlash + # https://www.playframework.com/documentation/latest/ScalaSessionFlash + # ~~~~~ + session { + # Sets the cookie to be sent only over HTTPS. + #secure = true + + # Sets the cookie to be accessed only by the server. + #httpOnly = true + + # Sets the max-age field of the cookie to 5 minutes. + # NOTE: this only sets when the browser will discard the cookie. Play will consider any + # cookie value with a valid signature to be a valid session forever. To implement a server side session timeout, + # you need to put a timestamp in the session and check it at regular intervals to possibly expire it. + #maxAge = 300 + + # Sets the domain on the session cookie. + #domain = "example.com" + } + + flash { + # Sets the cookie to be sent only over HTTPS. + #secure = true + + # Sets the cookie to be accessed only by the server. + #httpOnly = true + } + } + + play.http.parser.maxDiskBuffer = 10MB + parsers.anyContent.maxLength = 10MB + + play.server.provider = play.core.server.NettyServerProvider + + ## Netty Provider + # https://www.playframework.com/documentation/latest/SettingsNetty + # ~~~~~ + play.server.netty { + # Whether the Netty wire should be logged + log.wire = true + + # If you run Play on Linux, you can use Netty's native socket transport + # for higher performance with less garbage. + transport = "native" + } + + ## WS (HTTP Client) + # https://www.playframework.com/documentation/latest/ScalaWS#Configuring-WS + # ~~~~~ + # The HTTP client primarily used for REST APIs. The default client can be + # configured directly, but you can also create different client instances + # with customized settings. You must enable this by adding to build.sbt: + # + # libraryDependencies += ws // or javaWs if using java + # + play.ws { + # Sets HTTP requests not to follow 302 requests + #followRedirects = false + + # Sets the maximum number of open HTTP connections for the client. + #ahc.maxConnectionsTotal = 50 + + ## WS SSL + # https://www.playframework.com/documentation/latest/WsSSL + # ~~~~~ + ssl { + # Configuring HTTPS with Play WS does not require programming. You can + # set up both trustManager and keyManager for mutual authentication, and + # turn on JSSE debugging in development with a reload. + #debug.handshake = true + #trustManager = { + # stores = [ + # { type = "JKS", path = "exampletrust.jks" } + # ] + #} + } + } + + ## Cache + # https://www.playframework.com/documentation/latest/JavaCache + # https://www.playframework.com/documentation/latest/ScalaCache + # ~~~~~ + # Play comes with an integrated cache API that can reduce the operational + # overhead of repeated requests. You must enable this by adding to build.sbt: + # + # libraryDependencies += cache + # + play.cache { + # If you want to bind several caches, you can bind the individually + #bindCaches = ["db-cache", "user-cache", "session-cache"] + } + + ## Filter Configuration + # https://www.playframework.com/documentation/latest/Filters + # ~~~~~ + # There are a number of built-in filters that can be enabled and configured + # to give Play greater security. + # + play.filters { + + # Enabled filters are run automatically against Play. + # CSRFFilter, AllowedHostFilters, and SecurityHeadersFilters are enabled by default. + enabled = [filters.AccessLogFilter] + + # Disabled filters remove elements from the enabled list. + # disabled += filters.CSRFFilter + + + ## CORS filter configuration + # https://www.playframework.com/documentation/latest/CorsFilter + # ~~~~~ + # CORS is a protocol that allows web applications to make requests from the browser + # across different domains. + # NOTE: You MUST apply the CORS configuration before the CSRF filter, as CSRF has + # dependencies on CORS settings. + cors { + # Filter paths by a whitelist of path prefixes + #pathPrefixes = ["/some/path", ...] + + # The allowed origins. If null, all origins are allowed. + #allowedOrigins = ["http://www.example.com"] + + # The allowed HTTP methods. If null, all methods are allowed + #allowedHttpMethods = ["GET", "POST"] + } + + ## Security headers filter configuration + # https://www.playframework.com/documentation/latest/SecurityHeaders + # ~~~~~ + # Defines security headers that prevent XSS attacks. + # If enabled, then all options are set to the below configuration by default: + headers { + # The X-Frame-Options header. If null, the header is not set. + #frameOptions = "DENY" + + # The X-XSS-Protection header. If null, the header is not set. + #xssProtection = "1; mode=block" + + # The X-Content-Type-Options header. If null, the header is not set. + #contentTypeOptions = "nosniff" + + # The X-Permitted-Cross-Domain-Policies header. If null, the header is not set. + #permittedCrossDomainPolicies = "master-only" + + # The Content-Security-Policy header. If null, the header is not set. + #contentSecurityPolicy = "default-src 'self'" + } + + ## Allowed hosts filter configuration + # https://www.playframework.com/documentation/latest/AllowedHostsFilter + # ~~~~~ + # Play provides a filter that lets you configure which hosts can access your application. + # This is useful to prevent cache poisoning attacks. + hosts { + # Allow requests to example.com, its subdomains, and localhost:9000. + #allowed = [".example.com", "localhost:9000"] + } + } + + play.http.parser.maxMemoryBuffer = 50MB + akka.http.parsing.max-content-length = 50MB + + schema.base_path = "../../schemas/" + + + content.relatedBoards.properties=["channel","board","subject","medium"] + + + # Path for elasticsearch.properties file + search.config.path=/home/learning/platform/search + + # Cache-Manager Configuration + cache.type="redis" + + search.es_conn_info="{{ .Values.es_connection }}" + search.fields.query=["name^100","title^100","lemma^100","code^100","domain","subject","description^10","keywords^100","ageGroup^10","filter^10","theme^10","genre^10","objects^25","contentType^100","language^200","teachingMode^25","skills^10","learningObjective^10","curriculum^100","gradeLevel^100","developer^100","attributions^10","identifier^100","IL_UNIQUE_ID^100","owner^50","board^100","relatedBoards^100","creator^100", "dialcodes^100","text","words","releaseNotes"] + search.fields.date=["lastUpdatedOn","createdOn","versionDate","lastSubmittedOn","lastPublishedOn"] + search.fields.mode_collection=["identifier","name","objectType","contentType","mimeType","size","childNodes","board","subject","medium","gradeLevel","appIcon", "origin", "originData"] + search.batch.size=500 + search.connection.timeout=30 + + platform-api-url="http://localhost:8080/learning-service" + language.map={"Hindi":"hi", "English":"en", "Telugu":"te", "Kannada":"ka", "Tamil":"ta", "Assamese":"as", "Bengali":"bn", "Bodo":"bo", "Gujarati":"gu", "Konkani":"ko", "Malayalam":"ml", "Marathi":"mr", "Nepali":"ne", "Odia":"or", "Punjabi":"pj", "Sanskrit":"sk"} + #Top N Config for Search Telemetry + telemetry.search.topn=5 + telemetry_env=dev + installation.id=ekstep + + # Configuration for default channel ID + channel.default="in.ekstep" + ekstepPlatformApiUserId="search-service" + + + content.tagging.property=["subject","medium"] + search.payload.log_enable=true diff --git a/knowlg-automation/helm_charts/search/values.yaml b/knowlg-automation/helm_charts/search/values.yaml index 07c4c6b77..b1784b919 100644 --- a/knowlg-automation/helm_charts/search/values.yaml +++ b/knowlg-automation/helm_charts/search/values.yaml @@ -1,6 +1,6 @@ ### Default variable file for search-service ### -namespace: knowlg-api +api_namespace: knowlg-api env: javaoptions: -Xmx600m @@ -23,7 +23,6 @@ strategy: maxsurge: 25% maxunavailable: 25% serviceType: ClusterIP - - +es_connection: elasticsearch.knowlg-db.svc.cluster.local:9200 diff --git a/knowlg-automation/helm_charts/taxonomy/taxonomy-service_application.conf b/knowlg-automation/helm_charts/taxonomy/taxonomy-service_application.conf index b48fa7441..68b072a6d 100644 --- a/knowlg-automation/helm_charts/taxonomy/taxonomy-service_application.conf +++ b/knowlg-automation/helm_charts/taxonomy/taxonomy-service_application.conf @@ -80,7 +80,7 @@ akka { # ~~~~~ # The secret key is used to sign Play's session cookie. # This must be changed for production, but we don't recommend you change it in this file. -play.http.secret.key="jd5ECm/o0BXwQCe8PfZY1NoUkB9HN41QjA80p22MKyRIcP5RW4qHw8sZztCzv87M" +play.http.secret.key="" ## Modules # https://www.playframework.com/documentation/latest/Modules @@ -342,7 +342,7 @@ environment.id: 10000000 graph { dir: "/data/graphDB" ids: ["domain"] - passport.key.base: "jd5ECm/o0BXwQCe8PfZY1NoUkB9HN41QjA80p22MKyRIcP5RW4qHw8sZztCzv87M" + passport.key.base: "" } route { domain: "bolt://neo4j-db.knowlg-db.svc.cluster.local:7687" @@ -358,7 +358,7 @@ route { } } } - +y shard.id: 1 platform { auth.check.enabled: false diff --git a/knowlg-automation/helm_charts/taxonomy/templates/deployment.yaml b/knowlg-automation/helm_charts/taxonomy/templates/deployment.yaml index 598850f0d..bd39c7617 100644 --- a/knowlg-automation/helm_charts/taxonomy/templates/deployment.yaml +++ b/knowlg-automation/helm_charts/taxonomy/templates/deployment.yaml @@ -3,7 +3,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: {{ .Chart.Name }} - namespace: {{ .Values.namespace }} + namespace: {{ .Values.api_namespace }} annotations: reloader.stakater.com/auto: "true" spec: @@ -62,7 +62,7 @@ apiVersion: v1 kind: Service metadata: name: {{ .Chart.Name }}-service - namespace: {{ .Values.namespace }} + namespace: {{ .Values.api_namespace }} labels: app: {{ .Chart.Name }} spec: diff --git a/knowlg-automation/helm_charts/taxonomy/templates/taxonomy-configmap.yaml b/knowlg-automation/helm_charts/taxonomy/templates/taxonomy-configmap.yaml new file mode 100644 index 000000000..5f6bdd394 --- /dev/null +++ b/knowlg-automation/helm_charts/taxonomy/templates/taxonomy-configmap.yaml @@ -0,0 +1,446 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: taxonomy-xml-config + namespace: knowlg-api +data: + taxonomy-service_logback.xml: | + + + + + + + + + + %d %msg%n + + + + + + + + + + + + + + + + + + + + + + +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: taxonomy-config + namespace: knowlg-api +data: + taxonomy-service_application.conf: | + # This is the main configuration file for the application. + # https://www.playframework.com/documentation/latest/ConfigFile + # ~~~~~ + # Play uses HOCON as its configuration file format. HOCON has a number + # of advantages over other config formats, but there are two things that + # can be used when modifying settings. + # + # You can include other configuration files in this main application.conf file: + #include "extra-config.conf" + # + # You can declare variables and substitute for them: + #mykey = ${some.value} + # + # And if an environment variable exists when there is no other substitution, then + # HOCON will fall back to substituting environment variable: + #mykey = ${JAVA_HOME} + + ## Akka + # https://www.playframework.com/documentation/latest/ScalaAkka#Configuration + # https://www.playframework.com/documentation/latest/JavaAkka#Configuration + # ~~~~~ + # Play uses Akka internally and exposes Akka Streams and actors in Websockets and + # other streaming HTTP responses. + akka { + # "akka.log-config-on-start" is extraordinarly useful because it log the complete + # configuration at INFO level, including defaults and overrides, so it s worth + # putting at the very top. + # + # Put the following in your conf/logback.xml file: + # + # + # + # And then uncomment this line to debug the configuration. + # + #log-config-on-start = true + default-dispatcher { + # This will be used if you have set "executor = "fork-join-executor"" + fork-join-executor { + # Min number of threads to cap factor-based parallelism number to + parallelism-min = 8 + + # The parallelism factor is used to determine thread pool size using the + # following formula: ceil(available processors * factor). Resulting size + # is then bounded by the parallelism-min and parallelism-max values. + parallelism-factor = 32.0 + + # Max number of threads to cap factor-based parallelism number to + parallelism-max = 64 + + # Setting to "FIFO" to use queue like peeking mode which "poll" or "LIFO" to use stack + # like peeking mode which "pop". + task-peeking-mode = "FIFO" + } + } + actors-dispatcher { + type = "Dispatcher" + executor = "fork-join-executor" + fork-join-executor { + parallelism-min = 8 + parallelism-factor = 32.0 + parallelism-max = 64 + } + # Throughput for default Dispatcher, set to 1 for as fair as possible + throughput = 1 + } + actor { + deployment { + /contentActor + { + router = smallest-mailbox-pool + nr-of-instances = 10 + dispatcher = actors-dispatcher + } + } + } + } + + ## Secret key + # http://www.playframework.com/documentation/latest/ApplicationSecret + # ~~~~~ + # The secret key is used to sign Play's session cookie. + # This must be changed for production, but we don't recommend you change it in this file. + play.http.secret.key="jd5ECm/o0BXwQCe8PfZY1NoUkB9HN41QjA80p22MKyRIcP5RW4qHw8sZztCzv87M" + + ## Modules + # https://www.playframework.com/documentation/latest/Modules + # ~~~~~ + # Control which modules are loaded when Play starts. Note that modules are + # the replacement for "GlobalSettings", which are deprecated in 2.5.x. + # Please see https://www.playframework.com/documentation/latest/GlobalSettings + # for more information. + # + # You can also extend Play functionality by using one of the publically available + # Play modules: https://playframework.com/documentation/latest/ModuleDirectory + play.modules { + # By default, Play will load any class called Module that is defined + # in the root package (the "app" directory), or you can define them + # explicitly below. + # If there are any built-in modules that you want to enable, you can list them here. + enabled += modules.TaxonomyModule + + # If there are any built-in modules that you want to disable, you can list them here. + #disabled += "" + } + + ## IDE + # https://www.playframework.com/documentation/latest/IDE + # ~~~~~ + # Depending on your IDE, you can add a hyperlink for errors that will jump you + # directly to the code location in the IDE in dev mode. The following line makes + # use of the IntelliJ IDEA REST interface: + #play.editor="http://localhost:63342/api/file/?file=%s&line=%s" + + ## Internationalisation + # https://www.playframework.com/documentation/latest/JavaI18N + # https://www.playframework.com/documentation/latest/ScalaI18N + # ~~~~~ + # Play comes with its own i18n settings, which allow the user's preferred language + # to map through to internal messages, or allow the language to be stored in a cookie. + play.i18n { + # The application languages + langs = [ "en" ] + + # Whether the language cookie should be secure or not + #langCookieSecure = true + + # Whether the HTTP only attribute of the cookie should be set to true + #langCookieHttpOnly = true + } + + ## Play HTTP settings + # ~~~~~ + play.http { + ## Router + # https://www.playframework.com/documentation/latest/JavaRouting + # https://www.playframework.com/documentation/latest/ScalaRouting + # ~~~~~ + # Define the Router object to use for this application. + # This router will be looked up first when the application is starting up, + # so make sure this is the entry point. + # Furthermore, it's assumed your route file is named properly. + # So for an application router like `my.application.Router`, + # you may need to define a router file `conf/my.application.routes`. + # Default to Routes in the root package (aka "apps" folder) (and conf/routes) + #router = my.application.Router + + ## Action Creator + # https://www.playframework.com/documentation/latest/JavaActionCreator + # ~~~~~ + #actionCreator = null + + ## ErrorHandler + # https://www.playframework.com/documentation/latest/JavaRouting + # https://www.playframework.com/documentation/latest/ScalaRouting + # ~~~~~ + # If null, will attempt to load a class called ErrorHandler in the root package, + #errorHandler = null + + ## Session & Flash + # https://www.playframework.com/documentation/latest/JavaSessionFlash + # https://www.playframework.com/documentation/latest/ScalaSessionFlash + # ~~~~~ + session { + # Sets the cookie to be sent only over HTTPS. + #secure = true + + # Sets the cookie to be accessed only by the server. + #httpOnly = true + + # Sets the max-age field of the cookie to 5 minutes. + # NOTE: this only sets when the browser will discard the cookie. Play will consider any + # cookie value with a valid signature to be a valid session forever. To implement a server side session timeout, + # you need to put a timestamp in the session and check it at regular intervals to possibly expire it. + #maxAge = 300 + + # Sets the domain on the session cookie. + #domain = "example.com" + } + + flash { + # Sets the cookie to be sent only over HTTPS. + #secure = true + + # Sets the cookie to be accessed only by the server. + #httpOnly = true + } + } + + play.server.http.idleTimeout = 60s + play.http.parser.maxDiskBuffer = 10MB + parsers.anyContent.maxLength = 10MB + + ## Netty Provider + # https://www.playframework.com/documentation/latest/SettingsNetty + # ~~~~~ + play.server.netty { + # Whether the Netty wire should be logged + log.wire = true + + # If you run Play on Linux, you can use Netty's native socket transport + # for higher performance with less garbage. + transport = "native" + } + + ## WS (HTTP Client) + # https://www.playframework.com/documentation/latest/ScalaWS#Configuring-WS + # ~~~~~ + # The HTTP client primarily used for REST APIs. The default client can be + # configured directly, but you can also create different client instances + # with customized settings. You must enable this by adding to build.sbt: + # + # libraryDependencies += ws // or javaWs if using java + # + play.ws { + # Sets HTTP requests not to follow 302 requests + #followRedirects = false + + # Sets the maximum number of open HTTP connections for the client. + #ahc.maxConnectionsTotal = 50 + + ## WS SSL + # https://www.playframework.com/documentation/latest/WsSSL + # ~~~~~ + ssl { + # Configuring HTTPS with Play WS does not require programming. You can + # set up both trustManager and keyManager for mutual authentication, and + # turn on JSSE debugging in development with a reload. + #debug.handshake = true + #trustManager = { + # stores = [ + # { type = "JKS", path = "exampletrust.jks" } + # ] + #} + } + } + + ## Cache + # https://www.playframework.com/documentation/latest/JavaCache + # https://www.playframework.com/documentation/latest/ScalaCache + # ~~~~~ + # Play comes with an integrated cache API that can reduce the operational + # overhead of repeated requests. You must enable this by adding to build.sbt: + # + # libraryDependencies += cache + # + play.cache { + # If you want to bind several caches, you can bind the individually + #bindCaches = ["db-cache", "user-cache", "session-cache"] + } + + ## Filter Configuration + # https://www.playframework.com/documentation/latest/Filters + # ~~~~~ + # There are a number of built-in filters that can be enabled and configured + # to give Play greater security. + # + play.filters { + + # Enabled filters are run automatically against Play. + # CSRFFilter, AllowedHostFilters, and SecurityHeadersFilters are enabled by default. + enabled = [filters.AccessLogFilter] + + # Disabled filters remove elements from the enabled list. + # disabled += filters.CSRFFilter + + + ## CORS filter configuration + # https://www.playframework.com/documentation/latest/CorsFilter + # ~~~~~ + # CORS is a protocol that allows web applications to make requests from the browser + # across different domains. + # NOTE: You MUST apply the CORS configuration before the CSRF filter, as CSRF has + # dependencies on CORS settings. + cors { + # Filter paths by a whitelist of path prefixes + #pathPrefixes = ["/some/path", ...] + + # The allowed origins. If null, all origins are allowed. + #allowedOrigins = ["http://www.example.com"] + + # The allowed HTTP methods. If null, all methods are allowed + #allowedHttpMethods = ["GET", "POST"] + } + + ## Security headers filter configuration + # https://www.playframework.com/documentation/latest/SecurityHeaders + # ~~~~~ + # Defines security headers that prevent XSS attacks. + # If enabled, then all options are set to the below configuration by default: + headers { + # The X-Frame-Options header. If null, the header is not set. + #frameOptions = "DENY" + + # The X-XSS-Protection header. If null, the header is not set. + #xssProtection = "1; mode=block" + + # The X-Content-Type-Options header. If null, the header is not set. + #contentTypeOptions = "nosniff" + + # The X-Permitted-Cross-Domain-Policies header. If null, the header is not set. + #permittedCrossDomainPolicies = "master-only" + + # The Content-Security-Policy header. If null, the header is not set. + #contentSecurityPolicy = "default-src 'self'" + } + + ## Allowed hosts filter configuration + # https://www.playframework.com/documentation/latest/AllowedHostsFilter + # ~~~~~ + # Play provides a filter that lets you configure which hosts can access your application. + # This is useful to prevent cache poisoning attacks. + hosts { + # Allow requests to example.com, its subdomains, and localhost:9000. + #allowed = [".example.com", "localhost:9000"] + } + } + + play.http.parser.maxMemoryBuffer = 50MB + akka.http.parsing.max-content-length = 50MB + schema.base_path="https://sunbirddev.blob.core.windows.net/sunbird-content-dev/schemas/local" + + # Cassandra Configuration + cassandra { + lp { + connection: "{{ .Values.cassandra_connection }}" + } + lpa { + connection: "{{ .Values.cassandra_connection }}" + } + } + + # Redis Configuration + redis { + host: "{{ .Values.redis_connection }}" + port: 6379 + maxConnections: 128 + } + + # Configuration + akka.request_timeout: 30 + environment.id: 10000000 + graph { + dir: "/data/graphDB" + ids: ["domain"] + passport.key.base: "jd5ECm/o0BXwQCe8PfZY1NoUkB9HN41QjA80p22MKyRIcP5RW4qHw8sZztCzv87M" + } + route { + domain: "bolt://{{ .Values.neo4j_domain_connection }}" + all: "bolt://{{ .Values.neo4j_all_connection }}" + bolt { + write { + domain: "bolt://{{ .Values.neo4j_domain_connection }}" + all: "bolt://{{ .Values.neo4j_all_connection }}" + }, + read { + domain: "bolt://{{ .Values.neo4j_domain_connection }}" + all: "bolt://{{ .Values.neo4j_all_connection }}" + } + } + } + + shard.id: 1 + platform { + auth.check.enabled: false + cache.ttl: 3600000 + } + + # Cloud Storage Config + cloud_storage_type: "azure" + azure_storage_key: "" + azure_storage_secret: "" + azure_storage_container: "" + + installation.id: ekstep + + kafka { + urls : "{{ .Values.kafka_connection }}" + } + + channel { + default: "org.sunbird" + } + + languageCode { + assamese : "as" + bengali : "bn" + english : "en" + gujarati : "gu" + hindi : "hi" + kannada : "ka" + marathi : "mr" + odia : "or" + tamil : "ta" + telugu : "te" + } + objectcategorydefinition.keyspace="{{ .Values.objectcategorydefinition_keyspace }}" + + # Framework master category validation Supported values are Yes/No + master.category.validation.enabled="Yes" diff --git a/knowlg-automation/helm_charts/taxonomy/values.yaml b/knowlg-automation/helm_charts/taxonomy/values.yaml index 194f61992..3a9451d19 100644 --- a/knowlg-automation/helm_charts/taxonomy/values.yaml +++ b/knowlg-automation/helm_charts/taxonomy/values.yaml @@ -1,6 +1,6 @@ ### Default variable file for taxonomy-service ### -namespace: knowlg-api +api_namespace: knowlg-api env: javaoptions: -Xmx600m @@ -23,6 +23,9 @@ strategy: maxsurge: 25% maxunavailable: 25% serviceType: ClusterIP - - - +cassandra_connection: cassandra-db.knowlg-db.svc.cluster.local:9042 +redis_connection: redis-db.knowlg-db.svc.cluster.local +neo4j_domain_connection: neo4j-db.knowlg-db.svc.cluster.local:7687 +neo4j_all_connection: neo4j-db.knowlg-db.svc.cluster.local:8687 +kafka_connection: kafka.knowlg-db.svc.cluster.local:9092 +objectcategorydefinition_keyspace: dev_category_store \ No newline at end of file diff --git a/knowlg-automation/terraform/aks.yaml b/knowlg-automation/terraform/aks.yaml index 5758313ce..f2297ea44 100644 --- a/knowlg-automation/terraform/aks.yaml +++ b/knowlg-automation/terraform/aks.yaml @@ -1,8 +1,7 @@ ╷ │ Warning: No outputs found │  -│ The state file either has no outputs defined, or all the defined outputs are empty. Please define an output -│ in your configuration with the `output` keyword and run `terraform refresh` for it to become available. If -│ you are using interpolation, please verify the interpolated value is not empty. You can use the `terraform -│ console` command to assist. +│ The state file either has no outputs defined, or all the defined outputs are empty. Please define an output in your +│ configuration with the `output` keyword and run `terraform refresh` for it to become available. If you are using +│ interpolation, please verify the interpolated value is not empty. You can use the `terraform console` command to assist. ╵ diff --git a/knowlg-automation/terraform/local/vars.tf b/knowlg-automation/terraform/local/vars.tf index f1f6e1e14..d6ab7d27d 100644 --- a/knowlg-automation/terraform/local/vars.tf +++ b/knowlg-automation/terraform/local/vars.tf @@ -138,3 +138,14 @@ variable "SEARCH_NAMESPACE" { default = "knowlg-api" } +#LEARNING +variable "LEARNING_CHART" { + description = "Learning Instance Running Namespace" + default = "../../helm_charts/learning" +} + +variable "LEARNING_NAMESPACE" { + description = "Learning Instance Running Namespace" + default = "knowlg-api" +} + From ab89fec165580c1893f84239d470fa76f6b99658 Mon Sep 17 00:00:00 2001 From: aimansharief <110388201+aimansharief@users.noreply.github.com> Date: Tue, 11 Apr 2023 10:21:06 +0530 Subject: [PATCH 203/490] Issue KN-767 feat:Added changes in Neo4j to connect with logstash --- .../helm_charts/logstash/.helmignore | 22 - .../helm_charts/logstash/Chart.lock | 6 - .../helm_charts/logstash/Chart.yaml | 26 - .../helm_charts/logstash/README.md | 340 --------- .../logstash/charts/common-2.2.4.tgz | Bin 12364 -> 0 bytes .../helm_charts/logstash/templates/NOTES.txt | 63 -- .../logstash/templates/_helpers.tpl | 67 -- .../logstash/templates/configuration-cm.yaml | 39 -- .../logstash/templates/extra-list.yaml | 4 - .../logstash/templates/headless-svc.yaml | 22 - .../logstash/templates/ingress.yaml | 62 -- .../helm_charts/logstash/templates/pdb.yaml | 23 - .../logstash/templates/serviceaccount.yaml | 21 - .../helm_charts/logstash/templates/sts.yaml | 219 ------ .../helm_charts/logstash/templates/svc.yaml | 39 -- .../logstash/templates/tls-secret.yaml | 44 -- .../helm_charts/logstash/values.yaml | 662 ------------------ .../neo4j/templates/deployment.yaml | 25 +- .../neo4j/templates/logstash-configmap.yaml | 39 ++ .../helm_charts/neo4j/values.yaml | 6 +- .../terraform/local/logstash-provision.tf | 11 - knowlg-automation/terraform/local/vars.tf | 11 - 22 files changed, 66 insertions(+), 1685 deletions(-) delete mode 100644 knowlg-automation/helm_charts/logstash/.helmignore delete mode 100644 knowlg-automation/helm_charts/logstash/Chart.lock delete mode 100644 knowlg-automation/helm_charts/logstash/Chart.yaml delete mode 100644 knowlg-automation/helm_charts/logstash/README.md delete mode 100644 knowlg-automation/helm_charts/logstash/charts/common-2.2.4.tgz delete mode 100644 knowlg-automation/helm_charts/logstash/templates/NOTES.txt delete mode 100644 knowlg-automation/helm_charts/logstash/templates/_helpers.tpl delete mode 100644 knowlg-automation/helm_charts/logstash/templates/configuration-cm.yaml delete mode 100644 knowlg-automation/helm_charts/logstash/templates/extra-list.yaml delete mode 100644 knowlg-automation/helm_charts/logstash/templates/headless-svc.yaml delete mode 100644 knowlg-automation/helm_charts/logstash/templates/ingress.yaml delete mode 100644 knowlg-automation/helm_charts/logstash/templates/pdb.yaml delete mode 100644 knowlg-automation/helm_charts/logstash/templates/serviceaccount.yaml delete mode 100644 knowlg-automation/helm_charts/logstash/templates/sts.yaml delete mode 100644 knowlg-automation/helm_charts/logstash/templates/svc.yaml delete mode 100644 knowlg-automation/helm_charts/logstash/templates/tls-secret.yaml delete mode 100644 knowlg-automation/helm_charts/logstash/values.yaml create mode 100644 knowlg-automation/helm_charts/neo4j/templates/logstash-configmap.yaml delete mode 100644 knowlg-automation/terraform/local/logstash-provision.tf diff --git a/knowlg-automation/helm_charts/logstash/.helmignore b/knowlg-automation/helm_charts/logstash/.helmignore deleted file mode 100644 index ff5fca451..000000000 --- a/knowlg-automation/helm_charts/logstash/.helmignore +++ /dev/null @@ -1,22 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*~ -# Various IDEs -.project -.idea/ -*.tmproj -files/conf/README.md \ No newline at end of file diff --git a/knowlg-automation/helm_charts/logstash/Chart.lock b/knowlg-automation/helm_charts/logstash/Chart.lock deleted file mode 100644 index d1e9ffaf9..000000000 --- a/knowlg-automation/helm_charts/logstash/Chart.lock +++ /dev/null @@ -1,6 +0,0 @@ -dependencies: -- name: common - repository: https://charts.bitnami.com/bitnami - version: 2.2.4 -digest: sha256:634d19e9b7f6e4c07d7c04a0161ab96b3f83335ebdd70b35b952319ef0a2586b -generated: "2023-03-23T12:09:58.799549989+05:30" diff --git a/knowlg-automation/helm_charts/logstash/Chart.yaml b/knowlg-automation/helm_charts/logstash/Chart.yaml deleted file mode 100644 index 862479cbc..000000000 --- a/knowlg-automation/helm_charts/logstash/Chart.yaml +++ /dev/null @@ -1,26 +0,0 @@ -annotations: - category: LogManagement - licenses: Apache-2.0 -apiVersion: v2 -appVersion: 8.6.2 -dependencies: - - name: common - repository: https://charts.bitnami.com/bitnami - tags: - - bitnami-common - version: 2.x.x -description: Logstash is an open source data processing engine. It ingests data from multiple sources, processes it, and sends the output to final destination in real-time. It is a core component of the ELK stack. -home: https://github.com/bitnami/charts/tree/main/bitnami/logstash -icon: https://bitnami.com/assets/stacks/logstash/img/logstash-stack-220x234.png -keywords: - - logstash - - logging - - elk -maintainers: - - name: Bitnami - url: https://github.com/bitnami/charts -name: logstash -sources: - - https://github.com/bitnami/containers/tree/main/bitnami/logstash - - https://www.elastic.co/products/logstash -version: 5.1.15 diff --git a/knowlg-automation/helm_charts/logstash/README.md b/knowlg-automation/helm_charts/logstash/README.md deleted file mode 100644 index 52fb6c8e9..000000000 --- a/knowlg-automation/helm_charts/logstash/README.md +++ /dev/null @@ -1,340 +0,0 @@ - - -# Logstash packaged by Bitnami - -Logstash is an open source data processing engine. It ingests data from multiple sources, processes it, and sends the output to final destination in real-time. It is a core component of the ELK stack. - -[Overview of Logstash](http://logstash.net) - -Trademarks: This software listing is packaged by Bitnami. The respective trademarks mentioned in the offering are owned by the respective companies, and use of them does not imply any affiliation or endorsement. - -## TL;DR - -```console -helm repo add my-repo https://charts.bitnami.com/bitnami -helm install my-release my-repo/logstash -``` - -## Introduction - -This chart bootstraps a [logstash](https://github.com/bitnami/containers/tree/main/bitnami/logstash) deployment on a [Kubernetes](https://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager. - -Bitnami charts can be used with [Kubeapps](https://kubeapps.dev/) for deployment and management of Helm Charts in clusters. - -## Prerequisites - -- Kubernetes 1.19+ -- Helm 3.2.0+ - -## Installing the Chart - -To install the chart with the release name `my-release`: - -```console -helm repo add my-repo https://charts.bitnami.com/bitnami -helm install my-release my-repo/logstash -``` - -These commands deploy logstash on the Kubernetes cluster in the default configuration. The [configuration](#configuration-and-installation-details) section lists the parameters that can be configured during installation. - -> **Tip**: List all releases using `helm list` - -## Uninstalling the Chart - -To uninstall/delete the `my-release` statefulset: - -```console -helm delete my-release -``` - -The command removes all the Kubernetes components associated with the chart and deletes the release. Use the option `--purge` to delete all history too. - -## Parameters - -### Global parameters - -| Name | Description | Value | -| ------------------------- | ----------------------------------------------- | ----- | -| `global.imageRegistry` | Global Docker image registry | `""` | -| `global.imagePullSecrets` | Global Docker registry secret names as an array | `[]` | -| `global.storageClass` | Global StorageClass for Persistent Volume(s) | `""` | - -### Common parameters - -| Name | Description | Value | -| ------------------------ | ---------------------------------------------------------------------------------------- | --------------- | -| `kubeVersion` | Force target Kubernetes version (using Helm capabilities if not set) | `""` | -| `nameOverride` | String to partially override logstash.fullname template (will maintain the release name) | `""` | -| `fullnameOverride` | String to fully override logstash.fullname template | `""` | -| `clusterDomain` | Default Kubernetes cluster domain | `cluster.local` | -| `commonAnnotations` | Annotations to add to all deployed objects | `{}` | -| `commonLabels` | Labels to add to all deployed objects | `{}` | -| `extraDeploy` | Array of extra objects to deploy with the release (evaluated as a template). | `[]` | -| `diagnosticMode.enabled` | Enable diagnostic mode (all probes will be disabled and the command will be overridden) | `false` | -| `diagnosticMode.command` | Command to override all containers in the deployment | `["sleep"]` | -| `diagnosticMode.args` | Args to override all containers in the deployment | `["infinity"]` | - -### Logstash parameters - -| Name | Description | Value | -| --------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | -| `image.registry` | Logstash image registry | `docker.io` | -| `image.repository` | Logstash image repository | `bitnami/logstash` | -| `image.tag` | Logstash image tag (immutable tags are recommended) | `8.6.2-debian-11-r3` | -| `image.digest` | Logstash image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag | `""` | -| `image.pullPolicy` | Logstash image pull policy | `IfNotPresent` | -| `image.pullSecrets` | Specify docker-registry secret names as an array | `[]` | -| `image.debug` | Specify if debug logs should be enabled | `false` | -| `hostAliases` | Add deployment host aliases | `[]` | -| `configFileName` | Logstash configuration file name. It must match the name of the configuration file mounted as a configmap. | `logstash.conf` | -| `enableMonitoringAPI` | Whether to enable the Logstash Monitoring API or not Kubernetes cluster domain | `true` | -| `monitoringAPIPort` | Logstash Monitoring API Port | `9600` | -| `extraEnvVars` | Array containing extra env vars to configure Logstash | `[]` | -| `extraEnvVarsSecret` | To add secrets to environment | `""` | -| `extraEnvVarsCM` | To add configmaps to environment | `""` | -| `input` | Input Plugins configuration | `""` | -| `filter` | Filter Plugins configuration | `""` | -| `output` | Output Plugins configuration | `""` | -| `existingConfiguration` | Name of existing ConfigMap object with the Logstash configuration (`input`, `filter`, and `output` will be ignored). | `""` | -| `enableMultiplePipelines` | Allows user to use multiple pipelines | `false` | -| `extraVolumes` | Array to add extra volumes (evaluated as a template) | `[]` | -| `extraVolumeMounts` | Array to add extra mounts (normally used with extraVolumes, evaluated as a template) | `[]` | -| `serviceAccount.create` | Enable creation of ServiceAccount for Logstash pods | `true` | -| `serviceAccount.name` | The name of the service account to use. If not set and `create` is `true`, a name is generated | `""` | -| `serviceAccount.automountServiceAccountToken` | Allows automount of ServiceAccountToken on the serviceAccount created | `true` | -| `serviceAccount.annotations` | Additional custom annotations for the ServiceAccount | `{}` | -| `containerPorts` | Array containing the ports to open in the Logstash container (evaluated as a template) | `[]` | -| `initContainers` | Add additional init containers to the Logstash pod(s) | `[]` | -| `sidecars` | Add additional sidecar containers to the Logstash pod(s) | `[]` | -| `replicaCount` | Number of Logstash replicas to deploy | `1` | -| `updateStrategy.type` | Update strategy type (`RollingUpdate`, or `OnDelete`) | `RollingUpdate` | -| `podManagementPolicy` | Pod management policy | `OrderedReady` | -| `podAnnotations` | Pod annotations | `{}` | -| `podLabels` | Extra labels for Logstash pods | `{}` | -| `podAffinityPreset` | Pod affinity preset. Ignored if `affinity` is set. Allowed values: `soft` or `hard` | `""` | -| `podAntiAffinityPreset` | Pod anti-affinity preset. Ignored if `affinity` is set. Allowed values: `soft` or `hard` | `soft` | -| `nodeAffinityPreset.type` | Node affinity preset type. Ignored if `affinity` is set. Allowed values: `soft` or `hard` | `""` | -| `nodeAffinityPreset.key` | Node label key to match. Ignored if `affinity` is set. | `""` | -| `nodeAffinityPreset.values` | Node label values to match. Ignored if `affinity` is set. | `[]` | -| `affinity` | Affinity for pod assignment | `{}` | -| `nodeSelector` | Node labels for pod assignment | `{}` | -| `tolerations` | Tolerations for pod assignment | `[]` | -| `priorityClassName` | Pod priority | `""` | -| `schedulerName` | Name of the k8s scheduler (other than default) | `""` | -| `terminationGracePeriodSeconds` | In seconds, time the given to the Logstash pod needs to terminate gracefully | `""` | -| `topologySpreadConstraints` | Topology Spread Constraints for pod assignment | `[]` | -| `podSecurityContext.enabled` | Enabled Logstash pods' Security Context | `true` | -| `podSecurityContext.fsGroup` | Set Logstash pod's Security Context fsGroup | `1001` | -| `containerSecurityContext.enabled` | Enabled Logstash containers' Security Context | `true` | -| `containerSecurityContext.runAsUser` | Set Logstash containers' Security Context runAsUser | `1001` | -| `containerSecurityContext.runAsNonRoot` | Set Logstash container's Security Context runAsNonRoot | `true` | -| `command` | Override default container command (useful when using custom images) | `[]` | -| `args` | Override default container args (useful when using custom images) | `[]` | -| `lifecycleHooks` | for the Logstash container(s) to automate configuration before or after startup | `{}` | -| `resources.limits` | The resources limits for the Logstash container | `{}` | -| `resources.requests` | The requested resources for the Logstash container | `{}` | -| `startupProbe.enabled` | Enable startupProbe | `false` | -| `startupProbe.initialDelaySeconds` | Initial delay seconds for startupProbe | `60` | -| `startupProbe.periodSeconds` | Period seconds for startupProbe | `10` | -| `startupProbe.timeoutSeconds` | Timeout seconds for startupProbe | `5` | -| `startupProbe.failureThreshold` | Failure threshold for startupProbe | `6` | -| `startupProbe.successThreshold` | Success threshold for startupProbe | `1` | -| `livenessProbe.enabled` | Enable livenessProbe | `true` | -| `livenessProbe.initialDelaySeconds` | Initial delay seconds for livenessProbe | `60` | -| `livenessProbe.periodSeconds` | Period seconds for livenessProbe | `10` | -| `livenessProbe.timeoutSeconds` | Timeout seconds for livenessProbe | `5` | -| `livenessProbe.failureThreshold` | Failure threshold for livenessProbe | `6` | -| `livenessProbe.successThreshold` | Success threshold for livenessProbe | `1` | -| `readinessProbe.enabled` | Enable readinessProbe | `true` | -| `readinessProbe.initialDelaySeconds` | Initial delay seconds for readinessProbe | `60` | -| `readinessProbe.periodSeconds` | Period seconds for readinessProbe | `10` | -| `readinessProbe.timeoutSeconds` | Timeout seconds for readinessProbe | `5` | -| `readinessProbe.failureThreshold` | Failure threshold for readinessProbe | `6` | -| `readinessProbe.successThreshold` | Success threshold for readinessProbe | `1` | -| `customStartupProbe` | Custom startup probe for the Web component | `{}` | -| `customLivenessProbe` | Custom liveness probe for the Web component | `{}` | -| `customReadinessProbe` | Custom readiness probe for the Web component | `{}` | -| `service.type` | Kubernetes service type (`ClusterIP`, `NodePort`, or `LoadBalancer`) | `ClusterIP` | -| `service.ports` | Logstash service ports (evaluated as a template) | `[]` | -| `service.loadBalancerIP` | loadBalancerIP if service type is `LoadBalancer` | `""` | -| `service.loadBalancerSourceRanges` | Addresses that are allowed when service is LoadBalancer | `[]` | -| `service.externalTrafficPolicy` | External traffic policy, configure to Local to preserve client source IP when using an external loadBalancer | `""` | -| `service.clusterIP` | Static clusterIP or None for headless services | `""` | -| `service.annotations` | Annotations for Logstash service | `{}` | -| `service.sessionAffinity` | Session Affinity for Kubernetes service, can be "None" or "ClientIP" | `None` | -| `service.sessionAffinityConfig` | Additional settings for the sessionAffinity | `{}` | -| `persistence.enabled` | Enable Logstash data persistence using PVC | `false` | -| `persistence.existingClaim` | A manually managed Persistent Volume and Claim | `""` | -| `persistence.storageClass` | PVC Storage Class for Logstash data volume | `""` | -| `persistence.accessModes` | PVC Access Mode for Logstash data volume | `["ReadWriteOnce"]` | -| `persistence.size` | PVC Storage Request for Logstash data volume | `2Gi` | -| `persistence.annotations` | Annotations for the PVC | `{}` | -| `persistence.mountPath` | Mount path of the Logstash data volume | `/bitnami/logstash/data` | -| `persistence.selector` | Selector to match an existing Persistent Volume for WordPress data PVC | `{}` | -| `volumePermissions.enabled` | Enable init container that changes the owner and group of the persistent volume(s) mountpoint to `runAsUser:fsGroup` | `false` | -| `volumePermissions.securityContext.runAsUser` | User ID for the volumePermissions init container | `0` | -| `volumePermissions.image.registry` | Init container volume-permissions image registry | `docker.io` | -| `volumePermissions.image.repository` | Init container volume-permissions image repository | `bitnami/bitnami-shell` | -| `volumePermissions.image.tag` | Init container volume-permissions image tag (immutable tags are recommended) | `11-debian-11-r90` | -| `volumePermissions.image.digest` | Init container volume-permissions image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag | `""` | -| `volumePermissions.image.pullPolicy` | Init container volume-permissions image pull policy | `IfNotPresent` | -| `volumePermissions.image.pullSecrets` | Specify docker-registry secret names as an array | `[]` | -| `volumePermissions.resources.limits` | Init container volume-permissions resource limits | `{}` | -| `volumePermissions.resources.requests` | Init container volume-permissions resource requests | `{}` | -| `ingress.enabled` | Enable ingress controller resource | `false` | -| `ingress.selfSigned` | Create a TLS secret for this ingress record using self-signed certificates generated by Helm | `false` | -| `ingress.pathType` | Ingress Path type | `ImplementationSpecific` | -| `ingress.apiVersion` | Override API Version (automatically detected if not set) | `""` | -| `ingress.hostname` | Default host for the ingress resource | `logstash.local` | -| `ingress.path` | The Path to Logstash. You may need to set this to '/*' in order to use this with ALB ingress controllers. | `/` | -| `ingress.annotations` | Additional annotations for the Ingress resource. To enable certificate autogeneration, place here your cert-manager annotations. | `{}` | -| `ingress.tls` | Enable TLS configuration for the hostname defined at ingress.hostname parameter | `false` | -| `ingress.extraHosts` | The list of additional hostnames to be covered with this ingress record. | `[]` | -| `ingress.extraPaths` | Any additional arbitrary paths that may need to be added to the ingress under the main host. | `[]` | -| `ingress.extraRules` | The list of additional rules to be added to this ingress record. Evaluated as a template | `[]` | -| `ingress.extraTls` | The tls configuration for additional hostnames to be covered with this ingress record. | `[]` | -| `ingress.secrets` | If you're providing your own certificates, please use this to add the certificates as secrets | `[]` | -| `ingress.ingressClassName` | IngressClass that will be be used to implement the Ingress (Kubernetes 1.18+) | `""` | -| `pdb.create` | If true, create a pod disruption budget for pods. | `false` | -| `pdb.minAvailable` | Minimum number / percentage of pods that should remain scheduled | `1` | -| `pdb.maxUnavailable` | Maximum number / percentage of pods that may be made unavailable | `""` | - -Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example, - -```console -helm install my-release \ - --set enableMonitoringAPI=false my-repo/logstash -``` - -The above command disables the Logstash Monitoring API. - -Alternatively, a YAML file that specifies the values for the parameters can be provided while installing the chart. For example, - -```console -helm install my-release -f values.yaml my-repo/logstash -``` - -> **Tip**: You can use the default [values.yaml](values.yaml) - -## Configuration and installation details - -### [Rolling vs Immutable tags](https://docs.bitnami.com/containers/how-to/understand-rolling-tags-containers/) - -It is strongly recommended to use immutable tags in a production environment. This ensures your deployment does not change automatically if the same tag is updated with a different image. - -Bitnami will release a new chart updating its containers if a new version of the main container, significant changes, or critical vulnerabilities exist. - -### Expose the Logstash service - -The service(s) created by the deployment can be exposed within or outside the cluster using any of the following approaches: - -- **Ingress**: Set `ingress.enabled=true` to expose Logstash through Ingress. -- **ClusterIP**: Set `service.type=ClusterIP` to choose this service type. -- **NodePort**: Set `service.type=NodePort` to choose this service type. -- **LoadBalancer**: Set `service.type=LoadBalancer` to choose this service type. - -For more information, refer to the [chart documentation on exposing the Logstash service](https://docs.bitnami.com/kubernetes/apps/logstash/get-started/expose-service/). - -### Use custom configuration - -By default, this Helm chart provides a basic configuration for Logstash: listening to HTTP requests on port 8080 and writing them to the standard output. - -This Logstash configuration can be adjusted using the *input*, *filter*, and *output* parameters, which allow specification of the input, filter and output plugins configuration respectively. In addition to these options, the chart also supports reading configuration from an external ConfigMap via the *existingConfiguration* parameter. - -Refer to the [chart documentation for more information on customizing the Logstash deployment](https://docs.bitnami.com/kubernetes/apps/logstash/configuration/customize-deployment/). - -### Create and use multiple pipelines - -The chart supports the use of [multiple pipelines](https://www.elastic.co/guide/en/logstash/master/multiple-pipelines.html) by setting the *enableMultiplePipelines* parameter to *true*. - -The chart supports setting an external ConfigMap with all the configuration files via the *existingConfiguration* parameter. - -For more information and an example, refer to the chart documentation on [using multiple pipelines](https://docs.bitnami.com/kubernetes/apps/logstash/configuration/use-multiple-pipelines/). - -### Add extra environment variables - -To add extra environment variables, use the `extraEnvVars` property. - -```yaml -extraEnvVars: - - name: ELASTICSEARCH_HOST - value: "x.y.z" -``` - -To add extra environment variables from an external ConfigMap or secret, use the `extraEnvVarsCM` and `extraEnvVarsSecret` properties. Note that the secret and ConfigMap should be already available in the namespace. - -```yaml -extraEnvVarsSecret: logstash-secrets -extraEnvVarsCM: logstash-configmap -``` - -### Set Pod affinity - -This chart allows you to set custom Pod affinity using the `affinity` parameter. Find more information about Pod affinity in the [kubernetes documentation](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity). - -As an alternative, use one of the preset configurations for pod affinity, pod anti-affinity, and node affinity available at the [bitnami/common](https://github.com/bitnami/charts/tree/main/bitnami/common#affinities) chart. To do so, set the `podAffinityPreset`, `podAntiAffinityPreset`, or `nodeAffinityPreset` parameters. - -## Persistence - -The [Bitnami Logstash](https://github.com/bitnami/containers/tree/main/bitnami/logstash) image stores the Logstash data at the `/bitnami/logstash/data` path of the container. - -Persistent Volume Claims (PVCs) are used to keep the data across deployments. This is known to work in GCE, AWS, and minikube. - -See the [Parameters](#parameters) section to configure the PVC or to disable persistence. - -## Troubleshooting - -Find more information about how to deal with common errors related to Bitnami's Helm charts in [this troubleshooting guide](https://docs.bitnami.com/general/how-to/troubleshoot-helm-chart-issues). - -## Upgrading - -### To 5.0.0 - -This major release is no longer contains the metrics section because the container `bitnami/logstash-exporter` has been deprecated due to the upstream project is not maintained. - -### To 4.0.0 - -This major release updates the chart to use Logstash 8. In addition, this chart has been standardized adding missing values and renaming others, in order to get aligned with the rest of the assets in the Bitnami charts repository. - -The following values have been renamed: - -- `securityContext` has been splitted between `containerSecurityContext` and `podSecurityContext`. -- Liveness and readiness probes httpGet field can not be modified. For customization, use customLivenessProbe and customReadinessProbe instead. -- `lifecycle` renamed as `lifecycleHooks`. -- `service.ports` is now evaluated as a template with array structure. -- Enabling `ingress.tls` no longer auto generates certificates. Use `ingress.selfSigned` to enable the creation of autogenerated certificates. -- `podDisruptionBudget.*` renamed as `pdb.*`. - -### To 3.0.0 - -This version standardizes the way of defining Ingress rules. When configuring a single hostname for the Ingress rule, set the `ingress.hostname` value. When defining more than one, set the `ingress.extraHosts` array. Apart from this case, no issues are expected to appear when upgrading. - -### To 2.0.0 - -This version drops support of including files in the `files/` folder, as it was working only under certain circumstances and the chart already provides alternative mechanisms like the `input` , `output` and `filter`, the `existingConfiguration` or the `extraDeploy` values. - -### To 1.2.0 - -This version introduces `bitnami/common`, a [library chart](https://helm.sh/docs/topics/library_charts/#helm) as a dependency. More documentation about this new utility could be found [here](https://github.com/bitnami/charts/tree/main/bitnami/common#bitnami-common-library-chart). Please, make sure that you have updated the chart dependencies before executing any upgrade. - -### To 1.0.0 - -[On November 13, 2020, Helm v2 support formally ended](https://github.com/helm/charts#status-of-the-project). Subsequently, a major version of the chart was released to incorporate the different features added in Helm v3 and to be consistent with the Helm project itself regarding the Helm v2 EOL. - -[Learn more about this change and related upgrade considerations](https://docs.bitnami.com/kubernetes/apps/logstash/administration/upgrade-helm3/). - -## License - -Copyright © 2023 Bitnami - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/knowlg-automation/helm_charts/logstash/charts/common-2.2.4.tgz b/knowlg-automation/helm_charts/logstash/charts/common-2.2.4.tgz deleted file mode 100644 index 9016f32e2cfd76371466da567554893ec977e7c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12364 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 diff --git a/knowlg-automation/helm_charts/logstash/templates/NOTES.txt b/knowlg-automation/helm_charts/logstash/templates/NOTES.txt deleted file mode 100644 index 061e5c724..000000000 --- a/knowlg-automation/helm_charts/logstash/templates/NOTES.txt +++ /dev/null @@ -1,63 +0,0 @@ -CHART NAME: {{ .Chart.Name }} -CHART VERSION: {{ .Chart.Version }} -APP VERSION: {{ .Chart.AppVersion }} - -** Please be patient while the chart is being deployed ** - -{{- if .Values.diagnosticMode.enabled }} -The chart has been deployed in diagnostic mode. All probes have been disabled and the command has been overwritten with: - - command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 4 }} - args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 4 }} - -Get the list of pods by executing: - - kubectl get pods --namespace {{ .Release.Namespace }} -l app.kubernetes.io/instance={{ .Release.Name }} - -Access the pod you want to debug by executing - - kubectl exec --namespace {{ .Release.Namespace }} -ti -- bash - -In order to replicate the container startup scripts execute this command: - - /opt/bitnami/scripts/logstash/entrypoint.sh /opt/bitnami/scripts/logstash/run.sh - -{{- else }} - -Logstash can be accessed through following DNS names from within your cluster: - - Logstash: {{ include "common.names.fullname" . }}.{{ .Release.Namespace }}.svc.{{ .Values.clusterDomain }} - -To access Logstash from outside the cluster execute the following commands: - -{{- if .Values.ingress.enabled }} - - You should be able to access your new Logstash server(s) through: - - curl {{ if .Values.ingress.tls }}https{{- else }}http{{ end }}://{{ .Values.ingress.hostname }} - -{{- else if contains "NodePort" .Values.service.type }} - - export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "common.names.fullname" . }}) - export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") - echo "http://${NODE_IP}:${NODE_PORT}" - -{{- else if contains "LoadBalancer" .Values.service.type }} - - NOTE: It may take a few minutes for the LoadBalancer IP to be available. - You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "common.names.fullname" . }}' - export SERVICE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].port}" services {{ include "common.names.fullname" . }}) - export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "common.names.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') - echo "http://${SERVICE_IP}:${SERVICE_PORT}" - -{{- else if contains "ClusterIP" .Values.service.type }} - - export SERVICE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].port}" services {{ include "common.names.fullname" . }}) - kubectl port-forward svc/{{ include "common.names.fullname" . }} ${SERVICE_PORT}:${SERVICE_PORT} & - echo "http://127.0.0.1:${SERVICE_PORT}" - -{{- end }} -{{- end }} - -{{- include "logstash.validateValues" . }} -{{- include "logstash.checkRollingTags" . }} diff --git a/knowlg-automation/helm_charts/logstash/templates/_helpers.tpl b/knowlg-automation/helm_charts/logstash/templates/_helpers.tpl deleted file mode 100644 index 33ecbd57e..000000000 --- a/knowlg-automation/helm_charts/logstash/templates/_helpers.tpl +++ /dev/null @@ -1,67 +0,0 @@ -{{/* vim: set filetype=mustache: */}} - -{{/* -Return the proper Logstash image name -*/}} -{{- define "logstash.image" -}} -{{ include "common.images.image" (dict "imageRoot" .Values.image "global" .Values.global) }} -{{- end -}} - - -{{/* -Return the proper Docker Image Registry Secret Names -*/}} -{{- define "logstash.imagePullSecrets" -}} -{{- include "common.images.pullSecrets" (dict "images" (list .Values.image) "global" .Values.global) -}} -{{- end -}} - -{{/* -Return the Logstash configuration configmap. -*/}} -{{- define "logstash.configmapName" -}} -{{- if .Values.existingConfiguration -}} - {{- printf "%s" (tpl .Values.existingConfiguration $) -}} -{{- else -}} - {{- printf "%s" (include "common.names.fullname" .) -}} -{{- end -}} -{{- end -}} - -{{/* -Create the name of the service account to use -*/}} -{{- define "logstash.serviceAccountName" -}} -{{- if .Values.serviceAccount.create -}} - {{ default (include "common.names.fullname" .) .Values.serviceAccount.name }} -{{- else -}} - {{ default "default" .Values.serviceAccount.name }} -{{- end -}} -{{- end -}} - -{{/* -Check if there are rolling tags in the images -*/}} -{{- define "logstash.checkRollingTags" -}} -{{- include "common.warnings.rollingTag" .Values.image }} -{{- include "common.warnings.rollingTag" .Values.volumePermissions.image }} -{{- end -}} - -{{/* -Compile all warnings into a single message, and call fail. -*/}} -{{- define "logstash.validateValues" -}} -{{- $messages := list -}} -{{- $messages := without $messages "" -}} -{{- $message := join "\n" $messages -}} - -{{- if $message -}} -{{- printf "\nVALUES VALIDATION:\n%s" $message | fail -}} -{{- end -}} -{{- end -}} - - -{{/* -Return the proper image name (for the init container volume-permissions image) -*/}} -{{- define "logstash.volumePermissions.image" -}} -{{- include "common.images.image" ( dict "imageRoot" .Values.volumePermissions.image "global" .Values.global ) -}} -{{- end -}} diff --git a/knowlg-automation/helm_charts/logstash/templates/configuration-cm.yaml b/knowlg-automation/helm_charts/logstash/templates/configuration-cm.yaml deleted file mode 100644 index 2e86cb3d3..000000000 --- a/knowlg-automation/helm_charts/logstash/templates/configuration-cm.yaml +++ /dev/null @@ -1,39 +0,0 @@ -{{ if and (or .Values.input .Values.filter .Values.output) (not .Values.existingConfiguration) }} -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "common.names.fullname" . }} - namespace: {{ include "common.names.namespace" . | quote }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - {{- if or .Values.ingress.annotations .Values.commonAnnotations }} - annotations: - {{- if .Values.ingress.annotations }} - {{- include "common.tplvalues.render" ( dict "value" .Values.ingress.annotations "context" $) | nindent 4 }} - {{- end }} - {{- if .Values.commonAnnotations }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} - {{- end }} -data: -{{- if (or .Values.input .Values.filter .Values.output) }} - logstash.conf: |- -{{- if .Values.input }} - input { - {{- include "common.tplvalues.render" (dict "value" .Values.input "context" $) | nindent 6 }} - } -{{- end }} -{{- if .Values.filter }} - filter { - {{- include "common.tplvalues.render" (dict "value" .Values.filter "context" $) | nindent 6 }} - } -{{- end }} -{{- if .Values.output }} - output { - {{- include "common.tplvalues.render" (dict "value" .Values.output "context" $) | nindent 6 }} - } -{{- end }} -{{- end }} -{{- end }} diff --git a/knowlg-automation/helm_charts/logstash/templates/extra-list.yaml b/knowlg-automation/helm_charts/logstash/templates/extra-list.yaml deleted file mode 100644 index 9ac65f9e1..000000000 --- a/knowlg-automation/helm_charts/logstash/templates/extra-list.yaml +++ /dev/null @@ -1,4 +0,0 @@ -{{- range .Values.extraDeploy }} ---- -{{ include "common.tplvalues.render" (dict "value" . "context" $) }} -{{- end }} diff --git a/knowlg-automation/helm_charts/logstash/templates/headless-svc.yaml b/knowlg-automation/helm_charts/logstash/templates/headless-svc.yaml deleted file mode 100644 index 82ff92f49..000000000 --- a/knowlg-automation/helm_charts/logstash/templates/headless-svc.yaml +++ /dev/null @@ -1,22 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: {{ printf "%s-headless" (include "common.names.fullname" .) | trunc 63 | trimSuffix "-" }} - namespace: {{ include "common.names.namespace" . | quote }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - annotations: - {{- if .Values.commonAnnotations }} - {{- include "common.tplvalues.render" (dict "value" .Values.commonAnnotations "context" $) | nindent 4 }} - {{- end }} - {{- if .Values.service.annotations }} - {{- include "common.tplvalues.render" ( dict "value" .Values.service.annotations "context" $) | nindent 4 }} - {{- end }} -spec: - type: ClusterIP - clusterIP: None - ports: - {{- include "common.tplvalues.render" (dict "value" .Values.service.ports "context" $) | nindent 4 }} - selector: {{ include "common.labels.matchLabels" . | nindent 4 }} diff --git a/knowlg-automation/helm_charts/logstash/templates/ingress.yaml b/knowlg-automation/helm_charts/logstash/templates/ingress.yaml deleted file mode 100644 index 61515de2d..000000000 --- a/knowlg-automation/helm_charts/logstash/templates/ingress.yaml +++ /dev/null @@ -1,62 +0,0 @@ -{{- if .Values.ingress.enabled }} -apiVersion: {{ include "common.capabilities.ingress.apiVersion" . }} -kind: Ingress -metadata: - name: {{ template "common.names.fullname" . }} - namespace: {{ include "common.names.namespace" . | quote }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - {{- if or .Values.ingress.annotations .Values.commonAnnotations }} - annotations: - {{- if .Values.ingress.annotations }} - {{- include "common.tplvalues.render" (dict "value" .Values.ingress.annotations "context" $) | nindent 4 }} - {{- end }} - {{- if .Values.commonAnnotations }} - {{- include "common.tplvalues.render" (dict "value" .Values.commonAnnotations "context" $) | nindent 4 }} - {{- end }} - {{- end }} -spec: - {{- if and .Values.ingress.ingressClassName (eq "true" (include "common.ingress.supportsIngressClassname" .)) }} - ingressClassName: {{ .Values.ingress.ingressClassName | quote }} - {{- end }} - rules: - {{- if .Values.ingress.hostname }} - - host: {{ .Values.ingress.hostname }} - http: - paths: - - path: {{ .Values.ingress.path }} - {{- if eq "true" (include "common.ingress.supportsPathType" .) }} - pathType: {{ .Values.ingress.pathType }} - {{- end }} - backend: {{- include "common.ingress.backend" (dict "serviceName" (include "common.names.fullname" .) "servicePort" "http" "context" $) | nindent 14 }} - {{- if .Values.ingress.extraPaths }} - {{- toYaml .Values.ingress.extraPaths | nindent 10 }} - {{- end }} - {{- end }} - {{- range .Values.ingress.extraHosts }} - - host: {{ .name | quote }} - http: - paths: - - path: {{ default "/" .path }} - {{- if eq "true" (include "common.ingress.supportsPathType" $) }} - pathType: {{ default "ImplementationSpecific" .pathType }} - {{- end }} - backend: {{- include "common.ingress.backend" (dict "serviceName" (include "common.names.fullname" $) "servicePort" "http" "context" $) | nindent 14 }} - {{- end }} - {{- if .Values.ingress.extraRules }} - {{- include "common.tplvalues.render" (dict "value" .Values.ingress.extraRules "context" $) | nindent 4 }} - {{- end }} - {{- if or (and .Values.ingress.tls (or (include "common.ingress.certManagerRequest" ( dict "annotations" .Values.ingress.annotations )) .Values.ingress.selfSigned)) .Values.ingress.extraTls }} - tls: - {{- if and .Values.ingress.tls (or (include "common.ingress.certManagerRequest" ( dict "annotations" .Values.ingress.annotations )) .Values.ingress.selfSigned) }} - - hosts: - - {{ .Values.ingress.hostname | quote }} - secretName: {{ printf "%s-tls" .Values.ingress.hostname }} - {{- end }} - {{- if .Values.ingress.extraTls }} - {{- include "common.tplvalues.render" (dict "value" .Values.ingress.extraTls "context" $) | nindent 4 }} - {{- end }} - {{- end }} -{{- end }} diff --git a/knowlg-automation/helm_charts/logstash/templates/pdb.yaml b/knowlg-automation/helm_charts/logstash/templates/pdb.yaml deleted file mode 100644 index fbdb5d7fd..000000000 --- a/knowlg-automation/helm_charts/logstash/templates/pdb.yaml +++ /dev/null @@ -1,23 +0,0 @@ -{{- if .Values.pdb.create }} -apiVersion: {{ include "common.capabilities.policy.apiVersion" . }} -kind: PodDisruptionBudget -metadata: - name: {{ include "common.names.fullname" . }} - namespace: {{ include "common.names.namespace" . | quote }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" (dict "value" .Values.commonLabels "context" $) | nindent 4 }} - {{- end }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" (dict "value" .Values.commonAnnotations "context" $) | nindent 4 }} - {{- end }} -spec: - {{- if .Values.pdb.minAvailable }} - minAvailable: {{ .Values.pdb.minAvailable }} - {{- end }} - {{- if .Values.pdb.maxUnavailable }} - maxUnavailable: {{ .Values.pdb.maxUnavailable }} - {{- end }} - selector: - matchLabels: {{ include "common.labels.matchLabels" . | nindent 6 }} -{{- end }} diff --git a/knowlg-automation/helm_charts/logstash/templates/serviceaccount.yaml b/knowlg-automation/helm_charts/logstash/templates/serviceaccount.yaml deleted file mode 100644 index 21c8ec5af..000000000 --- a/knowlg-automation/helm_charts/logstash/templates/serviceaccount.yaml +++ /dev/null @@ -1,21 +0,0 @@ -{{- if .Values.serviceAccount.create -}} -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ template "logstash.serviceAccountName" . }} - namespace: {{ include "common.names.namespace" . | quote }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - {{- if or .Values.commonAnnotations .Values.serviceAccount.annotations }} - annotations: - {{- if .Values.serviceAccount.annotations }} - {{- include "common.tplvalues.render" ( dict "value" .Values.serviceAccount.annotations "context" $ ) | nindent 4 }} - {{- end }} - {{- if .Values.commonAnnotations }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} - {{- end }} -automountServiceAccountToken: {{ .Values.serviceAccount.automountServiceAccountToken }} -{{- end -}} diff --git a/knowlg-automation/helm_charts/logstash/templates/sts.yaml b/knowlg-automation/helm_charts/logstash/templates/sts.yaml deleted file mode 100644 index e77c3a9df..000000000 --- a/knowlg-automation/helm_charts/logstash/templates/sts.yaml +++ /dev/null @@ -1,219 +0,0 @@ -apiVersion: {{ include "common.capabilities.statefulset.apiVersion" . }} -kind: StatefulSet -metadata: - name: {{ include "common.names.fullname" . }} - namespace: {{ include "common.names.namespace" . | quote }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -spec: - serviceName: {{ printf "%s-headless" (include "common.names.fullname" .) }} - replicas: {{ .Values.replicaCount }} - podManagementPolicy: {{ .Values.podManagementPolicy }} - {{- if .Values.updateStrategy }} - updateStrategy: {{- toYaml .Values.updateStrategy | nindent 4 }} - {{- end }} - selector: - matchLabels: {{- include "common.labels.matchLabels" . | nindent 6 }} - template: - metadata: - labels: {{- include "common.labels.standard" . | nindent 8 }} - {{- if .Values.podLabels }} - {{- include "common.tplvalues.render" (dict "value" .Values.podLabels "context" $) | nindent 8 }} - {{- end }} - annotations: - checksum/configuration: {{ include (print $.Template.BasePath "/configuration-cm.yaml") . | sha256sum }} - {{- if .Values.podAnnotations }} - {{- include "common.tplvalues.render" (dict "value" .Values.podAnnotations "context" $) | nindent 8 }} - {{- end }} - spec: - {{- include "logstash.imagePullSecrets" . | nindent 6 }} - serviceAccountName: {{ template "logstash.serviceAccountName" . }} - {{- if .Values.hostAliases }} - hostAliases: {{- include "common.tplvalues.render" (dict "value" .Values.hostAliases "context" $) | nindent 8 }} - {{- end }} - {{- if .Values.affinity }} - affinity: {{- include "common.tplvalues.render" ( dict "value" .Values.affinity "context" $) | nindent 8 }} - {{- else }} - affinity: - podAffinity: {{- include "common.affinities.pods" (dict "type" .Values.podAffinityPreset "context" $) | nindent 10 }} - podAntiAffinity: {{- include "common.affinities.pods" (dict "type" .Values.podAntiAffinityPreset "context" $) | nindent 10 }} - nodeAffinity: {{- include "common.affinities.nodes" (dict "type" .Values.nodeAffinityPreset.type "key" .Values.nodeAffinityPreset.key "values" .Values.nodeAffinityPreset.values) | nindent 10 }} - {{- end }} - {{- if .Values.nodeSelector }} - nodeSelector: {{- include "common.tplvalues.render" ( dict "value" .Values.nodeSelector "context" $) | nindent 8 }} - {{- end }} - {{- if .Values.tolerations }} - tolerations: {{- include "common.tplvalues.render" (dict "value" .Values.tolerations "context" .) | nindent 8 }} - {{- end }} - {{- if .Values.schedulerName }} - schedulerName: {{ .Values.schedulerName }} - {{- end }} - {{- if .Values.topologySpreadConstraints }} - topologySpreadConstraints: {{- include "common.tplvalues.render" (dict "value" .Values.topologySpreadConstraints "context" .) | nindent 8 }} - {{- end }} - {{- if .Values.priorityClassName }} - priorityClassName: {{ .Values.priorityClassName | quote }} - {{- end }} - {{- if .Values.terminationGracePeriodSeconds }} - terminationGracePeriodSeconds: {{ .Values.terminationGracePeriodSeconds }} - {{- end }} - {{- if .Values.podSecurityContext.enabled }} - securityContext: {{- omit .Values.podSecurityContext "enabled" | toYaml | nindent 8 }} - {{- end }} - initContainers: - {{- if and .Values.persistence.enabled .Values.volumePermissions.enabled }} - - name: volume-permissions - image: {{ include "logstash.volumePermissions.image" . }} - imagePullPolicy: {{ .Values.volumePermissions.image.pullPolicy | quote }} - command: - - /bin/bash - - -ec - - | - mkdir -p "{{ .Values.persistence.mountPath }}" - chown "{{ .Values.containerSecurityContext.runAsUser }}:{{ .Values.podSecurityContext.fsGroup }}" "{{ .Values.persistence.mountPath }}" - find "{{ .Values.persistence.mountPath }}" -mindepth 1 -maxdepth 1 -not -name ".snapshot" -not -name "lost+found" | xargs -r chown -R "{{ .Values.containerSecurityContext.runAsUser }}:{{ .Values.podSecurityContext.fsGroup }}" - securityContext: {{ .Values.volumePermissions.securityContext | toYaml | nindent 12 }} - {{- if .Values.volumePermissions.resources }} - resources: {{- toYaml .Values.volumePermissions.resources | nindent 12 }} - {{- end }} - volumeMounts: - - name: data - mountPath: {{ .Values.persistence.mountPath }} - {{- end }} - {{- if .Values.initContainers }} - {{- include "common.tplvalues.render" (dict "value" .Values.initContainers "context" $) | nindent 8 }} - {{- end }} - containers: - - name: logstash - image: {{ include "logstash.image" . }} - imagePullPolicy: {{ .Values.image.pullPolicy | quote }} - {{- if .Values.containerSecurityContext.enabled }} - securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 12 }} - {{- end }} - {{- if .Values.diagnosticMode.enabled }} - command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 12 }} - {{- else if .Values.command }} - command: {{- include "common.tplvalues.render" (dict "value" .Values.command "context" $) | nindent 12 }} - {{- end }} - {{- if .Values.diagnosticMode.enabled }} - args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 12 }} - {{- else if .Values.args }} - args: {{- include "common.tplvalues.render" (dict "value" .Values.args "context" $) | nindent 12 }} - {{- end }} - {{- if .Values.lifecycleHooks }} - lifecycle: {{- include "common.tplvalues.render" (dict "value" .Values.lifecycleHooks "context" $) | nindent 12 }} - {{- end }} - env: - - name: BITNAMI_DEBUG - value: {{ ternary "true" "false" (or .Values.image.debug .Values.diagnosticMode.enabled) | quote }} - {{- if .Values.configFileName }} - - name: LOGSTASH_CONF_FILENAME - value: {{ .Values.configFileName | quote }} - {{- end }} - - name: LOGSTASH_ENABLE_MULTIPLE_PIPELINES - value: {{ ternary "true" "false" .Values.enableMultiplePipelines | quote }} - - name: LOGSTASH_EXPOSE_API - value: {{ ternary "yes" "no" .Values.enableMonitoringAPI | quote }} - - name: LOGSTASH_API_PORT_NUMBER - value: {{ .Values.monitoringAPIPort | quote }} - {{- if .Values.persistence.enabled }} - - name: LOGSTASH_DATA_DIR - value: {{ .Values.persistence.mountPath }} - {{- end }} - {{- if .Values.extraEnvVars }} - {{- include "common.tplvalues.render" (dict "value" .Values.extraEnvVars "context" $) | nindent 12 }} - {{- end }} - envFrom: - {{- if .Values.extraEnvVarsCM }} - - configMapRef: - name: {{ .Values.extraEnvVarsCM }} - {{- end }} - {{- if .Values.extraEnvVarsSecret }} - - secretRef: - name: {{ .Values.extraEnvVarsSecret }} - {{- end }} - {{- if .Values.containerPorts }} - ports: {{ toYaml .Values.containerPorts | nindent 12 }} - {{- end }} - {{- if not .Values.diagnosticMode.enabled }} - {{- if .Values.customLivenessProbe }} - livenessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.customLivenessProbe "context" $) | nindent 12 }} - {{- else if .Values.livenessProbe.enabled }} - livenessProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.livenessProbe "enabled") "context" $) | nindent 12 }} - httpGet: - path: / - port: monitoring - {{- end }} - {{- if .Values.customReadinessProbe }} - readinessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.customReadinessProbe "context" $) | nindent 12 }} - {{- else if .Values.readinessProbe.enabled }} - readinessProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.readinessProbe "enabled") "context" $) | nindent 12 }} - httpGet: - path: / - port: monitoring - {{- end }} - {{- if .Values.customStartupProbe }} - startupProbe: {{- include "common.tplvalues.render" (dict "value" .Values.customStartupProbe "context" $) | nindent 12 }} - {{- else if .Values.startupProbe.enabled }} - startupProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.startupProbe "enabled") "context" $) | nindent 12 }} - tcpSocket: - port: monitoring - {{- end }} - {{- end }} - {{- if .Values.resources }} - resources: {{- toYaml .Values.resources | nindent 12 }} - {{- end }} - volumeMounts: - {{- if .Values.persistence.enabled }} - - name: data - mountPath: {{ .Values.persistence.mountPath }} - {{- end }} - {{- if or .Values.input .Values.filter .Values.output .Values.existingConfiguration }} - - name: configurations - mountPath: /bitnami/logstash/config - {{- end }} - {{- if .Values.extraVolumeMounts }} - {{- include "common.tplvalues.render" ( dict "value" .Values.extraVolumeMounts "context" $ ) | nindent 12 }} - {{- end }} - {{- if .Values.sidecars }} - {{- include "common.tplvalues.render" ( dict "value" .Values.sidecars "context" $) | nindent 8 }} - {{- end }} - volumes: - {{- if or .Values.input .Values.filter .Values.output .Values.existingConfiguration }} - - name: configurations - configMap: - name: {{ include "logstash.configmapName" . }} - {{- end }} - {{- if and .Values.persistence.enabled }} - - name: data - persistentVolumeClaim: - claimName: {{ .Values.persistence.existingClaim | default (include "common.names.fullname" .) }} - {{- end }} - {{- if .Values.extraVolumes }} - {{- include "common.tplvalues.render" ( dict "value" .Values.extraVolumes "context" $ ) | nindent 8 }} - {{- end }} - {{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }} - volumeClaimTemplates: - - metadata: - name: data - {{- if .Values.persistence.annotations }} - annotations: {{- include "common.tplvalues.render" (dict "value" .Values.persistence.annotations "context" $) | nindent 10 }} - {{- end }} - spec: - accessModes: - {{- range .Values.persistence.accessModes }} - - {{ . | quote }} - {{- end }} - resources: - requests: - storage: {{ .Values.persistence.size | quote }} - {{- if .Values.persistence.selector }} - selector: {{- include "common.tplvalues.render" ( dict "value" .Values.persistence.selector "context" $) | nindent 10 }} - {{- end }} - {{ include "common.storage.class" (dict "persistence" .Values.persistence "global" .Values.global) }} - {{- end }} diff --git a/knowlg-automation/helm_charts/logstash/templates/svc.yaml b/knowlg-automation/helm_charts/logstash/templates/svc.yaml deleted file mode 100644 index 295116b24..000000000 --- a/knowlg-automation/helm_charts/logstash/templates/svc.yaml +++ /dev/null @@ -1,39 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: {{ include "common.names.fullname" . }} - namespace: {{ include "common.names.namespace" . | quote }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - annotations: - {{- if .Values.commonAnnotations }} - {{- include "common.tplvalues.render" (dict "value" .Values.commonAnnotations "context" $) | nindent 4 }} - {{- end }} - {{- if .Values.service.annotations }} - {{- include "common.tplvalues.render" ( dict "value" .Values.service.annotations "context" $) | nindent 4 }} - {{- end }} -spec: - type: {{ .Values.service.type }} - {{- if and .Values.service.loadBalancerIP (eq .Values.service.type "LoadBalancer") }} - loadBalancerIP: {{ .Values.service.loadBalancerIP }} - {{- end }} - {{- if and (eq .Values.service.type "LoadBalancer") .Values.service.loadBalancerSourceRanges }} - loadBalancerSourceRanges: {{- toYaml .Values.service.loadBalancerSourceRanges | nindent 4 }} - {{- end }} - {{- if and .Values.service.clusterIP (eq .Values.service.type "ClusterIP") }} - clusterIP: {{ .Values.service.clusterIP }} - {{- end }} - {{- if or (eq .Values.service.type "LoadBalancer") (eq .Values.service.type "NodePort") }} - externalTrafficPolicy: {{ .Values.service.externalTrafficPolicy | quote }} - {{- end }} - {{- if .Values.service.sessionAffinity }} - sessionAffinity: {{ .Values.service.sessionAffinity }} - {{- end }} - {{- if .Values.service.sessionAffinityConfig }} - sessionAffinityConfig: {{- include "common.tplvalues.render" (dict "value" .Values.service.sessionAffinityConfig "context" $) | nindent 4 }} - {{- end }} - ports: - {{- include "common.tplvalues.render" (dict "value" .Values.service.ports "context" $) | nindent 4 }} - selector: {{ include "common.labels.matchLabels" . | nindent 4 }} diff --git a/knowlg-automation/helm_charts/logstash/templates/tls-secret.yaml b/knowlg-automation/helm_charts/logstash/templates/tls-secret.yaml deleted file mode 100644 index d9dfbe943..000000000 --- a/knowlg-automation/helm_charts/logstash/templates/tls-secret.yaml +++ /dev/null @@ -1,44 +0,0 @@ -{{- if .Values.ingress.enabled }} -{{- if .Values.ingress.secrets }} -{{- range .Values.ingress.secrets }} -apiVersion: v1 -kind: Secret -metadata: - name: {{ .name }} - namespace: {{ include "common.names.namespace" $ | quote }} - labels: {{- include "common.labels.standard" $ | nindent 4 }} - {{- if $.Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - {{- if $.Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -type: kubernetes.io/tls -data: - tls.crt: {{ .certificate | b64enc }} - tls.key: {{ .key | b64enc }} ---- -{{- end }} -{{- else if and .Values.ingress.tls .Values.ingress.selfSigned }} -{{- $secretName := printf "%s-tls" .Values.ingress.hostname }} -{{- $ca := genCA "logstash-ca" 365 }} -{{- $cert := genSignedCert .Values.ingress.hostname nil (list .Values.ingress.hostname) 365 $ca }} -apiVersion: v1 -kind: Secret -metadata: - name: {{ $secretName }} - namespace: {{ include "common.names.namespace" $ | quote }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -type: kubernetes.io/tls -data: - tls.crt: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "tls.crt" "defaultValue" $cert.Cert "context" $) }} - tls.key: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "tls.key" "defaultValue" $cert.Key "context" $) }} - ca.crt: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "ca.crt" "defaultValue" $ca.Cert "context" $) }} -{{- end }} -{{- end }} diff --git a/knowlg-automation/helm_charts/logstash/values.yaml b/knowlg-automation/helm_charts/logstash/values.yaml deleted file mode 100644 index 43424a1af..000000000 --- a/knowlg-automation/helm_charts/logstash/values.yaml +++ /dev/null @@ -1,662 +0,0 @@ -## @section Global parameters -## Global Docker image parameters -## Please, note that this will override the image parameters, including dependencies, configured to use the global value -## Current available global Docker image parameters: imageRegistry, imagePullSecrets and storageClass - -## @param global.imageRegistry Global Docker image registry -## @param global.imagePullSecrets Global Docker registry secret names as an array -## @param global.storageClass Global StorageClass for Persistent Volume(s) -## -global: - imageRegistry: "" - ## E.g. - ## imagePullSecrets: - ## - myRegistryKeySecretName - ## - imagePullSecrets: [] - storageClass: "" -db_namespace: knowlg-db -## @section Common parameters - -## @param kubeVersion Force target Kubernetes version (using Helm capabilities if not set) -## -kubeVersion: "" -## @param nameOverride String to partially override logstash.fullname template (will maintain the release name) -## -nameOverride: "" -## @param fullnameOverride String to fully override logstash.fullname template -## -fullnameOverride: "" -## @param clusterDomain Default Kubernetes cluster domain -## -clusterDomain: cluster.local -## @param commonAnnotations Annotations to add to all deployed objects -## -commonAnnotations: {} -## @param commonLabels Labels to add to all deployed objects -## -commonLabels: {} -## @param extraDeploy Array of extra objects to deploy with the release (evaluated as a template). -## -extraDeploy: [] - -## Enable diagnostic mode in the deployment -## -diagnosticMode: - ## @param diagnosticMode.enabled Enable diagnostic mode (all probes will be disabled and the command will be overridden) - ## - enabled: false - ## @param diagnosticMode.command Command to override all containers in the deployment - ## - command: - - sleep - ## @param diagnosticMode.args Args to override all containers in the deployment - ## - args: - - infinity - -## @section Logstash parameters - -## Bitnami Logstash image -## ref: https://hub.docker.com/r/bitnami/logstash/tags/ -## @param image.registry Logstash image registry -## @param image.repository Logstash image repository -## @param image.tag Logstash image tag (immutable tags are recommended) -## @param image.digest Logstash image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag -## @param image.pullPolicy Logstash image pull policy -## @param image.pullSecrets Specify docker-registry secret names as an array -## @param image.debug Specify if debug logs should be enabled -## -image: - registry: docker.io - repository: bitnami/logstash - tag: 8.6.2-debian-11-r3 - digest: "" - ## Specify a imagePullPolicy. Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' - ## ref: https://kubernetes.io/docs/user-guide/images/#pre-pulling-images - ## - pullPolicy: IfNotPresent - ## Optionally specify an array of imagePullSecrets (secrets must be manually created in the namespace) - ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ - ## Example: - ## pullSecrets: - ## - myRegistryKeySecretName - ## - pullSecrets: [] - ## Set to true if you would like to see extra information on logs - ## - debug: false -## @param hostAliases Add deployment host aliases -## https://kubernetes.io/docs/concepts/services-networking/add-entries-to-pod-etc-hosts-with-host-aliases/ -## -hostAliases: [] -## @param configFileName Logstash configuration file name. It must match the name of the configuration file mounted as a configmap. -## -configFileName: logstash.conf -## @param enableMonitoringAPI Whether to enable the Logstash Monitoring API or not Kubernetes cluster domain -## -enableMonitoringAPI: true -## @param monitoringAPIPort Logstash Monitoring API Port -## -monitoringAPIPort: 9600 -## @param extraEnvVars Array containing extra env vars to configure Logstash -## For example: -## extraEnvVars: -## - name: ELASTICSEARCH_HOST -## value: "x.y.z" -## -extraEnvVars: [] -## @param extraEnvVarsSecret To add secrets to environment -## -extraEnvVarsSecret: "" -## @param extraEnvVarsCM To add configmaps to environment -## -extraEnvVarsCM: "" -## @param input [string] Input Plugins configuration -## ref: https://www.elastic.co/guide/en/logstash/current/input-plugins.html -## -input: |- - # udp { - # port => 1514 - # type => syslog - # } - # tcp { - # port => 1514 - # type => syslog - # } - http { port => 8080 } -## @param filter Filter Plugins configuration -## ref: https://www.elastic.co/guide/en/logstash/current/filter-plugins.html -## e.g: -## filter: |- -## grok { -## match => { "message" => "%{COMBINEDAPACHELOG}" } -## } -## date { -## match => [ "timestamp" , "dd/MMM/yyyy:HH:mm:ss Z" ] -## } -## -filter: "" -## @param output [string] Output Plugins configuration -## ref: https://www.elastic.co/guide/en/logstash/current/output-plugins.html -## -output: |- - # elasticsearch { - # hosts => ["${ELASTICSEARCH_HOST}:${ELASTICSEARCH_PORT}"] - # manage_template => false - # index => "%{[@metadata][beat]}-%{+YYYY.MM.dd}" - # } - # gelf { - # host => "${GRAYLOG_HOST}" - # port => ${GRAYLOG_PORT} - # } - stdout {} -## @param existingConfiguration Name of existing ConfigMap object with the Logstash configuration (`input`, `filter`, and `output` will be ignored). -## -existingConfiguration: "" -## @param enableMultiplePipelines Allows user to use multiple pipelines -## ref: https://www.elastic.co/guide/en/logstash/master/multiple-pipelines.html -## -enableMultiplePipelines: false -## @param extraVolumes Array to add extra volumes (evaluated as a template) -## extraVolumes: -## - name: myvolume -## configMap: -## name: myconfigmap -## -extraVolumes: [] -## @param extraVolumeMounts Array to add extra mounts (normally used with extraVolumes, evaluated as a template) -## extraVolumeMounts: -## - mountPath: /opt/bitnami/desired-path -## name: myvolume -## readOnly: true -## -extraVolumeMounts: [] - - ## ServiceAccount for Logstash - ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ - ## -serviceAccount: - ## @param serviceAccount.create Enable creation of ServiceAccount for Logstash pods - ## - create: true - ## @param serviceAccount.name The name of the service account to use. If not set and `create` is `true`, a name is generated - ## If not set and create is true, a name is generated using the logstash.serviceAccountName template - ## - name: "" - ## @param serviceAccount.automountServiceAccountToken Allows automount of ServiceAccountToken on the serviceAccount created - ## Can be set to false if pods using this serviceAccount do not need to use K8s API - ## - automountServiceAccountToken: true - ## @param serviceAccount.annotations Additional custom annotations for the ServiceAccount - ## - annotations: {} - -## @param containerPorts [array] Array containing the ports to open in the Logstash container (evaluated as a template) -## -containerPorts: - - name: http - containerPort: 8080 - protocol: TCP - - name: monitoring - containerPort: 9600 - protocol: TCP - ## - name: syslog-udp - ## containerPort: 1514 - ## protocol: UDP - ## - name: syslog-tcp - ## containerPort: 1514 - ## protocol: TCP - ## -## @param initContainers Add additional init containers to the Logstash pod(s) -## ref: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ -## e.g: -## initContainers: -## - name: your-image-name -## image: your-image -## imagePullPolicy: Always -## command: ['sh', '-c', 'echo "hello world"'] -## -initContainers: [] -## @param sidecars Add additional sidecar containers to the Logstash pod(s) -## e.g: -## sidecars: -## - name: your-image-name -## image: your-image -## imagePullPolicy: Always -## ports: -## - name: portname -## containerPort: 1234 -## -sidecars: [] -## @param replicaCount Number of Logstash replicas to deploy -## -replicaCount: 1 -## @param updateStrategy.type Update strategy type (`RollingUpdate`, or `OnDelete`) -## ref: https://kubernetes.io/docs/tutorials/stateful-application/basic-stateful-set/#updating-statefulsets -## -updateStrategy: - type: RollingUpdate -## @param podManagementPolicy Pod management policy -## https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#pod-management-policies -## -podManagementPolicy: OrderedReady -## @param podAnnotations Pod annotations -## Ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ -## -podAnnotations: {} -## @param podLabels Extra labels for Logstash pods -## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ -## -podLabels: {} -## @param podAffinityPreset Pod affinity preset. Ignored if `affinity` is set. Allowed values: `soft` or `hard` -## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity -## -podAffinityPreset: "" -## @param podAntiAffinityPreset Pod anti-affinity preset. Ignored if `affinity` is set. Allowed values: `soft` or `hard` -## Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity -## -podAntiAffinityPreset: soft -## Node affinity preset -## Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity -## -nodeAffinityPreset: - ## @param nodeAffinityPreset.type Node affinity preset type. Ignored if `affinity` is set. Allowed values: `soft` or `hard` - ## - type: "" - ## @param nodeAffinityPreset.key Node label key to match. Ignored if `affinity` is set. - ## E.g. - ## key: "kubernetes.io/e2e-az-name" - ## - key: "" - ## @param nodeAffinityPreset.values Node label values to match. Ignored if `affinity` is set. - ## E.g. - ## values: - ## - e2e-az1 - ## - e2e-az2 - ## - values: [] -## @param affinity Affinity for pod assignment -## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity -## Note: podAffinityPreset, podAntiAffinityPreset, and nodeAffinityPreset will be ignored when it's set -## -affinity: {} -## @param nodeSelector Node labels for pod assignment -## ref: https://kubernetes.io/docs/user-guide/node-selection/ -## -nodeSelector: {} -## @param tolerations Tolerations for pod assignment -## ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ -## -tolerations: [] -## @param priorityClassName Pod priority -## ref: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/ -## -priorityClassName: "" -## @param schedulerName Name of the k8s scheduler (other than default) -## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ -## -schedulerName: "" -## @param terminationGracePeriodSeconds In seconds, time the given to the Logstash pod needs to terminate gracefully -## ref: https://kubernetes.io/docs/concepts/workloads/pods/pod/#termination-of-pods -## -terminationGracePeriodSeconds: "" -## @param topologySpreadConstraints Topology Spread Constraints for pod assignment -## https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ -## The value is evaluated as a template -## -topologySpreadConstraints: [] - -## K8s Security Context for Logstash pods -## Configure Pods Security Context -## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod -## @param podSecurityContext.enabled Enabled Logstash pods' Security Context -## @param podSecurityContext.fsGroup Set Logstash pod's Security Context fsGroup -## -podSecurityContext: - enabled: true - fsGroup: 1001 -## Configure Container Security Context -## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod -## @param containerSecurityContext.enabled Enabled Logstash containers' Security Context -## @param containerSecurityContext.runAsUser Set Logstash containers' Security Context runAsUser -## @param containerSecurityContext.runAsNonRoot Set Logstash container's Security Context runAsNonRoot -## -containerSecurityContext: - enabled: true - runAsUser: 1001 - runAsNonRoot: true -## @param command Override default container command (useful when using custom images) -## -command: [] -## @param args Override default container args (useful when using custom images) -## -args: [] -## @param lifecycleHooks for the Logstash container(s) to automate configuration before or after startup -## -lifecycleHooks: {} -## Logstash containers' resource requests and limits -## ref: https://kubernetes.io/docs/user-guide/compute-resources/ -## We usually recommend not to specify default resources and to leave this as a conscious -## choice for the user. This also increases chances charts run on environments with little -## resources, such as Minikube. If you do want to specify resources, uncomment the following -## lines, adjust them as necessary, and remove the curly braces after 'resources:'. -## @param resources.limits The resources limits for the Logstash container -## @param resources.requests The requested resources for the Logstash container -## -resources: - ## Example: - ## limits: - ## cpu: 100m - ## memory: 128Mi - limits: {} - ## Examples: - ## requests: - ## cpu: 100m - ## memory: 128Mi - requests: {} -## Configure extra options for Logstash containers' liveness, readiness and startup probes -## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes -## @param startupProbe.enabled Enable startupProbe -## @param startupProbe.initialDelaySeconds Initial delay seconds for startupProbe -## @param startupProbe.periodSeconds Period seconds for startupProbe -## @param startupProbe.timeoutSeconds Timeout seconds for startupProbe -## @param startupProbe.failureThreshold Failure threshold for startupProbe -## @param startupProbe.successThreshold Success threshold for startupProbe -## -startupProbe: - enabled: false - initialDelaySeconds: 60 - periodSeconds: 10 - timeoutSeconds: 5 - successThreshold: 1 - failureThreshold: 6 -## @param livenessProbe.enabled Enable livenessProbe -## @param livenessProbe.initialDelaySeconds Initial delay seconds for livenessProbe -## @param livenessProbe.periodSeconds Period seconds for livenessProbe -## @param livenessProbe.timeoutSeconds Timeout seconds for livenessProbe -## @param livenessProbe.failureThreshold Failure threshold for livenessProbe -## @param livenessProbe.successThreshold Success threshold for livenessProbe -## -livenessProbe: - enabled: true - initialDelaySeconds: 60 - periodSeconds: 10 - timeoutSeconds: 5 - successThreshold: 1 - failureThreshold: 6 -## @param readinessProbe.enabled Enable readinessProbe -## @param readinessProbe.initialDelaySeconds Initial delay seconds for readinessProbe -## @param readinessProbe.periodSeconds Period seconds for readinessProbe -## @param readinessProbe.timeoutSeconds Timeout seconds for readinessProbe -## @param readinessProbe.failureThreshold Failure threshold for readinessProbe -## @param readinessProbe.successThreshold Success threshold for readinessProbe -## -readinessProbe: - enabled: true - initialDelaySeconds: 60 - periodSeconds: 10 - timeoutSeconds: 5 - successThreshold: 1 - failureThreshold: 6 -## @param customStartupProbe Custom startup probe for the Web component -## -customStartupProbe: {} -## @param customLivenessProbe Custom liveness probe for the Web component -## -customLivenessProbe: {} -## @param customReadinessProbe Custom readiness probe for the Web component -## -customReadinessProbe: {} -## Service parameters -## -service: - ## @param service.type Kubernetes service type (`ClusterIP`, `NodePort`, or `LoadBalancer`) - ## - type: ClusterIP - ## @param service.ports [array] Logstash service ports (evaluated as a template) - ## - ports: - - name: http - port: 8080 - targetPort: http - protocol: TCP - ## - name: syslog-udp - ## port: 1514 - ## targetPort: syslog-udp - ## protocol: UDP - ## - name: syslog-tcp - ## port: 1514 - ## targetPort: syslog-tcp - ## protocol: TCP - ## - ## @param service.loadBalancerIP loadBalancerIP if service type is `LoadBalancer` - ## - loadBalancerIP: "" - ## @param service.loadBalancerSourceRanges Addresses that are allowed when service is LoadBalancer - ## https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/#restrict-access-for-loadbalancer-service - ## e.g: - ## loadBalancerSourceRanges: - ## - 10.10.10.0/24 - ## - loadBalancerSourceRanges: [] - ## @param service.externalTrafficPolicy External traffic policy, configure to Local to preserve client source IP when using an external loadBalancer - ## ref https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip - ## - externalTrafficPolicy: "" - ## @param service.clusterIP Static clusterIP or None for headless services - ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#choosing-your-own-ip-address - ## e.g: - ## clusterIP: None - ## - clusterIP: "" - ## @param service.annotations Annotations for Logstash service - ## - annotations: {} - ## @param service.sessionAffinity Session Affinity for Kubernetes service, can be "None" or "ClientIP" - ## If "ClientIP", consecutive client requests will be directed to the same Pod - ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies - ## - sessionAffinity: None - ## @param service.sessionAffinityConfig Additional settings for the sessionAffinity - ## sessionAffinityConfig: - ## clientIP: - ## timeoutSeconds: 300 - ## - sessionAffinityConfig: {} -## Persistence parameters -## -persistence: - ## @param persistence.enabled Enable Logstash data persistence using PVC - ## - enabled: false - ## @param persistence.existingClaim A manually managed Persistent Volume and Claim - ## If defined, PVC must be created manually before volume will be bound - ## The value is evaluated as a template - ## - existingClaim: "" - ## @param persistence.storageClass PVC Storage Class for Logstash data volume - ## If defined, storageClassName: - ## If set to "-", storageClassName: "", which disables dynamic provisioning - ## If undefined (the default) or set to null, no storageClassName spec is - ## set, choosing the default provisioner. - ## - storageClass: "" - ## @param persistence.accessModes PVC Access Mode for Logstash data volume - ## - accessModes: - - ReadWriteOnce - ## @param persistence.size PVC Storage Request for Logstash data volume - ## - size: 2Gi - ## @param persistence.annotations Annotations for the PVC - ## - annotations: {} - ## @param persistence.mountPath Mount path of the Logstash data volume - ## - mountPath: /bitnami/logstash/data - ## @param persistence.selector Selector to match an existing Persistent Volume for WordPress data PVC - ## If set, the PVC can't have a PV dynamically provisioned for it - ## E.g. - ## selector: - ## matchLabels: - ## app: my-app - ## - selector: {} -## Init Container parameters -## Change the owner and group of the persistent volume(s) mountpoint(s) to 'runAsUser:fsGroup' on each component -## values from the securityContext section of the component -## -volumePermissions: - ## @param volumePermissions.enabled Enable init container that changes the owner and group of the persistent volume(s) mountpoint to `runAsUser:fsGroup` - ## - enabled: false - ## The security context for the volumePermissions init container - ## @param volumePermissions.securityContext.runAsUser User ID for the volumePermissions init container - ## - securityContext: - runAsUser: 0 - ## @param volumePermissions.image.registry Init container volume-permissions image registry - ## @param volumePermissions.image.repository Init container volume-permissions image repository - ## @param volumePermissions.image.tag Init container volume-permissions image tag (immutable tags are recommended) - ## @param volumePermissions.image.digest Init container volume-permissions image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag - ## @param volumePermissions.image.pullPolicy Init container volume-permissions image pull policy - ## @param volumePermissions.image.pullSecrets Specify docker-registry secret names as an array - ## - image: - registry: docker.io - repository: bitnami/bitnami-shell - tag: 11-debian-11-r90 - digest: "" - ## Specify a imagePullPolicy - ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' - ## ref: https://kubernetes.io/docs/user-guide/images/#pre-pulling-images - ## - pullPolicy: IfNotPresent - ## Optionally specify an array of imagePullSecrets (secrets must be manually created in the namespace) - ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ - ## Example: - ## pullSecrets: - ## - myRegistryKeySecretName - ## - pullSecrets: [] - ## Init Container resource requests and limits - ## ref: https://kubernetes.io/docs/user-guide/compute-resources/ - ## We usually recommend not to specify default resources and to leave this as a conscious - ## choice for the user. This also increases chances charts run on environments with little - ## resources, such as Minikube. If you do want to specify resources, uncomment the following - ## lines, adjust them as necessary, and remove the curly braces after 'resources:'. - ## @param volumePermissions.resources.limits Init container volume-permissions resource limits - ## @param volumePermissions.resources.requests Init container volume-permissions resource requests - ## - resources: - ## Example: - ## limits: - ## cpu: 100m - ## memory: 128Mi - limits: {} - ## Examples: - ## requests: - ## cpu: 100m - ## memory: 128Mi - requests: {} -## Configure the ingress resource that allows you to access the -## Logstash installation. Set up the URL -## ref: https://kubernetes.io/docs/user-guide/ingress/ -## -ingress: - ## @param ingress.enabled Enable ingress controller resource - ## - enabled: false - ## @param ingress.selfSigned Create a TLS secret for this ingress record using self-signed certificates generated by Helm - ## - selfSigned: false - ## @param ingress.pathType Ingress Path type - ## - pathType: ImplementationSpecific - ## @param ingress.apiVersion Override API Version (automatically detected if not set) - ## - apiVersion: "" - ## @param ingress.hostname Default host for the ingress resource - ## - hostname: logstash.local - ## @param ingress.path The Path to Logstash. You may need to set this to '/*' in order to use this with ALB ingress controllers. - ## - path: / - ## @param ingress.annotations Additional annotations for the Ingress resource. To enable certificate autogeneration, place here your cert-manager annotations. - ## For a full list of possible ingress annotations, please see - ## ref: https://github.com/kubernetes/ingress-nginx/blob/master/docs/user-guide/nginx-configuration/annotations.md - ## Use this parameter to set the required annotations for cert-manager, see - ## ref: https://cert-manager.io/docs/usage/ingress/#supported-annotations - ## - ## e.g: - ## annotations: - ## kubernetes.io/ingress.class: nginx - ## cert-manager.io/cluster-issuer: cluster-issuer-name - ## - annotations: {} - ## @param ingress.tls Enable TLS configuration for the hostname defined at ingress.hostname parameter - ## TLS certificates will be retrieved from a TLS secret with name: {{- printf "%s-tls" .Values.ingress.hostname }} - ## You can use the ingress.secrets parameter to create this TLS secret or relay on cert-manager to create it - ## - tls: false - ## @param ingress.extraHosts The list of additional hostnames to be covered with this ingress record. - ## Most likely the hostname above will be enough, but in the event more hosts are needed, this is an array - ## extraHosts: - ## - name: logstash.local - ## path: / - ## - extraHosts: [] - ## @param ingress.extraPaths Any additional arbitrary paths that may need to be added to the ingress under the main host. - ## For example: The ALB ingress controller requires a special rule for handling SSL redirection. - ## extraPaths: - ## - path: /* - ## backend: - ## serviceName: ssl-redirect - ## servicePort: use-annotation - ## - extraPaths: [] - ## @param ingress.extraRules The list of additional rules to be added to this ingress record. Evaluated as a template - ## Useful when looking for additional customization, such as using different backend - ## - extraRules: [] - ## @param ingress.extraTls The tls configuration for additional hostnames to be covered with this ingress record. - ## see: https://kubernetes.io/docs/concepts/services-networking/ingress/#tls - ## extraTls: - ## - hosts: - ## - logstash.local - ## secretName: logstash.local-tls - ## - extraTls: [] - ## @param ingress.secrets If you're providing your own certificates, please use this to add the certificates as secrets - ## key and certificate should start with -----BEGIN CERTIFICATE----- or - ## -----BEGIN RSA PRIVATE KEY----- - ## - ## name should line up with a tlsSecret set further up - ## If you're using cert-manager, this is unneeded, as it will create the secret for you if it is not set - ## - ## It is also possible to create and manage the certificates outside of this helm chart - ## Please see README.md for more information - ## - ## secrets: - ## - name: logstash.local-tls - ## key: - ## certificate: - ## - secrets: [] - ## @param ingress.ingressClassName IngressClass that will be be used to implement the Ingress (Kubernetes 1.18+) - ## This is supported in Kubernetes 1.18+ and required if you have more than one IngressClass marked as the default for your cluster . - ## ref: https://kubernetes.io/blog/2020/04/02/improvements-to-the-ingress-api-in-kubernetes-1.18/ - ## - ingressClassName: "" - -## Pod disruption budget configuration -## ref: https://kubernetes.io/docs/concepts/workloads/pods/disruptions/ -## @param pdb.create If true, create a pod disruption budget for pods. -## @param pdb.minAvailable Minimum number / percentage of pods that should remain scheduled -## @param pdb.maxUnavailable Maximum number / percentage of pods that may be made unavailable -## -pdb: - create: false - minAvailable: 1 - maxUnavailable: "" diff --git a/knowlg-automation/helm_charts/neo4j/templates/deployment.yaml b/knowlg-automation/helm_charts/neo4j/templates/deployment.yaml index 61080e988..1dc61d6ca 100644 --- a/knowlg-automation/helm_charts/neo4j/templates/deployment.yaml +++ b/knowlg-automation/helm_charts/neo4j/templates/deployment.yaml @@ -27,13 +27,34 @@ spec: name: bolt-port-1 - containerPort: 8687 name: bolt-port-2 - + volumeMounts: + - name: shared-data + mountPath: /var/lib/neo4j/logs/plugins/txn-handler + - name: logstash + image: logstash:6.8.21 + imagePullPolicy: Always + volumeMounts: + - name: config-volume + mountPath: /usr/share/logstash/pipeline/logstash.conf + subPath: logstash.conf + - name: shared-data + mountPath: /txn-handler + volumes: + - name: config-volume + configMap: + name: logstash-config + items: + - key: logstash.conf + path: logstash.conf + - name: shared-data + emptyDir: {} + --- apiVersion: v1 kind: Service metadata: name: {{ .Chart.Name }}-db - namespace: {{ .Values.db_namespace }} + namespace: {{ .Values.namespace }} labels: app: {{ .Chart.Name }} spec: diff --git a/knowlg-automation/helm_charts/neo4j/templates/logstash-configmap.yaml b/knowlg-automation/helm_charts/neo4j/templates/logstash-configmap.yaml new file mode 100644 index 000000000..1f14970e5 --- /dev/null +++ b/knowlg-automation/helm_charts/neo4j/templates/logstash-configmap.yaml @@ -0,0 +1,39 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: logstash-config +data: + logstash.conf: |- + input { + file { + start_position =>"beginning" + type => "graph_event" + path => ["/txn-handler/learning_graph_event_neo4j.log"] + sincedb_path => "/usr/share/logstash/.sincedb_learning_graph_event_mw" + } + } + filter { + grok { + match => [ "message", + "%{TIMESTAMP_ISO8601:timestamp} %{GREEDYDATA:msg}"] + } + mutate { + gsub => [ "message","%{timestamp}","" ] + strip => [ "message" ] + } + json { + source => "message" + } + } + output { + kafka { + bootstrap_servers => "{{ .Values.kafka_connection}}" + codec => plain { + format => "%{message}" + } + message_key => "%{nodeUniqueId}" + topic_id => "{{ .Values.input_topic}}" + retries => 20 + retry_backoff_ms => 180000 + } + } \ No newline at end of file diff --git a/knowlg-automation/helm_charts/neo4j/values.yaml b/knowlg-automation/helm_charts/neo4j/values.yaml index 1b7e2af86..e4c6eadac 100644 --- a/knowlg-automation/helm_charts/neo4j/values.yaml +++ b/knowlg-automation/helm_charts/neo4j/values.yaml @@ -1,6 +1,6 @@ db_namespace: knowlg-db replicaCount: 1 -image: neo4j:3.3.0 +image: pallakartheekreddy/neo4j:3.3.0 authEnabled: false network: port: 7474 @@ -15,4 +15,6 @@ volumes: mode: defaultStorageClass defaultStorageClass: requests: - storage: 2Gi \ No newline at end of file + storage: 2Gi +kafka_connection: kafka-headless.knowlg-db.svc.cluster.local:9092 +input_topic: graph-events \ No newline at end of file diff --git a/knowlg-automation/terraform/local/logstash-provision.tf b/knowlg-automation/terraform/local/logstash-provision.tf deleted file mode 100644 index 075944e6f..000000000 --- a/knowlg-automation/terraform/local/logstash-provision.tf +++ /dev/null @@ -1,11 +0,0 @@ - -resource "helm_release" "logstash" { - name = "logstash" - chart = var.LOGSTASH_CHART - namespace = var.LOGSTASH_NAMESPACE - create_namespace = true - dependency_update = true - depends_on = [kind_cluster.one-click] - wait_for_jobs = true - -} diff --git a/knowlg-automation/terraform/local/vars.tf b/knowlg-automation/terraform/local/vars.tf index d6ab7d27d..195576ad4 100644 --- a/knowlg-automation/terraform/local/vars.tf +++ b/knowlg-automation/terraform/local/vars.tf @@ -93,17 +93,6 @@ variable "REDIS_NAMESPACE" { default = "knowlg-db" } -#LOGSTASH - -variable "LOGSTASH_CHART" { - description = "Redis Instance Running Namespace" - default = "../../helm_charts/logstash" -} - -variable "LOGSTASH_NAMESPACE" { - description = "Logstash Instance Running Namespace" - default = "knowlg-db" -} #TAXONOMY variable "TAXONOMY_CHART" { From 5a84e0e91971609158767126f8bd9c4ca8d643ac Mon Sep 17 00:00:00 2001 From: Aiman Sharief Date: Tue, 11 Apr 2023 11:19:44 +0530 Subject: [PATCH 204/490] Delete terraform.tfstate --- .../terraform/local/terraform.tfstate | 588 ------------------ 1 file changed, 588 deletions(-) delete mode 100644 knowlg-automation/terraform/local/terraform.tfstate diff --git a/knowlg-automation/terraform/local/terraform.tfstate b/knowlg-automation/terraform/local/terraform.tfstate deleted file mode 100644 index 1bdc628b5..000000000 --- a/knowlg-automation/terraform/local/terraform.tfstate +++ /dev/null @@ -1,588 +0,0 @@ -{ - "version": 4, - "terraform_version": "1.4.2", - "serial": 213, - "lineage": "9487d65f-b5a7-e87c-8d2c-35cd1692aa74", - "outputs": {}, - "resources": [ - { - "mode": "managed", - "type": "helm_release", - "name": "cassandra", - "provider": "provider[\"registry.terraform.io/hashicorp/helm\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "atomic": false, - "chart": "../../helm_charts/cassandra", - "cleanup_on_fail": false, - "create_namespace": true, - "dependency_update": true, - "description": null, - "devel": null, - "disable_crd_hooks": false, - "disable_openapi_validation": false, - "disable_webhooks": false, - "force_update": false, - "id": "cassandra", - "keyring": null, - "lint": false, - "manifest": null, - "max_history": 0, - "metadata": [ - { - "app_version": "1.0", - "chart": "cassandra", - "name": "cassandra", - "namespace": "knowlg-db", - "revision": 1, - "values": "null", - "version": "0.1.0" - } - ], - "name": "cassandra", - "namespace": "knowlg-db", - "pass_credentials": false, - "postrender": [], - "recreate_pods": false, - "render_subchart_notes": true, - "replace": false, - "repository": null, - "repository_ca_file": null, - "repository_cert_file": null, - "repository_key_file": null, - "repository_password": null, - "repository_username": null, - "reset_values": false, - "reuse_values": false, - "set": [], - "set_sensitive": [], - "skip_crds": false, - "status": "deployed", - "timeout": 300, - "values": null, - "verify": false, - "version": "0.1.0", - "wait": true, - "wait_for_jobs": true - }, - "sensitive_attributes": [], - "private": "bnVsbA==", - "dependencies": [ - "kind_cluster.one-click" - ] - } - ] - }, - { - "mode": "managed", - "type": "helm_release", - "name": "elasticsearch", - "provider": "provider[\"registry.terraform.io/hashicorp/helm\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "atomic": false, - "chart": "../../helm_charts/elasticsearch", - "cleanup_on_fail": false, - "create_namespace": true, - "dependency_update": true, - "description": null, - "devel": null, - "disable_crd_hooks": false, - "disable_openapi_validation": false, - "disable_webhooks": false, - "force_update": false, - "id": "elasticsearch", - "keyring": null, - "lint": false, - "manifest": null, - "max_history": 0, - "metadata": [ - { - "app_version": "8.6.0", - "chart": "elasticsearch", - "name": "elasticsearch", - "namespace": "knowlg-db", - "revision": 1, - "values": "null", - "version": "19.5.8" - } - ], - "name": "elasticsearch", - "namespace": "knowlg-db", - "pass_credentials": false, - "postrender": [], - "recreate_pods": false, - "render_subchart_notes": true, - "replace": false, - "repository": null, - "repository_ca_file": null, - "repository_cert_file": null, - "repository_key_file": null, - "repository_password": null, - "repository_username": null, - "reset_values": false, - "reuse_values": false, - "set": [], - "set_sensitive": [], - "skip_crds": false, - "status": "deployed", - "timeout": 300, - "values": null, - "verify": false, - "version": "19.5.8", - "wait": true, - "wait_for_jobs": true - }, - "sensitive_attributes": [], - "private": "bnVsbA==", - "dependencies": [ - "kind_cluster.one-click" - ] - } - ] - }, - { - "mode": "managed", - "type": "helm_release", - "name": "kafka", - "provider": "provider[\"registry.terraform.io/hashicorp/helm\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "atomic": false, - "chart": "../../helm_charts/kafka", - "cleanup_on_fail": false, - "create_namespace": true, - "dependency_update": true, - "description": null, - "devel": null, - "disable_crd_hooks": false, - "disable_openapi_validation": false, - "disable_webhooks": false, - "force_update": false, - "id": "kafka", - "keyring": null, - "lint": false, - "manifest": null, - "max_history": 0, - "metadata": [ - { - "app_version": "3.3.1", - "chart": "kafka", - "name": "kafka", - "namespace": "knowlg-db", - "revision": 1, - "values": "{\"advertisedListeners\":[],\"affinity\":{},\"allowEveryoneIfNoAclFound\":true,\"allowPlaintextListener\":true,\"args\":[],\"auth\":{\"clientProtocol\":\"plaintext\",\"externalClientProtocol\":\"\",\"interBrokerProtocol\":\"plaintext\",\"sasl\":{\"interBrokerMechanism\":\"plain\",\"jaas\":{\"clientPasswords\":[],\"clientUsers\":[\"user\"],\"existingSecret\":\"\",\"interBrokerPassword\":\"\",\"interBrokerUser\":\"admin\",\"zookeeperPassword\":\"\",\"zookeeperUser\":\"\"},\"mechanisms\":\"plain,scram-sha-256,scram-sha-512\"},\"tls\":{\"autoGenerated\":false,\"endpointIdentificationAlgorithm\":\"https\",\"existingSecret\":\"\",\"existingSecrets\":[],\"jksKeystoreSAN\":\"\",\"jksTruststore\":\"\",\"jksTruststoreSecret\":\"\",\"password\":\"\",\"pemChainIncluded\":false,\"type\":\"jks\"},\"zookeeper\":{\"tls\":{\"enabled\":false,\"existingSecret\":\"\",\"existingSecretKeystoreKey\":\"zookeeper.keystore.jks\",\"existingSecretTruststoreKey\":\"zookeeper.truststore.jks\",\"passwordsSecret\":\"\",\"passwordsSecretKeystoreKey\":\"keystore-password\",\"passwordsSecretTruststoreKey\":\"truststore-password\",\"type\":\"jks\",\"verifyHostname\":true}}},\"authorizerClassName\":\"\",\"autoCreateTopicsEnable\":true,\"brokerRackAssignment\":\"\",\"clusterDomain\":\"cluster.local\",\"command\":[\"/scripts/setup.sh\"],\"commonAnnotations\":{},\"commonLabels\":{},\"config\":\"\",\"containerPorts\":{\"client\":9092,\"external\":9094,\"internal\":9093},\"containerSecurityContext\":{\"allowPrivilegeEscalation\":false,\"enabled\":true,\"runAsNonRoot\":true,\"runAsUser\":1001},\"customLivenessProbe\":{},\"customReadinessProbe\":{},\"customStartupProbe\":{},\"defaultReplicationFactor\":1,\"deleteTopicEnable\":false,\"diagnosticMode\":{\"args\":[\"infinity\"],\"command\":[\"sleep\"],\"enabled\":false},\"existingConfigmap\":\"\",\"existingLog4jConfigMap\":\"\",\"externalAccess\":{\"autoDiscovery\":{\"enabled\":false,\"image\":{\"digest\":\"\",\"pullPolicy\":\"IfNotPresent\",\"pullSecrets\":[],\"registry\":\"docker.io\",\"repository\":\"bitnami/kubectl\",\"tag\":\"1.25.5-debian-11-r2\"},\"resources\":{\"limits\":{},\"requests\":{}}},\"enabled\":false,\"service\":{\"annotations\":{},\"domain\":\"\",\"extraPorts\":[],\"labels\":{},\"loadBalancerAnnotations\":[],\"loadBalancerIPs\":[],\"loadBalancerNames\":[],\"loadBalancerSourceRanges\":[],\"nodePorts\":[],\"ports\":{\"external\":9094},\"publishNotReadyAddresses\":false,\"type\":\"LoadBalancer\",\"useHostIPs\":false,\"usePodIPs\":false}},\"externalZookeeper\":{\"servers\":[]},\"extraDeploy\":[],\"extraEnvVars\":[],\"extraEnvVarsCM\":\"\",\"extraEnvVarsSecret\":\"\",\"extraVolumeMounts\":[],\"extraVolumes\":[],\"fullnameOverride\":\"\",\"global\":{\"imagePullSecrets\":[],\"imageRegistry\":\"\",\"storageClass\":\"\"},\"heapOpts\":\"-Xmx1024m -Xms1024m\",\"hostAliases\":[],\"hostIPC\":false,\"hostNetwork\":false,\"image\":{\"debug\":false,\"digest\":\"\",\"pullPolicy\":\"IfNotPresent\",\"pullSecrets\":[],\"registry\":\"docker.io\",\"repository\":\"bitnami/kafka\",\"tag\":\"3.3.1-debian-11-r25\"},\"initContainers\":[],\"interBrokerListenerName\":\"INTERNAL\",\"kubeVersion\":\"\",\"lifecycleHooks\":{},\"listenerSecurityProtocolMap\":\"\",\"listeners\":[],\"livenessProbe\":{\"enabled\":true,\"failureThreshold\":3,\"initialDelaySeconds\":10,\"periodSeconds\":10,\"successThreshold\":1,\"timeoutSeconds\":5},\"log4j\":\"\",\"logFlushIntervalMessages\":\"_10000\",\"logFlushIntervalMs\":1000,\"logPersistence\":{\"accessModes\":[\"ReadWriteOnce\"],\"annotations\":{},\"enabled\":false,\"existingClaim\":\"\",\"mountPath\":\"/opt/bitnami/kafka/logs\",\"selector\":{},\"size\":\"8Gi\",\"storageClass\":\"\"},\"logRetentionBytes\":\"_1073741824\",\"logRetentionCheckIntervalMs\":300000,\"logRetentionHours\":168,\"logSegmentBytes\":\"_1073741824\",\"logsDirs\":\"/bitnami/kafka/data\",\"maxMessageBytes\":\"_1000012\",\"metrics\":{\"jmx\":{\"config\":\"jmxUrl: service:jmx:rmi:///jndi/rmi://127.0.0.1:5555/jmxrmi\\nlowercaseOutputName: true\\nlowercaseOutputLabelNames: true\\nssl: false\\n{{- if .Values.metrics.jmx.whitelistObjectNames }}\\nwhitelistObjectNames: [\\\"{{ join \\\"\\\\\\\",\\\\\\\"\\\" .Values.metrics.jmx.whitelistObjectNames }}\\\"]\\n{{- end }}\",\"containerPorts\":{\"metrics\":5556},\"containerSecurityContext\":{\"enabled\":true,\"runAsNonRoot\":true,\"runAsUser\":1001},\"enabled\":false,\"existingConfigmap\":\"\",\"extraRules\":\"\",\"image\":{\"digest\":\"\",\"pullPolicy\":\"IfNotPresent\",\"pullSecrets\":[],\"registry\":\"docker.io\",\"repository\":\"bitnami/jmx-exporter\",\"tag\":\"0.17.2-debian-11-r29\"},\"resources\":{\"limits\":{},\"requests\":{}},\"service\":{\"annotations\":{\"prometheus.io/path\":\"/\",\"prometheus.io/port\":\"{{ .Values.metrics.jmx.service.ports.metrics }}\",\"prometheus.io/scrape\":\"true\"},\"clusterIP\":\"\",\"ports\":{\"metrics\":5556},\"sessionAffinity\":\"None\"},\"whitelistObjectNames\":[\"kafka.controller:*\",\"kafka.server:*\",\"java.lang:*\",\"kafka.network:*\",\"kafka.log:*\"]},\"kafka\":{\"affinity\":{},\"args\":[],\"certificatesSecret\":\"\",\"command\":[],\"containerPorts\":{\"metrics\":9308},\"containerSecurityContext\":{\"enabled\":true,\"runAsNonRoot\":true,\"runAsUser\":1001},\"enabled\":false,\"extraFlags\":{},\"extraVolumeMounts\":[],\"extraVolumes\":[],\"hostAliases\":[],\"image\":{\"digest\":\"\",\"pullPolicy\":\"IfNotPresent\",\"pullSecrets\":[],\"registry\":\"docker.io\",\"repository\":\"bitnami/kafka-exporter\",\"tag\":\"1.6.0-debian-11-r40\"},\"initContainers\":[],\"nodeAffinityPreset\":{\"key\":\"\",\"type\":\"\",\"values\":[]},\"nodeSelector\":{},\"podAffinityPreset\":\"\",\"podAnnotations\":{},\"podAntiAffinityPreset\":\"soft\",\"podLabels\":{},\"podSecurityContext\":{\"enabled\":true,\"fsGroup\":1001},\"priorityClassName\":\"\",\"resources\":{\"limits\":{},\"requests\":{}},\"schedulerName\":\"\",\"service\":{\"annotations\":{\"prometheus.io/path\":\"/metrics\",\"prometheus.io/port\":\"{{ .Values.metrics.kafka.service.ports.metrics }}\",\"prometheus.io/scrape\":\"true\"},\"clusterIP\":\"\",\"ports\":{\"metrics\":9308},\"sessionAffinity\":\"None\"},\"serviceAccount\":{\"automountServiceAccountToken\":true,\"create\":true,\"name\":\"\"},\"sidecars\":[],\"tlsCaCert\":\"ca-file\",\"tlsCaSecret\":\"\",\"tlsCert\":\"cert-file\",\"tlsKey\":\"key-file\",\"tolerations\":[],\"topologySpreadConstraints\":[]},\"prometheusRule\":{\"enabled\":false,\"groups\":[],\"labels\":{},\"namespace\":\"\"},\"serviceMonitor\":{\"enabled\":false,\"honorLabels\":false,\"interval\":\"\",\"jobLabel\":\"\",\"labels\":{},\"metricRelabelings\":[],\"namespace\":\"\",\"relabelings\":[],\"scrapeTimeout\":\"\",\"selector\":{}}},\"minBrokerId\":0,\"nameOverride\":\"\",\"networkPolicy\":{\"allowExternal\":true,\"egressRules\":{\"customRules\":[]},\"enabled\":false,\"explicitNamespacesSelector\":{},\"externalAccess\":{\"from\":[]}},\"nodeAffinityPreset\":{\"key\":\"\",\"type\":\"\",\"values\":[]},\"nodeSelector\":{},\"numIoThreads\":8,\"numNetworkThreads\":3,\"numPartitions\":1,\"numRecoveryThreadsPerDataDir\":1,\"offsetsTopicReplicationFactor\":1,\"pdb\":{\"create\":false,\"maxUnavailable\":1,\"minAvailable\":\"\"},\"persistence\":{\"accessModes\":[\"ReadWriteOnce\"],\"annotations\":{},\"enabled\":true,\"existingClaim\":\"\",\"labels\":{},\"mountPath\":\"/bitnami/kafka\",\"selector\":{},\"size\":\"8Gi\",\"storageClass\":\"\"},\"podAffinityPreset\":\"\",\"podAnnotations\":{},\"podAntiAffinityPreset\":\"soft\",\"podLabels\":{},\"podManagementPolicy\":\"Parallel\",\"podSecurityContext\":{\"enabled\":true,\"fsGroup\":1001},\"priorityClassName\":\"\",\"provisioning\":{\"args\":[],\"auth\":{\"tls\":{\"caCert\":\"ca.crt\",\"cert\":\"tls.crt\",\"certificatesSecret\":\"\",\"key\":\"tls.key\",\"keyPassword\":\"\",\"keyPasswordSecretKey\":\"key-password\",\"keystore\":\"keystore.jks\",\"keystorePassword\":\"\",\"keystorePasswordSecretKey\":\"keystore-password\",\"passwordsSecret\":\"\",\"truststore\":\"truststore.jks\",\"truststorePassword\":\"\",\"truststorePasswordSecretKey\":\"truststore-password\",\"type\":\"jks\"}},\"command\":[],\"containerSecurityContext\":{\"enabled\":true,\"runAsNonRoot\":true,\"runAsUser\":1001},\"enabled\":true,\"extraEnvVars\":[],\"extraEnvVarsCM\":\"\",\"extraEnvVarsSecret\":\"\",\"extraProvisioningCommands\":[],\"extraVolumeMounts\":[],\"extraVolumes\":[],\"initContainers\":[],\"nodeSelector\":{},\"numPartitions\":1,\"parallel\":1,\"podAnnotations\":{},\"podLabels\":{},\"podSecurityContext\":{\"enabled\":true,\"fsGroup\":1001},\"postScript\":\"\",\"preScript\":\"\",\"replicationFactor\":1,\"resources\":{\"limits\":{},\"requests\":{}},\"schedulerName\":\"\",\"serviceAccount\":{\"automountServiceAccountToken\":true,\"create\":false,\"name\":\"\"},\"sidecars\":[],\"tolerations\":[],\"topics\":[{\"config\":{\"flush.messages\":1,\"max.message.bytes\":64000},\"name\":\"dev.telemetry.denorm\",\"partitions\":1,\"replicationFactor\":1},{\"config\":{\"flush.messages\":1,\"max.message.bytes\":64000},\"name\":\"dev.druid.events.telemetry\",\"partitions\":1,\"replicationFactor\":1},{\"config\":{\"flush.messages\":1,\"max.message.bytes\":64000},\"name\":\"dev.druid.events.summary\",\"partitions\":1,\"replicationFactor\":1},{\"config\":{\"flush.messages\":1,\"max.message.bytes\":64000},\"name\":\"dev.telemetry.failed\",\"partitions\":1,\"replicationFactor\":1},{\"config\":{\"flush.messages\":1,\"max.message.bytes\":64000},\"name\":\"dev.telemetry.duplicate\",\"partitions\":1,\"replicationFactor\":1}],\"waitForKafka\":true},\"rbac\":{\"create\":false},\"readinessProbe\":{\"enabled\":true,\"failureThreshold\":6,\"initialDelaySeconds\":5,\"periodSeconds\":10,\"successThreshold\":1,\"timeoutSeconds\":5},\"replicaCount\":1,\"resources\":{\"limits\":{},\"requests\":{}},\"schedulerName\":\"\",\"service\":{\"annotations\":{},\"clusterIP\":\"\",\"externalTrafficPolicy\":\"Cluster\",\"extraPorts\":[],\"headless\":{\"annotations\":{},\"labels\":{},\"publishNotReadyAddresses\":false},\"loadBalancerIP\":\"\",\"loadBalancerSourceRanges\":[],\"nodePorts\":{\"client\":\"\",\"external\":\"\"},\"ports\":{\"client\":9092,\"external\":9094,\"internal\":9093},\"sessionAffinity\":\"None\",\"sessionAffinityConfig\":{},\"type\":\"ClusterIP\"},\"serviceAccount\":{\"annotations\":{},\"automountServiceAccountToken\":true,\"create\":true,\"name\":\"\"},\"sidecars\":[],\"socketReceiveBufferBytes\":102400,\"socketRequestMaxBytes\":\"_104857600\",\"socketSendBufferBytes\":102400,\"startupProbe\":{\"enabled\":false,\"failureThreshold\":15,\"initialDelaySeconds\":30,\"periodSeconds\":10,\"successThreshold\":1,\"timeoutSeconds\":1},\"superUsers\":\"User:admin\",\"terminationGracePeriodSeconds\":\"\",\"tolerations\":[],\"topologySpreadConstraints\":[],\"transactionStateLogMinIsr\":1,\"transactionStateLogReplicationFactor\":1,\"updateStrategy\":{\"rollingUpdate\":{},\"type\":\"RollingUpdate\"},\"volumePermissions\":{\"containerSecurityContext\":{\"runAsUser\":0},\"enabled\":false,\"image\":{\"digest\":\"\",\"pullPolicy\":\"IfNotPresent\",\"pullSecrets\":[],\"registry\":\"docker.io\",\"repository\":\"bitnami/bitnami-shell\",\"tag\":\"11-debian-11-r63\"},\"resources\":{\"limits\":{},\"requests\":{}}},\"zookeeper\":{\"auth\":{\"client\":{\"clientPassword\":\"\",\"clientUser\":\"\",\"enabled\":false,\"serverPasswords\":\"\",\"serverUsers\":\"\"}},\"enabled\":true,\"persistence\":{\"accessModes\":[\"ReadWriteOnce\"],\"enabled\":true,\"size\":\"8Gi\",\"storageClass\":\"\"},\"replicaCount\":1},\"zookeeperChrootPath\":\"\",\"zookeeperConnectionTimeoutMs\":6000}", - "version": "20.0.2" - } - ], - "name": "kafka", - "namespace": "knowlg-db", - "pass_credentials": false, - "postrender": [], - "recreate_pods": false, - "render_subchart_notes": true, - "replace": false, - "repository": null, - "repository_ca_file": null, - "repository_cert_file": null, - "repository_key_file": null, - "repository_password": null, - "repository_username": null, - "reset_values": false, - "reuse_values": false, - "set": [], - "set_sensitive": [], - "skip_crds": false, - "status": "deployed", - "timeout": 300, - "values": [ - "## @section Global parameters\n## Global Docker image parameters\n## Please, note that this will override the image parameters, including dependencies, configured to use the global value\n## Current available global Docker image parameters: imageRegistry, imagePullSecrets and storageClass\n\n## @param global.imageRegistry Global Docker image registry\n## @param global.imagePullSecrets Global Docker registry secret names as an array\n## @param global.storageClass Global StorageClass for Persistent Volume(s)\n##\nglobal:\n imageRegistry: \"\"\n ## E.g.\n ## imagePullSecrets:\n ## - myRegistryKeySecretName\n ##\n imagePullSecrets: []\n storageClass: \"\"\n\n## @section Common parameters\n\n## @param kubeVersion Override Kubernetes version\n##\nkubeVersion: \"\"\n## @param nameOverride String to partially override common.names.fullname\n##\nnameOverride: \"\"\n## @param fullnameOverride String to fully override common.names.fullname\n##\nfullnameOverride: \"\"\n## @param clusterDomain Default Kubernetes cluster domain\n##\nclusterDomain: cluster.local\n## @param commonLabels Labels to add to all deployed objects\n##\ncommonLabels: {}\n## @param commonAnnotations Annotations to add to all deployed objects\n##\ncommonAnnotations: {}\n## @param extraDeploy Array of extra objects to deploy with the release\n##\nextraDeploy: []\n## Enable diagnostic mode in the statefulset\n##\ndiagnosticMode:\n ## @param diagnosticMode.enabled Enable diagnostic mode (all probes will be disabled and the command will be overridden)\n ##\n enabled: false\n ## @param diagnosticMode.command Command to override all containers in the statefulset\n ##\n command:\n - sleep\n ## @param diagnosticMode.args Args to override all containers in the statefulset\n ##\n args:\n - infinity\n\n## @section Kafka parameters\n\n## Bitnami Kafka image version\n## ref: https://hub.docker.com/r/bitnami/kafka/tags/\n## @param image.registry Kafka image registry\n## @param image.repository Kafka image repository\n## @param image.tag Kafka image tag (immutable tags are recommended)\n## @param image.digest Kafka image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag\n## @param image.pullPolicy Kafka image pull policy\n## @param image.pullSecrets Specify docker-registry secret names as an array\n## @param image.debug Specify if debug values should be set\n##\nimage:\n registry: docker.io\n repository: bitnami/kafka\n tag: 3.3.1-debian-11-r25\n digest: \"\"\n ## Specify a imagePullPolicy\n ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent'\n ## ref: https://kubernetes.io/docs/user-guide/images/#pre-pulling-images\n ##\n pullPolicy: IfNotPresent\n ## Optionally specify an array of imagePullSecrets.\n ## Secrets must be manually created in the namespace.\n ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/\n ## e.g:\n ## pullSecrets:\n ## - myRegistryKeySecretName\n ##\n pullSecrets: []\n ## Set to true if you would like to see extra information on logs\n ##\n debug: false\n## @param config Configuration file for Kafka. Auto-generated based on other parameters when not specified\n## Specify content for server.properties\n## NOTE: This will override any KAFKA_CFG_ environment variables (including those set by the chart)\n## The server.properties is auto-generated based on other parameters when this parameter is not specified\n## e.g:\n## config: |-\n## broker.id=-1\n## listeners=PLAINTEXT://:9092\n## advertised.listeners=PLAINTEXT://KAFKA_IP:9092\n## num.network.threads=3\n## num.io.threads=8\n## socket.send.buffer.bytes=102400\n## socket.receive.buffer.bytes=102400\n## socket.request.max.bytes=104857600\n## log.dirs=/bitnami/kafka/data\n## num.partitions=1\n## num.recovery.threads.per.data.dir=1\n## offsets.topic.replication.factor=1\n## transaction.state.log.replication.factor=1\n## transaction.state.log.min.isr=1\n## log.flush.interval.messages=10000\n## log.flush.interval.ms=1000\n## log.retention.hours=168\n## log.retention.bytes=1073741824\n## log.segment.bytes=1073741824\n## log.retention.check.interval.ms=300000\n## zookeeper.connect=ZOOKEEPER_SERVICE_NAME\n## zookeeper.connection.timeout.ms=6000\n## group.initial.rebalance.delay.ms=0\n##\nconfig: \"\"\n## @param existingConfigmap ConfigMap with Kafka Configuration\n## NOTE: This will override `config` AND any KAFKA_CFG_ environment variables\n##\nexistingConfigmap: \"\"\n## @param log4j An optional log4j.properties file to overwrite the default of the Kafka brokers\n## An optional log4j.properties file to overwrite the default of the Kafka brokers\n## ref: https://github.com/apache/kafka/blob/trunk/config/log4j.properties\n##\nlog4j: \"\"\n## @param existingLog4jConfigMap The name of an existing ConfigMap containing a log4j.properties file\n## The name of an existing ConfigMap containing a log4j.properties file\n## NOTE: this will override `log4j`\n##\nexistingLog4jConfigMap: \"\"\n## @param heapOpts Kafka Java Heap size\n##\nheapOpts: -Xmx1024m -Xms1024m\n## @param deleteTopicEnable Switch to enable topic deletion or not\n##\ndeleteTopicEnable: false\n## @param autoCreateTopicsEnable Switch to enable auto creation of topics. Enabling auto creation of topics not recommended for production or similar environments\n##\nautoCreateTopicsEnable: true\n## @param logFlushIntervalMessages The number of messages to accept before forcing a flush of data to disk\n##\nlogFlushIntervalMessages: _10000\n## @param logFlushIntervalMs The maximum amount of time a message can sit in a log before we force a flush\n##\nlogFlushIntervalMs: 1000\n## @param logRetentionBytes A size-based retention policy for logs\n##\nlogRetentionBytes: _1073741824\n## @param logRetentionCheckIntervalMs The interval at which log segments are checked to see if they can be deleted\n##\nlogRetentionCheckIntervalMs: 300000\n## @param logRetentionHours The minimum age of a log file to be eligible for deletion due to age\n##\nlogRetentionHours: 168\n## @param logSegmentBytes The maximum size of a log segment file. When this size is reached a new log segment will be created\n##\nlogSegmentBytes: _1073741824\n## @param logsDirs A comma separated list of directories in which kafka's log data is kept\n## ref: https://kafka.apache.org/documentation/#brokerconfigs_log.dirs\nlogsDirs: /bitnami/kafka/data\n## @param maxMessageBytes The largest record batch size allowed by Kafka\n##\nmaxMessageBytes: _1000012\n## @param defaultReplicationFactor Default replication factors for automatically created topics\n##\ndefaultReplicationFactor: 1\n## @param offsetsTopicReplicationFactor The replication factor for the offsets topic\n##\noffsetsTopicReplicationFactor: 1\n## @param transactionStateLogReplicationFactor The replication factor for the transaction topic\n##\ntransactionStateLogReplicationFactor: 1\n## @param transactionStateLogMinIsr Overridden min.insync.replicas config for the transaction topic\n##\ntransactionStateLogMinIsr: 1\n## @param numIoThreads The number of threads doing disk I/O\n##\nnumIoThreads: 8\n## @param numNetworkThreads The number of threads handling network requests\n##\nnumNetworkThreads: 3\n## @param numPartitions The default number of log partitions per topic\n##\nnumPartitions: 1\n## @param numRecoveryThreadsPerDataDir The number of threads per data directory to be used for log recovery at startup and flushing at shutdown\n##\nnumRecoveryThreadsPerDataDir: 1\n## @param socketReceiveBufferBytes The receive buffer (SO_RCVBUF) used by the socket server\n##\nsocketReceiveBufferBytes: 102400\n## @param socketRequestMaxBytes The maximum size of a request that the socket server will accept (protection against OOM)\n##\nsocketRequestMaxBytes: _104857600\n## @param socketSendBufferBytes The send buffer (SO_SNDBUF) used by the socket server\n##\nsocketSendBufferBytes: 102400\n## @param zookeeperConnectionTimeoutMs Timeout in ms for connecting to ZooKeeper\n##\nzookeeperConnectionTimeoutMs: 6000\n## @param zookeeperChrootPath Path which puts data under some path in the global ZooKeeper namespace\n## ref: https://kafka.apache.org/documentation/#brokerconfigs_zookeeper.connect\n##\nzookeeperChrootPath: \"\"\n## @param authorizerClassName The Authorizer is configured by setting authorizer.class.name=kafka.security.authorizer.AclAuthorizer in server.properties\n##\nauthorizerClassName: \"\"\n## @param allowEveryoneIfNoAclFound By default, if a resource has no associated ACLs, then no one is allowed to access that resource except super users\n##\nallowEveryoneIfNoAclFound: true\n## @param superUsers You can add super users in server.properties\n##\nsuperUsers: User:admin\n## Authentication parameters\n## https://github.com/bitnami/containers/tree/main/bitnami/kafka#security\n##\nauth:\n ## Authentication protocol for client and inter-broker communications\n ## This table shows the security provided on each protocol:\n ## | Method | Authentication | Encryption via TLS |\n ## | plaintext | None | No |\n ## | tls | None | Yes |\n ## | mtls | Yes (two-way authentication) | Yes |\n ## | sasl | Yes (via SASL) | No |\n ## | sasl_tls | Yes (via SASL) | Yes |\n ## @param auth.clientProtocol Authentication protocol for communications with clients. Allowed protocols: `plaintext`, `tls`, `mtls`, `sasl` and `sasl_tls`\n ## @param auth.externalClientProtocol Authentication protocol for communications with external clients. Defaults to value of `auth.clientProtocol`. Allowed protocols: `plaintext`, `tls`, `mtls`, `sasl` and `sasl_tls`\n ## @param auth.interBrokerProtocol Authentication protocol for inter-broker communications. Allowed protocols: `plaintext`, `tls`, `mtls`, `sasl` and `sasl_tls`\n ##\n clientProtocol: plaintext\n # Note: empty by default for backwards compatibility reasons, find more information at\n # https://github.com/bitnami/charts/pull/8902/\n externalClientProtocol: \"\"\n interBrokerProtocol: plaintext\n ## SASL configuration\n ##\n sasl:\n ## @param auth.sasl.mechanisms SASL mechanisms when either `auth.interBrokerProtocol`, `auth.clientProtocol` or `auth.externalClientProtocol` are `sasl`. Allowed types: `plain`, `scram-sha-256`, `scram-sha-512`\n ##\n mechanisms: plain,scram-sha-256,scram-sha-512\n ## @param auth.sasl.interBrokerMechanism SASL mechanism for inter broker communication.\n ##\n interBrokerMechanism: plain\n ## JAAS configuration for SASL authentication.\n ##\n jaas:\n ## @param auth.sasl.jaas.clientUsers Kafka client user list\n ##\n ## clientUsers:\n ## - user1\n ## - user2\n ##\n clientUsers:\n - user\n ## @param auth.sasl.jaas.clientPasswords Kafka client passwords. This is mandatory if more than one user is specified in clientUsers\n ##\n ## clientPasswords:\n ## - password1\n ## - password2\"\n ##\n clientPasswords: []\n ## @param auth.sasl.jaas.interBrokerUser Kafka inter broker communication user for SASL authentication\n ##\n interBrokerUser: admin\n ## @param auth.sasl.jaas.interBrokerPassword Kafka inter broker communication password for SASL authentication\n ##\n interBrokerPassword: \"\"\n ## @param auth.sasl.jaas.zookeeperUser Kafka ZooKeeper user for SASL authentication\n ##\n zookeeperUser: \"\"\n ## @param auth.sasl.jaas.zookeeperPassword Kafka ZooKeeper password for SASL authentication\n ##\n zookeeperPassword: \"\"\n ## @param auth.sasl.jaas.existingSecret Name of the existing secret containing credentials for clientUsers, interBrokerUser and zookeeperUser\n ## Create this secret running the command below where SECRET_NAME is the name of the secret you want to create:\n ## kubectl create secret generic SECRET_NAME --from-literal=client-passwords=CLIENT_PASSWORD1,CLIENT_PASSWORD2 --from-literal=inter-broker-password=INTER_BROKER_PASSWORD --from-literal=zookeeper-password=ZOOKEEPER_PASSWORD\n ##\n existingSecret: \"\"\n ## TLS configuration\n ##\n tls:\n ## @param auth.tls.type Format to use for TLS certificates. Allowed types: `jks` and `pem`\n ##\n type: jks\n ## @param auth.tls.pemChainIncluded Flag to denote that the Certificate Authority (CA) certificates are bundled with the endpoint cert.\n ## Certificates must be in proper order, where the top certificate is the leaf and the bottom certificate is the top-most intermediate CA.\n ##\n pemChainIncluded: false\n ## @param auth.tls.existingSecrets Array existing secrets containing the TLS certificates for the Kafka brokers\n ## When using 'jks' format for certificates, each secret should contain a truststore and a keystore.\n ## Create these secrets following the steps below:\n ## 1) Generate your truststore and keystore files. Helpful script: https://raw.githubusercontent.com/confluentinc/confluent-platform-security-tools/master/kafka-generate-ssl.sh\n ## 2) Rename your truststore to `kafka.truststore.jks`.\n ## 3) Rename your keystores to `kafka-X.keystore.jks` where X is the ID of each Kafka broker.\n ## 4) Run the command below one time per broker to create its associated secret (SECRET_NAME_X is the name of the secret you want to create):\n ## kubectl create secret generic SECRET_NAME_0 --from-file=kafka.truststore.jks=./kafka.truststore.jks --from-file=kafka.keystore.jks=./kafka-0.keystore.jks\n ## kubectl create secret generic SECRET_NAME_1 --from-file=kafka.truststore.jks=./kafka.truststore.jks --from-file=kafka.keystore.jks=./kafka-1.keystore.jks\n ## ...\n ##\n ## When using 'pem' format for certificates, each secret should contain a public CA certificate, a public certificate and one private key.\n ## Create these secrets following the steps below:\n ## 1) Create a certificate key and signing request per Kafka broker, and sign the signing request with your CA\n ## 2) Rename your CA file to `kafka.ca.crt`.\n ## 3) Rename your certificates to `kafka-X.tls.crt` where X is the ID of each Kafka broker.\n ## 3) Rename your keys to `kafka-X.tls.key` where X is the ID of each Kafka broker.\n ## 4) Run the command below one time per broker to create its associated secret (SECRET_NAME_X is the name of the secret you want to create):\n ## kubectl create secret generic SECRET_NAME_0 --from-file=ca.crt=./kafka.ca.crt --from-file=tls.crt=./kafka-0.tls.crt --from-file=tls.key=./kafka-0.tls.key\n ## kubectl create secret generic SECRET_NAME_1 --from-file=ca.crt=./kafka.ca.crt --from-file=tls.crt=./kafka-1.tls.crt --from-file=tls.key=./kafka-1.tls.key\n ## ...\n ##\n existingSecrets: []\n ## @param auth.tls.autoGenerated Generate automatically self-signed TLS certificates for Kafka brokers. Currently only supported if `auth.tls.type` is `pem`\n ## Note: ignored when using 'jks' format or `auth.tls.existingSecrets` is not empty\n ##\n autoGenerated: false\n ## @param auth.tls.password Password to access the JKS files or PEM key when they are password-protected.\n ## Note: ignored when using 'existingSecret'.\n ##\n password: \"\"\n ## @param auth.tls.existingSecret Name of the secret containing the password to access the JKS files or PEM key when they are password-protected. (`key`: `password`)\n ##\n existingSecret: \"\"\n ## @param auth.tls.jksTruststoreSecret Name of the existing secret containing your truststore if truststore not existing or different from the ones in the `auth.tls.existingSecrets`\n ## Note: ignored when using 'pem' format for certificates.\n ##\n jksTruststoreSecret: \"\"\n ## @param auth.tls.jksKeystoreSAN The secret key from the `auth.tls.existingSecrets` containing the keystore with a SAN certificate\n ## The SAN certificate in it should be issued with Subject Alternative Names for all headless services:\n ## - kafka-0.kafka-headless.kafka.svc.cluster.local\n ## - kafka-1.kafka-headless.kafka.svc.cluster.local\n ## - kafka-2.kafka-headless.kafka.svc.cluster.local\n ## Note: ignored when using 'pem' format for certificates.\n ##\n jksKeystoreSAN: \"\"\n ## @param auth.tls.jksTruststore The secret key from the `auth.tls.existingSecrets` or `auth.tls.jksTruststoreSecret` containing the truststore\n ## Note: ignored when using 'pem' format for certificates.\n ##\n jksTruststore: \"\"\n ## @param auth.tls.endpointIdentificationAlgorithm The endpoint identification algorithm to validate server hostname using server certificate\n ## Disable server host name verification by setting it to an empty string.\n ## ref: https://docs.confluent.io/current/kafka/authentication_ssl.html#optional-settings\n ##\n endpointIdentificationAlgorithm: https\n ## Zookeeper client configuration for kafka brokers\n ##\n zookeeper:\n ## TLS configuration\n ##\n tls:\n ## @param auth.zookeeper.tls.enabled Enable TLS for Zookeeper client connections.\n ##\n enabled: false\n ## @param auth.zookeeper.tls.type Format to use for TLS certificates. Allowed types: `jks` and `pem`.\n ##\n type: jks\n ## @param auth.zookeeper.tls.verifyHostname Hostname validation.\n ##\n verifyHostname: true\n ## @param auth.zookeeper.tls.existingSecret Name of the existing secret containing the TLS certificates for ZooKeeper client communications.\n ##\n existingSecret: \"\"\n ## @param auth.zookeeper.tls.existingSecretKeystoreKey The secret key from the auth.zookeeper.tls.existingSecret containing the Keystore.\n ##\n existingSecretKeystoreKey: zookeeper.keystore.jks\n ## @param auth.zookeeper.tls.existingSecretTruststoreKey The secret key from the auth.zookeeper.tls.existingSecret containing the Truststore.\n ##\n existingSecretTruststoreKey: zookeeper.truststore.jks\n ## @param auth.zookeeper.tls.passwordsSecret Existing secret containing Keystore and Truststore passwords.\n ##\n passwordsSecret: \"\"\n ## @param auth.zookeeper.tls.passwordsSecretKeystoreKey The secret key from the auth.zookeeper.tls.passwordsSecret containing the password for the Keystore.\n ##\n passwordsSecretKeystoreKey: keystore-password\n ## @param auth.zookeeper.tls.passwordsSecretTruststoreKey The secret key from the auth.zookeeper.tls.passwordsSecret containing the password for the Truststore.\n ##\n passwordsSecretTruststoreKey: truststore-password\n## @param listeners The address(es) the socket server listens on. Auto-calculated it's set to an empty array\n## When it's set to an empty array, the listeners will be configured\n## based on the authentication protocols (auth.clientProtocol, auth.externalClientProtocol and auth.interBrokerProtocol parameters)\n##\nlisteners: []\n## @param advertisedListeners The address(es) (hostname:port) the broker will advertise to producers and consumers. Auto-calculated it's set to an empty array\n## When it's set to an empty array, the advertised listeners will be configured\n## based on the authentication protocols (auth.clientProtocol, auth.externalClientProtocol and auth.interBrokerProtocol parameters)\n##\nadvertisedListeners: []\n## @param listenerSecurityProtocolMap The protocol-\u003elistener mapping. Auto-calculated it's set to nil\n## When it's nil, the listeners will be configured based on the authentication protocols (auth.clientProtocol, auth.externalClientProtocol and auth.interBrokerProtocol parameters)\n##\nlistenerSecurityProtocolMap: \"\"\n## @param allowPlaintextListener Allow to use the PLAINTEXT listener\n##\nallowPlaintextListener: true\n## @param interBrokerListenerName The listener that the brokers should communicate on\n##\ninterBrokerListenerName: INTERNAL\n## @param command Override Kafka container command\n##\ncommand:\n - /scripts/setup.sh\n## @param args Override Kafka container arguments\n##\nargs: []\n## @param extraEnvVars Extra environment variables to add to Kafka pods\n## ref: https://github.com/bitnami/containers/tree/main/bitnami/kafka#configuration\n## e.g:\n## extraEnvVars:\n## - name: KAFKA_CFG_BACKGROUND_THREADS\n## value: \"10\"\n##\nextraEnvVars: []\n## @param extraEnvVarsCM ConfigMap with extra environment variables\n##\nextraEnvVarsCM: \"\"\n## @param extraEnvVarsSecret Secret with extra environment variables\n##\nextraEnvVarsSecret: \"\"\n\n## @section Statefulset parameters\n\n## @param replicaCount Number of Kafka nodes\n##\nreplicaCount: 1\n## @param minBrokerId Minimal broker.id value, nodes increment their `broker.id` respectively\n## Brokers increment their ID starting at this minimal value.\n## E.g., with `minBrokerId=100` and 3 nodes, IDs will be 100, 101, 102 for brokers 0, 1, and 2, respectively.\n##\nminBrokerId: 0\n## @param brokerRackAssignment Set Broker Assignment for multi tenant environment Allowed values: `aws-az`\n## ref: https://cwiki.apache.org/confluence/display/KAFKA/KIP-392%3A+Allow+consumers+to+fetch+from+closest+replica\n##\nbrokerRackAssignment: \"\"\n## @param containerPorts.client Kafka client container port\n## @param containerPorts.internal Kafka inter-broker container port\n## @param containerPorts.external Kafka external container port\n##\ncontainerPorts:\n client: 9092\n internal: 9093\n external: 9094\n## Configure extra options for Kafka containers' liveness, readiness and startup probes\n## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#configure-probes\n## @param livenessProbe.enabled Enable livenessProbe on Kafka containers\n## @param livenessProbe.initialDelaySeconds Initial delay seconds for livenessProbe\n## @param livenessProbe.periodSeconds Period seconds for livenessProbe\n## @param livenessProbe.timeoutSeconds Timeout seconds for livenessProbe\n## @param livenessProbe.failureThreshold Failure threshold for livenessProbe\n## @param livenessProbe.successThreshold Success threshold for livenessProbe\n##\nlivenessProbe:\n enabled: true\n initialDelaySeconds: 10\n timeoutSeconds: 5\n failureThreshold: 3\n periodSeconds: 10\n successThreshold: 1\n## @param readinessProbe.enabled Enable readinessProbe on Kafka containers\n## @param readinessProbe.initialDelaySeconds Initial delay seconds for readinessProbe\n## @param readinessProbe.periodSeconds Period seconds for readinessProbe\n## @param readinessProbe.timeoutSeconds Timeout seconds for readinessProbe\n## @param readinessProbe.failureThreshold Failure threshold for readinessProbe\n## @param readinessProbe.successThreshold Success threshold for readinessProbe\n##\nreadinessProbe:\n enabled: true\n initialDelaySeconds: 5\n failureThreshold: 6\n timeoutSeconds: 5\n periodSeconds: 10\n successThreshold: 1\n## @param startupProbe.enabled Enable startupProbe on Kafka containers\n## @param startupProbe.initialDelaySeconds Initial delay seconds for startupProbe\n## @param startupProbe.periodSeconds Period seconds for startupProbe\n## @param startupProbe.timeoutSeconds Timeout seconds for startupProbe\n## @param startupProbe.failureThreshold Failure threshold for startupProbe\n## @param startupProbe.successThreshold Success threshold for startupProbe\n##\nstartupProbe:\n enabled: false\n initialDelaySeconds: 30\n periodSeconds: 10\n timeoutSeconds: 1\n failureThreshold: 15\n successThreshold: 1\n## @param customLivenessProbe Custom livenessProbe that overrides the default one\n##\ncustomLivenessProbe: {}\n## @param customReadinessProbe Custom readinessProbe that overrides the default one\n##\ncustomReadinessProbe: {}\n## @param customStartupProbe Custom startupProbe that overrides the default one\n##\ncustomStartupProbe: {}\n## @param lifecycleHooks lifecycleHooks for the Kafka container to automate configuration before or after startup\n##\nlifecycleHooks: {}\n## Kafka resource requests and limits\n## ref: https://kubernetes.io/docs/user-guide/compute-resources/\n## @param resources.limits The resources limits for the container\n## @param resources.requests The requested resources for the container\n##\nresources:\n limits: {}\n requests: {}\n## Kafka pods' Security Context\n## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod\n## @param podSecurityContext.enabled Enable security context for the pods\n## @param podSecurityContext.fsGroup Set Kafka pod's Security Context fsGroup\n##\npodSecurityContext:\n enabled: true\n fsGroup: 1001\n## Kafka containers' Security Context\n## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container\n## @param containerSecurityContext.enabled Enable Kafka containers' Security Context\n## @param containerSecurityContext.runAsUser Set Kafka containers' Security Context runAsUser\n## @param containerSecurityContext.runAsNonRoot Set Kafka containers' Security Context runAsNonRoot\n## @param containerSecurityContext.allowPrivilegeEscalation Force the child process to be run as nonprivilege\n## e.g:\n## containerSecurityContext:\n## enabled: true\n## capabilities:\n## drop: [\"NET_RAW\"]\n## readOnlyRootFilesystem: true\n##\ncontainerSecurityContext:\n enabled: true\n runAsUser: 1001\n runAsNonRoot: true\n allowPrivilegeEscalation: false\n## @param hostAliases Kafka pods host aliases\n## https://kubernetes.io/docs/concepts/services-networking/add-entries-to-pod-etc-hosts-with-host-aliases/\n##\nhostAliases: []\n## @param hostNetwork Specify if host network should be enabled for Kafka pods\n##\nhostNetwork: false\n## @param hostIPC Specify if host IPC should be enabled for Kafka pods\n##\nhostIPC: false\n## @param podLabels Extra labels for Kafka pods\n## Ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/\n##\npodLabels: {}\n## @param podAnnotations Extra annotations for Kafka pods\n## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/\n##\npodAnnotations: {}\n## @param podAffinityPreset Pod affinity preset. Ignored if `affinity` is set. Allowed values: `soft` or `hard`\n## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity\n##\npodAffinityPreset: \"\"\n## @param podAntiAffinityPreset Pod anti-affinity preset. Ignored if `affinity` is set. Allowed values: `soft` or `hard`\n## Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity\n##\npodAntiAffinityPreset: soft\n## Node affinity preset\n## Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity\n##\nnodeAffinityPreset:\n ## @param nodeAffinityPreset.type Node affinity preset type. Ignored if `affinity` is set. Allowed values: `soft` or `hard`\n ##\n type: \"\"\n ## @param nodeAffinityPreset.key Node label key to match Ignored if `affinity` is set.\n ## E.g.\n ## key: \"kubernetes.io/e2e-az-name\"\n ##\n key: \"\"\n ## @param nodeAffinityPreset.values Node label values to match. Ignored if `affinity` is set.\n ## E.g.\n ## values:\n ## - e2e-az1\n ## - e2e-az2\n ##\n values: []\n## @param affinity Affinity for pod assignment\n## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity\n## Note: podAffinityPreset, podAntiAffinityPreset, and nodeAffinityPreset will be ignored when it's set\n##\naffinity: {}\n## @param nodeSelector Node labels for pod assignment\n## Ref: https://kubernetes.io/docs/user-guide/node-selection/\n##\nnodeSelector: {}\n## @param tolerations Tolerations for pod assignment\n## Ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/\n##\ntolerations: []\n## @param topologySpreadConstraints Topology Spread Constraints for pod assignment spread across your cluster among failure-domains. Evaluated as a template\n## Ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/#spread-constraints-for-pods\n##\ntopologySpreadConstraints: []\n## @param terminationGracePeriodSeconds Seconds the pod needs to gracefully terminate\n## ref: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#hook-handler-execution\n##\nterminationGracePeriodSeconds: \"\"\n## @param podManagementPolicy StatefulSet controller supports relax its ordering guarantees while preserving its uniqueness and identity guarantees. There are two valid pod management policies: OrderedReady and Parallel\n## ref: https://kubernetes.io/docs/tutorials/stateful-application/basic-stateful-set/#pod-management-policy\n##\npodManagementPolicy: Parallel\n## @param priorityClassName Name of the existing priority class to be used by kafka pods\n## Ref: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/\n##\npriorityClassName: \"\"\n## @param schedulerName Name of the k8s scheduler (other than default)\n## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/\n##\nschedulerName: \"\"\n## @param updateStrategy.type Kafka statefulset strategy type\n## @param updateStrategy.rollingUpdate Kafka statefulset rolling update configuration parameters\n## ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#update-strategies\n##\nupdateStrategy:\n type: RollingUpdate\n rollingUpdate: {}\n## @param extraVolumes Optionally specify extra list of additional volumes for the Kafka pod(s)\n## e.g:\n## extraVolumes:\n## - name: kafka-jaas\n## secret:\n## secretName: kafka-jaas\n##\nextraVolumes: []\n## @param extraVolumeMounts Optionally specify extra list of additional volumeMounts for the Kafka container(s)\n## extraVolumeMounts:\n## - name: kafka-jaas\n## mountPath: /bitnami/kafka/config/kafka_jaas.conf\n## subPath: kafka_jaas.conf\n##\nextraVolumeMounts: []\n## @param sidecars Add additional sidecar containers to the Kafka pod(s)\n## e.g:\n## sidecars:\n## - name: your-image-name\n## image: your-image\n## imagePullPolicy: Always\n## ports:\n## - name: portname\n## containerPort: 1234\n##\nsidecars: []\n## @param initContainers Add additional Add init containers to the Kafka pod(s)\n## e.g:\n## initContainers:\n## - name: your-image-name\n## image: your-image\n## imagePullPolicy: Always\n## ports:\n## - name: portname\n## containerPort: 1234\n##\ninitContainers: []\n## Kafka Pod Disruption Budget\n## ref: https://kubernetes.io/docs/concepts/workloads/pods/disruptions/\n## @param pdb.create Deploy a pdb object for the Kafka pod\n## @param pdb.minAvailable Maximum number/percentage of unavailable Kafka replicas\n## @param pdb.maxUnavailable Maximum number/percentage of unavailable Kafka replicas\n##\npdb:\n create: false\n minAvailable: \"\"\n maxUnavailable: 1\n\n## @section Traffic Exposure parameters\n\n## Service parameters\n##\nservice:\n ## @param service.type Kubernetes Service type\n ##\n type: ClusterIP\n ## @param service.ports.client Kafka svc port for client connections\n ## @param service.ports.internal Kafka svc port for inter-broker connections\n ## @param service.ports.external Kafka svc port for external connections\n ##\n ports:\n client: 9092\n internal: 9093\n external: 9094\n ## @param service.nodePorts.client Node port for the Kafka client connections\n ## @param service.nodePorts.external Node port for the Kafka external connections\n ## NOTE: choose port between \u003c30000-32767\u003e\n ##\n nodePorts:\n client: \"\"\n external: \"\"\n ## @param service.sessionAffinity Control where client requests go, to the same pod or round-robin\n ## Values: ClientIP or None\n ## ref: https://kubernetes.io/docs/user-guide/services/\n ##\n sessionAffinity: None\n ## @param service.sessionAffinityConfig Additional settings for the sessionAffinity\n ## sessionAffinityConfig:\n ## clientIP:\n ## timeoutSeconds: 300\n ##\n sessionAffinityConfig: {}\n ## @param service.clusterIP Kafka service Cluster IP\n ## e.g.:\n ## clusterIP: None\n ##\n clusterIP: \"\"\n ## @param service.loadBalancerIP Kafka service Load Balancer IP\n ## ref: https://kubernetes.io/docs/user-guide/services/#type-loadbalancer\n ##\n loadBalancerIP: \"\"\n ## @param service.loadBalancerSourceRanges Kafka service Load Balancer sources\n ## ref: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/#restrict-access-for-loadbalancer-service\n ## e.g:\n ## loadBalancerSourceRanges:\n ## - 10.10.10.0/24\n ##\n loadBalancerSourceRanges: []\n ## @param service.externalTrafficPolicy Kafka service external traffic policy\n ## ref https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip\n ##\n externalTrafficPolicy: Cluster\n ## @param service.annotations Additional custom annotations for Kafka service\n ##\n annotations: {}\n ## Headless service properties\n ##\n headless:\n ## @param service.headless.publishNotReadyAddresses Indicates that any agent which deals with endpoints for this Service should disregard any indications of ready/not-ready\n ## ref: https://kubernetes.io/docs/reference/kubernetes-api/service-resources/service-v1/\n publishNotReadyAddresses: false\n ## @param service.headless.annotations Annotations for the headless service.\n ##\n annotations: {}\n ## @param service.headless.labels Labels for the headless service.\n ##\n labels: {}\n ## @param service.extraPorts Extra ports to expose in the Kafka service (normally used with the `sidecar` value)\n ##\n extraPorts: []\n## External Access to Kafka brokers configuration\n##\nexternalAccess:\n ## @param externalAccess.enabled Enable Kubernetes external cluster access to Kafka brokers\n ##\n enabled: false\n ## External IPs auto-discovery configuration\n ## An init container is used to auto-detect LB IPs or node ports by querying the K8s API\n ## Note: RBAC might be required\n ##\n autoDiscovery:\n ## @param externalAccess.autoDiscovery.enabled Enable using an init container to auto-detect external IPs/ports by querying the K8s API\n ##\n enabled: false\n ## Bitnami Kubectl image\n ## ref: https://hub.docker.com/r/bitnami/kubectl/tags/\n ## @param externalAccess.autoDiscovery.image.registry Init container auto-discovery image registry\n ## @param externalAccess.autoDiscovery.image.repository Init container auto-discovery image repository\n ## @param externalAccess.autoDiscovery.image.tag Init container auto-discovery image tag (immutable tags are recommended)\n ## @param externalAccess.autoDiscovery.image.digest Petete image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag\n ## @param externalAccess.autoDiscovery.image.pullPolicy Init container auto-discovery image pull policy\n ## @param externalAccess.autoDiscovery.image.pullSecrets Init container auto-discovery image pull secrets\n ##\n image:\n registry: docker.io\n repository: bitnami/kubectl\n tag: 1.25.5-debian-11-r2\n digest: \"\"\n ## Specify a imagePullPolicy\n ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent'\n ## ref: https://kubernetes.io/docs/user-guide/images/#pre-pulling-images\n ##\n pullPolicy: IfNotPresent\n ## Optionally specify an array of imagePullSecrets (secrets must be manually created in the namespace)\n ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/\n ## e.g:\n ## pullSecrets:\n ## - myRegistryKeySecretName\n ##\n pullSecrets: []\n ## Init Container resource requests and limits\n ## ref: https://kubernetes.io/docs/user-guide/compute-resources/\n ## @param externalAccess.autoDiscovery.resources.limits The resources limits for the auto-discovery init container\n ## @param externalAccess.autoDiscovery.resources.requests The requested resources for the auto-discovery init container\n ##\n resources:\n limits: {}\n requests: {}\n ## Parameters to configure K8s service(s) used to externally access Kafka brokers\n ## Note: A new service per broker will be created\n ##\n service:\n ## @param externalAccess.service.type Kubernetes Service type for external access. It can be NodePort, LoadBalancer or ClusterIP\n ##\n type: LoadBalancer\n ## @param externalAccess.service.ports.external Kafka port used for external access when service type is LoadBalancer\n ##\n ports:\n external: 9094\n ## @param externalAccess.service.loadBalancerIPs Array of load balancer IPs for each Kafka broker. Length must be the same as replicaCount\n ## e.g:\n ## loadBalancerIPs:\n ## - X.X.X.X\n ## - Y.Y.Y.Y\n ##\n loadBalancerIPs: []\n ## @param externalAccess.service.loadBalancerNames Array of load balancer Names for each Kafka broker. Length must be the same as replicaCount\n ## e.g:\n ## loadBalancerNames:\n ## - broker1.external.example.com\n ## - broker2.external.example.com\n ##\n loadBalancerNames: []\n ## @param externalAccess.service.loadBalancerAnnotations Array of load balancer annotations for each Kafka broker. Length must be the same as replicaCount\n ## e.g:\n ## loadBalancerAnnotations:\n ## - external-dns.alpha.kubernetes.io/hostname: broker1.external.example.com.\n ## - external-dns.alpha.kubernetes.io/hostname: broker2.external.example.com.\n ##\n loadBalancerAnnotations: []\n ## @param externalAccess.service.loadBalancerSourceRanges Address(es) that are allowed when service is LoadBalancer\n ## ref: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/#restrict-access-for-loadbalancer-service\n ## e.g:\n ## loadBalancerSourceRanges:\n ## - 10.10.10.0/24\n ##\n loadBalancerSourceRanges: []\n ## @param externalAccess.service.nodePorts Array of node ports used for each Kafka broker. Length must be the same as replicaCount\n ## e.g:\n ## nodePorts:\n ## - 30001\n ## - 30002\n ##\n nodePorts: []\n ## @param externalAccess.service.useHostIPs Use service host IPs to configure Kafka external listener when service type is NodePort\n ##\n useHostIPs: false\n ## @param externalAccess.service.usePodIPs using the MY_POD_IP address for external access.\n ##\n usePodIPs: false\n ## @param externalAccess.service.domain Domain or external ip used to configure Kafka external listener when service type is NodePort or ClusterIP\n ## NodePort: If not specified, the container will try to get the kubernetes node external IP\n ## ClusterIP: Must be specified, ingress IP or domain where tcp for external ports is configured\n ##\n domain: \"\"\n ## @param externalAccess.service.publishNotReadyAddresses Indicates that any agent which deals with endpoints for this Service should disregard any indications of ready/not-ready\n ## ref: https://kubernetes.io/docs/reference/kubernetes-api/service-resources/service-v1/\n publishNotReadyAddresses: false\n ## @param externalAccess.service.labels Service labels for external access\n ##\n labels: {}\n ## @param externalAccess.service.annotations Service annotations for external access\n ##\n annotations: {}\n ## @param externalAccess.service.extraPorts Extra ports to expose in the Kafka external service\n ##\n extraPorts: []\n## Network policies\n## Ref: https://kubernetes.io/docs/concepts/services-networking/network-policies/\n##\nnetworkPolicy:\n ## @param networkPolicy.enabled Specifies whether a NetworkPolicy should be created\n ##\n enabled: false\n ## @param networkPolicy.allowExternal Don't require client label for connections\n ## When set to false, only pods with the correct client label will have network access to the port Kafka is\n ## listening on. When true, zookeeper accept connections from any source (with the correct destination port).\n ##\n allowExternal: true\n ## @param networkPolicy.explicitNamespacesSelector A Kubernetes LabelSelector to explicitly select namespaces from which traffic could be allowed\n ## If explicitNamespacesSelector is missing or set to {}, only client Pods that are in the networkPolicy's namespace\n ## and that match other criteria, the ones that have the good label, can reach the kafka.\n ## But sometimes, we want the kafka to be accessible to clients from other namespaces, in this case, we can use this\n ## LabelSelector to select these namespaces, note that the networkPolicy's namespace should also be explicitly added.\n ##\n ## e.g:\n ## explicitNamespacesSelector:\n ## matchLabels:\n ## role: frontend\n ## matchExpressions:\n ## - {key: role, operator: In, values: [frontend]}\n ##\n explicitNamespacesSelector: {}\n ## @param networkPolicy.externalAccess.from customize the from section for External Access on tcp-external port\n ## e.g:\n ## - ipBlock:\n ## cidr: 172.9.0.0/16\n ## except:\n ## - 172.9.1.0/24\n ##\n externalAccess:\n from: []\n ## @param networkPolicy.egressRules.customRules [object] Custom network policy rule\n ##\n egressRules:\n ## Additional custom egress rules\n ## e.g:\n ## customRules:\n ## - to:\n ## - namespaceSelector:\n ## matchLabels:\n ## label: example\n customRules: []\n\n## @section Persistence parameters\n\n## Enable persistence using Persistent Volume Claims\n## ref: https://kubernetes.io/docs/user-guide/persistent-volumes/\n##\npersistence:\n ## @param persistence.enabled Enable Kafka data persistence using PVC, note that ZooKeeper persistence is unaffected\n ##\n enabled: true\n ## @param persistence.existingClaim A manually managed Persistent Volume and Claim\n ## If defined, PVC must be created manually before volume will be bound\n ## The value is evaluated as a template\n ##\n existingClaim: \"\"\n ## @param persistence.storageClass PVC Storage Class for Kafka data volume\n ## If defined, storageClassName: \u003cstorageClass\u003e\n ## If set to \"-\", storageClassName: \"\", which disables dynamic provisioning\n ## If undefined (the default) or set to null, no storageClassName spec is\n ## set, choosing the default provisioner.\n ##\n storageClass: \"\"\n ## @param persistence.accessModes Persistent Volume Access Modes\n ##\n accessModes:\n - ReadWriteOnce\n ## @param persistence.size PVC Storage Request for Kafka data volume\n ##\n size: 8Gi\n ## @param persistence.annotations Annotations for the PVC\n ##\n annotations: {}\n ## @param persistence.labels Labels for the PVC\n ##\n labels: {}\n ## @param persistence.selector Selector to match an existing Persistent Volume for Kafka data PVC. If set, the PVC can't have a PV dynamically provisioned for it\n ## selector:\n ## matchLabels:\n ## app: my-app\n ##\n selector: {}\n ## @param persistence.mountPath Mount path of the Kafka data volume\n ##\n mountPath: /bitnami/kafka\n## Log Persistence parameters\n##\nlogPersistence:\n ## @param logPersistence.enabled Enable Kafka logs persistence using PVC, note that ZooKeeper persistence is unaffected\n ##\n enabled: false\n ## @param logPersistence.existingClaim A manually managed Persistent Volume and Claim\n ## If defined, PVC must be created manually before volume will be bound\n ## The value is evaluated as a template\n ##\n existingClaim: \"\"\n ## @param logPersistence.storageClass PVC Storage Class for Kafka logs volume\n ## If defined, storageClassName: \u003cstorageClass\u003e\n ## If set to \"-\", storageClassName: \"\", which disables dynamic provisioning\n ## If undefined (the default) or set to null, no storageClassName spec is\n ## set, choosing the default provisioner.\n ##\n storageClass: \"\"\n ## @param logPersistence.accessModes Persistent Volume Access Modes\n ##\n accessModes:\n - ReadWriteOnce\n ## @param logPersistence.size PVC Storage Request for Kafka logs volume\n ##\n size: 8Gi\n ## @param logPersistence.annotations Annotations for the PVC\n ##\n annotations: {}\n ## @param logPersistence.selector Selector to match an existing Persistent Volume for Kafka log data PVC. If set, the PVC can't have a PV dynamically provisioned for it\n ## selector:\n ## matchLabels:\n ## app: my-app\n ##\n selector: {}\n ## @param logPersistence.mountPath Mount path of the Kafka logs volume\n ##\n mountPath: /opt/bitnami/kafka/logs\n\n## @section Volume Permissions parameters\n##\n\n## Init containers parameters:\n## volumePermissions: Change the owner and group of the persistent volume(s) mountpoint(s) to 'runAsUser:fsGroup' on each node\n##\nvolumePermissions:\n ## @param volumePermissions.enabled Enable init container that changes the owner and group of the persistent volume\n ##\n enabled: false\n ## @param volumePermissions.image.registry Init container volume-permissions image registry\n ## @param volumePermissions.image.repository Init container volume-permissions image repository\n ## @param volumePermissions.image.tag Init container volume-permissions image tag (immutable tags are recommended)\n ## @param volumePermissions.image.digest Init container volume-permissions image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag\n ## @param volumePermissions.image.pullPolicy Init container volume-permissions image pull policy\n ## @param volumePermissions.image.pullSecrets Init container volume-permissions image pull secrets\n ##\n image:\n registry: docker.io\n repository: bitnami/bitnami-shell\n tag: 11-debian-11-r63\n digest: \"\"\n pullPolicy: IfNotPresent\n ## Optionally specify an array of imagePullSecrets.\n ## Secrets must be manually created in the namespace.\n ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/\n ## Example:\n ## pullSecrets:\n ## - myRegistryKeySecretName\n ##\n pullSecrets: []\n ## Init container resource requests and limits\n ## ref: https://kubernetes.io/docs/user-guide/compute-resources/\n ## @param volumePermissions.resources.limits Init container volume-permissions resource limits\n ## @param volumePermissions.resources.requests Init container volume-permissions resource requests\n ##\n resources:\n limits: {}\n requests: {}\n ## Init container' Security Context\n ## Note: the chown of the data folder is done to containerSecurityContext.runAsUser\n ## and not the below volumePermissions.containerSecurityContext.runAsUser\n ## @param volumePermissions.containerSecurityContext.runAsUser User ID for the init container\n ##\n containerSecurityContext:\n runAsUser: 0\n\n## @section Other Parameters\n\n## ServiceAccount for Kafka\n## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/\n##\nserviceAccount:\n ## @param serviceAccount.create Enable creation of ServiceAccount for Kafka pods\n ##\n create: true\n ## @param serviceAccount.name The name of the service account to use. If not set and `create` is `true`, a name is generated\n ## If not set and create is true, a name is generated using the kafka.serviceAccountName template\n ##\n name: \"\"\n ## @param serviceAccount.automountServiceAccountToken Allows auto mount of ServiceAccountToken on the serviceAccount created\n ## Can be set to false if pods using this serviceAccount do not need to use K8s API\n ##\n automountServiceAccountToken: true\n ## @param serviceAccount.annotations Additional custom annotations for the ServiceAccount\n ##\n annotations: {}\n## Role Based Access Control\n## ref: https://kubernetes.io/docs/admin/authorization/rbac/\n##\nrbac:\n ## @param rbac.create Whether to create \u0026 use RBAC resources or not\n ## binding Kafka ServiceAccount to a role\n ## that allows Kafka pods querying the K8s API\n ##\n create: false\n\n## @section Metrics parameters\n\n## Prometheus Exporters / Metrics\n##\nmetrics:\n ## Prometheus Kafka exporter: exposes complimentary metrics to JMX exporter\n ##\n kafka:\n ## @param metrics.kafka.enabled Whether or not to create a standalone Kafka exporter to expose Kafka metrics\n ##\n enabled: false\n ## Bitnami Kafka exporter image\n ## ref: https://hub.docker.com/r/bitnami/kafka-exporter/tags/\n ## @param metrics.kafka.image.registry Kafka exporter image registry\n ## @param metrics.kafka.image.repository Kafka exporter image repository\n ## @param metrics.kafka.image.tag Kafka exporter image tag (immutable tags are recommended)\n ## @param metrics.kafka.image.digest Kafka exporter image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag\n ## @param metrics.kafka.image.pullPolicy Kafka exporter image pull policy\n ## @param metrics.kafka.image.pullSecrets Specify docker-registry secret names as an array\n ##\n image:\n registry: docker.io\n repository: bitnami/kafka-exporter\n tag: 1.6.0-debian-11-r40\n digest: \"\"\n ## Specify a imagePullPolicy\n ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent'\n ## ref: https://kubernetes.io/docs/user-guide/images/#pre-pulling-images\n ##\n pullPolicy: IfNotPresent\n ## Optionally specify an array of imagePullSecrets (secrets must be manually created in the namespace)\n ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/\n ## e.g:\n ## pullSecrets:\n ## - myRegistryKeySecretName\n ##\n pullSecrets: []\n\n ## @param metrics.kafka.certificatesSecret Name of the existing secret containing the optional certificate and key files\n ## for Kafka exporter client authentication\n ##\n certificatesSecret: \"\"\n ## @param metrics.kafka.tlsCert The secret key from the certificatesSecret if 'client-cert' key different from the default (cert-file)\n ##\n tlsCert: cert-file\n ## @param metrics.kafka.tlsKey The secret key from the certificatesSecret if 'client-key' key different from the default (key-file)\n ##\n tlsKey: key-file\n ## @param metrics.kafka.tlsCaSecret Name of the existing secret containing the optional ca certificate for Kafka exporter client authentication\n ##\n tlsCaSecret: \"\"\n ## @param metrics.kafka.tlsCaCert The secret key from the certificatesSecret or tlsCaSecret if 'ca-cert' key different from the default (ca-file)\n ##\n tlsCaCert: ca-file\n ## @param metrics.kafka.extraFlags Extra flags to be passed to Kafka exporter\n ## e.g:\n ## extraFlags:\n ## tls.insecure-skip-tls-verify: \"\"\n ## web.telemetry-path: \"/metrics\"\n ##\n extraFlags: {}\n ## @param metrics.kafka.command Override Kafka exporter container command\n ##\n command: []\n ## @param metrics.kafka.args Override Kafka exporter container arguments\n ##\n args: []\n ## @param metrics.kafka.containerPorts.metrics Kafka exporter metrics container port\n ##\n containerPorts:\n metrics: 9308\n ## Kafka exporter resource requests and limits\n ## ref: https://kubernetes.io/docs/user-guide/compute-resources/\n ## @param metrics.kafka.resources.limits The resources limits for the container\n ## @param metrics.kafka.resources.requests The requested resources for the container\n ##\n resources:\n limits: {}\n requests: {}\n ## Kafka exporter pods' Security Context\n ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod\n ## @param metrics.kafka.podSecurityContext.enabled Enable security context for the pods\n ## @param metrics.kafka.podSecurityContext.fsGroup Set Kafka exporter pod's Security Context fsGroup\n ##\n podSecurityContext:\n enabled: true\n fsGroup: 1001\n ## Kafka exporter containers' Security Context\n ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container\n ## @param metrics.kafka.containerSecurityContext.enabled Enable Kafka exporter containers' Security Context\n ## @param metrics.kafka.containerSecurityContext.runAsUser Set Kafka exporter containers' Security Context runAsUser\n ## @param metrics.kafka.containerSecurityContext.runAsNonRoot Set Kafka exporter containers' Security Context runAsNonRoot\n ## e.g:\n ## containerSecurityContext:\n ## enabled: true\n ## capabilities:\n ## drop: [\"NET_RAW\"]\n ## readOnlyRootFilesystem: true\n ##\n containerSecurityContext:\n enabled: true\n runAsUser: 1001\n runAsNonRoot: true\n ## @param metrics.kafka.hostAliases Kafka exporter pods host aliases\n ## https://kubernetes.io/docs/concepts/services-networking/add-entries-to-pod-etc-hosts-with-host-aliases/\n ##\n hostAliases: []\n ## @param metrics.kafka.podLabels Extra labels for Kafka exporter pods\n ## Ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/\n ##\n podLabels: {}\n ## @param metrics.kafka.podAnnotations Extra annotations for Kafka exporter pods\n ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/\n ##\n podAnnotations: {}\n ## @param metrics.kafka.podAffinityPreset Pod affinity preset. Ignored if `metrics.kafka.affinity` is set. Allowed values: `soft` or `hard`\n ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity\n ##\n podAffinityPreset: \"\"\n ## @param metrics.kafka.podAntiAffinityPreset Pod anti-affinity preset. Ignored if `metrics.kafka.affinity` is set. Allowed values: `soft` or `hard`\n ## Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity\n ##\n podAntiAffinityPreset: soft\n ## Node metrics.kafka.affinity preset\n ## Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity\n ##\n nodeAffinityPreset:\n ## @param metrics.kafka.nodeAffinityPreset.type Node affinity preset type. Ignored if `metrics.kafka.affinity` is set. Allowed values: `soft` or `hard`\n ##\n type: \"\"\n ## @param metrics.kafka.nodeAffinityPreset.key Node label key to match Ignored if `metrics.kafka.affinity` is set.\n ## E.g.\n ## key: \"kubernetes.io/e2e-az-name\"\n ##\n key: \"\"\n ## @param metrics.kafka.nodeAffinityPreset.values Node label values to match. Ignored if `metrics.kafka.affinity` is set.\n ## E.g.\n ## values:\n ## - e2e-az1\n ## - e2e-az2\n ##\n values: []\n ## @param metrics.kafka.affinity Affinity for pod assignment\n ## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity\n ## Note: metrics.kafka.podAffinityPreset, metrics.kafka.podAntiAffinityPreset, and metrics.kafka.nodeAffinityPreset will be ignored when it's set\n ##\n affinity: {}\n ## @param metrics.kafka.nodeSelector Node labels for pod assignment\n ## Ref: https://kubernetes.io/docs/user-guide/node-selection/\n ##\n nodeSelector: {}\n ## @param metrics.kafka.tolerations Tolerations for pod assignment\n ## Ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/\n ##\n tolerations: []\n ## @param metrics.kafka.schedulerName Name of the k8s scheduler (other than default) for Kafka exporter\n ## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/\n ##\n schedulerName: \"\"\n ## @param metrics.kafka.priorityClassName Kafka exporter pods' priorityClassName\n ##\n priorityClassName: \"\"\n ## @param metrics.kafka.topologySpreadConstraints Topology Spread Constraints for pod assignment\n ## https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/\n ## The value is evaluated as a template\n ##\n topologySpreadConstraints: []\n ## @param metrics.kafka.extraVolumes Optionally specify extra list of additional volumes for the Kafka exporter pod(s)\n ## e.g:\n ## extraVolumes:\n ## - name: kafka-jaas\n ## secret:\n ## secretName: kafka-jaas\n ##\n extraVolumes: []\n ## @param metrics.kafka.extraVolumeMounts Optionally specify extra list of additional volumeMounts for the Kafka exporter container(s)\n ## extraVolumeMounts:\n ## - name: kafka-jaas\n ## mountPath: /bitnami/kafka/config/kafka_jaas.conf\n ## subPath: kafka_jaas.conf\n ##\n extraVolumeMounts: []\n ## @param metrics.kafka.sidecars Add additional sidecar containers to the Kafka exporter pod(s)\n ## e.g:\n ## sidecars:\n ## - name: your-image-name\n ## image: your-image\n ## imagePullPolicy: Always\n ## ports:\n ## - name: portname\n ## containerPort: 1234\n ##\n sidecars: []\n ## @param metrics.kafka.initContainers Add init containers to the Kafka exporter pods\n ## e.g:\n ## initContainers:\n ## - name: your-image-name\n ## image: your-image\n ## imagePullPolicy: Always\n ## ports:\n ## - name: portname\n ## containerPort: 1234\n ##\n initContainers: []\n ## Kafka exporter service configuration\n ##\n service:\n ## @param metrics.kafka.service.ports.metrics Kafka exporter metrics service port\n ##\n ports:\n metrics: 9308\n ## @param metrics.kafka.service.clusterIP Static clusterIP or None for headless services\n ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#choosing-your-own-ip-address\n ##\n clusterIP: \"\"\n ## @param metrics.kafka.service.sessionAffinity Control where client requests go, to the same pod or round-robin\n ## Values: ClientIP or None\n ## ref: https://kubernetes.io/docs/user-guide/services/\n ##\n sessionAffinity: None\n ## @param metrics.kafka.service.annotations [object] Annotations for the Kafka exporter service\n ##\n annotations:\n prometheus.io/scrape: \"true\"\n prometheus.io/port: \"{{ .Values.metrics.kafka.service.ports.metrics }}\"\n prometheus.io/path: \"/metrics\"\n ## Kafka exporter pods ServiceAccount\n ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/\n ##\n serviceAccount:\n ## @param metrics.kafka.serviceAccount.create Enable creation of ServiceAccount for Kafka exporter pods\n ##\n create: true\n ## @param metrics.kafka.serviceAccount.name The name of the service account to use. If not set and `create` is `true`, a name is generated\n ## If not set and create is true, a name is generated using the kafka.metrics.kafka.serviceAccountName template\n ##\n name: \"\"\n ## @param metrics.kafka.serviceAccount.automountServiceAccountToken Allows auto mount of ServiceAccountToken on the serviceAccount created\n ## Can be set to false if pods using this serviceAccount do not need to use K8s API\n ##\n automountServiceAccountToken: true\n ## Prometheus JMX exporter: exposes the majority of Kafkas metrics\n ##\n jmx:\n ## @param metrics.jmx.enabled Whether or not to expose JMX metrics to Prometheus\n ##\n enabled: false\n ## Bitnami JMX exporter image\n ## ref: https://hub.docker.com/r/bitnami/jmx-exporter/tags/\n ## @param metrics.jmx.image.registry JMX exporter image registry\n ## @param metrics.jmx.image.repository JMX exporter image repository\n ## @param metrics.jmx.image.tag JMX exporter image tag (immutable tags are recommended)\n ## @param metrics.jmx.image.digest JMX exporter image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag\n ## @param metrics.jmx.image.pullPolicy JMX exporter image pull policy\n ## @param metrics.jmx.image.pullSecrets Specify docker-registry secret names as an array\n ##\n image:\n registry: docker.io\n repository: bitnami/jmx-exporter\n tag: 0.17.2-debian-11-r29\n digest: \"\"\n ## Specify a imagePullPolicy\n ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent'\n ## ref: https://kubernetes.io/docs/user-guide/images/#pre-pulling-images\n ##\n pullPolicy: IfNotPresent\n ## Optionally specify an array of imagePullSecrets (secrets must be manually created in the namespace)\n ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/\n ## e.g:\n ## pullSecrets:\n ## - myRegistryKeySecretName\n ##\n pullSecrets: []\n ## Prometheus JMX exporter containers' Security Context\n ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container\n ## @param metrics.jmx.containerSecurityContext.enabled Enable Prometheus JMX exporter containers' Security Context\n ## @param metrics.jmx.containerSecurityContext.runAsUser Set Prometheus JMX exporter containers' Security Context runAsUser\n ## @param metrics.jmx.containerSecurityContext.runAsNonRoot Set Prometheus JMX exporter containers' Security Context runAsNonRoot\n ## e.g:\n ## containerSecurityContext:\n ## enabled: true\n ## capabilities:\n ## drop: [\"NET_RAW\"]\n ## readOnlyRootFilesystem: true\n ##\n containerSecurityContext:\n enabled: true\n runAsUser: 1001\n runAsNonRoot: true\n ## @param metrics.jmx.containerPorts.metrics Prometheus JMX exporter metrics container port\n ##\n containerPorts:\n metrics: 5556\n ## Prometheus JMX exporter resource requests and limits\n ## ref: https://kubernetes.io/docs/user-guide/compute-resources/\n ## @param metrics.jmx.resources.limits The resources limits for the JMX exporter container\n ## @param metrics.jmx.resources.requests The requested resources for the JMX exporter container\n ##\n resources:\n limits: {}\n requests: {}\n ## Prometheus JMX exporter service configuration\n ##\n service:\n ## @param metrics.jmx.service.ports.metrics Prometheus JMX exporter metrics service port\n ##\n ports:\n metrics: 5556\n ## @param metrics.jmx.service.clusterIP Static clusterIP or None for headless services\n ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#choosing-your-own-ip-address\n ##\n clusterIP: \"\"\n ## @param metrics.jmx.service.sessionAffinity Control where client requests go, to the same pod or round-robin\n ## Values: ClientIP or None\n ## ref: https://kubernetes.io/docs/user-guide/services/\n ##\n sessionAffinity: None\n ## @param metrics.jmx.service.annotations [object] Annotations for the Prometheus JMX exporter service\n ##\n annotations:\n prometheus.io/scrape: \"true\"\n prometheus.io/port: \"{{ .Values.metrics.jmx.service.ports.metrics }}\"\n prometheus.io/path: \"/\"\n ## @param metrics.jmx.whitelistObjectNames Allows setting which JMX objects you want to expose to via JMX stats to JMX exporter\n ## Only whitelisted values will be exposed via JMX exporter. They must also be exposed via Rules. To expose all metrics\n ## (warning its crazy excessive and they aren't formatted in a prometheus style) (1) `whitelistObjectNames: []`\n ## (2) commented out above `overrideConfig`.\n ##\n whitelistObjectNames:\n - kafka.controller:*\n - kafka.server:*\n - java.lang:*\n - kafka.network:*\n - kafka.log:*\n ## @param metrics.jmx.config [string] Configuration file for JMX exporter\n ## Specify content for jmx-kafka-prometheus.yml. Evaluated as a template\n ##\n ## Credits to the incubator/kafka chart for the JMX configuration.\n ## https://github.com/helm/charts/tree/master/incubator/kafka\n ##\n config: |-\n jmxUrl: service:jmx:rmi:///jndi/rmi://127.0.0.1:5555/jmxrmi\n lowercaseOutputName: true\n lowercaseOutputLabelNames: true\n ssl: false\n {{- if .Values.metrics.jmx.whitelistObjectNames }}\n whitelistObjectNames: [\"{{ join \"\\\",\\\"\" .Values.metrics.jmx.whitelistObjectNames }}\"]\n {{- end }}\n ## @param metrics.jmx.existingConfigmap Name of existing ConfigMap with JMX exporter configuration\n ## NOTE: This will override metrics.jmx.config\n ##\n existingConfigmap: \"\"\n ## @param metrics.jmx.extraRules Add extra rules to JMX exporter configuration\n ## e.g:\n ## extraRules: |-\n ## - pattern: kafka.server\u003ctype=socket-server-metrics, listener=(.+), networkProcessor=(.+)\u003e\u003c\u003e(connection-count)\n ## name: kafka_server_socket_server_metrics_$3\n ## labels:\n ## listener: $1\n extraRules: \"\"\n ## Prometheus Operator ServiceMonitor configuration\n ##\n serviceMonitor:\n ## @param metrics.serviceMonitor.enabled if `true`, creates a Prometheus Operator ServiceMonitor (requires `metrics.kafka.enabled` or `metrics.jmx.enabled` to be `true`)\n ##\n enabled: false\n ## @param metrics.serviceMonitor.namespace Namespace in which Prometheus is running\n ##\n namespace: \"\"\n ## @param metrics.serviceMonitor.interval Interval at which metrics should be scraped\n ## ref: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#endpoint\n ##\n interval: \"\"\n ## @param metrics.serviceMonitor.scrapeTimeout Timeout after which the scrape is ended\n ## ref: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#endpoint\n ##\n scrapeTimeout: \"\"\n ## @param metrics.serviceMonitor.labels Additional labels that can be used so ServiceMonitor will be discovered by Prometheus\n ##\n labels: {}\n ## @param metrics.serviceMonitor.selector Prometheus instance selector labels\n ## ref: https://github.com/bitnami/charts/tree/main/bitnami/prometheus-operator#prometheus-configuration\n ##\n selector: {}\n ## @param metrics.serviceMonitor.relabelings RelabelConfigs to apply to samples before scraping\n ##\n relabelings: []\n ## @param metrics.serviceMonitor.metricRelabelings MetricRelabelConfigs to apply to samples before ingestion\n ##\n metricRelabelings: []\n ## @param metrics.serviceMonitor.honorLabels Specify honorLabels parameter to add the scrape endpoint\n ##\n honorLabels: false\n ## @param metrics.serviceMonitor.jobLabel The name of the label on the target service to use as the job name in prometheus.\n ##\n jobLabel: \"\"\n\n prometheusRule:\n ## @param metrics.prometheusRule.enabled if `true`, creates a Prometheus Operator PrometheusRule (requires `metrics.kafka.enabled` or `metrics.jmx.enabled` to be `true`)\n ##\n enabled: false\n ## @param metrics.prometheusRule.namespace Namespace in which Prometheus is running\n ##\n namespace: \"\"\n ## @param metrics.prometheusRule.labels Additional labels that can be used so PrometheusRule will be discovered by Prometheus\n ##\n labels: {}\n ## @param metrics.prometheusRule.groups Prometheus Rule Groups for Kafka\n ##\n groups: []\n\n## @section Kafka provisioning parameters\n\n## Kafka provisioning\n##\nprovisioning:\n ## @param provisioning.enabled Enable kafka provisioning Job\n ##\n enabled: true\n ## @param provisioning.numPartitions Default number of partitions for topics when unspecified\n ##\n numPartitions: 1\n ## @param provisioning.replicationFactor Default replication factor for topics when unspecified\n ##\n replicationFactor: 1\n ## @param provisioning.topics Kafka topics to provision\n\n topics: \n - name: \"dev.telemetry.denorm\"\n partitions: 1\n replicationFactor: 1\n # https://kafka.apache.org/documentation/#topicconfigs\n config:\n max.message.bytes: 64000\n flush.messages: 1\n - name: \"dev.druid.events.telemetry\"\n partitions: 1\n replicationFactor: 1\n # https://kafka.apache.org/documentation/#topicconfigs\n config:\n max.message.bytes: 64000\n flush.messages: 1\n - name: \"dev.druid.events.summary\"\n partitions: 1\n replicationFactor: 1\n # https://kafka.apache.org/documentation/#topicconfigs\n config:\n max.message.bytes: 64000\n flush.messages: 1\n - name: \"dev.telemetry.failed\"\n partitions: 1\n replicationFactor: 1\n # https://kafka.apache.org/documentation/#topicconfigs\n config:\n max.message.bytes: 64000\n flush.messages: 1\n - name: \"dev.telemetry.duplicate\"\n partitions: 1\n replicationFactor: 1\n # https://kafka.apache.org/documentation/#topicconfigs\n config:\n max.message.bytes: 64000\n flush.messages: 1\n \n ## @param provisioning.nodeSelector Node labels for pod assignment\n ## Ref: https://kubernetes.io/docs/user-guide/node-selection/\n ##\n nodeSelector: {}\n ## @param provisioning.tolerations Tolerations for pod assignment\n ## Ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/\n ##\n tolerations: []\n ## @param provisioning.extraProvisioningCommands Extra commands to run to provision cluster resources\n ## - echo \"Allow user to consume from any topic\"\n ## - \u003e-\n ## /opt/bitnami/kafka/bin/kafka-acls.sh\n ## --bootstrap-server $KAFKA_SERVICE\n ## --command-config $CLIENT_CONF\n ## --add\n ## --allow-principal User:user\n ## --consumer --topic '*'\n ## - \"/opt/bitnami/kafka/bin/kafka-acls.sh\n ## --bootstrap-server $KAFKA_SERVICE\n ## --command-config $CLIENT_CONF\n ## --list\"\n ##\n extraProvisioningCommands: []\n ## @param provisioning.parallel Number of provisioning commands to run at the same time\n ##\n parallel: 1\n ## @param provisioning.preScript Extra bash script to run before topic provisioning. $CLIENT_CONF is path to properties file with most needed configurations\n ##\n preScript: \"\"\n ## @param provisioning.postScript Extra bash script to run after topic provisioning. $CLIENT_CONF is path to properties file with most needed configurations\n ##\n postScript: \"\"\n ## Auth Configuration for kafka provisioning Job\n ##\n auth:\n ## TLS configuration for kafka provisioning Job\n ##\n tls:\n ## @param provisioning.auth.tls.type Format to use for TLS certificates. Allowed types: `jks` and `pem`.\n ## Note: ignored if auth.tls.clientProtocol different from one of these values: \"tls\" \"mtls\" \"sasl_tls\".\n ##\n type: jks\n ## @param provisioning.auth.tls.certificatesSecret Existing secret containing the TLS certificates for the Kafka provisioning Job.\n ## When using 'jks' format for certificates, the secret should contain a truststore and a keystore.\n ## When using 'pem' format for certificates, the secret should contain a public CA certificate, a public certificate and one private key.\n ##\n certificatesSecret: \"\"\n ## @param provisioning.auth.tls.cert The secret key from the certificatesSecret if 'cert' key different from the default (tls.crt)\n ##\n cert: tls.crt\n ## @param provisioning.auth.tls.key The secret key from the certificatesSecret if 'key' key different from the default (tls.key)\n ##\n key: tls.key\n ## @param provisioning.auth.tls.caCert The secret key from the certificatesSecret if 'caCert' key different from the default (ca.crt)\n ##\n caCert: ca.crt\n ## @param provisioning.auth.tls.keystore The secret key from the certificatesSecret if 'keystore' key different from the default (keystore.jks)\n ##\n keystore: keystore.jks\n ## @param provisioning.auth.tls.truststore The secret key from the certificatesSecret if 'truststore' key different from the default (truststore.jks)\n ##\n truststore: truststore.jks\n ## @param provisioning.auth.tls.passwordsSecret Name of the secret containing passwords to access the JKS files or PEM key when they are password-protected.\n ## It should contain two keys called \"keystore-password\" and \"truststore-password\", or \"key-password\" if using a password-protected PEM key.\n ##\n passwordsSecret: \"\"\n ## @param provisioning.auth.tls.keyPasswordSecretKey The secret key from the passwordsSecret if 'keyPasswordSecretKey' key different from the default (key-password)\n ## Note: must not be used if `passwordsSecret` is not defined.\n ##\n keyPasswordSecretKey: key-password\n ## @param provisioning.auth.tls.keystorePasswordSecretKey The secret key from the passwordsSecret if 'keystorePasswordSecretKey' key different from the default (keystore-password)\n ## Note: must not be used if `passwordsSecret` is not defined.\n ##\n keystorePasswordSecretKey: keystore-password\n ## @param provisioning.auth.tls.truststorePasswordSecretKey The secret key from the passwordsSecret if 'truststorePasswordSecretKey' key different from the default (truststore-password)\n ## Note: must not be used if `passwordsSecret` is not defined.\n ##\n truststorePasswordSecretKey: truststore-password\n ## @param provisioning.auth.tls.keyPassword Password to access the password-protected PEM key if necessary. Ignored if 'passwordsSecret' is provided.\n ##\n keyPassword: \"\"\n ## @param provisioning.auth.tls.keystorePassword Password to access the JKS keystore. Ignored if 'passwordsSecret' is provided.\n ##\n keystorePassword: \"\"\n ## @param provisioning.auth.tls.truststorePassword Password to access the JKS truststore. Ignored if 'passwordsSecret' is provided.\n ##\n truststorePassword: \"\"\n ## @param provisioning.command Override provisioning container command\n ##\n command: []\n ## @param provisioning.args Override provisioning container arguments\n ##\n args: []\n ## @param provisioning.extraEnvVars Extra environment variables to add to the provisioning pod\n ## e.g:\n ## extraEnvVars:\n ## - name: KAFKA_CFG_BACKGROUND_THREADS\n ## value: \"10\"\n ##\n extraEnvVars: []\n ## @param provisioning.extraEnvVarsCM ConfigMap with extra environment variables\n ##\n extraEnvVarsCM: \"\"\n ## @param provisioning.extraEnvVarsSecret Secret with extra environment variables\n ##\n extraEnvVarsSecret: \"\"\n ## @param provisioning.podAnnotations Extra annotations for Kafka provisioning pods\n ##\n podAnnotations: {}\n ## @param provisioning.podLabels Extra labels for Kafka provisioning pods\n ## Ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/\n ##\n podLabels: {}\n ## Kafka provisioning pods ServiceAccount\n ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/\n ##\n serviceAccount:\n ## @param provisioning.serviceAccount.create Enable creation of ServiceAccount for Kafka provisioning pods\n ##\n create: false\n ## @param provisioning.serviceAccount.name The name of the service account to use. If not set and `create` is `true`, a name is generated\n ## If not set and create is true, a name is generated using the provisioning.serviceAccount.name template\n ##\n name: \"\"\n ## @param provisioning.serviceAccount.automountServiceAccountToken Allows auto mount of ServiceAccountToken on the serviceAccount created\n ## Can be set to false if pods using this serviceAccount do not need to use K8s API\n ##\n automountServiceAccountToken: true\n ## Kafka provisioning resource requests and limits\n ## ref: https://kubernetes.io/docs/user-guide/compute-resources/\n ## @param provisioning.resources.limits The resources limits for the Kafka provisioning container\n ## @param provisioning.resources.requests The requested resources for the Kafka provisioning container\n ##\n resources:\n limits: {}\n requests: {}\n ## Kafka provisioning pods' Security Context\n ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod\n ## @param provisioning.podSecurityContext.enabled Enable security context for the pods\n ## @param provisioning.podSecurityContext.fsGroup Set Kafka provisioning pod's Security Context fsGroup\n ##\n podSecurityContext:\n enabled: true\n fsGroup: 1001\n ## Kafka provisioning containers' Security Context\n ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container\n ## @param provisioning.containerSecurityContext.enabled Enable Kafka provisioning containers' Security Context\n ## @param provisioning.containerSecurityContext.runAsUser Set Kafka provisioning containers' Security Context runAsUser\n ## @param provisioning.containerSecurityContext.runAsNonRoot Set Kafka provisioning containers' Security Context runAsNonRoot\n ## e.g:\n ## containerSecurityContext:\n ## enabled: true\n ## capabilities:\n ## drop: [\"NET_RAW\"]\n ## readOnlyRootFilesystem: true\n ##\n containerSecurityContext:\n enabled: true\n runAsUser: 1001\n runAsNonRoot: true\n ## @param provisioning.schedulerName Name of the k8s scheduler (other than default) for kafka provisioning\n ## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/\n ##\n schedulerName: \"\"\n ## @param provisioning.extraVolumes Optionally specify extra list of additional volumes for the Kafka provisioning pod(s)\n ## e.g:\n ## extraVolumes:\n ## - name: kafka-jaas\n ## secret:\n ## secretName: kafka-jaas\n ##\n extraVolumes: []\n ## @param provisioning.extraVolumeMounts Optionally specify extra list of additional volumeMounts for the Kafka provisioning container(s)\n ## extraVolumeMounts:\n ## - name: kafka-jaas\n ## mountPath: /bitnami/kafka/config/kafka_jaas.conf\n ## subPath: kafka_jaas.conf\n ##\n extraVolumeMounts: []\n ## @param provisioning.sidecars Add additional sidecar containers to the Kafka provisioning pod(s)\n ## e.g:\n ## sidecars:\n ## - name: your-image-name\n ## image: your-image\n ## imagePullPolicy: Always\n ## ports:\n ## - name: portname\n ## containerPort: 1234\n ##\n sidecars: []\n ## @param provisioning.initContainers Add additional Add init containers to the Kafka provisioning pod(s)\n ## e.g:\n ## initContainers:\n ## - name: your-image-name\n ## image: your-image\n ## imagePullPolicy: Always\n ## ports:\n ## - name: portname\n ## containerPort: 1234\n ##\n initContainers: []\n ## @param provisioning.waitForKafka If true use an init container to wait until kafka is ready before starting provisioning\n ##\n waitForKafka: true\n\n## @section ZooKeeper chart parameters\n\n## ZooKeeper chart configuration\n## https://github.com/bitnami/charts/blob/main/bitnami/zookeeper/values.yaml\n##\nzookeeper:\n ## @param zookeeper.enabled Switch to enable or disable the ZooKeeper helm chart\n ##\n enabled: true\n ## @param zookeeper.replicaCount Number of ZooKeeper nodes\n ##\n replicaCount: 1\n ## ZooKeeper authenticaiton\n ##\n auth:\n client:\n ## @param zookeeper.auth.client.enabled Enable ZooKeeper auth\n ##\n enabled: false\n ## @param zookeeper.auth.client.clientUser User that will use ZooKeeper clients to auth\n ##\n clientUser: \"\"\n ## @param zookeeper.auth.client.clientPassword Password that will use ZooKeeper clients to auth\n ##\n clientPassword: \"\"\n ## @param zookeeper.auth.client.serverUsers Comma, semicolon or whitespace separated list of user to be created. Specify them as a string, for example: \"user1,user2,admin\"\n ##\n serverUsers: \"\"\n ## @param zookeeper.auth.client.serverPasswords Comma, semicolon or whitespace separated list of passwords to assign to users when created. Specify them as a string, for example: \"pass4user1, pass4user2, pass4admin\"\n ##\n serverPasswords: \"\"\n ## ZooKeeper Persistence parameters\n ## ref: https://kubernetes.io/docs/user-guide/persistent-volumes/\n ## @param zookeeper.persistence.enabled Enable persistence on ZooKeeper using PVC(s)\n ## @param zookeeper.persistence.storageClass Persistent Volume storage class\n ## @param zookeeper.persistence.accessModes Persistent Volume access modes\n ## @param zookeeper.persistence.size Persistent Volume size\n ##\n persistence:\n enabled: true\n storageClass: \"\"\n accessModes:\n - ReadWriteOnce\n size: 8Gi\n\n## External Zookeeper Configuration\n## All of these values are only used if `zookeeper.enabled=false`\n##\nexternalZookeeper:\n ## @param externalZookeeper.servers List of external zookeeper servers to use. Typically used in combination with 'zookeeperChrootPath'.\n ##\n servers: []\n" - ], - "verify": false, - "version": "20.0.2", - "wait": true, - "wait_for_jobs": true - }, - "sensitive_attributes": [], - "private": "bnVsbA==", - "dependencies": [ - "kind_cluster.one-click" - ] - } - ] - }, - { - "mode": "managed", - "type": "helm_release", - "name": "logstash", - "provider": "provider[\"registry.terraform.io/hashicorp/helm\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "atomic": false, - "chart": "../../helm_charts/logstash", - "cleanup_on_fail": false, - "create_namespace": true, - "dependency_update": true, - "description": null, - "devel": null, - "disable_crd_hooks": false, - "disable_openapi_validation": false, - "disable_webhooks": false, - "force_update": false, - "id": "logstash", - "keyring": null, - "lint": false, - "manifest": null, - "max_history": 0, - "metadata": [ - { - "app_version": "8.6.2", - "chart": "logstash", - "name": "logstash", - "namespace": "knowlg-db", - "revision": 1, - "values": "null", - "version": "5.1.15" - } - ], - "name": "logstash", - "namespace": "knowlg-db", - "pass_credentials": false, - "postrender": [], - "recreate_pods": false, - "render_subchart_notes": true, - "replace": false, - "repository": null, - "repository_ca_file": null, - "repository_cert_file": null, - "repository_key_file": null, - "repository_password": null, - "repository_username": null, - "reset_values": false, - "reuse_values": false, - "set": [], - "set_sensitive": [], - "skip_crds": false, - "status": "deployed", - "timeout": 300, - "values": null, - "verify": false, - "version": "5.1.15", - "wait": true, - "wait_for_jobs": true - }, - "sensitive_attributes": [], - "private": "bnVsbA==", - "dependencies": [ - "kind_cluster.one-click" - ] - } - ] - }, - { - "mode": "managed", - "type": "helm_release", - "name": "neo4j", - "provider": "provider[\"registry.terraform.io/hashicorp/helm\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "atomic": false, - "chart": "../../helm_charts/neo4j", - "cleanup_on_fail": false, - "create_namespace": true, - "dependency_update": true, - "description": null, - "devel": null, - "disable_crd_hooks": false, - "disable_openapi_validation": false, - "disable_webhooks": false, - "force_update": false, - "id": "neo4j", - "keyring": null, - "lint": false, - "manifest": null, - "max_history": 0, - "metadata": [ - { - "app_version": "1.0", - "chart": "neo4j", - "name": "neo4j", - "namespace": "knowlg-db", - "revision": 1, - "values": "null", - "version": "0.1.0" - } - ], - "name": "neo4j", - "namespace": "knowlg-db", - "pass_credentials": false, - "postrender": [], - "recreate_pods": false, - "render_subchart_notes": true, - "replace": false, - "repository": null, - "repository_ca_file": null, - "repository_cert_file": null, - "repository_key_file": null, - "repository_password": null, - "repository_username": null, - "reset_values": false, - "reuse_values": false, - "set": [], - "set_sensitive": [], - "skip_crds": false, - "status": "deployed", - "timeout": 300, - "values": null, - "verify": false, - "version": "0.1.0", - "wait": true, - "wait_for_jobs": true - }, - "sensitive_attributes": [], - "private": "bnVsbA==", - "dependencies": [ - "kind_cluster.one-click" - ] - } - ] - }, - { - "mode": "managed", - "type": "helm_release", - "name": "redis", - "provider": "provider[\"registry.terraform.io/hashicorp/helm\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "atomic": false, - "chart": "../../helm_charts/redis", - "cleanup_on_fail": false, - "create_namespace": true, - "dependency_update": true, - "description": null, - "devel": null, - "disable_crd_hooks": false, - "disable_openapi_validation": false, - "disable_webhooks": false, - "force_update": false, - "id": "redis", - "keyring": null, - "lint": false, - "manifest": null, - "max_history": 0, - "metadata": [ - { - "app_version": "1.0", - "chart": "redis", - "name": "redis", - "namespace": "knowlg-db", - "revision": 1, - "values": "null", - "version": "0.1.0" - } - ], - "name": "redis", - "namespace": "knowlg-db", - "pass_credentials": false, - "postrender": [], - "recreate_pods": false, - "render_subchart_notes": true, - "replace": false, - "repository": null, - "repository_ca_file": null, - "repository_cert_file": null, - "repository_key_file": null, - "repository_password": null, - "repository_username": null, - "reset_values": false, - "reuse_values": false, - "set": [], - "set_sensitive": [], - "skip_crds": false, - "status": "deployed", - "timeout": 300, - "values": null, - "verify": false, - "version": "0.1.0", - "wait": true, - "wait_for_jobs": true - }, - "sensitive_attributes": [], - "private": "bnVsbA==", - "dependencies": [ - "kind_cluster.one-click" - ] - } - ] - }, - { - "mode": "managed", - "type": "helm_release", - "name": "taxonomy", - "provider": "provider[\"registry.terraform.io/hashicorp/helm\"]", - "instances": [ - { - "status": "tainted", - "schema_version": 0, - "attributes": { - "atomic": false, - "chart": "../../helm_charts/taxonomy", - "cleanup_on_fail": false, - "create_namespace": true, - "dependency_update": true, - "description": null, - "devel": null, - "disable_crd_hooks": false, - "disable_openapi_validation": false, - "disable_webhooks": false, - "force_update": false, - "id": "taxonomy", - "keyring": null, - "lint": false, - "manifest": null, - "max_history": 0, - "metadata": [ - { - "app_version": "1.0", - "chart": "taxonomy", - "name": "taxonomy", - "namespace": "knowlg-db", - "revision": 1, - "values": "{}", - "version": "0.1.0" - } - ], - "name": "taxonomy", - "namespace": "knowlg-db", - "pass_credentials": false, - "postrender": [], - "recreate_pods": false, - "render_subchart_notes": true, - "replace": false, - "repository": null, - "repository_ca_file": null, - "repository_cert_file": null, - "repository_key_file": null, - "repository_password": null, - "repository_username": null, - "reset_values": false, - "reuse_values": false, - "set": [], - "set_sensitive": [], - "skip_crds": false, - "status": "failed", - "timeout": 300, - "values": null, - "verify": false, - "version": "0.1.0", - "wait": true, - "wait_for_jobs": true - }, - "sensitive_attributes": [], - "private": "bnVsbA==", - "dependencies": [ - "kind_cluster.one-click" - ] - } - ] - }, - { - "mode": "managed", - "type": "kind_cluster", - "name": "one-click", - "provider": "provider[\"registry.terraform.io/kyma-incubator/kind\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "client_certificate": "-----BEGIN CERTIFICATE-----\nMIIDITCCAgmgAwIBAgIIU6PEtyrh6EQwDQYJKoZIhvcNAQELBQAwFTETMBEGA1UE\nAxMKa3ViZXJuZXRlczAeFw0yMzAzMjgwMzU1MjZaFw0yNDAzMjcwMzU1MjhaMDQx\nFzAVBgNVBAoTDnN5c3RlbTptYXN0ZXJzMRkwFwYDVQQDExBrdWJlcm5ldGVzLWFk\nbWluMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAntJBGxbamEyt8v/e\nGtmeSN/Dg9K0DYpKdthXA223UcFSza4RjMCo/cuwgcUB7MnRIPhTnR8/L4j16Dh7\nGi2e2Y52eOcHFykT7OsfY9blgbDaiyFEvv8fcDoOkFb4Acd6fI/lnP0+0XaE7CHR\nNpVUodC3KsR7US+/49Yzod/xrBXoxENp8RWDv+C4AsNtsc/C2oYDoeysFMzQgy1k\nl5sjsKlQ3apKKyxOtpZ4N1j4LZUwmVpc9iBBfLhklLmiqwIaDYNDoTvID59+cTda\nthAO0pvCEBZR/aJNLiD0ApbeCa184mOyobZWa43Zyw1N4rUTlN9vGxAF1EJMqNvT\nZATS+QIDAQABo1YwVDAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUH\nAwIwDAYDVR0TAQH/BAIwADAfBgNVHSMEGDAWgBR2VBGD3GjdF/AQHDjWmm/zCwX2\nZjANBgkqhkiG9w0BAQsFAAOCAQEAUSa48JJgExqFenBcmJYFWrvaUqtBO9jxXYbg\n0CSG2CY88BwYLwuv4UXiDgbQlLXwbdWGRFK9gzawHHcFBVjmVUrt9vFhSQ/k8RYX\n6AIUCGCoaq0TBOI+fcdsSE1TT6zLjrabsiUsx61+7iA2dRDlhsDfflEKAtyNPqZi\n8DKPRlIQrr2h705winQjrdTxyY/eLXlH5PHSlM/LErco/HvXdcFvRr+1G9WRpzgK\nUbs145U20WU+SucC+Jjs1XMUjJkKMsto0/Xsbi7MJlQUkJ2MwtGlkp6p2AU+fUZk\nRWJoGuCPhvAfl5K9m5VJwTK4vIlmUE6TxdprgGQNlNa73NAa4Q==\n-----END CERTIFICATE-----\n", - "client_key": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAntJBGxbamEyt8v/eGtmeSN/Dg9K0DYpKdthXA223UcFSza4R\njMCo/cuwgcUB7MnRIPhTnR8/L4j16Dh7Gi2e2Y52eOcHFykT7OsfY9blgbDaiyFE\nvv8fcDoOkFb4Acd6fI/lnP0+0XaE7CHRNpVUodC3KsR7US+/49Yzod/xrBXoxENp\n8RWDv+C4AsNtsc/C2oYDoeysFMzQgy1kl5sjsKlQ3apKKyxOtpZ4N1j4LZUwmVpc\n9iBBfLhklLmiqwIaDYNDoTvID59+cTdathAO0pvCEBZR/aJNLiD0ApbeCa184mOy\nobZWa43Zyw1N4rUTlN9vGxAF1EJMqNvTZATS+QIDAQABAoIBAEyX7ZoY3XcRk9pb\nF0wvoyxV7b6HQRo+lUbXcq9OnFgrObnDtucAiv9K2mbbEeCWO+sAl6JwzVIpZR6H\nDqdDVRZqpCwOjtbWkqtxgQPvQWPYXK04cCUEeK7w8NEfgB+riiDEbOHXKBJwbA5N\nCM6gGd6n/436j9ZJhutkBCaPD8gDTmZ/FijYzl+6pL92fOFvweCYkQrCnwJxVoGI\njsU7a2vx4tNa4gK6DF/etXpd5XGwncKx3723cVOFKq8iV1SuiK6nwT8n9YPuvG3s\nxuop6J/KWQVQe3Xke9kNg+qnB/xrPEfMJMM5+3UO8P/RTSR3AaHsjNXbTPm8UXYb\nQUJf43kCgYEAxeYgU/JKdff6v6DRuVdVGACH/35CpUxMHXztq6P+C0gHlEjb5Vxm\nByrcLgc6xojnNzshHUGwq7k/2zqmRDVh+8V40UUOE5GIOQJV4NUcv4CI/OkErfl5\ngfOqz7jmOKE1F8AIkTO39btUp3d0cN4umBBuw9L+Iyu2mH/Xig/sbm8CgYEAzXMY\nX22OpwHV5YJl2hD+Rv7HEg9imt65vwFEXocoIUZkSS64OMwFKCbkvNbIc6Sps67/\nQFLfh/AIFAhwNEzaP0D9kaZpLxtrANba9QT0hLdoxDc9cA/CNG5vO6FBsN2VHGw0\ns3DB+wdChun5Z4I7XWUgBaOH6GrAIba1JBrrCRcCgYEAp2phGuM0HsxkMkzIXzUE\nZcO9SWT02IXNNn0DS1r2hXCwCMACVuxt285kagPvsxkKEW1SPD/gR8X1wW7CTZ2j\ngKyz4sW6fM93IFNbd1KSeWcxfFNchBkys78BV60iBfJKygbIuJ26GIYbvXiO5xTu\n8fGk4sDSAcrvM9+i5cdGyr0CgYEAgoEf9E5FlXSnCJkiVbkBrNteXOyE+FeOqfQG\nF/3G0gxC2/Hfd8SxaeX62rGct0jbu5Xn8lYyQ8mHeCI8FBzVbeS8re6FVjUWbXwN\nriKtqDhRRoLIdgyskl2sLraqYcz/UQSzHWD895aO/eBWWxfw2czI4IwWYt6cmU3U\nFtE6h5UCgYB9ZTAWEFGmKusS3NMT9do5tCVB6Rca2woivCugtX1Lej/OtapA/nwI\n5BBpM2MfHgsZZW+GWqujN2ZYIXfL/6PNiOXw06MuMWJTgRHL2j/kVeS0Wo2dqAbu\nCf/7UVArE/usBnP6Qdue7HDEyAWZaU09R4M8sEa71D2BVV0VP6WdiA==\n-----END RSA PRIVATE KEY-----\n", - "cluster_ca_certificate": "-----BEGIN CERTIFICATE-----\nMIIC5zCCAc+gAwIBAgIBADANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDEwprdWJl\ncm5ldGVzMB4XDTIzMDMyODAzNTUyNloXDTMzMDMyNTAzNTUyNlowFTETMBEGA1UE\nAxMKa3ViZXJuZXRlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL5U\nuTVslBXDnjUNb/D6z71o8E7WlAgyp+oq7gYJALyarC41egt1NZlJD8GHD6+PzHbx\n3kwIMdcrUmzd0QTHX8nc72K53/mXxSgkBM8oISYqm5aHh1hasV1elQP9/j36Vfmt\n5L/uiUCVwcb3Wfx6Sj4tfPj61czuUUq6D+oVP2yi/0gtKyZzSCvFovZFYR8mFu4q\nkJ9oL59i4KJ/txjRu4ImhLumfBnFdwiy/kJx24VnZDURVjQJE6MruFzM8virB11m\nIXhpv6Zr/wP1UqY8lvEiFX9MfwI3RtlmlSbW+3mAImIadDWnDWVB4fjjCPtIsJS1\ndvP55cHuRA3NJY5hqK0CAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgKkMA8GA1UdEwEB\n/wQFMAMBAf8wHQYDVR0OBBYEFHZUEYPcaN0X8BAcONaab/MLBfZmMA0GCSqGSIb3\nDQEBCwUAA4IBAQAfXZZ+raUxvTBY/e6TDAqSCq5uz9gUlT+eyVYzzmWOOjF+rwRS\n+HNNamZygWxo9w7H+AMWhyPHAuE4Zx7nIlGz3cTgcsQONtBhC5u+jQGK5TY/m/Rv\njQ9ceP1jnmZHBQx8pvOUhSoO08MqG7J1bycV2gkLgN/iU2PZ2I7ADN03K98ev8Ey\nmIBpKoow2qFlsfAb2EZV9akI0uraJQfoMGLbF4Ve2Z57HKEKAJITNfFKCa8J7Hij\njsW4tnzBzfA6cUvtHqDOv3x10cVYn6b+yh28bo+nMFhn20xbZ4vwDBy27TtSwuVW\ntgtV0WA7cgcZtQtnFy3NXpQ8qOWZeBPuW9jm\n-----END CERTIFICATE-----\n", - "endpoint": "https://127.0.0.1:37145", - "id": "one-click-", - "kind_config": [ - { - "api_version": "kind.x-k8s.io/v1alpha4", - "containerd_config_patches": [], - "kind": "Cluster", - "networking": [], - "node": [ - { - "extra_mounts": [], - "extra_port_mappings": [ - { - "container_port": 80, - "host_port": 80, - "listen_address": "", - "protocol": "" - }, - { - "container_port": 443, - "host_port": 443, - "listen_address": "", - "protocol": "" - } - ], - "image": "", - "kubeadm_config_patches": [ - "kind: InitConfiguration\nnodeRegistration:\n kubeletExtraArgs:\n node-labels: \"ingress-ready=true\"\n" - ], - "role": "control-plane" - }, - { - "extra_mounts": [], - "extra_port_mappings": [], - "image": "", - "kubeadm_config_patches": [ - "kind: InitConfiguration\nnodeRegistration:\n kubeletExtraArgs:\n node-labels: \"worker-node=true\"\n" - ], - "role": "worker" - }, - { - "extra_mounts": [], - "extra_port_mappings": [], - "image": "", - "kubeadm_config_patches": [ - "kind: InitConfiguration\nnodeRegistration:\n kubeletExtraArgs:\n node-labels: \"worker-node=true\"\n" - ], - "role": "worker" - }, - { - "extra_mounts": [], - "extra_port_mappings": [], - "image": "", - "kubeadm_config_patches": [ - "kind: InitConfiguration\nnodeRegistration:\n kubeletExtraArgs:\n node-labels: \"worker-node=true\"\n" - ], - "role": "worker" - } - ] - } - ], - "kubeconfig": "apiVersion: v1\nclusters:\n- cluster:\n certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM1ekNDQWMrZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRJek1ETXlPREF6TlRVeU5sb1hEVE16TURNeU5UQXpOVFV5Tmxvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTDVVCnVUVnNsQlhEbmpVTmIvRDZ6NzFvOEU3V2xBZ3lwK29xN2dZSkFMeWFyQzQxZWd0MU5abEpEOEdIRDYrUHpIYngKM2t3SU1kY3JVbXpkMFFUSFg4bmM3Mks1My9tWHhTZ2tCTThvSVNZcW01YUhoMWhhc1YxZWxRUDkvajM2VmZtdAo1TC91aVVDVndjYjNXZng2U2o0dGZQajYxY3p1VVVxNkQrb1ZQMnlpLzBndEt5WnpTQ3ZGb3ZaRllSOG1GdTRxCmtKOW9MNTlpNEtKL3R4alJ1NEltaEx1bWZCbkZkd2l5L2tKeDI0Vm5aRFVSVmpRSkU2TXJ1RnpNOHZpckIxMW0KSVhocHY2WnIvd1AxVXFZOGx2RWlGWDlNZndJM1J0bG1sU2JXKzNtQUltSWFkRFduRFdWQjRmampDUHRJc0pTMQpkdlA1NWNIdVJBM05KWTVocUswQ0F3RUFBYU5DTUVBd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0hRWURWUjBPQkJZRUZIWlVFWVBjYU4wWDhCQWNPTmFhYi9NTEJmWm1NQTBHQ1NxR1NJYjMKRFFFQkN3VUFBNElCQVFBZlhaWityYVV4dlRCWS9lNlREQXFTQ3E1dXo5Z1VsVCtleVZZenptV09PakYrcndSUworSE5OYW1aeWdXeG85dzdIK0FNV2h5UEhBdUU0Wng3bklsR3ozY1RnY3NRT050QmhDNXUralFHSzVUWS9tL1J2CmpROWNlUDFqbm1aSEJReDhwdk9VaFNvTzA4TXFHN0oxYnljVjJna0xnTi9pVTJQWjJJN0FETjAzSzk4ZXY4RXkKbUlCcEtvb3cycUZsc2ZBYjJFWlY5YWtJMHVyYUpRZm9NR0xiRjRWZTJaNTdIS0VLQUpJVE5mRktDYThKN0hpagpqc1c0dG56QnpmQTZjVXZ0SHFET3YzeDEwY1ZZbjZiK3loMjhibytuTUZobjIweGJaNHZ3REJ5MjdUdFN3dVZXCnRndFYwV0E3Y2djWnRRdG5GeTNOWHBROHFPV1plQlB1VzlqbQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==\n server: https://127.0.0.1:37145\n name: kind-one-click\ncontexts:\n- context:\n cluster: kind-one-click\n user: kind-one-click\n name: kind-one-click\ncurrent-context: kind-one-click\nkind: Config\npreferences: {}\nusers:\n- name: kind-one-click\n user:\n client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURJVENDQWdtZ0F3SUJBZ0lJVTZQRXR5cmg2RVF3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB5TXpBek1qZ3dNelUxTWpaYUZ3MHlOREF6TWpjd016VTFNamhhTURReApGekFWQmdOVkJBb1REbk41YzNSbGJUcHRZWE4wWlhKek1Sa3dGd1lEVlFRREV4QnJkV0psY201bGRHVnpMV0ZrCmJXbHVNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQW50SkJHeGJhbUV5dDh2L2UKR3RtZVNOL0RnOUswRFlwS2R0aFhBMjIzVWNGU3phNFJqTUNvL2N1d2djVUI3TW5SSVBoVG5SOC9MNGoxNkRoNwpHaTJlMlk1MmVPY0hGeWtUN09zZlk5YmxnYkRhaXlGRXZ2OGZjRG9Pa0ZiNEFjZDZmSS9sblAwKzBYYUU3Q0hSCk5wVlVvZEMzS3NSN1VTKy80OVl6b2QveHJCWG94RU5wOFJXRHYrQzRBc050c2MvQzJvWURvZXlzRk16UWd5MWsKbDVzanNLbFEzYXBLS3l4T3RwWjROMWo0TFpVd21WcGM5aUJCZkxoa2xMbWlxd0lhRFlORG9UdklENTkrY1RkYQp0aEFPMHB2Q0VCWlIvYUpOTGlEMEFwYmVDYTE4NG1PeW9iWldhNDNaeXcxTjRyVVRsTjl2R3hBRjFFSk1xTnZUClpBVFMrUUlEQVFBQm8xWXdWREFPQmdOVkhROEJBZjhFQkFNQ0JhQXdFd1lEVlIwbEJBd3dDZ1lJS3dZQkJRVUgKQXdJd0RBWURWUjBUQVFIL0JBSXdBREFmQmdOVkhTTUVHREFXZ0JSMlZCR0QzR2pkRi9BUUhEaldtbS96Q3dYMgpaakFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBVVNhNDhKSmdFeHFGZW5CY21KWUZXcnZhVXF0Qk85anhYWWJnCjBDU0cyQ1k4OEJ3WUx3dXY0VVhpRGdiUWxMWHdiZFdHUkZLOWd6YXdISGNGQlZqbVZVcnQ5dkZoU1EvazhSWVgKNkFJVUNHQ29hcTBUQk9JK2ZjZHNTRTFUVDZ6TGpyYWJzaVVzeDYxKzdpQTJkUkRsaHNEZmZsRUtBdHlOUHFaaQo4REtQUmxJUXJyMmg3MDV3aW5RanJkVHh5WS9lTFhsSDVQSFNsTS9MRXJjby9IdlhkY0Z2UnIrMUc5V1JwemdLClViczE0NVUyMFdVK1N1Y0MrSmpzMVhNVWpKa0tNc3RvMC9Yc2JpN01KbFFVa0oyTXd0R2xrcDZwMkFVK2ZVWmsKUldKb0d1Q1BodkFmbDVLOW01Vkp3VEs0dklsbVVFNlR4ZHByZ0dRTmxOYTczTkFhNFE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==\n client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcEFJQkFBS0NBUUVBbnRKQkd4YmFtRXl0OHYvZUd0bWVTTi9EZzlLMERZcEtkdGhYQTIyM1VjRlN6YTRSCmpNQ28vY3V3Z2NVQjdNblJJUGhUblI4L0w0ajE2RGg3R2kyZTJZNTJlT2NIRnlrVDdPc2ZZOWJsZ2JEYWl5RkUKdnY4ZmNEb09rRmI0QWNkNmZJL2xuUDArMFhhRTdDSFJOcFZVb2RDM0tzUjdVUysvNDlZem9kL3hyQlhveEVOcAo4UldEditDNEFzTnRzYy9DMm9ZRG9leXNGTXpRZ3kxa2w1c2pzS2xRM2FwS0t5eE90cFo0TjFqNExaVXdtVnBjCjlpQkJmTGhrbExtaXF3SWFEWU5Eb1R2SUQ1OStjVGRhdGhBTzBwdkNFQlpSL2FKTkxpRDBBcGJlQ2ExODRtT3kKb2JaV2E0M1p5dzFONHJVVGxOOXZHeEFGMUVKTXFOdlRaQVRTK1FJREFRQUJBb0lCQUV5WDdab1kzWGNSazlwYgpGMHd2b3l4VjdiNkhRUm8rbFViWGNxOU9uRmdyT2JuRHR1Y0FpdjlLMm1iYkVlQ1dPK3NBbDZKd3pWSXBaUjZICkRxZERWUlpxcEN3T2p0YldrcXR4Z1FQdlFXUFlYSzA0Y0NVRWVLN3c4TkVmZ0IrcmlpREViT0hYS0JKd2JBNU4KQ002Z0dkNm4vNDM2ajlaSmh1dGtCQ2FQRDhnRFRtWi9GaWpZemwrNnBMOTJmT0Z2d2VDWWtRckNud0p4Vm9HSQpqc1U3YTJ2eDR0TmE0Z0s2REYvZXRYcGQ1WEd3bmNLeDM3MjNjVk9GS3E4aVYxU3VpSzZud1Q4bjlZUHV2RzNzCnh1b3A2Si9LV1FWUWUzWGtlOWtOZytxbkIveHJQRWZNSk1NNSszVU84UC9SVFNSM0FhSHNqTlhiVFBtOFVYWWIKUVVKZjQza0NnWUVBeGVZZ1UvSktkZmY2djZEUnVWZFZHQUNILzM1Q3BVeE1IWHp0cTZQK0MwZ0hsRWpiNVZ4bQpCeXJjTGdjNnhvam5OenNoSFVHd3E3ay8yenFtUkRWaCs4VjQwVVVPRTVHSU9RSlY0TlVjdjRDSS9Pa0VyZmw1CmdmT3F6N2ptT0tFMUY4QUlrVE8zOWJ0VXAzZDBjTjR1bUJCdXc5TCtJeXUybUgvWGlnL3NibThDZ1lFQXpYTVkKWDIyT3B3SFY1WUpsMmhEK1J2N0hFZzlpbXQ2NXZ3RkVYb2NvSVVaa1NTNjRPTXdGS0Nia3ZOYkljNlNwczY3LwpRRkxmaC9BSUZBaHdORXphUDBEOWthWnBMeHRyQU5iYTlRVDBoTGRveERjOWNBL0NORzV2TzZGQnNOMlZIR3cwCnMzREIrd2RDaHVuNVo0STdYV1VnQmFPSDZHckFJYmExSkJyckNSY0NnWUVBcDJwaEd1TTBIc3hrTWt6SVh6VUUKWmNPOVNXVDAySVhOTm4wRFMxcjJoWEN3Q01BQ1Z1eHQyODVrYWdQdnN4a0tFVzFTUEQvZ1I4WDF3VzdDVFoyagpnS3l6NHNXNmZNOTNJRk5iZDFLU2VXY3hmRk5jaEJreXM3OEJWNjBpQmZKS3lnYkl1SjI2R0lZYnZYaU81eFR1CjhmR2s0c0RTQWNydk05K2k1Y2RHeXIwQ2dZRUFnb0VmOUU1RmxYU25DSmtpVmJrQnJOdGVYT3lFK0ZlT3FmUUcKRi8zRzBneEMyL0hmZDhTeGFlWDYyckdjdDBqYnU1WG44bFl5UThtSGVDSThGQnpWYmVTOHJlNkZWalVXYlh3TgpyaUt0cURoUlJvTElkZ3lza2wyc0xyYXFZY3ovVVFTekhXRDg5NWFPL2VCV1d4ZncyY3pJNEl3V1l0NmNtVTNVCkZ0RTZoNVVDZ1lCOVpUQVdFRkdtS3VzUzNOTVQ5ZG81dENWQjZSY2Eyd29pdkN1Z3RYMUxlai9PdGFwQS9ud0kKNUJCcE0yTWZIZ3NaWlcrR1dxdWpOMlpZSVhmTC82UE5pT1h3MDZNdU1XSlRnUkhMMmova1ZlUzBXbzJkcUFidQpDZi83VVZBckUvdXNCblA2UWR1ZTdIREV5QVdaYVUwOVI0TThzRWE3MUQyQlZWMFZQNldkaUE9PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=\n", - "kubeconfig_path": "/home/stpl/.kube/config", - "name": "one-click", - "node_image": null, - "timeouts": null, - "wait_for_ready": true - }, - "sensitive_attributes": [], - "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjozMDAwMDAwMDAwMDAsImRlbGV0ZSI6MzAwMDAwMDAwMDAwLCJ1cGRhdGUiOjMwMDAwMDAwMDAwMH19" - } - ] - } - ], - "check_results": null -} From 5d276c3dc74d5a7b24a1f56768b7ff5a4e3719cf Mon Sep 17 00:00:00 2001 From: Aiman Sharief Date: Tue, 11 Apr 2023 11:20:49 +0530 Subject: [PATCH 205/490] Delete terraform.tfstate.backup --- .../terraform/local/terraform.tfstate.backup | 588 ------------------ 1 file changed, 588 deletions(-) delete mode 100644 knowlg-automation/terraform/local/terraform.tfstate.backup diff --git a/knowlg-automation/terraform/local/terraform.tfstate.backup b/knowlg-automation/terraform/local/terraform.tfstate.backup deleted file mode 100644 index ab93801e4..000000000 --- a/knowlg-automation/terraform/local/terraform.tfstate.backup +++ /dev/null @@ -1,588 +0,0 @@ -{ - "version": 4, - "terraform_version": "1.4.2", - "serial": 212, - "lineage": "9487d65f-b5a7-e87c-8d2c-35cd1692aa74", - "outputs": {}, - "resources": [ - { - "mode": "managed", - "type": "helm_release", - "name": "cassandra", - "provider": "provider[\"registry.terraform.io/hashicorp/helm\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "atomic": false, - "chart": "../../helm_charts/cassandra", - "cleanup_on_fail": false, - "create_namespace": true, - "dependency_update": true, - "description": null, - "devel": null, - "disable_crd_hooks": false, - "disable_openapi_validation": false, - "disable_webhooks": false, - "force_update": false, - "id": "cassandra", - "keyring": null, - "lint": false, - "manifest": null, - "max_history": 0, - "metadata": [ - { - "app_version": "1.0", - "chart": "cassandra", - "name": "cassandra", - "namespace": "knowlg-db", - "revision": 1, - "values": "null", - "version": "0.1.0" - } - ], - "name": "cassandra", - "namespace": "knowlg-db", - "pass_credentials": false, - "postrender": [], - "recreate_pods": false, - "render_subchart_notes": true, - "replace": false, - "repository": null, - "repository_ca_file": null, - "repository_cert_file": null, - "repository_key_file": null, - "repository_password": null, - "repository_username": null, - "reset_values": false, - "reuse_values": false, - "set": [], - "set_sensitive": [], - "skip_crds": false, - "status": "deployed", - "timeout": 300, - "values": null, - "verify": false, - "version": "0.1.0", - "wait": true, - "wait_for_jobs": true - }, - "sensitive_attributes": [], - "private": "bnVsbA==", - "dependencies": [ - "kind_cluster.one-click" - ] - } - ] - }, - { - "mode": "managed", - "type": "helm_release", - "name": "elasticsearch", - "provider": "provider[\"registry.terraform.io/hashicorp/helm\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "atomic": false, - "chart": "../../helm_charts/elasticsearch", - "cleanup_on_fail": false, - "create_namespace": true, - "dependency_update": true, - "description": null, - "devel": null, - "disable_crd_hooks": false, - "disable_openapi_validation": false, - "disable_webhooks": false, - "force_update": false, - "id": "elasticsearch", - "keyring": null, - "lint": false, - "manifest": null, - "max_history": 0, - "metadata": [ - { - "app_version": "8.6.0", - "chart": "elasticsearch", - "name": "elasticsearch", - "namespace": "knowlg-db", - "revision": 1, - "values": "null", - "version": "19.5.8" - } - ], - "name": "elasticsearch", - "namespace": "knowlg-db", - "pass_credentials": false, - "postrender": [], - "recreate_pods": false, - "render_subchart_notes": true, - "replace": false, - "repository": null, - "repository_ca_file": null, - "repository_cert_file": null, - "repository_key_file": null, - "repository_password": null, - "repository_username": null, - "reset_values": false, - "reuse_values": false, - "set": [], - "set_sensitive": [], - "skip_crds": false, - "status": "deployed", - "timeout": 300, - "values": null, - "verify": false, - "version": "19.5.8", - "wait": true, - "wait_for_jobs": true - }, - "sensitive_attributes": [], - "private": "bnVsbA==", - "dependencies": [ - "kind_cluster.one-click" - ] - } - ] - }, - { - "mode": "managed", - "type": "helm_release", - "name": "kafka", - "provider": "provider[\"registry.terraform.io/hashicorp/helm\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "atomic": false, - "chart": "../../helm_charts/kafka", - "cleanup_on_fail": false, - "create_namespace": true, - "dependency_update": true, - "description": null, - "devel": null, - "disable_crd_hooks": false, - "disable_openapi_validation": false, - "disable_webhooks": false, - "force_update": false, - "id": "kafka", - "keyring": null, - "lint": false, - "manifest": null, - "max_history": 0, - "metadata": [ - { - "app_version": "3.3.1", - "chart": "kafka", - "name": "kafka", - "namespace": "knowlg-db", - "revision": 1, - "values": "{\"advertisedListeners\":[],\"affinity\":{},\"allowEveryoneIfNoAclFound\":true,\"allowPlaintextListener\":true,\"args\":[],\"auth\":{\"clientProtocol\":\"plaintext\",\"externalClientProtocol\":\"\",\"interBrokerProtocol\":\"plaintext\",\"sasl\":{\"interBrokerMechanism\":\"plain\",\"jaas\":{\"clientPasswords\":[],\"clientUsers\":[\"user\"],\"existingSecret\":\"\",\"interBrokerPassword\":\"\",\"interBrokerUser\":\"admin\",\"zookeeperPassword\":\"\",\"zookeeperUser\":\"\"},\"mechanisms\":\"plain,scram-sha-256,scram-sha-512\"},\"tls\":{\"autoGenerated\":false,\"endpointIdentificationAlgorithm\":\"https\",\"existingSecret\":\"\",\"existingSecrets\":[],\"jksKeystoreSAN\":\"\",\"jksTruststore\":\"\",\"jksTruststoreSecret\":\"\",\"password\":\"\",\"pemChainIncluded\":false,\"type\":\"jks\"},\"zookeeper\":{\"tls\":{\"enabled\":false,\"existingSecret\":\"\",\"existingSecretKeystoreKey\":\"zookeeper.keystore.jks\",\"existingSecretTruststoreKey\":\"zookeeper.truststore.jks\",\"passwordsSecret\":\"\",\"passwordsSecretKeystoreKey\":\"keystore-password\",\"passwordsSecretTruststoreKey\":\"truststore-password\",\"type\":\"jks\",\"verifyHostname\":true}}},\"authorizerClassName\":\"\",\"autoCreateTopicsEnable\":true,\"brokerRackAssignment\":\"\",\"clusterDomain\":\"cluster.local\",\"command\":[\"/scripts/setup.sh\"],\"commonAnnotations\":{},\"commonLabels\":{},\"config\":\"\",\"containerPorts\":{\"client\":9092,\"external\":9094,\"internal\":9093},\"containerSecurityContext\":{\"allowPrivilegeEscalation\":false,\"enabled\":true,\"runAsNonRoot\":true,\"runAsUser\":1001},\"customLivenessProbe\":{},\"customReadinessProbe\":{},\"customStartupProbe\":{},\"defaultReplicationFactor\":1,\"deleteTopicEnable\":false,\"diagnosticMode\":{\"args\":[\"infinity\"],\"command\":[\"sleep\"],\"enabled\":false},\"existingConfigmap\":\"\",\"existingLog4jConfigMap\":\"\",\"externalAccess\":{\"autoDiscovery\":{\"enabled\":false,\"image\":{\"digest\":\"\",\"pullPolicy\":\"IfNotPresent\",\"pullSecrets\":[],\"registry\":\"docker.io\",\"repository\":\"bitnami/kubectl\",\"tag\":\"1.25.5-debian-11-r2\"},\"resources\":{\"limits\":{},\"requests\":{}}},\"enabled\":false,\"service\":{\"annotations\":{},\"domain\":\"\",\"extraPorts\":[],\"labels\":{},\"loadBalancerAnnotations\":[],\"loadBalancerIPs\":[],\"loadBalancerNames\":[],\"loadBalancerSourceRanges\":[],\"nodePorts\":[],\"ports\":{\"external\":9094},\"publishNotReadyAddresses\":false,\"type\":\"LoadBalancer\",\"useHostIPs\":false,\"usePodIPs\":false}},\"externalZookeeper\":{\"servers\":[]},\"extraDeploy\":[],\"extraEnvVars\":[],\"extraEnvVarsCM\":\"\",\"extraEnvVarsSecret\":\"\",\"extraVolumeMounts\":[],\"extraVolumes\":[],\"fullnameOverride\":\"\",\"global\":{\"imagePullSecrets\":[],\"imageRegistry\":\"\",\"storageClass\":\"\"},\"heapOpts\":\"-Xmx1024m -Xms1024m\",\"hostAliases\":[],\"hostIPC\":false,\"hostNetwork\":false,\"image\":{\"debug\":false,\"digest\":\"\",\"pullPolicy\":\"IfNotPresent\",\"pullSecrets\":[],\"registry\":\"docker.io\",\"repository\":\"bitnami/kafka\",\"tag\":\"3.3.1-debian-11-r25\"},\"initContainers\":[],\"interBrokerListenerName\":\"INTERNAL\",\"kubeVersion\":\"\",\"lifecycleHooks\":{},\"listenerSecurityProtocolMap\":\"\",\"listeners\":[],\"livenessProbe\":{\"enabled\":true,\"failureThreshold\":3,\"initialDelaySeconds\":10,\"periodSeconds\":10,\"successThreshold\":1,\"timeoutSeconds\":5},\"log4j\":\"\",\"logFlushIntervalMessages\":\"_10000\",\"logFlushIntervalMs\":1000,\"logPersistence\":{\"accessModes\":[\"ReadWriteOnce\"],\"annotations\":{},\"enabled\":false,\"existingClaim\":\"\",\"mountPath\":\"/opt/bitnami/kafka/logs\",\"selector\":{},\"size\":\"8Gi\",\"storageClass\":\"\"},\"logRetentionBytes\":\"_1073741824\",\"logRetentionCheckIntervalMs\":300000,\"logRetentionHours\":168,\"logSegmentBytes\":\"_1073741824\",\"logsDirs\":\"/bitnami/kafka/data\",\"maxMessageBytes\":\"_1000012\",\"metrics\":{\"jmx\":{\"config\":\"jmxUrl: service:jmx:rmi:///jndi/rmi://127.0.0.1:5555/jmxrmi\\nlowercaseOutputName: true\\nlowercaseOutputLabelNames: true\\nssl: false\\n{{- if .Values.metrics.jmx.whitelistObjectNames }}\\nwhitelistObjectNames: [\\\"{{ join \\\"\\\\\\\",\\\\\\\"\\\" .Values.metrics.jmx.whitelistObjectNames }}\\\"]\\n{{- end }}\",\"containerPorts\":{\"metrics\":5556},\"containerSecurityContext\":{\"enabled\":true,\"runAsNonRoot\":true,\"runAsUser\":1001},\"enabled\":false,\"existingConfigmap\":\"\",\"extraRules\":\"\",\"image\":{\"digest\":\"\",\"pullPolicy\":\"IfNotPresent\",\"pullSecrets\":[],\"registry\":\"docker.io\",\"repository\":\"bitnami/jmx-exporter\",\"tag\":\"0.17.2-debian-11-r29\"},\"resources\":{\"limits\":{},\"requests\":{}},\"service\":{\"annotations\":{\"prometheus.io/path\":\"/\",\"prometheus.io/port\":\"{{ .Values.metrics.jmx.service.ports.metrics }}\",\"prometheus.io/scrape\":\"true\"},\"clusterIP\":\"\",\"ports\":{\"metrics\":5556},\"sessionAffinity\":\"None\"},\"whitelistObjectNames\":[\"kafka.controller:*\",\"kafka.server:*\",\"java.lang:*\",\"kafka.network:*\",\"kafka.log:*\"]},\"kafka\":{\"affinity\":{},\"args\":[],\"certificatesSecret\":\"\",\"command\":[],\"containerPorts\":{\"metrics\":9308},\"containerSecurityContext\":{\"enabled\":true,\"runAsNonRoot\":true,\"runAsUser\":1001},\"enabled\":false,\"extraFlags\":{},\"extraVolumeMounts\":[],\"extraVolumes\":[],\"hostAliases\":[],\"image\":{\"digest\":\"\",\"pullPolicy\":\"IfNotPresent\",\"pullSecrets\":[],\"registry\":\"docker.io\",\"repository\":\"bitnami/kafka-exporter\",\"tag\":\"1.6.0-debian-11-r40\"},\"initContainers\":[],\"nodeAffinityPreset\":{\"key\":\"\",\"type\":\"\",\"values\":[]},\"nodeSelector\":{},\"podAffinityPreset\":\"\",\"podAnnotations\":{},\"podAntiAffinityPreset\":\"soft\",\"podLabels\":{},\"podSecurityContext\":{\"enabled\":true,\"fsGroup\":1001},\"priorityClassName\":\"\",\"resources\":{\"limits\":{},\"requests\":{}},\"schedulerName\":\"\",\"service\":{\"annotations\":{\"prometheus.io/path\":\"/metrics\",\"prometheus.io/port\":\"{{ .Values.metrics.kafka.service.ports.metrics }}\",\"prometheus.io/scrape\":\"true\"},\"clusterIP\":\"\",\"ports\":{\"metrics\":9308},\"sessionAffinity\":\"None\"},\"serviceAccount\":{\"automountServiceAccountToken\":true,\"create\":true,\"name\":\"\"},\"sidecars\":[],\"tlsCaCert\":\"ca-file\",\"tlsCaSecret\":\"\",\"tlsCert\":\"cert-file\",\"tlsKey\":\"key-file\",\"tolerations\":[],\"topologySpreadConstraints\":[]},\"prometheusRule\":{\"enabled\":false,\"groups\":[],\"labels\":{},\"namespace\":\"\"},\"serviceMonitor\":{\"enabled\":false,\"honorLabels\":false,\"interval\":\"\",\"jobLabel\":\"\",\"labels\":{},\"metricRelabelings\":[],\"namespace\":\"\",\"relabelings\":[],\"scrapeTimeout\":\"\",\"selector\":{}}},\"minBrokerId\":0,\"nameOverride\":\"\",\"networkPolicy\":{\"allowExternal\":true,\"egressRules\":{\"customRules\":[]},\"enabled\":false,\"explicitNamespacesSelector\":{},\"externalAccess\":{\"from\":[]}},\"nodeAffinityPreset\":{\"key\":\"\",\"type\":\"\",\"values\":[]},\"nodeSelector\":{},\"numIoThreads\":8,\"numNetworkThreads\":3,\"numPartitions\":1,\"numRecoveryThreadsPerDataDir\":1,\"offsetsTopicReplicationFactor\":1,\"pdb\":{\"create\":false,\"maxUnavailable\":1,\"minAvailable\":\"\"},\"persistence\":{\"accessModes\":[\"ReadWriteOnce\"],\"annotations\":{},\"enabled\":true,\"existingClaim\":\"\",\"labels\":{},\"mountPath\":\"/bitnami/kafka\",\"selector\":{},\"size\":\"8Gi\",\"storageClass\":\"\"},\"podAffinityPreset\":\"\",\"podAnnotations\":{},\"podAntiAffinityPreset\":\"soft\",\"podLabels\":{},\"podManagementPolicy\":\"Parallel\",\"podSecurityContext\":{\"enabled\":true,\"fsGroup\":1001},\"priorityClassName\":\"\",\"provisioning\":{\"args\":[],\"auth\":{\"tls\":{\"caCert\":\"ca.crt\",\"cert\":\"tls.crt\",\"certificatesSecret\":\"\",\"key\":\"tls.key\",\"keyPassword\":\"\",\"keyPasswordSecretKey\":\"key-password\",\"keystore\":\"keystore.jks\",\"keystorePassword\":\"\",\"keystorePasswordSecretKey\":\"keystore-password\",\"passwordsSecret\":\"\",\"truststore\":\"truststore.jks\",\"truststorePassword\":\"\",\"truststorePasswordSecretKey\":\"truststore-password\",\"type\":\"jks\"}},\"command\":[],\"containerSecurityContext\":{\"enabled\":true,\"runAsNonRoot\":true,\"runAsUser\":1001},\"enabled\":true,\"extraEnvVars\":[],\"extraEnvVarsCM\":\"\",\"extraEnvVarsSecret\":\"\",\"extraProvisioningCommands\":[],\"extraVolumeMounts\":[],\"extraVolumes\":[],\"initContainers\":[],\"nodeSelector\":{},\"numPartitions\":1,\"parallel\":1,\"podAnnotations\":{},\"podLabels\":{},\"podSecurityContext\":{\"enabled\":true,\"fsGroup\":1001},\"postScript\":\"\",\"preScript\":\"\",\"replicationFactor\":1,\"resources\":{\"limits\":{},\"requests\":{}},\"schedulerName\":\"\",\"serviceAccount\":{\"automountServiceAccountToken\":true,\"create\":false,\"name\":\"\"},\"sidecars\":[],\"tolerations\":[],\"topics\":[{\"config\":{\"flush.messages\":1,\"max.message.bytes\":64000},\"name\":\"dev.telemetry.denorm\",\"partitions\":1,\"replicationFactor\":1},{\"config\":{\"flush.messages\":1,\"max.message.bytes\":64000},\"name\":\"dev.druid.events.telemetry\",\"partitions\":1,\"replicationFactor\":1},{\"config\":{\"flush.messages\":1,\"max.message.bytes\":64000},\"name\":\"dev.druid.events.summary\",\"partitions\":1,\"replicationFactor\":1},{\"config\":{\"flush.messages\":1,\"max.message.bytes\":64000},\"name\":\"dev.telemetry.failed\",\"partitions\":1,\"replicationFactor\":1},{\"config\":{\"flush.messages\":1,\"max.message.bytes\":64000},\"name\":\"dev.telemetry.duplicate\",\"partitions\":1,\"replicationFactor\":1}],\"waitForKafka\":true},\"rbac\":{\"create\":false},\"readinessProbe\":{\"enabled\":true,\"failureThreshold\":6,\"initialDelaySeconds\":5,\"periodSeconds\":10,\"successThreshold\":1,\"timeoutSeconds\":5},\"replicaCount\":1,\"resources\":{\"limits\":{},\"requests\":{}},\"schedulerName\":\"\",\"service\":{\"annotations\":{},\"clusterIP\":\"\",\"externalTrafficPolicy\":\"Cluster\",\"extraPorts\":[],\"headless\":{\"annotations\":{},\"labels\":{},\"publishNotReadyAddresses\":false},\"loadBalancerIP\":\"\",\"loadBalancerSourceRanges\":[],\"nodePorts\":{\"client\":\"\",\"external\":\"\"},\"ports\":{\"client\":9092,\"external\":9094,\"internal\":9093},\"sessionAffinity\":\"None\",\"sessionAffinityConfig\":{},\"type\":\"ClusterIP\"},\"serviceAccount\":{\"annotations\":{},\"automountServiceAccountToken\":true,\"create\":true,\"name\":\"\"},\"sidecars\":[],\"socketReceiveBufferBytes\":102400,\"socketRequestMaxBytes\":\"_104857600\",\"socketSendBufferBytes\":102400,\"startupProbe\":{\"enabled\":false,\"failureThreshold\":15,\"initialDelaySeconds\":30,\"periodSeconds\":10,\"successThreshold\":1,\"timeoutSeconds\":1},\"superUsers\":\"User:admin\",\"terminationGracePeriodSeconds\":\"\",\"tolerations\":[],\"topologySpreadConstraints\":[],\"transactionStateLogMinIsr\":1,\"transactionStateLogReplicationFactor\":1,\"updateStrategy\":{\"rollingUpdate\":{},\"type\":\"RollingUpdate\"},\"volumePermissions\":{\"containerSecurityContext\":{\"runAsUser\":0},\"enabled\":false,\"image\":{\"digest\":\"\",\"pullPolicy\":\"IfNotPresent\",\"pullSecrets\":[],\"registry\":\"docker.io\",\"repository\":\"bitnami/bitnami-shell\",\"tag\":\"11-debian-11-r63\"},\"resources\":{\"limits\":{},\"requests\":{}}},\"zookeeper\":{\"auth\":{\"client\":{\"clientPassword\":\"\",\"clientUser\":\"\",\"enabled\":false,\"serverPasswords\":\"\",\"serverUsers\":\"\"}},\"enabled\":true,\"persistence\":{\"accessModes\":[\"ReadWriteOnce\"],\"enabled\":true,\"size\":\"8Gi\",\"storageClass\":\"\"},\"replicaCount\":1},\"zookeeperChrootPath\":\"\",\"zookeeperConnectionTimeoutMs\":6000}", - "version": "20.0.2" - } - ], - "name": "kafka", - "namespace": "knowlg-db", - "pass_credentials": false, - "postrender": [], - "recreate_pods": false, - "render_subchart_notes": true, - "replace": false, - "repository": null, - "repository_ca_file": null, - "repository_cert_file": null, - "repository_key_file": null, - "repository_password": null, - "repository_username": null, - "reset_values": false, - "reuse_values": false, - "set": [], - "set_sensitive": [], - "skip_crds": false, - "status": "deployed", - "timeout": 300, - "values": [ - "## @section Global parameters\n## Global Docker image parameters\n## Please, note that this will override the image parameters, including dependencies, configured to use the global value\n## Current available global Docker image parameters: imageRegistry, imagePullSecrets and storageClass\n\n## @param global.imageRegistry Global Docker image registry\n## @param global.imagePullSecrets Global Docker registry secret names as an array\n## @param global.storageClass Global StorageClass for Persistent Volume(s)\n##\nglobal:\n imageRegistry: \"\"\n ## E.g.\n ## imagePullSecrets:\n ## - myRegistryKeySecretName\n ##\n imagePullSecrets: []\n storageClass: \"\"\n\n## @section Common parameters\n\n## @param kubeVersion Override Kubernetes version\n##\nkubeVersion: \"\"\n## @param nameOverride String to partially override common.names.fullname\n##\nnameOverride: \"\"\n## @param fullnameOverride String to fully override common.names.fullname\n##\nfullnameOverride: \"\"\n## @param clusterDomain Default Kubernetes cluster domain\n##\nclusterDomain: cluster.local\n## @param commonLabels Labels to add to all deployed objects\n##\ncommonLabels: {}\n## @param commonAnnotations Annotations to add to all deployed objects\n##\ncommonAnnotations: {}\n## @param extraDeploy Array of extra objects to deploy with the release\n##\nextraDeploy: []\n## Enable diagnostic mode in the statefulset\n##\ndiagnosticMode:\n ## @param diagnosticMode.enabled Enable diagnostic mode (all probes will be disabled and the command will be overridden)\n ##\n enabled: false\n ## @param diagnosticMode.command Command to override all containers in the statefulset\n ##\n command:\n - sleep\n ## @param diagnosticMode.args Args to override all containers in the statefulset\n ##\n args:\n - infinity\n\n## @section Kafka parameters\n\n## Bitnami Kafka image version\n## ref: https://hub.docker.com/r/bitnami/kafka/tags/\n## @param image.registry Kafka image registry\n## @param image.repository Kafka image repository\n## @param image.tag Kafka image tag (immutable tags are recommended)\n## @param image.digest Kafka image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag\n## @param image.pullPolicy Kafka image pull policy\n## @param image.pullSecrets Specify docker-registry secret names as an array\n## @param image.debug Specify if debug values should be set\n##\nimage:\n registry: docker.io\n repository: bitnami/kafka\n tag: 3.3.1-debian-11-r25\n digest: \"\"\n ## Specify a imagePullPolicy\n ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent'\n ## ref: https://kubernetes.io/docs/user-guide/images/#pre-pulling-images\n ##\n pullPolicy: IfNotPresent\n ## Optionally specify an array of imagePullSecrets.\n ## Secrets must be manually created in the namespace.\n ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/\n ## e.g:\n ## pullSecrets:\n ## - myRegistryKeySecretName\n ##\n pullSecrets: []\n ## Set to true if you would like to see extra information on logs\n ##\n debug: false\n## @param config Configuration file for Kafka. Auto-generated based on other parameters when not specified\n## Specify content for server.properties\n## NOTE: This will override any KAFKA_CFG_ environment variables (including those set by the chart)\n## The server.properties is auto-generated based on other parameters when this parameter is not specified\n## e.g:\n## config: |-\n## broker.id=-1\n## listeners=PLAINTEXT://:9092\n## advertised.listeners=PLAINTEXT://KAFKA_IP:9092\n## num.network.threads=3\n## num.io.threads=8\n## socket.send.buffer.bytes=102400\n## socket.receive.buffer.bytes=102400\n## socket.request.max.bytes=104857600\n## log.dirs=/bitnami/kafka/data\n## num.partitions=1\n## num.recovery.threads.per.data.dir=1\n## offsets.topic.replication.factor=1\n## transaction.state.log.replication.factor=1\n## transaction.state.log.min.isr=1\n## log.flush.interval.messages=10000\n## log.flush.interval.ms=1000\n## log.retention.hours=168\n## log.retention.bytes=1073741824\n## log.segment.bytes=1073741824\n## log.retention.check.interval.ms=300000\n## zookeeper.connect=ZOOKEEPER_SERVICE_NAME\n## zookeeper.connection.timeout.ms=6000\n## group.initial.rebalance.delay.ms=0\n##\nconfig: \"\"\n## @param existingConfigmap ConfigMap with Kafka Configuration\n## NOTE: This will override `config` AND any KAFKA_CFG_ environment variables\n##\nexistingConfigmap: \"\"\n## @param log4j An optional log4j.properties file to overwrite the default of the Kafka brokers\n## An optional log4j.properties file to overwrite the default of the Kafka brokers\n## ref: https://github.com/apache/kafka/blob/trunk/config/log4j.properties\n##\nlog4j: \"\"\n## @param existingLog4jConfigMap The name of an existing ConfigMap containing a log4j.properties file\n## The name of an existing ConfigMap containing a log4j.properties file\n## NOTE: this will override `log4j`\n##\nexistingLog4jConfigMap: \"\"\n## @param heapOpts Kafka Java Heap size\n##\nheapOpts: -Xmx1024m -Xms1024m\n## @param deleteTopicEnable Switch to enable topic deletion or not\n##\ndeleteTopicEnable: false\n## @param autoCreateTopicsEnable Switch to enable auto creation of topics. Enabling auto creation of topics not recommended for production or similar environments\n##\nautoCreateTopicsEnable: true\n## @param logFlushIntervalMessages The number of messages to accept before forcing a flush of data to disk\n##\nlogFlushIntervalMessages: _10000\n## @param logFlushIntervalMs The maximum amount of time a message can sit in a log before we force a flush\n##\nlogFlushIntervalMs: 1000\n## @param logRetentionBytes A size-based retention policy for logs\n##\nlogRetentionBytes: _1073741824\n## @param logRetentionCheckIntervalMs The interval at which log segments are checked to see if they can be deleted\n##\nlogRetentionCheckIntervalMs: 300000\n## @param logRetentionHours The minimum age of a log file to be eligible for deletion due to age\n##\nlogRetentionHours: 168\n## @param logSegmentBytes The maximum size of a log segment file. When this size is reached a new log segment will be created\n##\nlogSegmentBytes: _1073741824\n## @param logsDirs A comma separated list of directories in which kafka's log data is kept\n## ref: https://kafka.apache.org/documentation/#brokerconfigs_log.dirs\nlogsDirs: /bitnami/kafka/data\n## @param maxMessageBytes The largest record batch size allowed by Kafka\n##\nmaxMessageBytes: _1000012\n## @param defaultReplicationFactor Default replication factors for automatically created topics\n##\ndefaultReplicationFactor: 1\n## @param offsetsTopicReplicationFactor The replication factor for the offsets topic\n##\noffsetsTopicReplicationFactor: 1\n## @param transactionStateLogReplicationFactor The replication factor for the transaction topic\n##\ntransactionStateLogReplicationFactor: 1\n## @param transactionStateLogMinIsr Overridden min.insync.replicas config for the transaction topic\n##\ntransactionStateLogMinIsr: 1\n## @param numIoThreads The number of threads doing disk I/O\n##\nnumIoThreads: 8\n## @param numNetworkThreads The number of threads handling network requests\n##\nnumNetworkThreads: 3\n## @param numPartitions The default number of log partitions per topic\n##\nnumPartitions: 1\n## @param numRecoveryThreadsPerDataDir The number of threads per data directory to be used for log recovery at startup and flushing at shutdown\n##\nnumRecoveryThreadsPerDataDir: 1\n## @param socketReceiveBufferBytes The receive buffer (SO_RCVBUF) used by the socket server\n##\nsocketReceiveBufferBytes: 102400\n## @param socketRequestMaxBytes The maximum size of a request that the socket server will accept (protection against OOM)\n##\nsocketRequestMaxBytes: _104857600\n## @param socketSendBufferBytes The send buffer (SO_SNDBUF) used by the socket server\n##\nsocketSendBufferBytes: 102400\n## @param zookeeperConnectionTimeoutMs Timeout in ms for connecting to ZooKeeper\n##\nzookeeperConnectionTimeoutMs: 6000\n## @param zookeeperChrootPath Path which puts data under some path in the global ZooKeeper namespace\n## ref: https://kafka.apache.org/documentation/#brokerconfigs_zookeeper.connect\n##\nzookeeperChrootPath: \"\"\n## @param authorizerClassName The Authorizer is configured by setting authorizer.class.name=kafka.security.authorizer.AclAuthorizer in server.properties\n##\nauthorizerClassName: \"\"\n## @param allowEveryoneIfNoAclFound By default, if a resource has no associated ACLs, then no one is allowed to access that resource except super users\n##\nallowEveryoneIfNoAclFound: true\n## @param superUsers You can add super users in server.properties\n##\nsuperUsers: User:admin\n## Authentication parameters\n## https://github.com/bitnami/containers/tree/main/bitnami/kafka#security\n##\nauth:\n ## Authentication protocol for client and inter-broker communications\n ## This table shows the security provided on each protocol:\n ## | Method | Authentication | Encryption via TLS |\n ## | plaintext | None | No |\n ## | tls | None | Yes |\n ## | mtls | Yes (two-way authentication) | Yes |\n ## | sasl | Yes (via SASL) | No |\n ## | sasl_tls | Yes (via SASL) | Yes |\n ## @param auth.clientProtocol Authentication protocol for communications with clients. Allowed protocols: `plaintext`, `tls`, `mtls`, `sasl` and `sasl_tls`\n ## @param auth.externalClientProtocol Authentication protocol for communications with external clients. Defaults to value of `auth.clientProtocol`. Allowed protocols: `plaintext`, `tls`, `mtls`, `sasl` and `sasl_tls`\n ## @param auth.interBrokerProtocol Authentication protocol for inter-broker communications. Allowed protocols: `plaintext`, `tls`, `mtls`, `sasl` and `sasl_tls`\n ##\n clientProtocol: plaintext\n # Note: empty by default for backwards compatibility reasons, find more information at\n # https://github.com/bitnami/charts/pull/8902/\n externalClientProtocol: \"\"\n interBrokerProtocol: plaintext\n ## SASL configuration\n ##\n sasl:\n ## @param auth.sasl.mechanisms SASL mechanisms when either `auth.interBrokerProtocol`, `auth.clientProtocol` or `auth.externalClientProtocol` are `sasl`. Allowed types: `plain`, `scram-sha-256`, `scram-sha-512`\n ##\n mechanisms: plain,scram-sha-256,scram-sha-512\n ## @param auth.sasl.interBrokerMechanism SASL mechanism for inter broker communication.\n ##\n interBrokerMechanism: plain\n ## JAAS configuration for SASL authentication.\n ##\n jaas:\n ## @param auth.sasl.jaas.clientUsers Kafka client user list\n ##\n ## clientUsers:\n ## - user1\n ## - user2\n ##\n clientUsers:\n - user\n ## @param auth.sasl.jaas.clientPasswords Kafka client passwords. This is mandatory if more than one user is specified in clientUsers\n ##\n ## clientPasswords:\n ## - password1\n ## - password2\"\n ##\n clientPasswords: []\n ## @param auth.sasl.jaas.interBrokerUser Kafka inter broker communication user for SASL authentication\n ##\n interBrokerUser: admin\n ## @param auth.sasl.jaas.interBrokerPassword Kafka inter broker communication password for SASL authentication\n ##\n interBrokerPassword: \"\"\n ## @param auth.sasl.jaas.zookeeperUser Kafka ZooKeeper user for SASL authentication\n ##\n zookeeperUser: \"\"\n ## @param auth.sasl.jaas.zookeeperPassword Kafka ZooKeeper password for SASL authentication\n ##\n zookeeperPassword: \"\"\n ## @param auth.sasl.jaas.existingSecret Name of the existing secret containing credentials for clientUsers, interBrokerUser and zookeeperUser\n ## Create this secret running the command below where SECRET_NAME is the name of the secret you want to create:\n ## kubectl create secret generic SECRET_NAME --from-literal=client-passwords=CLIENT_PASSWORD1,CLIENT_PASSWORD2 --from-literal=inter-broker-password=INTER_BROKER_PASSWORD --from-literal=zookeeper-password=ZOOKEEPER_PASSWORD\n ##\n existingSecret: \"\"\n ## TLS configuration\n ##\n tls:\n ## @param auth.tls.type Format to use for TLS certificates. Allowed types: `jks` and `pem`\n ##\n type: jks\n ## @param auth.tls.pemChainIncluded Flag to denote that the Certificate Authority (CA) certificates are bundled with the endpoint cert.\n ## Certificates must be in proper order, where the top certificate is the leaf and the bottom certificate is the top-most intermediate CA.\n ##\n pemChainIncluded: false\n ## @param auth.tls.existingSecrets Array existing secrets containing the TLS certificates for the Kafka brokers\n ## When using 'jks' format for certificates, each secret should contain a truststore and a keystore.\n ## Create these secrets following the steps below:\n ## 1) Generate your truststore and keystore files. Helpful script: https://raw.githubusercontent.com/confluentinc/confluent-platform-security-tools/master/kafka-generate-ssl.sh\n ## 2) Rename your truststore to `kafka.truststore.jks`.\n ## 3) Rename your keystores to `kafka-X.keystore.jks` where X is the ID of each Kafka broker.\n ## 4) Run the command below one time per broker to create its associated secret (SECRET_NAME_X is the name of the secret you want to create):\n ## kubectl create secret generic SECRET_NAME_0 --from-file=kafka.truststore.jks=./kafka.truststore.jks --from-file=kafka.keystore.jks=./kafka-0.keystore.jks\n ## kubectl create secret generic SECRET_NAME_1 --from-file=kafka.truststore.jks=./kafka.truststore.jks --from-file=kafka.keystore.jks=./kafka-1.keystore.jks\n ## ...\n ##\n ## When using 'pem' format for certificates, each secret should contain a public CA certificate, a public certificate and one private key.\n ## Create these secrets following the steps below:\n ## 1) Create a certificate key and signing request per Kafka broker, and sign the signing request with your CA\n ## 2) Rename your CA file to `kafka.ca.crt`.\n ## 3) Rename your certificates to `kafka-X.tls.crt` where X is the ID of each Kafka broker.\n ## 3) Rename your keys to `kafka-X.tls.key` where X is the ID of each Kafka broker.\n ## 4) Run the command below one time per broker to create its associated secret (SECRET_NAME_X is the name of the secret you want to create):\n ## kubectl create secret generic SECRET_NAME_0 --from-file=ca.crt=./kafka.ca.crt --from-file=tls.crt=./kafka-0.tls.crt --from-file=tls.key=./kafka-0.tls.key\n ## kubectl create secret generic SECRET_NAME_1 --from-file=ca.crt=./kafka.ca.crt --from-file=tls.crt=./kafka-1.tls.crt --from-file=tls.key=./kafka-1.tls.key\n ## ...\n ##\n existingSecrets: []\n ## @param auth.tls.autoGenerated Generate automatically self-signed TLS certificates for Kafka brokers. Currently only supported if `auth.tls.type` is `pem`\n ## Note: ignored when using 'jks' format or `auth.tls.existingSecrets` is not empty\n ##\n autoGenerated: false\n ## @param auth.tls.password Password to access the JKS files or PEM key when they are password-protected.\n ## Note: ignored when using 'existingSecret'.\n ##\n password: \"\"\n ## @param auth.tls.existingSecret Name of the secret containing the password to access the JKS files or PEM key when they are password-protected. (`key`: `password`)\n ##\n existingSecret: \"\"\n ## @param auth.tls.jksTruststoreSecret Name of the existing secret containing your truststore if truststore not existing or different from the ones in the `auth.tls.existingSecrets`\n ## Note: ignored when using 'pem' format for certificates.\n ##\n jksTruststoreSecret: \"\"\n ## @param auth.tls.jksKeystoreSAN The secret key from the `auth.tls.existingSecrets` containing the keystore with a SAN certificate\n ## The SAN certificate in it should be issued with Subject Alternative Names for all headless services:\n ## - kafka-0.kafka-headless.kafka.svc.cluster.local\n ## - kafka-1.kafka-headless.kafka.svc.cluster.local\n ## - kafka-2.kafka-headless.kafka.svc.cluster.local\n ## Note: ignored when using 'pem' format for certificates.\n ##\n jksKeystoreSAN: \"\"\n ## @param auth.tls.jksTruststore The secret key from the `auth.tls.existingSecrets` or `auth.tls.jksTruststoreSecret` containing the truststore\n ## Note: ignored when using 'pem' format for certificates.\n ##\n jksTruststore: \"\"\n ## @param auth.tls.endpointIdentificationAlgorithm The endpoint identification algorithm to validate server hostname using server certificate\n ## Disable server host name verification by setting it to an empty string.\n ## ref: https://docs.confluent.io/current/kafka/authentication_ssl.html#optional-settings\n ##\n endpointIdentificationAlgorithm: https\n ## Zookeeper client configuration for kafka brokers\n ##\n zookeeper:\n ## TLS configuration\n ##\n tls:\n ## @param auth.zookeeper.tls.enabled Enable TLS for Zookeeper client connections.\n ##\n enabled: false\n ## @param auth.zookeeper.tls.type Format to use for TLS certificates. Allowed types: `jks` and `pem`.\n ##\n type: jks\n ## @param auth.zookeeper.tls.verifyHostname Hostname validation.\n ##\n verifyHostname: true\n ## @param auth.zookeeper.tls.existingSecret Name of the existing secret containing the TLS certificates for ZooKeeper client communications.\n ##\n existingSecret: \"\"\n ## @param auth.zookeeper.tls.existingSecretKeystoreKey The secret key from the auth.zookeeper.tls.existingSecret containing the Keystore.\n ##\n existingSecretKeystoreKey: zookeeper.keystore.jks\n ## @param auth.zookeeper.tls.existingSecretTruststoreKey The secret key from the auth.zookeeper.tls.existingSecret containing the Truststore.\n ##\n existingSecretTruststoreKey: zookeeper.truststore.jks\n ## @param auth.zookeeper.tls.passwordsSecret Existing secret containing Keystore and Truststore passwords.\n ##\n passwordsSecret: \"\"\n ## @param auth.zookeeper.tls.passwordsSecretKeystoreKey The secret key from the auth.zookeeper.tls.passwordsSecret containing the password for the Keystore.\n ##\n passwordsSecretKeystoreKey: keystore-password\n ## @param auth.zookeeper.tls.passwordsSecretTruststoreKey The secret key from the auth.zookeeper.tls.passwordsSecret containing the password for the Truststore.\n ##\n passwordsSecretTruststoreKey: truststore-password\n## @param listeners The address(es) the socket server listens on. Auto-calculated it's set to an empty array\n## When it's set to an empty array, the listeners will be configured\n## based on the authentication protocols (auth.clientProtocol, auth.externalClientProtocol and auth.interBrokerProtocol parameters)\n##\nlisteners: []\n## @param advertisedListeners The address(es) (hostname:port) the broker will advertise to producers and consumers. Auto-calculated it's set to an empty array\n## When it's set to an empty array, the advertised listeners will be configured\n## based on the authentication protocols (auth.clientProtocol, auth.externalClientProtocol and auth.interBrokerProtocol parameters)\n##\nadvertisedListeners: []\n## @param listenerSecurityProtocolMap The protocol-\u003elistener mapping. Auto-calculated it's set to nil\n## When it's nil, the listeners will be configured based on the authentication protocols (auth.clientProtocol, auth.externalClientProtocol and auth.interBrokerProtocol parameters)\n##\nlistenerSecurityProtocolMap: \"\"\n## @param allowPlaintextListener Allow to use the PLAINTEXT listener\n##\nallowPlaintextListener: true\n## @param interBrokerListenerName The listener that the brokers should communicate on\n##\ninterBrokerListenerName: INTERNAL\n## @param command Override Kafka container command\n##\ncommand:\n - /scripts/setup.sh\n## @param args Override Kafka container arguments\n##\nargs: []\n## @param extraEnvVars Extra environment variables to add to Kafka pods\n## ref: https://github.com/bitnami/containers/tree/main/bitnami/kafka#configuration\n## e.g:\n## extraEnvVars:\n## - name: KAFKA_CFG_BACKGROUND_THREADS\n## value: \"10\"\n##\nextraEnvVars: []\n## @param extraEnvVarsCM ConfigMap with extra environment variables\n##\nextraEnvVarsCM: \"\"\n## @param extraEnvVarsSecret Secret with extra environment variables\n##\nextraEnvVarsSecret: \"\"\n\n## @section Statefulset parameters\n\n## @param replicaCount Number of Kafka nodes\n##\nreplicaCount: 1\n## @param minBrokerId Minimal broker.id value, nodes increment their `broker.id` respectively\n## Brokers increment their ID starting at this minimal value.\n## E.g., with `minBrokerId=100` and 3 nodes, IDs will be 100, 101, 102 for brokers 0, 1, and 2, respectively.\n##\nminBrokerId: 0\n## @param brokerRackAssignment Set Broker Assignment for multi tenant environment Allowed values: `aws-az`\n## ref: https://cwiki.apache.org/confluence/display/KAFKA/KIP-392%3A+Allow+consumers+to+fetch+from+closest+replica\n##\nbrokerRackAssignment: \"\"\n## @param containerPorts.client Kafka client container port\n## @param containerPorts.internal Kafka inter-broker container port\n## @param containerPorts.external Kafka external container port\n##\ncontainerPorts:\n client: 9092\n internal: 9093\n external: 9094\n## Configure extra options for Kafka containers' liveness, readiness and startup probes\n## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#configure-probes\n## @param livenessProbe.enabled Enable livenessProbe on Kafka containers\n## @param livenessProbe.initialDelaySeconds Initial delay seconds for livenessProbe\n## @param livenessProbe.periodSeconds Period seconds for livenessProbe\n## @param livenessProbe.timeoutSeconds Timeout seconds for livenessProbe\n## @param livenessProbe.failureThreshold Failure threshold for livenessProbe\n## @param livenessProbe.successThreshold Success threshold for livenessProbe\n##\nlivenessProbe:\n enabled: true\n initialDelaySeconds: 10\n timeoutSeconds: 5\n failureThreshold: 3\n periodSeconds: 10\n successThreshold: 1\n## @param readinessProbe.enabled Enable readinessProbe on Kafka containers\n## @param readinessProbe.initialDelaySeconds Initial delay seconds for readinessProbe\n## @param readinessProbe.periodSeconds Period seconds for readinessProbe\n## @param readinessProbe.timeoutSeconds Timeout seconds for readinessProbe\n## @param readinessProbe.failureThreshold Failure threshold for readinessProbe\n## @param readinessProbe.successThreshold Success threshold for readinessProbe\n##\nreadinessProbe:\n enabled: true\n initialDelaySeconds: 5\n failureThreshold: 6\n timeoutSeconds: 5\n periodSeconds: 10\n successThreshold: 1\n## @param startupProbe.enabled Enable startupProbe on Kafka containers\n## @param startupProbe.initialDelaySeconds Initial delay seconds for startupProbe\n## @param startupProbe.periodSeconds Period seconds for startupProbe\n## @param startupProbe.timeoutSeconds Timeout seconds for startupProbe\n## @param startupProbe.failureThreshold Failure threshold for startupProbe\n## @param startupProbe.successThreshold Success threshold for startupProbe\n##\nstartupProbe:\n enabled: false\n initialDelaySeconds: 30\n periodSeconds: 10\n timeoutSeconds: 1\n failureThreshold: 15\n successThreshold: 1\n## @param customLivenessProbe Custom livenessProbe that overrides the default one\n##\ncustomLivenessProbe: {}\n## @param customReadinessProbe Custom readinessProbe that overrides the default one\n##\ncustomReadinessProbe: {}\n## @param customStartupProbe Custom startupProbe that overrides the default one\n##\ncustomStartupProbe: {}\n## @param lifecycleHooks lifecycleHooks for the Kafka container to automate configuration before or after startup\n##\nlifecycleHooks: {}\n## Kafka resource requests and limits\n## ref: https://kubernetes.io/docs/user-guide/compute-resources/\n## @param resources.limits The resources limits for the container\n## @param resources.requests The requested resources for the container\n##\nresources:\n limits: {}\n requests: {}\n## Kafka pods' Security Context\n## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod\n## @param podSecurityContext.enabled Enable security context for the pods\n## @param podSecurityContext.fsGroup Set Kafka pod's Security Context fsGroup\n##\npodSecurityContext:\n enabled: true\n fsGroup: 1001\n## Kafka containers' Security Context\n## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container\n## @param containerSecurityContext.enabled Enable Kafka containers' Security Context\n## @param containerSecurityContext.runAsUser Set Kafka containers' Security Context runAsUser\n## @param containerSecurityContext.runAsNonRoot Set Kafka containers' Security Context runAsNonRoot\n## @param containerSecurityContext.allowPrivilegeEscalation Force the child process to be run as nonprivilege\n## e.g:\n## containerSecurityContext:\n## enabled: true\n## capabilities:\n## drop: [\"NET_RAW\"]\n## readOnlyRootFilesystem: true\n##\ncontainerSecurityContext:\n enabled: true\n runAsUser: 1001\n runAsNonRoot: true\n allowPrivilegeEscalation: false\n## @param hostAliases Kafka pods host aliases\n## https://kubernetes.io/docs/concepts/services-networking/add-entries-to-pod-etc-hosts-with-host-aliases/\n##\nhostAliases: []\n## @param hostNetwork Specify if host network should be enabled for Kafka pods\n##\nhostNetwork: false\n## @param hostIPC Specify if host IPC should be enabled for Kafka pods\n##\nhostIPC: false\n## @param podLabels Extra labels for Kafka pods\n## Ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/\n##\npodLabels: {}\n## @param podAnnotations Extra annotations for Kafka pods\n## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/\n##\npodAnnotations: {}\n## @param podAffinityPreset Pod affinity preset. Ignored if `affinity` is set. Allowed values: `soft` or `hard`\n## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity\n##\npodAffinityPreset: \"\"\n## @param podAntiAffinityPreset Pod anti-affinity preset. Ignored if `affinity` is set. Allowed values: `soft` or `hard`\n## Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity\n##\npodAntiAffinityPreset: soft\n## Node affinity preset\n## Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity\n##\nnodeAffinityPreset:\n ## @param nodeAffinityPreset.type Node affinity preset type. Ignored if `affinity` is set. Allowed values: `soft` or `hard`\n ##\n type: \"\"\n ## @param nodeAffinityPreset.key Node label key to match Ignored if `affinity` is set.\n ## E.g.\n ## key: \"kubernetes.io/e2e-az-name\"\n ##\n key: \"\"\n ## @param nodeAffinityPreset.values Node label values to match. Ignored if `affinity` is set.\n ## E.g.\n ## values:\n ## - e2e-az1\n ## - e2e-az2\n ##\n values: []\n## @param affinity Affinity for pod assignment\n## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity\n## Note: podAffinityPreset, podAntiAffinityPreset, and nodeAffinityPreset will be ignored when it's set\n##\naffinity: {}\n## @param nodeSelector Node labels for pod assignment\n## Ref: https://kubernetes.io/docs/user-guide/node-selection/\n##\nnodeSelector: {}\n## @param tolerations Tolerations for pod assignment\n## Ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/\n##\ntolerations: []\n## @param topologySpreadConstraints Topology Spread Constraints for pod assignment spread across your cluster among failure-domains. Evaluated as a template\n## Ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/#spread-constraints-for-pods\n##\ntopologySpreadConstraints: []\n## @param terminationGracePeriodSeconds Seconds the pod needs to gracefully terminate\n## ref: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#hook-handler-execution\n##\nterminationGracePeriodSeconds: \"\"\n## @param podManagementPolicy StatefulSet controller supports relax its ordering guarantees while preserving its uniqueness and identity guarantees. There are two valid pod management policies: OrderedReady and Parallel\n## ref: https://kubernetes.io/docs/tutorials/stateful-application/basic-stateful-set/#pod-management-policy\n##\npodManagementPolicy: Parallel\n## @param priorityClassName Name of the existing priority class to be used by kafka pods\n## Ref: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/\n##\npriorityClassName: \"\"\n## @param schedulerName Name of the k8s scheduler (other than default)\n## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/\n##\nschedulerName: \"\"\n## @param updateStrategy.type Kafka statefulset strategy type\n## @param updateStrategy.rollingUpdate Kafka statefulset rolling update configuration parameters\n## ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#update-strategies\n##\nupdateStrategy:\n type: RollingUpdate\n rollingUpdate: {}\n## @param extraVolumes Optionally specify extra list of additional volumes for the Kafka pod(s)\n## e.g:\n## extraVolumes:\n## - name: kafka-jaas\n## secret:\n## secretName: kafka-jaas\n##\nextraVolumes: []\n## @param extraVolumeMounts Optionally specify extra list of additional volumeMounts for the Kafka container(s)\n## extraVolumeMounts:\n## - name: kafka-jaas\n## mountPath: /bitnami/kafka/config/kafka_jaas.conf\n## subPath: kafka_jaas.conf\n##\nextraVolumeMounts: []\n## @param sidecars Add additional sidecar containers to the Kafka pod(s)\n## e.g:\n## sidecars:\n## - name: your-image-name\n## image: your-image\n## imagePullPolicy: Always\n## ports:\n## - name: portname\n## containerPort: 1234\n##\nsidecars: []\n## @param initContainers Add additional Add init containers to the Kafka pod(s)\n## e.g:\n## initContainers:\n## - name: your-image-name\n## image: your-image\n## imagePullPolicy: Always\n## ports:\n## - name: portname\n## containerPort: 1234\n##\ninitContainers: []\n## Kafka Pod Disruption Budget\n## ref: https://kubernetes.io/docs/concepts/workloads/pods/disruptions/\n## @param pdb.create Deploy a pdb object for the Kafka pod\n## @param pdb.minAvailable Maximum number/percentage of unavailable Kafka replicas\n## @param pdb.maxUnavailable Maximum number/percentage of unavailable Kafka replicas\n##\npdb:\n create: false\n minAvailable: \"\"\n maxUnavailable: 1\n\n## @section Traffic Exposure parameters\n\n## Service parameters\n##\nservice:\n ## @param service.type Kubernetes Service type\n ##\n type: ClusterIP\n ## @param service.ports.client Kafka svc port for client connections\n ## @param service.ports.internal Kafka svc port for inter-broker connections\n ## @param service.ports.external Kafka svc port for external connections\n ##\n ports:\n client: 9092\n internal: 9093\n external: 9094\n ## @param service.nodePorts.client Node port for the Kafka client connections\n ## @param service.nodePorts.external Node port for the Kafka external connections\n ## NOTE: choose port between \u003c30000-32767\u003e\n ##\n nodePorts:\n client: \"\"\n external: \"\"\n ## @param service.sessionAffinity Control where client requests go, to the same pod or round-robin\n ## Values: ClientIP or None\n ## ref: https://kubernetes.io/docs/user-guide/services/\n ##\n sessionAffinity: None\n ## @param service.sessionAffinityConfig Additional settings for the sessionAffinity\n ## sessionAffinityConfig:\n ## clientIP:\n ## timeoutSeconds: 300\n ##\n sessionAffinityConfig: {}\n ## @param service.clusterIP Kafka service Cluster IP\n ## e.g.:\n ## clusterIP: None\n ##\n clusterIP: \"\"\n ## @param service.loadBalancerIP Kafka service Load Balancer IP\n ## ref: https://kubernetes.io/docs/user-guide/services/#type-loadbalancer\n ##\n loadBalancerIP: \"\"\n ## @param service.loadBalancerSourceRanges Kafka service Load Balancer sources\n ## ref: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/#restrict-access-for-loadbalancer-service\n ## e.g:\n ## loadBalancerSourceRanges:\n ## - 10.10.10.0/24\n ##\n loadBalancerSourceRanges: []\n ## @param service.externalTrafficPolicy Kafka service external traffic policy\n ## ref https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip\n ##\n externalTrafficPolicy: Cluster\n ## @param service.annotations Additional custom annotations for Kafka service\n ##\n annotations: {}\n ## Headless service properties\n ##\n headless:\n ## @param service.headless.publishNotReadyAddresses Indicates that any agent which deals with endpoints for this Service should disregard any indications of ready/not-ready\n ## ref: https://kubernetes.io/docs/reference/kubernetes-api/service-resources/service-v1/\n publishNotReadyAddresses: false\n ## @param service.headless.annotations Annotations for the headless service.\n ##\n annotations: {}\n ## @param service.headless.labels Labels for the headless service.\n ##\n labels: {}\n ## @param service.extraPorts Extra ports to expose in the Kafka service (normally used with the `sidecar` value)\n ##\n extraPorts: []\n## External Access to Kafka brokers configuration\n##\nexternalAccess:\n ## @param externalAccess.enabled Enable Kubernetes external cluster access to Kafka brokers\n ##\n enabled: false\n ## External IPs auto-discovery configuration\n ## An init container is used to auto-detect LB IPs or node ports by querying the K8s API\n ## Note: RBAC might be required\n ##\n autoDiscovery:\n ## @param externalAccess.autoDiscovery.enabled Enable using an init container to auto-detect external IPs/ports by querying the K8s API\n ##\n enabled: false\n ## Bitnami Kubectl image\n ## ref: https://hub.docker.com/r/bitnami/kubectl/tags/\n ## @param externalAccess.autoDiscovery.image.registry Init container auto-discovery image registry\n ## @param externalAccess.autoDiscovery.image.repository Init container auto-discovery image repository\n ## @param externalAccess.autoDiscovery.image.tag Init container auto-discovery image tag (immutable tags are recommended)\n ## @param externalAccess.autoDiscovery.image.digest Petete image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag\n ## @param externalAccess.autoDiscovery.image.pullPolicy Init container auto-discovery image pull policy\n ## @param externalAccess.autoDiscovery.image.pullSecrets Init container auto-discovery image pull secrets\n ##\n image:\n registry: docker.io\n repository: bitnami/kubectl\n tag: 1.25.5-debian-11-r2\n digest: \"\"\n ## Specify a imagePullPolicy\n ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent'\n ## ref: https://kubernetes.io/docs/user-guide/images/#pre-pulling-images\n ##\n pullPolicy: IfNotPresent\n ## Optionally specify an array of imagePullSecrets (secrets must be manually created in the namespace)\n ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/\n ## e.g:\n ## pullSecrets:\n ## - myRegistryKeySecretName\n ##\n pullSecrets: []\n ## Init Container resource requests and limits\n ## ref: https://kubernetes.io/docs/user-guide/compute-resources/\n ## @param externalAccess.autoDiscovery.resources.limits The resources limits for the auto-discovery init container\n ## @param externalAccess.autoDiscovery.resources.requests The requested resources for the auto-discovery init container\n ##\n resources:\n limits: {}\n requests: {}\n ## Parameters to configure K8s service(s) used to externally access Kafka brokers\n ## Note: A new service per broker will be created\n ##\n service:\n ## @param externalAccess.service.type Kubernetes Service type for external access. It can be NodePort, LoadBalancer or ClusterIP\n ##\n type: LoadBalancer\n ## @param externalAccess.service.ports.external Kafka port used for external access when service type is LoadBalancer\n ##\n ports:\n external: 9094\n ## @param externalAccess.service.loadBalancerIPs Array of load balancer IPs for each Kafka broker. Length must be the same as replicaCount\n ## e.g:\n ## loadBalancerIPs:\n ## - X.X.X.X\n ## - Y.Y.Y.Y\n ##\n loadBalancerIPs: []\n ## @param externalAccess.service.loadBalancerNames Array of load balancer Names for each Kafka broker. Length must be the same as replicaCount\n ## e.g:\n ## loadBalancerNames:\n ## - broker1.external.example.com\n ## - broker2.external.example.com\n ##\n loadBalancerNames: []\n ## @param externalAccess.service.loadBalancerAnnotations Array of load balancer annotations for each Kafka broker. Length must be the same as replicaCount\n ## e.g:\n ## loadBalancerAnnotations:\n ## - external-dns.alpha.kubernetes.io/hostname: broker1.external.example.com.\n ## - external-dns.alpha.kubernetes.io/hostname: broker2.external.example.com.\n ##\n loadBalancerAnnotations: []\n ## @param externalAccess.service.loadBalancerSourceRanges Address(es) that are allowed when service is LoadBalancer\n ## ref: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/#restrict-access-for-loadbalancer-service\n ## e.g:\n ## loadBalancerSourceRanges:\n ## - 10.10.10.0/24\n ##\n loadBalancerSourceRanges: []\n ## @param externalAccess.service.nodePorts Array of node ports used for each Kafka broker. Length must be the same as replicaCount\n ## e.g:\n ## nodePorts:\n ## - 30001\n ## - 30002\n ##\n nodePorts: []\n ## @param externalAccess.service.useHostIPs Use service host IPs to configure Kafka external listener when service type is NodePort\n ##\n useHostIPs: false\n ## @param externalAccess.service.usePodIPs using the MY_POD_IP address for external access.\n ##\n usePodIPs: false\n ## @param externalAccess.service.domain Domain or external ip used to configure Kafka external listener when service type is NodePort or ClusterIP\n ## NodePort: If not specified, the container will try to get the kubernetes node external IP\n ## ClusterIP: Must be specified, ingress IP or domain where tcp for external ports is configured\n ##\n domain: \"\"\n ## @param externalAccess.service.publishNotReadyAddresses Indicates that any agent which deals with endpoints for this Service should disregard any indications of ready/not-ready\n ## ref: https://kubernetes.io/docs/reference/kubernetes-api/service-resources/service-v1/\n publishNotReadyAddresses: false\n ## @param externalAccess.service.labels Service labels for external access\n ##\n labels: {}\n ## @param externalAccess.service.annotations Service annotations for external access\n ##\n annotations: {}\n ## @param externalAccess.service.extraPorts Extra ports to expose in the Kafka external service\n ##\n extraPorts: []\n## Network policies\n## Ref: https://kubernetes.io/docs/concepts/services-networking/network-policies/\n##\nnetworkPolicy:\n ## @param networkPolicy.enabled Specifies whether a NetworkPolicy should be created\n ##\n enabled: false\n ## @param networkPolicy.allowExternal Don't require client label for connections\n ## When set to false, only pods with the correct client label will have network access to the port Kafka is\n ## listening on. When true, zookeeper accept connections from any source (with the correct destination port).\n ##\n allowExternal: true\n ## @param networkPolicy.explicitNamespacesSelector A Kubernetes LabelSelector to explicitly select namespaces from which traffic could be allowed\n ## If explicitNamespacesSelector is missing or set to {}, only client Pods that are in the networkPolicy's namespace\n ## and that match other criteria, the ones that have the good label, can reach the kafka.\n ## But sometimes, we want the kafka to be accessible to clients from other namespaces, in this case, we can use this\n ## LabelSelector to select these namespaces, note that the networkPolicy's namespace should also be explicitly added.\n ##\n ## e.g:\n ## explicitNamespacesSelector:\n ## matchLabels:\n ## role: frontend\n ## matchExpressions:\n ## - {key: role, operator: In, values: [frontend]}\n ##\n explicitNamespacesSelector: {}\n ## @param networkPolicy.externalAccess.from customize the from section for External Access on tcp-external port\n ## e.g:\n ## - ipBlock:\n ## cidr: 172.9.0.0/16\n ## except:\n ## - 172.9.1.0/24\n ##\n externalAccess:\n from: []\n ## @param networkPolicy.egressRules.customRules [object] Custom network policy rule\n ##\n egressRules:\n ## Additional custom egress rules\n ## e.g:\n ## customRules:\n ## - to:\n ## - namespaceSelector:\n ## matchLabels:\n ## label: example\n customRules: []\n\n## @section Persistence parameters\n\n## Enable persistence using Persistent Volume Claims\n## ref: https://kubernetes.io/docs/user-guide/persistent-volumes/\n##\npersistence:\n ## @param persistence.enabled Enable Kafka data persistence using PVC, note that ZooKeeper persistence is unaffected\n ##\n enabled: true\n ## @param persistence.existingClaim A manually managed Persistent Volume and Claim\n ## If defined, PVC must be created manually before volume will be bound\n ## The value is evaluated as a template\n ##\n existingClaim: \"\"\n ## @param persistence.storageClass PVC Storage Class for Kafka data volume\n ## If defined, storageClassName: \u003cstorageClass\u003e\n ## If set to \"-\", storageClassName: \"\", which disables dynamic provisioning\n ## If undefined (the default) or set to null, no storageClassName spec is\n ## set, choosing the default provisioner.\n ##\n storageClass: \"\"\n ## @param persistence.accessModes Persistent Volume Access Modes\n ##\n accessModes:\n - ReadWriteOnce\n ## @param persistence.size PVC Storage Request for Kafka data volume\n ##\n size: 8Gi\n ## @param persistence.annotations Annotations for the PVC\n ##\n annotations: {}\n ## @param persistence.labels Labels for the PVC\n ##\n labels: {}\n ## @param persistence.selector Selector to match an existing Persistent Volume for Kafka data PVC. If set, the PVC can't have a PV dynamically provisioned for it\n ## selector:\n ## matchLabels:\n ## app: my-app\n ##\n selector: {}\n ## @param persistence.mountPath Mount path of the Kafka data volume\n ##\n mountPath: /bitnami/kafka\n## Log Persistence parameters\n##\nlogPersistence:\n ## @param logPersistence.enabled Enable Kafka logs persistence using PVC, note that ZooKeeper persistence is unaffected\n ##\n enabled: false\n ## @param logPersistence.existingClaim A manually managed Persistent Volume and Claim\n ## If defined, PVC must be created manually before volume will be bound\n ## The value is evaluated as a template\n ##\n existingClaim: \"\"\n ## @param logPersistence.storageClass PVC Storage Class for Kafka logs volume\n ## If defined, storageClassName: \u003cstorageClass\u003e\n ## If set to \"-\", storageClassName: \"\", which disables dynamic provisioning\n ## If undefined (the default) or set to null, no storageClassName spec is\n ## set, choosing the default provisioner.\n ##\n storageClass: \"\"\n ## @param logPersistence.accessModes Persistent Volume Access Modes\n ##\n accessModes:\n - ReadWriteOnce\n ## @param logPersistence.size PVC Storage Request for Kafka logs volume\n ##\n size: 8Gi\n ## @param logPersistence.annotations Annotations for the PVC\n ##\n annotations: {}\n ## @param logPersistence.selector Selector to match an existing Persistent Volume for Kafka log data PVC. If set, the PVC can't have a PV dynamically provisioned for it\n ## selector:\n ## matchLabels:\n ## app: my-app\n ##\n selector: {}\n ## @param logPersistence.mountPath Mount path of the Kafka logs volume\n ##\n mountPath: /opt/bitnami/kafka/logs\n\n## @section Volume Permissions parameters\n##\n\n## Init containers parameters:\n## volumePermissions: Change the owner and group of the persistent volume(s) mountpoint(s) to 'runAsUser:fsGroup' on each node\n##\nvolumePermissions:\n ## @param volumePermissions.enabled Enable init container that changes the owner and group of the persistent volume\n ##\n enabled: false\n ## @param volumePermissions.image.registry Init container volume-permissions image registry\n ## @param volumePermissions.image.repository Init container volume-permissions image repository\n ## @param volumePermissions.image.tag Init container volume-permissions image tag (immutable tags are recommended)\n ## @param volumePermissions.image.digest Init container volume-permissions image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag\n ## @param volumePermissions.image.pullPolicy Init container volume-permissions image pull policy\n ## @param volumePermissions.image.pullSecrets Init container volume-permissions image pull secrets\n ##\n image:\n registry: docker.io\n repository: bitnami/bitnami-shell\n tag: 11-debian-11-r63\n digest: \"\"\n pullPolicy: IfNotPresent\n ## Optionally specify an array of imagePullSecrets.\n ## Secrets must be manually created in the namespace.\n ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/\n ## Example:\n ## pullSecrets:\n ## - myRegistryKeySecretName\n ##\n pullSecrets: []\n ## Init container resource requests and limits\n ## ref: https://kubernetes.io/docs/user-guide/compute-resources/\n ## @param volumePermissions.resources.limits Init container volume-permissions resource limits\n ## @param volumePermissions.resources.requests Init container volume-permissions resource requests\n ##\n resources:\n limits: {}\n requests: {}\n ## Init container' Security Context\n ## Note: the chown of the data folder is done to containerSecurityContext.runAsUser\n ## and not the below volumePermissions.containerSecurityContext.runAsUser\n ## @param volumePermissions.containerSecurityContext.runAsUser User ID for the init container\n ##\n containerSecurityContext:\n runAsUser: 0\n\n## @section Other Parameters\n\n## ServiceAccount for Kafka\n## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/\n##\nserviceAccount:\n ## @param serviceAccount.create Enable creation of ServiceAccount for Kafka pods\n ##\n create: true\n ## @param serviceAccount.name The name of the service account to use. If not set and `create` is `true`, a name is generated\n ## If not set and create is true, a name is generated using the kafka.serviceAccountName template\n ##\n name: \"\"\n ## @param serviceAccount.automountServiceAccountToken Allows auto mount of ServiceAccountToken on the serviceAccount created\n ## Can be set to false if pods using this serviceAccount do not need to use K8s API\n ##\n automountServiceAccountToken: true\n ## @param serviceAccount.annotations Additional custom annotations for the ServiceAccount\n ##\n annotations: {}\n## Role Based Access Control\n## ref: https://kubernetes.io/docs/admin/authorization/rbac/\n##\nrbac:\n ## @param rbac.create Whether to create \u0026 use RBAC resources or not\n ## binding Kafka ServiceAccount to a role\n ## that allows Kafka pods querying the K8s API\n ##\n create: false\n\n## @section Metrics parameters\n\n## Prometheus Exporters / Metrics\n##\nmetrics:\n ## Prometheus Kafka exporter: exposes complimentary metrics to JMX exporter\n ##\n kafka:\n ## @param metrics.kafka.enabled Whether or not to create a standalone Kafka exporter to expose Kafka metrics\n ##\n enabled: false\n ## Bitnami Kafka exporter image\n ## ref: https://hub.docker.com/r/bitnami/kafka-exporter/tags/\n ## @param metrics.kafka.image.registry Kafka exporter image registry\n ## @param metrics.kafka.image.repository Kafka exporter image repository\n ## @param metrics.kafka.image.tag Kafka exporter image tag (immutable tags are recommended)\n ## @param metrics.kafka.image.digest Kafka exporter image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag\n ## @param metrics.kafka.image.pullPolicy Kafka exporter image pull policy\n ## @param metrics.kafka.image.pullSecrets Specify docker-registry secret names as an array\n ##\n image:\n registry: docker.io\n repository: bitnami/kafka-exporter\n tag: 1.6.0-debian-11-r40\n digest: \"\"\n ## Specify a imagePullPolicy\n ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent'\n ## ref: https://kubernetes.io/docs/user-guide/images/#pre-pulling-images\n ##\n pullPolicy: IfNotPresent\n ## Optionally specify an array of imagePullSecrets (secrets must be manually created in the namespace)\n ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/\n ## e.g:\n ## pullSecrets:\n ## - myRegistryKeySecretName\n ##\n pullSecrets: []\n\n ## @param metrics.kafka.certificatesSecret Name of the existing secret containing the optional certificate and key files\n ## for Kafka exporter client authentication\n ##\n certificatesSecret: \"\"\n ## @param metrics.kafka.tlsCert The secret key from the certificatesSecret if 'client-cert' key different from the default (cert-file)\n ##\n tlsCert: cert-file\n ## @param metrics.kafka.tlsKey The secret key from the certificatesSecret if 'client-key' key different from the default (key-file)\n ##\n tlsKey: key-file\n ## @param metrics.kafka.tlsCaSecret Name of the existing secret containing the optional ca certificate for Kafka exporter client authentication\n ##\n tlsCaSecret: \"\"\n ## @param metrics.kafka.tlsCaCert The secret key from the certificatesSecret or tlsCaSecret if 'ca-cert' key different from the default (ca-file)\n ##\n tlsCaCert: ca-file\n ## @param metrics.kafka.extraFlags Extra flags to be passed to Kafka exporter\n ## e.g:\n ## extraFlags:\n ## tls.insecure-skip-tls-verify: \"\"\n ## web.telemetry-path: \"/metrics\"\n ##\n extraFlags: {}\n ## @param metrics.kafka.command Override Kafka exporter container command\n ##\n command: []\n ## @param metrics.kafka.args Override Kafka exporter container arguments\n ##\n args: []\n ## @param metrics.kafka.containerPorts.metrics Kafka exporter metrics container port\n ##\n containerPorts:\n metrics: 9308\n ## Kafka exporter resource requests and limits\n ## ref: https://kubernetes.io/docs/user-guide/compute-resources/\n ## @param metrics.kafka.resources.limits The resources limits for the container\n ## @param metrics.kafka.resources.requests The requested resources for the container\n ##\n resources:\n limits: {}\n requests: {}\n ## Kafka exporter pods' Security Context\n ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod\n ## @param metrics.kafka.podSecurityContext.enabled Enable security context for the pods\n ## @param metrics.kafka.podSecurityContext.fsGroup Set Kafka exporter pod's Security Context fsGroup\n ##\n podSecurityContext:\n enabled: true\n fsGroup: 1001\n ## Kafka exporter containers' Security Context\n ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container\n ## @param metrics.kafka.containerSecurityContext.enabled Enable Kafka exporter containers' Security Context\n ## @param metrics.kafka.containerSecurityContext.runAsUser Set Kafka exporter containers' Security Context runAsUser\n ## @param metrics.kafka.containerSecurityContext.runAsNonRoot Set Kafka exporter containers' Security Context runAsNonRoot\n ## e.g:\n ## containerSecurityContext:\n ## enabled: true\n ## capabilities:\n ## drop: [\"NET_RAW\"]\n ## readOnlyRootFilesystem: true\n ##\n containerSecurityContext:\n enabled: true\n runAsUser: 1001\n runAsNonRoot: true\n ## @param metrics.kafka.hostAliases Kafka exporter pods host aliases\n ## https://kubernetes.io/docs/concepts/services-networking/add-entries-to-pod-etc-hosts-with-host-aliases/\n ##\n hostAliases: []\n ## @param metrics.kafka.podLabels Extra labels for Kafka exporter pods\n ## Ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/\n ##\n podLabels: {}\n ## @param metrics.kafka.podAnnotations Extra annotations for Kafka exporter pods\n ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/\n ##\n podAnnotations: {}\n ## @param metrics.kafka.podAffinityPreset Pod affinity preset. Ignored if `metrics.kafka.affinity` is set. Allowed values: `soft` or `hard`\n ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity\n ##\n podAffinityPreset: \"\"\n ## @param metrics.kafka.podAntiAffinityPreset Pod anti-affinity preset. Ignored if `metrics.kafka.affinity` is set. Allowed values: `soft` or `hard`\n ## Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity\n ##\n podAntiAffinityPreset: soft\n ## Node metrics.kafka.affinity preset\n ## Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity\n ##\n nodeAffinityPreset:\n ## @param metrics.kafka.nodeAffinityPreset.type Node affinity preset type. Ignored if `metrics.kafka.affinity` is set. Allowed values: `soft` or `hard`\n ##\n type: \"\"\n ## @param metrics.kafka.nodeAffinityPreset.key Node label key to match Ignored if `metrics.kafka.affinity` is set.\n ## E.g.\n ## key: \"kubernetes.io/e2e-az-name\"\n ##\n key: \"\"\n ## @param metrics.kafka.nodeAffinityPreset.values Node label values to match. Ignored if `metrics.kafka.affinity` is set.\n ## E.g.\n ## values:\n ## - e2e-az1\n ## - e2e-az2\n ##\n values: []\n ## @param metrics.kafka.affinity Affinity for pod assignment\n ## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity\n ## Note: metrics.kafka.podAffinityPreset, metrics.kafka.podAntiAffinityPreset, and metrics.kafka.nodeAffinityPreset will be ignored when it's set\n ##\n affinity: {}\n ## @param metrics.kafka.nodeSelector Node labels for pod assignment\n ## Ref: https://kubernetes.io/docs/user-guide/node-selection/\n ##\n nodeSelector: {}\n ## @param metrics.kafka.tolerations Tolerations for pod assignment\n ## Ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/\n ##\n tolerations: []\n ## @param metrics.kafka.schedulerName Name of the k8s scheduler (other than default) for Kafka exporter\n ## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/\n ##\n schedulerName: \"\"\n ## @param metrics.kafka.priorityClassName Kafka exporter pods' priorityClassName\n ##\n priorityClassName: \"\"\n ## @param metrics.kafka.topologySpreadConstraints Topology Spread Constraints for pod assignment\n ## https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/\n ## The value is evaluated as a template\n ##\n topologySpreadConstraints: []\n ## @param metrics.kafka.extraVolumes Optionally specify extra list of additional volumes for the Kafka exporter pod(s)\n ## e.g:\n ## extraVolumes:\n ## - name: kafka-jaas\n ## secret:\n ## secretName: kafka-jaas\n ##\n extraVolumes: []\n ## @param metrics.kafka.extraVolumeMounts Optionally specify extra list of additional volumeMounts for the Kafka exporter container(s)\n ## extraVolumeMounts:\n ## - name: kafka-jaas\n ## mountPath: /bitnami/kafka/config/kafka_jaas.conf\n ## subPath: kafka_jaas.conf\n ##\n extraVolumeMounts: []\n ## @param metrics.kafka.sidecars Add additional sidecar containers to the Kafka exporter pod(s)\n ## e.g:\n ## sidecars:\n ## - name: your-image-name\n ## image: your-image\n ## imagePullPolicy: Always\n ## ports:\n ## - name: portname\n ## containerPort: 1234\n ##\n sidecars: []\n ## @param metrics.kafka.initContainers Add init containers to the Kafka exporter pods\n ## e.g:\n ## initContainers:\n ## - name: your-image-name\n ## image: your-image\n ## imagePullPolicy: Always\n ## ports:\n ## - name: portname\n ## containerPort: 1234\n ##\n initContainers: []\n ## Kafka exporter service configuration\n ##\n service:\n ## @param metrics.kafka.service.ports.metrics Kafka exporter metrics service port\n ##\n ports:\n metrics: 9308\n ## @param metrics.kafka.service.clusterIP Static clusterIP or None for headless services\n ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#choosing-your-own-ip-address\n ##\n clusterIP: \"\"\n ## @param metrics.kafka.service.sessionAffinity Control where client requests go, to the same pod or round-robin\n ## Values: ClientIP or None\n ## ref: https://kubernetes.io/docs/user-guide/services/\n ##\n sessionAffinity: None\n ## @param metrics.kafka.service.annotations [object] Annotations for the Kafka exporter service\n ##\n annotations:\n prometheus.io/scrape: \"true\"\n prometheus.io/port: \"{{ .Values.metrics.kafka.service.ports.metrics }}\"\n prometheus.io/path: \"/metrics\"\n ## Kafka exporter pods ServiceAccount\n ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/\n ##\n serviceAccount:\n ## @param metrics.kafka.serviceAccount.create Enable creation of ServiceAccount for Kafka exporter pods\n ##\n create: true\n ## @param metrics.kafka.serviceAccount.name The name of the service account to use. If not set and `create` is `true`, a name is generated\n ## If not set and create is true, a name is generated using the kafka.metrics.kafka.serviceAccountName template\n ##\n name: \"\"\n ## @param metrics.kafka.serviceAccount.automountServiceAccountToken Allows auto mount of ServiceAccountToken on the serviceAccount created\n ## Can be set to false if pods using this serviceAccount do not need to use K8s API\n ##\n automountServiceAccountToken: true\n ## Prometheus JMX exporter: exposes the majority of Kafkas metrics\n ##\n jmx:\n ## @param metrics.jmx.enabled Whether or not to expose JMX metrics to Prometheus\n ##\n enabled: false\n ## Bitnami JMX exporter image\n ## ref: https://hub.docker.com/r/bitnami/jmx-exporter/tags/\n ## @param metrics.jmx.image.registry JMX exporter image registry\n ## @param metrics.jmx.image.repository JMX exporter image repository\n ## @param metrics.jmx.image.tag JMX exporter image tag (immutable tags are recommended)\n ## @param metrics.jmx.image.digest JMX exporter image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag\n ## @param metrics.jmx.image.pullPolicy JMX exporter image pull policy\n ## @param metrics.jmx.image.pullSecrets Specify docker-registry secret names as an array\n ##\n image:\n registry: docker.io\n repository: bitnami/jmx-exporter\n tag: 0.17.2-debian-11-r29\n digest: \"\"\n ## Specify a imagePullPolicy\n ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent'\n ## ref: https://kubernetes.io/docs/user-guide/images/#pre-pulling-images\n ##\n pullPolicy: IfNotPresent\n ## Optionally specify an array of imagePullSecrets (secrets must be manually created in the namespace)\n ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/\n ## e.g:\n ## pullSecrets:\n ## - myRegistryKeySecretName\n ##\n pullSecrets: []\n ## Prometheus JMX exporter containers' Security Context\n ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container\n ## @param metrics.jmx.containerSecurityContext.enabled Enable Prometheus JMX exporter containers' Security Context\n ## @param metrics.jmx.containerSecurityContext.runAsUser Set Prometheus JMX exporter containers' Security Context runAsUser\n ## @param metrics.jmx.containerSecurityContext.runAsNonRoot Set Prometheus JMX exporter containers' Security Context runAsNonRoot\n ## e.g:\n ## containerSecurityContext:\n ## enabled: true\n ## capabilities:\n ## drop: [\"NET_RAW\"]\n ## readOnlyRootFilesystem: true\n ##\n containerSecurityContext:\n enabled: true\n runAsUser: 1001\n runAsNonRoot: true\n ## @param metrics.jmx.containerPorts.metrics Prometheus JMX exporter metrics container port\n ##\n containerPorts:\n metrics: 5556\n ## Prometheus JMX exporter resource requests and limits\n ## ref: https://kubernetes.io/docs/user-guide/compute-resources/\n ## @param metrics.jmx.resources.limits The resources limits for the JMX exporter container\n ## @param metrics.jmx.resources.requests The requested resources for the JMX exporter container\n ##\n resources:\n limits: {}\n requests: {}\n ## Prometheus JMX exporter service configuration\n ##\n service:\n ## @param metrics.jmx.service.ports.metrics Prometheus JMX exporter metrics service port\n ##\n ports:\n metrics: 5556\n ## @param metrics.jmx.service.clusterIP Static clusterIP or None for headless services\n ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#choosing-your-own-ip-address\n ##\n clusterIP: \"\"\n ## @param metrics.jmx.service.sessionAffinity Control where client requests go, to the same pod or round-robin\n ## Values: ClientIP or None\n ## ref: https://kubernetes.io/docs/user-guide/services/\n ##\n sessionAffinity: None\n ## @param metrics.jmx.service.annotations [object] Annotations for the Prometheus JMX exporter service\n ##\n annotations:\n prometheus.io/scrape: \"true\"\n prometheus.io/port: \"{{ .Values.metrics.jmx.service.ports.metrics }}\"\n prometheus.io/path: \"/\"\n ## @param metrics.jmx.whitelistObjectNames Allows setting which JMX objects you want to expose to via JMX stats to JMX exporter\n ## Only whitelisted values will be exposed via JMX exporter. They must also be exposed via Rules. To expose all metrics\n ## (warning its crazy excessive and they aren't formatted in a prometheus style) (1) `whitelistObjectNames: []`\n ## (2) commented out above `overrideConfig`.\n ##\n whitelistObjectNames:\n - kafka.controller:*\n - kafka.server:*\n - java.lang:*\n - kafka.network:*\n - kafka.log:*\n ## @param metrics.jmx.config [string] Configuration file for JMX exporter\n ## Specify content for jmx-kafka-prometheus.yml. Evaluated as a template\n ##\n ## Credits to the incubator/kafka chart for the JMX configuration.\n ## https://github.com/helm/charts/tree/master/incubator/kafka\n ##\n config: |-\n jmxUrl: service:jmx:rmi:///jndi/rmi://127.0.0.1:5555/jmxrmi\n lowercaseOutputName: true\n lowercaseOutputLabelNames: true\n ssl: false\n {{- if .Values.metrics.jmx.whitelistObjectNames }}\n whitelistObjectNames: [\"{{ join \"\\\",\\\"\" .Values.metrics.jmx.whitelistObjectNames }}\"]\n {{- end }}\n ## @param metrics.jmx.existingConfigmap Name of existing ConfigMap with JMX exporter configuration\n ## NOTE: This will override metrics.jmx.config\n ##\n existingConfigmap: \"\"\n ## @param metrics.jmx.extraRules Add extra rules to JMX exporter configuration\n ## e.g:\n ## extraRules: |-\n ## - pattern: kafka.server\u003ctype=socket-server-metrics, listener=(.+), networkProcessor=(.+)\u003e\u003c\u003e(connection-count)\n ## name: kafka_server_socket_server_metrics_$3\n ## labels:\n ## listener: $1\n extraRules: \"\"\n ## Prometheus Operator ServiceMonitor configuration\n ##\n serviceMonitor:\n ## @param metrics.serviceMonitor.enabled if `true`, creates a Prometheus Operator ServiceMonitor (requires `metrics.kafka.enabled` or `metrics.jmx.enabled` to be `true`)\n ##\n enabled: false\n ## @param metrics.serviceMonitor.namespace Namespace in which Prometheus is running\n ##\n namespace: \"\"\n ## @param metrics.serviceMonitor.interval Interval at which metrics should be scraped\n ## ref: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#endpoint\n ##\n interval: \"\"\n ## @param metrics.serviceMonitor.scrapeTimeout Timeout after which the scrape is ended\n ## ref: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#endpoint\n ##\n scrapeTimeout: \"\"\n ## @param metrics.serviceMonitor.labels Additional labels that can be used so ServiceMonitor will be discovered by Prometheus\n ##\n labels: {}\n ## @param metrics.serviceMonitor.selector Prometheus instance selector labels\n ## ref: https://github.com/bitnami/charts/tree/main/bitnami/prometheus-operator#prometheus-configuration\n ##\n selector: {}\n ## @param metrics.serviceMonitor.relabelings RelabelConfigs to apply to samples before scraping\n ##\n relabelings: []\n ## @param metrics.serviceMonitor.metricRelabelings MetricRelabelConfigs to apply to samples before ingestion\n ##\n metricRelabelings: []\n ## @param metrics.serviceMonitor.honorLabels Specify honorLabels parameter to add the scrape endpoint\n ##\n honorLabels: false\n ## @param metrics.serviceMonitor.jobLabel The name of the label on the target service to use as the job name in prometheus.\n ##\n jobLabel: \"\"\n\n prometheusRule:\n ## @param metrics.prometheusRule.enabled if `true`, creates a Prometheus Operator PrometheusRule (requires `metrics.kafka.enabled` or `metrics.jmx.enabled` to be `true`)\n ##\n enabled: false\n ## @param metrics.prometheusRule.namespace Namespace in which Prometheus is running\n ##\n namespace: \"\"\n ## @param metrics.prometheusRule.labels Additional labels that can be used so PrometheusRule will be discovered by Prometheus\n ##\n labels: {}\n ## @param metrics.prometheusRule.groups Prometheus Rule Groups for Kafka\n ##\n groups: []\n\n## @section Kafka provisioning parameters\n\n## Kafka provisioning\n##\nprovisioning:\n ## @param provisioning.enabled Enable kafka provisioning Job\n ##\n enabled: true\n ## @param provisioning.numPartitions Default number of partitions for topics when unspecified\n ##\n numPartitions: 1\n ## @param provisioning.replicationFactor Default replication factor for topics when unspecified\n ##\n replicationFactor: 1\n ## @param provisioning.topics Kafka topics to provision\n\n topics: \n - name: \"dev.telemetry.denorm\"\n partitions: 1\n replicationFactor: 1\n # https://kafka.apache.org/documentation/#topicconfigs\n config:\n max.message.bytes: 64000\n flush.messages: 1\n - name: \"dev.druid.events.telemetry\"\n partitions: 1\n replicationFactor: 1\n # https://kafka.apache.org/documentation/#topicconfigs\n config:\n max.message.bytes: 64000\n flush.messages: 1\n - name: \"dev.druid.events.summary\"\n partitions: 1\n replicationFactor: 1\n # https://kafka.apache.org/documentation/#topicconfigs\n config:\n max.message.bytes: 64000\n flush.messages: 1\n - name: \"dev.telemetry.failed\"\n partitions: 1\n replicationFactor: 1\n # https://kafka.apache.org/documentation/#topicconfigs\n config:\n max.message.bytes: 64000\n flush.messages: 1\n - name: \"dev.telemetry.duplicate\"\n partitions: 1\n replicationFactor: 1\n # https://kafka.apache.org/documentation/#topicconfigs\n config:\n max.message.bytes: 64000\n flush.messages: 1\n \n ## @param provisioning.nodeSelector Node labels for pod assignment\n ## Ref: https://kubernetes.io/docs/user-guide/node-selection/\n ##\n nodeSelector: {}\n ## @param provisioning.tolerations Tolerations for pod assignment\n ## Ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/\n ##\n tolerations: []\n ## @param provisioning.extraProvisioningCommands Extra commands to run to provision cluster resources\n ## - echo \"Allow user to consume from any topic\"\n ## - \u003e-\n ## /opt/bitnami/kafka/bin/kafka-acls.sh\n ## --bootstrap-server $KAFKA_SERVICE\n ## --command-config $CLIENT_CONF\n ## --add\n ## --allow-principal User:user\n ## --consumer --topic '*'\n ## - \"/opt/bitnami/kafka/bin/kafka-acls.sh\n ## --bootstrap-server $KAFKA_SERVICE\n ## --command-config $CLIENT_CONF\n ## --list\"\n ##\n extraProvisioningCommands: []\n ## @param provisioning.parallel Number of provisioning commands to run at the same time\n ##\n parallel: 1\n ## @param provisioning.preScript Extra bash script to run before topic provisioning. $CLIENT_CONF is path to properties file with most needed configurations\n ##\n preScript: \"\"\n ## @param provisioning.postScript Extra bash script to run after topic provisioning. $CLIENT_CONF is path to properties file with most needed configurations\n ##\n postScript: \"\"\n ## Auth Configuration for kafka provisioning Job\n ##\n auth:\n ## TLS configuration for kafka provisioning Job\n ##\n tls:\n ## @param provisioning.auth.tls.type Format to use for TLS certificates. Allowed types: `jks` and `pem`.\n ## Note: ignored if auth.tls.clientProtocol different from one of these values: \"tls\" \"mtls\" \"sasl_tls\".\n ##\n type: jks\n ## @param provisioning.auth.tls.certificatesSecret Existing secret containing the TLS certificates for the Kafka provisioning Job.\n ## When using 'jks' format for certificates, the secret should contain a truststore and a keystore.\n ## When using 'pem' format for certificates, the secret should contain a public CA certificate, a public certificate and one private key.\n ##\n certificatesSecret: \"\"\n ## @param provisioning.auth.tls.cert The secret key from the certificatesSecret if 'cert' key different from the default (tls.crt)\n ##\n cert: tls.crt\n ## @param provisioning.auth.tls.key The secret key from the certificatesSecret if 'key' key different from the default (tls.key)\n ##\n key: tls.key\n ## @param provisioning.auth.tls.caCert The secret key from the certificatesSecret if 'caCert' key different from the default (ca.crt)\n ##\n caCert: ca.crt\n ## @param provisioning.auth.tls.keystore The secret key from the certificatesSecret if 'keystore' key different from the default (keystore.jks)\n ##\n keystore: keystore.jks\n ## @param provisioning.auth.tls.truststore The secret key from the certificatesSecret if 'truststore' key different from the default (truststore.jks)\n ##\n truststore: truststore.jks\n ## @param provisioning.auth.tls.passwordsSecret Name of the secret containing passwords to access the JKS files or PEM key when they are password-protected.\n ## It should contain two keys called \"keystore-password\" and \"truststore-password\", or \"key-password\" if using a password-protected PEM key.\n ##\n passwordsSecret: \"\"\n ## @param provisioning.auth.tls.keyPasswordSecretKey The secret key from the passwordsSecret if 'keyPasswordSecretKey' key different from the default (key-password)\n ## Note: must not be used if `passwordsSecret` is not defined.\n ##\n keyPasswordSecretKey: key-password\n ## @param provisioning.auth.tls.keystorePasswordSecretKey The secret key from the passwordsSecret if 'keystorePasswordSecretKey' key different from the default (keystore-password)\n ## Note: must not be used if `passwordsSecret` is not defined.\n ##\n keystorePasswordSecretKey: keystore-password\n ## @param provisioning.auth.tls.truststorePasswordSecretKey The secret key from the passwordsSecret if 'truststorePasswordSecretKey' key different from the default (truststore-password)\n ## Note: must not be used if `passwordsSecret` is not defined.\n ##\n truststorePasswordSecretKey: truststore-password\n ## @param provisioning.auth.tls.keyPassword Password to access the password-protected PEM key if necessary. Ignored if 'passwordsSecret' is provided.\n ##\n keyPassword: \"\"\n ## @param provisioning.auth.tls.keystorePassword Password to access the JKS keystore. Ignored if 'passwordsSecret' is provided.\n ##\n keystorePassword: \"\"\n ## @param provisioning.auth.tls.truststorePassword Password to access the JKS truststore. Ignored if 'passwordsSecret' is provided.\n ##\n truststorePassword: \"\"\n ## @param provisioning.command Override provisioning container command\n ##\n command: []\n ## @param provisioning.args Override provisioning container arguments\n ##\n args: []\n ## @param provisioning.extraEnvVars Extra environment variables to add to the provisioning pod\n ## e.g:\n ## extraEnvVars:\n ## - name: KAFKA_CFG_BACKGROUND_THREADS\n ## value: \"10\"\n ##\n extraEnvVars: []\n ## @param provisioning.extraEnvVarsCM ConfigMap with extra environment variables\n ##\n extraEnvVarsCM: \"\"\n ## @param provisioning.extraEnvVarsSecret Secret with extra environment variables\n ##\n extraEnvVarsSecret: \"\"\n ## @param provisioning.podAnnotations Extra annotations for Kafka provisioning pods\n ##\n podAnnotations: {}\n ## @param provisioning.podLabels Extra labels for Kafka provisioning pods\n ## Ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/\n ##\n podLabels: {}\n ## Kafka provisioning pods ServiceAccount\n ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/\n ##\n serviceAccount:\n ## @param provisioning.serviceAccount.create Enable creation of ServiceAccount for Kafka provisioning pods\n ##\n create: false\n ## @param provisioning.serviceAccount.name The name of the service account to use. If not set and `create` is `true`, a name is generated\n ## If not set and create is true, a name is generated using the provisioning.serviceAccount.name template\n ##\n name: \"\"\n ## @param provisioning.serviceAccount.automountServiceAccountToken Allows auto mount of ServiceAccountToken on the serviceAccount created\n ## Can be set to false if pods using this serviceAccount do not need to use K8s API\n ##\n automountServiceAccountToken: true\n ## Kafka provisioning resource requests and limits\n ## ref: https://kubernetes.io/docs/user-guide/compute-resources/\n ## @param provisioning.resources.limits The resources limits for the Kafka provisioning container\n ## @param provisioning.resources.requests The requested resources for the Kafka provisioning container\n ##\n resources:\n limits: {}\n requests: {}\n ## Kafka provisioning pods' Security Context\n ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod\n ## @param provisioning.podSecurityContext.enabled Enable security context for the pods\n ## @param provisioning.podSecurityContext.fsGroup Set Kafka provisioning pod's Security Context fsGroup\n ##\n podSecurityContext:\n enabled: true\n fsGroup: 1001\n ## Kafka provisioning containers' Security Context\n ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container\n ## @param provisioning.containerSecurityContext.enabled Enable Kafka provisioning containers' Security Context\n ## @param provisioning.containerSecurityContext.runAsUser Set Kafka provisioning containers' Security Context runAsUser\n ## @param provisioning.containerSecurityContext.runAsNonRoot Set Kafka provisioning containers' Security Context runAsNonRoot\n ## e.g:\n ## containerSecurityContext:\n ## enabled: true\n ## capabilities:\n ## drop: [\"NET_RAW\"]\n ## readOnlyRootFilesystem: true\n ##\n containerSecurityContext:\n enabled: true\n runAsUser: 1001\n runAsNonRoot: true\n ## @param provisioning.schedulerName Name of the k8s scheduler (other than default) for kafka provisioning\n ## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/\n ##\n schedulerName: \"\"\n ## @param provisioning.extraVolumes Optionally specify extra list of additional volumes for the Kafka provisioning pod(s)\n ## e.g:\n ## extraVolumes:\n ## - name: kafka-jaas\n ## secret:\n ## secretName: kafka-jaas\n ##\n extraVolumes: []\n ## @param provisioning.extraVolumeMounts Optionally specify extra list of additional volumeMounts for the Kafka provisioning container(s)\n ## extraVolumeMounts:\n ## - name: kafka-jaas\n ## mountPath: /bitnami/kafka/config/kafka_jaas.conf\n ## subPath: kafka_jaas.conf\n ##\n extraVolumeMounts: []\n ## @param provisioning.sidecars Add additional sidecar containers to the Kafka provisioning pod(s)\n ## e.g:\n ## sidecars:\n ## - name: your-image-name\n ## image: your-image\n ## imagePullPolicy: Always\n ## ports:\n ## - name: portname\n ## containerPort: 1234\n ##\n sidecars: []\n ## @param provisioning.initContainers Add additional Add init containers to the Kafka provisioning pod(s)\n ## e.g:\n ## initContainers:\n ## - name: your-image-name\n ## image: your-image\n ## imagePullPolicy: Always\n ## ports:\n ## - name: portname\n ## containerPort: 1234\n ##\n initContainers: []\n ## @param provisioning.waitForKafka If true use an init container to wait until kafka is ready before starting provisioning\n ##\n waitForKafka: true\n\n## @section ZooKeeper chart parameters\n\n## ZooKeeper chart configuration\n## https://github.com/bitnami/charts/blob/main/bitnami/zookeeper/values.yaml\n##\nzookeeper:\n ## @param zookeeper.enabled Switch to enable or disable the ZooKeeper helm chart\n ##\n enabled: true\n ## @param zookeeper.replicaCount Number of ZooKeeper nodes\n ##\n replicaCount: 1\n ## ZooKeeper authenticaiton\n ##\n auth:\n client:\n ## @param zookeeper.auth.client.enabled Enable ZooKeeper auth\n ##\n enabled: false\n ## @param zookeeper.auth.client.clientUser User that will use ZooKeeper clients to auth\n ##\n clientUser: \"\"\n ## @param zookeeper.auth.client.clientPassword Password that will use ZooKeeper clients to auth\n ##\n clientPassword: \"\"\n ## @param zookeeper.auth.client.serverUsers Comma, semicolon or whitespace separated list of user to be created. Specify them as a string, for example: \"user1,user2,admin\"\n ##\n serverUsers: \"\"\n ## @param zookeeper.auth.client.serverPasswords Comma, semicolon or whitespace separated list of passwords to assign to users when created. Specify them as a string, for example: \"pass4user1, pass4user2, pass4admin\"\n ##\n serverPasswords: \"\"\n ## ZooKeeper Persistence parameters\n ## ref: https://kubernetes.io/docs/user-guide/persistent-volumes/\n ## @param zookeeper.persistence.enabled Enable persistence on ZooKeeper using PVC(s)\n ## @param zookeeper.persistence.storageClass Persistent Volume storage class\n ## @param zookeeper.persistence.accessModes Persistent Volume access modes\n ## @param zookeeper.persistence.size Persistent Volume size\n ##\n persistence:\n enabled: true\n storageClass: \"\"\n accessModes:\n - ReadWriteOnce\n size: 8Gi\n\n## External Zookeeper Configuration\n## All of these values are only used if `zookeeper.enabled=false`\n##\nexternalZookeeper:\n ## @param externalZookeeper.servers List of external zookeeper servers to use. Typically used in combination with 'zookeeperChrootPath'.\n ##\n servers: []\n" - ], - "verify": false, - "version": "20.0.2", - "wait": true, - "wait_for_jobs": true - }, - "sensitive_attributes": [], - "private": "bnVsbA==", - "dependencies": [ - "kind_cluster.one-click" - ] - } - ] - }, - { - "mode": "managed", - "type": "helm_release", - "name": "logstash", - "provider": "provider[\"registry.terraform.io/hashicorp/helm\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "atomic": false, - "chart": "../../helm_charts/logstash", - "cleanup_on_fail": false, - "create_namespace": true, - "dependency_update": true, - "description": null, - "devel": null, - "disable_crd_hooks": false, - "disable_openapi_validation": false, - "disable_webhooks": false, - "force_update": false, - "id": "logstash", - "keyring": null, - "lint": false, - "manifest": null, - "max_history": 0, - "metadata": [ - { - "app_version": "8.6.2", - "chart": "logstash", - "name": "logstash", - "namespace": "knowlg-db", - "revision": 1, - "values": "null", - "version": "5.1.15" - } - ], - "name": "logstash", - "namespace": "knowlg-db", - "pass_credentials": false, - "postrender": [], - "recreate_pods": false, - "render_subchart_notes": true, - "replace": false, - "repository": null, - "repository_ca_file": null, - "repository_cert_file": null, - "repository_key_file": null, - "repository_password": null, - "repository_username": null, - "reset_values": false, - "reuse_values": false, - "set": [], - "set_sensitive": [], - "skip_crds": false, - "status": "deployed", - "timeout": 300, - "values": null, - "verify": false, - "version": "5.1.15", - "wait": true, - "wait_for_jobs": true - }, - "sensitive_attributes": [], - "private": "bnVsbA==", - "dependencies": [ - "kind_cluster.one-click" - ] - } - ] - }, - { - "mode": "managed", - "type": "helm_release", - "name": "neo4j", - "provider": "provider[\"registry.terraform.io/hashicorp/helm\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "atomic": false, - "chart": "../../helm_charts/neo4j", - "cleanup_on_fail": false, - "create_namespace": true, - "dependency_update": true, - "description": null, - "devel": null, - "disable_crd_hooks": false, - "disable_openapi_validation": false, - "disable_webhooks": false, - "force_update": false, - "id": "neo4j", - "keyring": null, - "lint": false, - "manifest": null, - "max_history": 0, - "metadata": [ - { - "app_version": "1.0", - "chart": "neo4j", - "name": "neo4j", - "namespace": "knowlg-db", - "revision": 1, - "values": "null", - "version": "0.1.0" - } - ], - "name": "neo4j", - "namespace": "knowlg-db", - "pass_credentials": false, - "postrender": [], - "recreate_pods": false, - "render_subchart_notes": true, - "replace": false, - "repository": null, - "repository_ca_file": null, - "repository_cert_file": null, - "repository_key_file": null, - "repository_password": null, - "repository_username": null, - "reset_values": false, - "reuse_values": false, - "set": [], - "set_sensitive": [], - "skip_crds": false, - "status": "deployed", - "timeout": 300, - "values": null, - "verify": false, - "version": "0.1.0", - "wait": true, - "wait_for_jobs": true - }, - "sensitive_attributes": [], - "private": "bnVsbA==", - "dependencies": [ - "kind_cluster.one-click" - ] - } - ] - }, - { - "mode": "managed", - "type": "helm_release", - "name": "redis", - "provider": "provider[\"registry.terraform.io/hashicorp/helm\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "atomic": false, - "chart": "../../helm_charts/redis", - "cleanup_on_fail": false, - "create_namespace": true, - "dependency_update": true, - "description": null, - "devel": null, - "disable_crd_hooks": false, - "disable_openapi_validation": false, - "disable_webhooks": false, - "force_update": false, - "id": "redis", - "keyring": null, - "lint": false, - "manifest": null, - "max_history": 0, - "metadata": [ - { - "app_version": "1.0", - "chart": "redis", - "name": "redis", - "namespace": "knowlg-db", - "revision": 1, - "values": "null", - "version": "0.1.0" - } - ], - "name": "redis", - "namespace": "knowlg-db", - "pass_credentials": false, - "postrender": [], - "recreate_pods": false, - "render_subchart_notes": true, - "replace": false, - "repository": null, - "repository_ca_file": null, - "repository_cert_file": null, - "repository_key_file": null, - "repository_password": null, - "repository_username": null, - "reset_values": false, - "reuse_values": false, - "set": [], - "set_sensitive": [], - "skip_crds": false, - "status": "deployed", - "timeout": 300, - "values": null, - "verify": false, - "version": "0.1.0", - "wait": true, - "wait_for_jobs": true - }, - "sensitive_attributes": [], - "private": "bnVsbA==", - "dependencies": [ - "kind_cluster.one-click" - ] - } - ] - }, - { - "mode": "managed", - "type": "helm_release", - "name": "taxonomy", - "provider": "provider[\"registry.terraform.io/hashicorp/helm\"]", - "instances": [ - { - "status": "tainted", - "schema_version": 0, - "attributes": { - "atomic": false, - "chart": "../../helm_charts/taxonomy", - "cleanup_on_fail": false, - "create_namespace": true, - "dependency_update": true, - "description": null, - "devel": null, - "disable_crd_hooks": false, - "disable_openapi_validation": false, - "disable_webhooks": false, - "force_update": false, - "id": "taxonomy", - "keyring": null, - "lint": false, - "manifest": null, - "max_history": 0, - "metadata": [ - { - "app_version": "1.0", - "chart": "taxonomy", - "name": "taxonomy", - "namespace": "knowlg-db", - "revision": 1, - "values": "{}", - "version": "0.1.0" - } - ], - "name": "taxonomy", - "namespace": "knowlg-db", - "pass_credentials": false, - "postrender": [], - "recreate_pods": false, - "render_subchart_notes": true, - "replace": false, - "repository": null, - "repository_ca_file": null, - "repository_cert_file": null, - "repository_key_file": null, - "repository_password": null, - "repository_username": null, - "reset_values": false, - "reuse_values": false, - "set": [], - "set_sensitive": [], - "skip_crds": false, - "status": "failed", - "timeout": 300, - "values": null, - "verify": false, - "version": "0.1.0", - "wait": true, - "wait_for_jobs": true - }, - "sensitive_attributes": [], - "private": "bnVsbA==", - "dependencies": [ - "kind_cluster.one-click" - ] - } - ] - }, - { - "mode": "managed", - "type": "kind_cluster", - "name": "one-click", - "provider": "provider[\"registry.terraform.io/kyma-incubator/kind\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "client_certificate": "-----BEGIN CERTIFICATE-----\nMIIDITCCAgmgAwIBAgIIU6PEtyrh6EQwDQYJKoZIhvcNAQELBQAwFTETMBEGA1UE\nAxMKa3ViZXJuZXRlczAeFw0yMzAzMjgwMzU1MjZaFw0yNDAzMjcwMzU1MjhaMDQx\nFzAVBgNVBAoTDnN5c3RlbTptYXN0ZXJzMRkwFwYDVQQDExBrdWJlcm5ldGVzLWFk\nbWluMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAntJBGxbamEyt8v/e\nGtmeSN/Dg9K0DYpKdthXA223UcFSza4RjMCo/cuwgcUB7MnRIPhTnR8/L4j16Dh7\nGi2e2Y52eOcHFykT7OsfY9blgbDaiyFEvv8fcDoOkFb4Acd6fI/lnP0+0XaE7CHR\nNpVUodC3KsR7US+/49Yzod/xrBXoxENp8RWDv+C4AsNtsc/C2oYDoeysFMzQgy1k\nl5sjsKlQ3apKKyxOtpZ4N1j4LZUwmVpc9iBBfLhklLmiqwIaDYNDoTvID59+cTda\nthAO0pvCEBZR/aJNLiD0ApbeCa184mOyobZWa43Zyw1N4rUTlN9vGxAF1EJMqNvT\nZATS+QIDAQABo1YwVDAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUH\nAwIwDAYDVR0TAQH/BAIwADAfBgNVHSMEGDAWgBR2VBGD3GjdF/AQHDjWmm/zCwX2\nZjANBgkqhkiG9w0BAQsFAAOCAQEAUSa48JJgExqFenBcmJYFWrvaUqtBO9jxXYbg\n0CSG2CY88BwYLwuv4UXiDgbQlLXwbdWGRFK9gzawHHcFBVjmVUrt9vFhSQ/k8RYX\n6AIUCGCoaq0TBOI+fcdsSE1TT6zLjrabsiUsx61+7iA2dRDlhsDfflEKAtyNPqZi\n8DKPRlIQrr2h705winQjrdTxyY/eLXlH5PHSlM/LErco/HvXdcFvRr+1G9WRpzgK\nUbs145U20WU+SucC+Jjs1XMUjJkKMsto0/Xsbi7MJlQUkJ2MwtGlkp6p2AU+fUZk\nRWJoGuCPhvAfl5K9m5VJwTK4vIlmUE6TxdprgGQNlNa73NAa4Q==\n-----END CERTIFICATE-----\n", - "client_key": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAntJBGxbamEyt8v/eGtmeSN/Dg9K0DYpKdthXA223UcFSza4R\njMCo/cuwgcUB7MnRIPhTnR8/L4j16Dh7Gi2e2Y52eOcHFykT7OsfY9blgbDaiyFE\nvv8fcDoOkFb4Acd6fI/lnP0+0XaE7CHRNpVUodC3KsR7US+/49Yzod/xrBXoxENp\n8RWDv+C4AsNtsc/C2oYDoeysFMzQgy1kl5sjsKlQ3apKKyxOtpZ4N1j4LZUwmVpc\n9iBBfLhklLmiqwIaDYNDoTvID59+cTdathAO0pvCEBZR/aJNLiD0ApbeCa184mOy\nobZWa43Zyw1N4rUTlN9vGxAF1EJMqNvTZATS+QIDAQABAoIBAEyX7ZoY3XcRk9pb\nF0wvoyxV7b6HQRo+lUbXcq9OnFgrObnDtucAiv9K2mbbEeCWO+sAl6JwzVIpZR6H\nDqdDVRZqpCwOjtbWkqtxgQPvQWPYXK04cCUEeK7w8NEfgB+riiDEbOHXKBJwbA5N\nCM6gGd6n/436j9ZJhutkBCaPD8gDTmZ/FijYzl+6pL92fOFvweCYkQrCnwJxVoGI\njsU7a2vx4tNa4gK6DF/etXpd5XGwncKx3723cVOFKq8iV1SuiK6nwT8n9YPuvG3s\nxuop6J/KWQVQe3Xke9kNg+qnB/xrPEfMJMM5+3UO8P/RTSR3AaHsjNXbTPm8UXYb\nQUJf43kCgYEAxeYgU/JKdff6v6DRuVdVGACH/35CpUxMHXztq6P+C0gHlEjb5Vxm\nByrcLgc6xojnNzshHUGwq7k/2zqmRDVh+8V40UUOE5GIOQJV4NUcv4CI/OkErfl5\ngfOqz7jmOKE1F8AIkTO39btUp3d0cN4umBBuw9L+Iyu2mH/Xig/sbm8CgYEAzXMY\nX22OpwHV5YJl2hD+Rv7HEg9imt65vwFEXocoIUZkSS64OMwFKCbkvNbIc6Sps67/\nQFLfh/AIFAhwNEzaP0D9kaZpLxtrANba9QT0hLdoxDc9cA/CNG5vO6FBsN2VHGw0\ns3DB+wdChun5Z4I7XWUgBaOH6GrAIba1JBrrCRcCgYEAp2phGuM0HsxkMkzIXzUE\nZcO9SWT02IXNNn0DS1r2hXCwCMACVuxt285kagPvsxkKEW1SPD/gR8X1wW7CTZ2j\ngKyz4sW6fM93IFNbd1KSeWcxfFNchBkys78BV60iBfJKygbIuJ26GIYbvXiO5xTu\n8fGk4sDSAcrvM9+i5cdGyr0CgYEAgoEf9E5FlXSnCJkiVbkBrNteXOyE+FeOqfQG\nF/3G0gxC2/Hfd8SxaeX62rGct0jbu5Xn8lYyQ8mHeCI8FBzVbeS8re6FVjUWbXwN\nriKtqDhRRoLIdgyskl2sLraqYcz/UQSzHWD895aO/eBWWxfw2czI4IwWYt6cmU3U\nFtE6h5UCgYB9ZTAWEFGmKusS3NMT9do5tCVB6Rca2woivCugtX1Lej/OtapA/nwI\n5BBpM2MfHgsZZW+GWqujN2ZYIXfL/6PNiOXw06MuMWJTgRHL2j/kVeS0Wo2dqAbu\nCf/7UVArE/usBnP6Qdue7HDEyAWZaU09R4M8sEa71D2BVV0VP6WdiA==\n-----END RSA PRIVATE KEY-----\n", - "cluster_ca_certificate": "-----BEGIN CERTIFICATE-----\nMIIC5zCCAc+gAwIBAgIBADANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDEwprdWJl\ncm5ldGVzMB4XDTIzMDMyODAzNTUyNloXDTMzMDMyNTAzNTUyNlowFTETMBEGA1UE\nAxMKa3ViZXJuZXRlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL5U\nuTVslBXDnjUNb/D6z71o8E7WlAgyp+oq7gYJALyarC41egt1NZlJD8GHD6+PzHbx\n3kwIMdcrUmzd0QTHX8nc72K53/mXxSgkBM8oISYqm5aHh1hasV1elQP9/j36Vfmt\n5L/uiUCVwcb3Wfx6Sj4tfPj61czuUUq6D+oVP2yi/0gtKyZzSCvFovZFYR8mFu4q\nkJ9oL59i4KJ/txjRu4ImhLumfBnFdwiy/kJx24VnZDURVjQJE6MruFzM8virB11m\nIXhpv6Zr/wP1UqY8lvEiFX9MfwI3RtlmlSbW+3mAImIadDWnDWVB4fjjCPtIsJS1\ndvP55cHuRA3NJY5hqK0CAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgKkMA8GA1UdEwEB\n/wQFMAMBAf8wHQYDVR0OBBYEFHZUEYPcaN0X8BAcONaab/MLBfZmMA0GCSqGSIb3\nDQEBCwUAA4IBAQAfXZZ+raUxvTBY/e6TDAqSCq5uz9gUlT+eyVYzzmWOOjF+rwRS\n+HNNamZygWxo9w7H+AMWhyPHAuE4Zx7nIlGz3cTgcsQONtBhC5u+jQGK5TY/m/Rv\njQ9ceP1jnmZHBQx8pvOUhSoO08MqG7J1bycV2gkLgN/iU2PZ2I7ADN03K98ev8Ey\nmIBpKoow2qFlsfAb2EZV9akI0uraJQfoMGLbF4Ve2Z57HKEKAJITNfFKCa8J7Hij\njsW4tnzBzfA6cUvtHqDOv3x10cVYn6b+yh28bo+nMFhn20xbZ4vwDBy27TtSwuVW\ntgtV0WA7cgcZtQtnFy3NXpQ8qOWZeBPuW9jm\n-----END CERTIFICATE-----\n", - "endpoint": "https://127.0.0.1:37145", - "id": "one-click-", - "kind_config": [ - { - "api_version": "kind.x-k8s.io/v1alpha4", - "containerd_config_patches": [], - "kind": "Cluster", - "networking": [], - "node": [ - { - "extra_mounts": [], - "extra_port_mappings": [ - { - "container_port": 80, - "host_port": 80, - "listen_address": "", - "protocol": "" - }, - { - "container_port": 443, - "host_port": 443, - "listen_address": "", - "protocol": "" - } - ], - "image": "", - "kubeadm_config_patches": [ - "kind: InitConfiguration\nnodeRegistration:\n kubeletExtraArgs:\n node-labels: \"ingress-ready=true\"\n" - ], - "role": "control-plane" - }, - { - "extra_mounts": [], - "extra_port_mappings": [], - "image": "", - "kubeadm_config_patches": [ - "kind: InitConfiguration\nnodeRegistration:\n kubeletExtraArgs:\n node-labels: \"worker-node=true\"\n" - ], - "role": "worker" - }, - { - "extra_mounts": [], - "extra_port_mappings": [], - "image": "", - "kubeadm_config_patches": [ - "kind: InitConfiguration\nnodeRegistration:\n kubeletExtraArgs:\n node-labels: \"worker-node=true\"\n" - ], - "role": "worker" - }, - { - "extra_mounts": [], - "extra_port_mappings": [], - "image": "", - "kubeadm_config_patches": [ - "kind: InitConfiguration\nnodeRegistration:\n kubeletExtraArgs:\n node-labels: \"worker-node=true\"\n" - ], - "role": "worker" - } - ] - } - ], - "kubeconfig": "apiVersion: v1\nclusters:\n- cluster:\n certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM1ekNDQWMrZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRJek1ETXlPREF6TlRVeU5sb1hEVE16TURNeU5UQXpOVFV5Tmxvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTDVVCnVUVnNsQlhEbmpVTmIvRDZ6NzFvOEU3V2xBZ3lwK29xN2dZSkFMeWFyQzQxZWd0MU5abEpEOEdIRDYrUHpIYngKM2t3SU1kY3JVbXpkMFFUSFg4bmM3Mks1My9tWHhTZ2tCTThvSVNZcW01YUhoMWhhc1YxZWxRUDkvajM2VmZtdAo1TC91aVVDVndjYjNXZng2U2o0dGZQajYxY3p1VVVxNkQrb1ZQMnlpLzBndEt5WnpTQ3ZGb3ZaRllSOG1GdTRxCmtKOW9MNTlpNEtKL3R4alJ1NEltaEx1bWZCbkZkd2l5L2tKeDI0Vm5aRFVSVmpRSkU2TXJ1RnpNOHZpckIxMW0KSVhocHY2WnIvd1AxVXFZOGx2RWlGWDlNZndJM1J0bG1sU2JXKzNtQUltSWFkRFduRFdWQjRmampDUHRJc0pTMQpkdlA1NWNIdVJBM05KWTVocUswQ0F3RUFBYU5DTUVBd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0hRWURWUjBPQkJZRUZIWlVFWVBjYU4wWDhCQWNPTmFhYi9NTEJmWm1NQTBHQ1NxR1NJYjMKRFFFQkN3VUFBNElCQVFBZlhaWityYVV4dlRCWS9lNlREQXFTQ3E1dXo5Z1VsVCtleVZZenptV09PakYrcndSUworSE5OYW1aeWdXeG85dzdIK0FNV2h5UEhBdUU0Wng3bklsR3ozY1RnY3NRT050QmhDNXUralFHSzVUWS9tL1J2CmpROWNlUDFqbm1aSEJReDhwdk9VaFNvTzA4TXFHN0oxYnljVjJna0xnTi9pVTJQWjJJN0FETjAzSzk4ZXY4RXkKbUlCcEtvb3cycUZsc2ZBYjJFWlY5YWtJMHVyYUpRZm9NR0xiRjRWZTJaNTdIS0VLQUpJVE5mRktDYThKN0hpagpqc1c0dG56QnpmQTZjVXZ0SHFET3YzeDEwY1ZZbjZiK3loMjhibytuTUZobjIweGJaNHZ3REJ5MjdUdFN3dVZXCnRndFYwV0E3Y2djWnRRdG5GeTNOWHBROHFPV1plQlB1VzlqbQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==\n server: https://127.0.0.1:37145\n name: kind-one-click\ncontexts:\n- context:\n cluster: kind-one-click\n user: kind-one-click\n name: kind-one-click\ncurrent-context: kind-one-click\nkind: Config\npreferences: {}\nusers:\n- name: kind-one-click\n user:\n client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURJVENDQWdtZ0F3SUJBZ0lJVTZQRXR5cmg2RVF3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB5TXpBek1qZ3dNelUxTWpaYUZ3MHlOREF6TWpjd016VTFNamhhTURReApGekFWQmdOVkJBb1REbk41YzNSbGJUcHRZWE4wWlhKek1Sa3dGd1lEVlFRREV4QnJkV0psY201bGRHVnpMV0ZrCmJXbHVNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQW50SkJHeGJhbUV5dDh2L2UKR3RtZVNOL0RnOUswRFlwS2R0aFhBMjIzVWNGU3phNFJqTUNvL2N1d2djVUI3TW5SSVBoVG5SOC9MNGoxNkRoNwpHaTJlMlk1MmVPY0hGeWtUN09zZlk5YmxnYkRhaXlGRXZ2OGZjRG9Pa0ZiNEFjZDZmSS9sblAwKzBYYUU3Q0hSCk5wVlVvZEMzS3NSN1VTKy80OVl6b2QveHJCWG94RU5wOFJXRHYrQzRBc050c2MvQzJvWURvZXlzRk16UWd5MWsKbDVzanNLbFEzYXBLS3l4T3RwWjROMWo0TFpVd21WcGM5aUJCZkxoa2xMbWlxd0lhRFlORG9UdklENTkrY1RkYQp0aEFPMHB2Q0VCWlIvYUpOTGlEMEFwYmVDYTE4NG1PeW9iWldhNDNaeXcxTjRyVVRsTjl2R3hBRjFFSk1xTnZUClpBVFMrUUlEQVFBQm8xWXdWREFPQmdOVkhROEJBZjhFQkFNQ0JhQXdFd1lEVlIwbEJBd3dDZ1lJS3dZQkJRVUgKQXdJd0RBWURWUjBUQVFIL0JBSXdBREFmQmdOVkhTTUVHREFXZ0JSMlZCR0QzR2pkRi9BUUhEaldtbS96Q3dYMgpaakFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBVVNhNDhKSmdFeHFGZW5CY21KWUZXcnZhVXF0Qk85anhYWWJnCjBDU0cyQ1k4OEJ3WUx3dXY0VVhpRGdiUWxMWHdiZFdHUkZLOWd6YXdISGNGQlZqbVZVcnQ5dkZoU1EvazhSWVgKNkFJVUNHQ29hcTBUQk9JK2ZjZHNTRTFUVDZ6TGpyYWJzaVVzeDYxKzdpQTJkUkRsaHNEZmZsRUtBdHlOUHFaaQo4REtQUmxJUXJyMmg3MDV3aW5RanJkVHh5WS9lTFhsSDVQSFNsTS9MRXJjby9IdlhkY0Z2UnIrMUc5V1JwemdLClViczE0NVUyMFdVK1N1Y0MrSmpzMVhNVWpKa0tNc3RvMC9Yc2JpN01KbFFVa0oyTXd0R2xrcDZwMkFVK2ZVWmsKUldKb0d1Q1BodkFmbDVLOW01Vkp3VEs0dklsbVVFNlR4ZHByZ0dRTmxOYTczTkFhNFE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==\n client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcEFJQkFBS0NBUUVBbnRKQkd4YmFtRXl0OHYvZUd0bWVTTi9EZzlLMERZcEtkdGhYQTIyM1VjRlN6YTRSCmpNQ28vY3V3Z2NVQjdNblJJUGhUblI4L0w0ajE2RGg3R2kyZTJZNTJlT2NIRnlrVDdPc2ZZOWJsZ2JEYWl5RkUKdnY4ZmNEb09rRmI0QWNkNmZJL2xuUDArMFhhRTdDSFJOcFZVb2RDM0tzUjdVUysvNDlZem9kL3hyQlhveEVOcAo4UldEditDNEFzTnRzYy9DMm9ZRG9leXNGTXpRZ3kxa2w1c2pzS2xRM2FwS0t5eE90cFo0TjFqNExaVXdtVnBjCjlpQkJmTGhrbExtaXF3SWFEWU5Eb1R2SUQ1OStjVGRhdGhBTzBwdkNFQlpSL2FKTkxpRDBBcGJlQ2ExODRtT3kKb2JaV2E0M1p5dzFONHJVVGxOOXZHeEFGMUVKTXFOdlRaQVRTK1FJREFRQUJBb0lCQUV5WDdab1kzWGNSazlwYgpGMHd2b3l4VjdiNkhRUm8rbFViWGNxOU9uRmdyT2JuRHR1Y0FpdjlLMm1iYkVlQ1dPK3NBbDZKd3pWSXBaUjZICkRxZERWUlpxcEN3T2p0YldrcXR4Z1FQdlFXUFlYSzA0Y0NVRWVLN3c4TkVmZ0IrcmlpREViT0hYS0JKd2JBNU4KQ002Z0dkNm4vNDM2ajlaSmh1dGtCQ2FQRDhnRFRtWi9GaWpZemwrNnBMOTJmT0Z2d2VDWWtRckNud0p4Vm9HSQpqc1U3YTJ2eDR0TmE0Z0s2REYvZXRYcGQ1WEd3bmNLeDM3MjNjVk9GS3E4aVYxU3VpSzZud1Q4bjlZUHV2RzNzCnh1b3A2Si9LV1FWUWUzWGtlOWtOZytxbkIveHJQRWZNSk1NNSszVU84UC9SVFNSM0FhSHNqTlhiVFBtOFVYWWIKUVVKZjQza0NnWUVBeGVZZ1UvSktkZmY2djZEUnVWZFZHQUNILzM1Q3BVeE1IWHp0cTZQK0MwZ0hsRWpiNVZ4bQpCeXJjTGdjNnhvam5OenNoSFVHd3E3ay8yenFtUkRWaCs4VjQwVVVPRTVHSU9RSlY0TlVjdjRDSS9Pa0VyZmw1CmdmT3F6N2ptT0tFMUY4QUlrVE8zOWJ0VXAzZDBjTjR1bUJCdXc5TCtJeXUybUgvWGlnL3NibThDZ1lFQXpYTVkKWDIyT3B3SFY1WUpsMmhEK1J2N0hFZzlpbXQ2NXZ3RkVYb2NvSVVaa1NTNjRPTXdGS0Nia3ZOYkljNlNwczY3LwpRRkxmaC9BSUZBaHdORXphUDBEOWthWnBMeHRyQU5iYTlRVDBoTGRveERjOWNBL0NORzV2TzZGQnNOMlZIR3cwCnMzREIrd2RDaHVuNVo0STdYV1VnQmFPSDZHckFJYmExSkJyckNSY0NnWUVBcDJwaEd1TTBIc3hrTWt6SVh6VUUKWmNPOVNXVDAySVhOTm4wRFMxcjJoWEN3Q01BQ1Z1eHQyODVrYWdQdnN4a0tFVzFTUEQvZ1I4WDF3VzdDVFoyagpnS3l6NHNXNmZNOTNJRk5iZDFLU2VXY3hmRk5jaEJreXM3OEJWNjBpQmZKS3lnYkl1SjI2R0lZYnZYaU81eFR1CjhmR2s0c0RTQWNydk05K2k1Y2RHeXIwQ2dZRUFnb0VmOUU1RmxYU25DSmtpVmJrQnJOdGVYT3lFK0ZlT3FmUUcKRi8zRzBneEMyL0hmZDhTeGFlWDYyckdjdDBqYnU1WG44bFl5UThtSGVDSThGQnpWYmVTOHJlNkZWalVXYlh3TgpyaUt0cURoUlJvTElkZ3lza2wyc0xyYXFZY3ovVVFTekhXRDg5NWFPL2VCV1d4ZncyY3pJNEl3V1l0NmNtVTNVCkZ0RTZoNVVDZ1lCOVpUQVdFRkdtS3VzUzNOTVQ5ZG81dENWQjZSY2Eyd29pdkN1Z3RYMUxlai9PdGFwQS9ud0kKNUJCcE0yTWZIZ3NaWlcrR1dxdWpOMlpZSVhmTC82UE5pT1h3MDZNdU1XSlRnUkhMMmova1ZlUzBXbzJkcUFidQpDZi83VVZBckUvdXNCblA2UWR1ZTdIREV5QVdaYVUwOVI0TThzRWE3MUQyQlZWMFZQNldkaUE9PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=\n", - "kubeconfig_path": "/home/stpl/.kube/config", - "name": "one-click", - "node_image": null, - "timeouts": null, - "wait_for_ready": true - }, - "sensitive_attributes": [], - "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjozMDAwMDAwMDAwMDAsImRlbGV0ZSI6MzAwMDAwMDAwMDAwLCJ1cGRhdGUiOjMwMDAwMDAwMDAwMH19" - } - ] - } - ], - "check_results": null -} From 04a79fd52ea18e51ba6b7ecdec86f931b56fc453 Mon Sep 17 00:00:00 2001 From: aimansharief <110388201+aimansharief@users.noreply.github.com> Date: Tue, 11 Apr 2023 15:57:33 +0530 Subject: [PATCH 206/490] Issue KN-767 feat:Added learning service terraform script --- .../terraform/local/learning-provision.tf | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 knowlg-automation/terraform/local/learning-provision.tf diff --git a/knowlg-automation/terraform/local/learning-provision.tf b/knowlg-automation/terraform/local/learning-provision.tf new file mode 100644 index 000000000..4c0740844 --- /dev/null +++ b/knowlg-automation/terraform/local/learning-provision.tf @@ -0,0 +1,10 @@ +resource "helm_release" "learning" { + name = "learning" + chart = var.LEARNING_CHART + namespace = var.LEARNING_NAMESPACE + create_namespace = true + dependency_update = true + depends_on = [kind_cluster.one-click] + wait_for_jobs = true + +} \ No newline at end of file From 2d3a6a1a9cf3f173e279cf58537194c0267aca1b Mon Sep 17 00:00:00 2001 From: Kartheek Palla Date: Tue, 11 Apr 2023 18:09:38 +0530 Subject: [PATCH 207/490] Issue #KN-767 fix: knowlg one click installation for azure added --- knowlg-automation/LICENSE | 2 +- .../helm_charts/flink/.helmignore | 22 -- .../helm_charts/flink/Chart.yaml | 21 -- .../helm_charts/flink/templates/_helpers.tpl | 67 ------ .../flink/templates/flink_job_configmap.yaml | 14 -- .../flink/templates/flink_job_deployment.yaml | 202 ------------------ .../helm_charts/flink/values.yaml | 183 ---------------- knowlg-automation/helm_charts/kafka/kafka | 13 ++ .../helm_charts/kafka/values.yaml | 46 +++- .../neo4j/templates/logstash-configmap.yaml | 2 +- .../helm_charts/neo4j/values.yaml | 2 +- knowlg-automation/terraform/aks.yaml | 7 +- knowlg-automation/terraform/azure/aks.tf | 21 ++ .../terraform/azure/cassandra-provision.tf | 11 + .../terraform/azure/content-provision.tf | 11 + knowlg-automation/terraform/azure/data.tf | 1 + .../azure/elasticsearch-provision.tf | 11 + knowlg-automation/terraform/azure/kafka.tf | 1 + .../terraform/azure/kafka_vars.tf | 1 + .../terraform/azure/learning-provision.tf | 10 + knowlg-automation/terraform/azure/local.tf | 18 ++ .../terraform/azure/local_file.tf | 4 + .../terraform/azure/neo4j-provision.tf | 10 + knowlg-automation/terraform/azure/provider.tf | 17 ++ .../terraform/azure/redis-provision.tf | 10 + .../terraform/azure/search-provision.tf | 10 + .../terraform/azure/storage_account.tf | 12 ++ .../terraform/azure/storage_container.tf | 5 + knowlg-automation/terraform/azure/subnets.tf | 7 + .../terraform/azure/taxonomy-provision.tf | 11 + .../terraform/azure/variables.tf | 189 ++++++++++++++++ .../terraform/azure/virtual_network.tf | 10 + knowlg-automation/terraform/helm/kafka.tf | 36 ++++ .../terraform/helm/kafka_exporter.tf | 15 ++ .../terraform/helm/kafka_exporter_vars.tf | 35 +++ .../terraform/helm/kafka_vars.tf | 107 ++++++++++ .../terraform/local/kafka-provision.tf | 15 +- knowlg-automation/terraform/local/vars.tf | 28 ++- 38 files changed, 643 insertions(+), 544 deletions(-) delete mode 100644 knowlg-automation/helm_charts/flink/.helmignore delete mode 100644 knowlg-automation/helm_charts/flink/Chart.yaml delete mode 100644 knowlg-automation/helm_charts/flink/templates/_helpers.tpl delete mode 100644 knowlg-automation/helm_charts/flink/templates/flink_job_configmap.yaml delete mode 100644 knowlg-automation/helm_charts/flink/templates/flink_job_deployment.yaml delete mode 100644 knowlg-automation/helm_charts/flink/values.yaml create mode 100644 knowlg-automation/helm_charts/kafka/kafka create mode 100644 knowlg-automation/terraform/azure/aks.tf create mode 100644 knowlg-automation/terraform/azure/cassandra-provision.tf create mode 100644 knowlg-automation/terraform/azure/content-provision.tf create mode 100644 knowlg-automation/terraform/azure/data.tf create mode 100644 knowlg-automation/terraform/azure/elasticsearch-provision.tf create mode 120000 knowlg-automation/terraform/azure/kafka.tf create mode 120000 knowlg-automation/terraform/azure/kafka_vars.tf create mode 100644 knowlg-automation/terraform/azure/learning-provision.tf create mode 100644 knowlg-automation/terraform/azure/local.tf create mode 100644 knowlg-automation/terraform/azure/local_file.tf create mode 100644 knowlg-automation/terraform/azure/neo4j-provision.tf create mode 100644 knowlg-automation/terraform/azure/provider.tf create mode 100644 knowlg-automation/terraform/azure/redis-provision.tf create mode 100644 knowlg-automation/terraform/azure/search-provision.tf create mode 100644 knowlg-automation/terraform/azure/storage_account.tf create mode 100644 knowlg-automation/terraform/azure/storage_container.tf create mode 100644 knowlg-automation/terraform/azure/subnets.tf create mode 100644 knowlg-automation/terraform/azure/taxonomy-provision.tf create mode 100644 knowlg-automation/terraform/azure/variables.tf create mode 100644 knowlg-automation/terraform/azure/virtual_network.tf create mode 100644 knowlg-automation/terraform/helm/kafka.tf create mode 100644 knowlg-automation/terraform/helm/kafka_exporter.tf create mode 100644 knowlg-automation/terraform/helm/kafka_exporter_vars.tf create mode 100644 knowlg-automation/terraform/helm/kafka_vars.tf diff --git a/knowlg-automation/LICENSE b/knowlg-automation/LICENSE index 5dcd61e28..2067f5315 100644 --- a/knowlg-automation/LICENSE +++ b/knowlg-automation/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2022 Sunbird Obsrv +Copyright (c) 2022 Sunbird Knowlg Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/knowlg-automation/helm_charts/flink/.helmignore b/knowlg-automation/helm_charts/flink/.helmignore deleted file mode 100644 index 50af03172..000000000 --- a/knowlg-automation/helm_charts/flink/.helmignore +++ /dev/null @@ -1,22 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*~ -# Various IDEs -.project -.idea/ -*.tmproj -.vscode/ diff --git a/knowlg-automation/helm_charts/flink/Chart.yaml b/knowlg-automation/helm_charts/flink/Chart.yaml deleted file mode 100644 index 31dcba4d2..000000000 --- a/knowlg-automation/helm_charts/flink/Chart.yaml +++ /dev/null @@ -1,21 +0,0 @@ -apiVersion: v2 -name: flink -description: A Helm chart for Kubernetes - -# A chart can be either an 'application' or a 'library' chart. -# -# Application charts are a collection of templates that can be packaged into versioned archives -# to be deployed. -# -# Library charts provide useful utilities or functions for the chart developer. They're included as -# a dependency of application charts to inject those utilities and functions into the rendering -# pipeline. Library charts do not define any templates and therefore cannot be deployed. -type: application - -# This is the chart version. This version number should be incremented each time you make changes -# to the chart and its templates, including the app version. -version: 0.1.0 - -# This is the version number of the application being deployed. This version number should be -# incremented each time you make changes to the application. -appVersion: 1.16.0 diff --git a/knowlg-automation/helm_charts/flink/templates/_helpers.tpl b/knowlg-automation/helm_charts/flink/templates/_helpers.tpl deleted file mode 100644 index 18df42085..000000000 --- a/knowlg-automation/helm_charts/flink/templates/_helpers.tpl +++ /dev/null @@ -1,67 +0,0 @@ -{{/* vim: set filetype=mustache: */}} -{{/* -Expand the name of the chart. -*/}} -{{- define "datapipeline_jobs.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{- define "name" -}} -{{- default .Release.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -*/}} -{{- define "datapipeline_jobs.fullname" -}} -{{- if .Values.fullnameOverride -}} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- $name := default .Chart.Name .Values.nameOverride -}} -{{- if contains $name .Release.Name -}} -{{- .Release.Name | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} -{{- end -}} -{{- end -}} -{{- end -}} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "datapipeline_jobs.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Common labels -*/}} -{{- define "datapipeline_jobs.labels" -}} -helm.sh/chart: {{ include "datapipeline_jobs.chart" . }} -{{ include "datapipeline_jobs.selectorLabels" . }} -{{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -{{- end }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- end -}} - -{{/* -Selector labels -*/}} -{{- define "datapipeline_jobs.selectorLabels" -}} -app.kubernetes.io/name: {{ include "datapipeline_jobs.name" . }} -app.kubernetes.io/instance: {{ .Release.Name }} -{{- end -}} - -{{/* -Create the name of the service account to use -*/}} -{{- define "datapipeline_jobs.serviceAccountName" -}} -{{- if .Values.serviceAccount.create -}} - {{ default (include "datapipeline_jobs.fullname" .) .Values.serviceAccount.name }} -{{- else -}} - {{ default "default" .Values.serviceAccount.name }} -{{- end -}} -{{- end -}} diff --git a/knowlg-automation/helm_charts/flink/templates/flink_job_configmap.yaml b/knowlg-automation/helm_charts/flink/templates/flink_job_configmap.yaml deleted file mode 100644 index 6f3356860..000000000 --- a/knowlg-automation/helm_charts/flink/templates/flink_job_configmap.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ .Release.Name }}-config - namespace: {{ .Values.namespace }} - labels: - app: flink -data: - base-config: |+ -{{ .Values.base_config | indent 4 }} -{{- $name := .Release.Name }} -{{ index .Values $name | toYaml | indent 2 }} - log4j_console_properties: |+ -{{ .Values.log4j_console_properties | indent 4 }} \ No newline at end of file diff --git a/knowlg-automation/helm_charts/flink/templates/flink_job_deployment.yaml b/knowlg-automation/helm_charts/flink/templates/flink_job_deployment.yaml deleted file mode 100644 index 6f4b3aaa8..000000000 --- a/knowlg-automation/helm_charts/flink/templates/flink_job_deployment.yaml +++ /dev/null @@ -1,202 +0,0 @@ ---- -apiVersion: v1 -kind: Service -metadata: - name: {{ .Release.Name }}-jobmanager - namespace: {{ .Values.namespace }} - labels: - app: flink - component: {{ .Release.Name }}-jobmanager -spec: - type: ClusterIP - ports: - - name: rpc - port: {{ .Values.jobmanager.rpc_port }} - - name: blob - port: {{ .Values.jobmanager.blob_port }} - - name: query - port: {{ .Values.jobmanager.query_port }} - - name: ui - port: {{ .Values.jobmanager.ui_port }} - - name: prom - port: {{ .Values.jobmanager.prom_port }} - selector: - app: flink - component: {{ .Release.Name }}-jobmanager ---- -apiVersion: v1 -kind: Service -metadata: - name: {{ .Release.Name }}-jobmanager-webui - namespace: {{ .Values.namespace }} - {{- if .Values.service.annotations }} - {{- with .Values.service.annotations }} -annotations: - {{ toYaml . | indent 4 }} - {{- end }} - {{- end }} -spec: - {{- if eq .Values.service.type "ClusterIP" }} - type: ClusterIP - {{- end }} - {{- if eq .Values.service.type "LoadBalancer" }} - type: LoadBalancer - {{- end }} - ports: - - name: rest - port: {{ .Values.rest_port }} - protocol: TCP - targetPort: {{ .Values.resttcp_port }} - selector: - app: flink - component: {{ .Release.Name }}-jobmanager ---- -apiVersion: batch/v1 -kind: Job -metadata: - name: {{ .Release.Name }}-jobmanager - namespace: {{ .Values.namespace }} -spec: - template: - metadata: - labels: - app: flink - component: {{ .Release.Name }}-jobmanager - annotations: - prometheus.io/scrape: 'true' - prometheus.io/port: "{{ .Values.jobmanager.prom_port }}" - spec: - volumes: - - name: flink-config-volume - configMap: - name: {{ .Release.Name }}-config - items: - - key: flink-conf - path: flink-conf.yaml - - key: base-config - path: base-config.conf - - key: {{ .Release.Name }} - path: {{ .Release.Name }}.conf - - key: log4j_console_properties - path: log4j-console.properties - restartPolicy: OnFailure -# imagePullSecrets: -# - name: {{ .Values.imagepullsecrets }} - containers: - - name: {{ .Release.Name }}-jobmanager - image: "{{ .Values.repository }}:{{ .Values.image_tag }}" - imagePullPolicy: Always - workingDir: /opt/flink - command: ["/opt/flink/bin/standalone-job.sh"] - args: ["start-foreground", -{{- if eq .Values.checkpoint_store_type "azure" }} - "-Dfs.azure.account.key.{{ .Values.azure_account }}.blob.core.windows.net={{ .Values.azure_secret }}", -{{- end }} -{{- if eq .Values.checkpoint_store_type "s3" }} - "-Ds3.access-key={{ .Values.s3_access_key }}", - "-Ds3.secret-key={{ .Values.s3_secret_key }}", - "-Ds3.endpoint={{ .Values.s3_endpoint }}", - "-Ds3.path.style.access={{ .Values.s3_path_style_access }}", -{{- end }} - "--job-classname={{ .Values.job_classname }}", - "-Dweb.submit.enable=false", - "-Dmetrics.reporter.prom.class=org.apache.flink.metrics.prometheus.PrometheusReporter", - "-Dmetrics.reporter.prom.port={{ .Values.jobmanager.prom_port }}", - "-Djobmanager.rpc.address={{ .Release.Name }}-jobmanager", - "-Djobmanager.rpc.port={{ .Values.jobmanager.rpc_port }}", - "-Dparallelism.default=1", - "-Dblob.server.port={{ .Values.jobmanager.blob_port }}", - "-Dqueryable-state.server.ports={{ .Values.jobmanager.query_port }}", - "--config.file.path", - "/data/flink/conf/{{ .Release.Name }}.conf"] - ports: - - containerPort: {{ .Values.jobmanager.rpc_port }} - name: rpc - - containerPort: {{ .Values.jobmanager.blob_port }} - name: blob - - containerPort: {{ .Values.jobmanager.query_port }} - name: query - - containerPort: {{ .Values.jobmanager.ui_port }} - name: ui - volumeMounts: - - name: flink-config-volume - mountPath: /opt/flink/conf/flink-conf.yaml - subPath: flink-conf.yaml - - name: flink-config-volume - mountPath: /data/flink/conf/base-config.conf - subPath: base-config.conf - - name: flink-config-volume - mountPath: /data/flink/conf/{{ .Release.Name }}.conf - subPath: {{ .Release.Name }}.conf - - name: flink-config-volume - mountPath: /opt/flink/conf/log4j-console.properties - subPath: log4j-console.properties - ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ .Release.Name }}-taskmanager - namespace: {{ .Values.namespace }} -spec: - replicas: {{ .Values.taskmanager.replicas }} - selector: - matchLabels: - app: flink - component: {{ .Release.Name }}-taskmanager - template: - metadata: - labels: - app: flink - component: {{ .Release.Name }}-taskmanager - spec: - volumes: - - name: flink-config-volume - configMap: - name: {{ .Release.Name }}-config - items: - - key: flink-conf - path: flink-conf.yaml - - key: log4j_console_properties - path: log4j-console.properties -# imagePullSecrets: -# - name: {{ .Values.imagepullsecrets }} - containers: - - name: {{ .Release.Name }}-taskmanager - image: "{{ .Values.repository }}:{{ .Values.image_tag }}" - imagePullPolicy: Always - resources: - requests: - cpu: "{{ .Values.taskmanager.cpu_requests }}" - workingDir: {{ .Values.taskmanager.flink_work_dir }} - command: ["/opt/flink/bin/taskmanager.sh"] - args: ["start-foreground", -{{- if eq .Values.checkpoint_store_type "azure" }} - "-Dfs.azure.account.key.{{ .Values.azure_account }}.blob.core.windows.net={{ .Values.azure_secret }}", -{{- end }} -{{- if eq .Values.checkpoint_store_type "s3" }} - "-Ds3.access-key={{ .Values.s3_access_key }}", - "-Ds3.secret-key={{ .Values.s3_secret_key }}", - "-Ds3.endpoint={{ .Values.s3_endpoint }}", - "-Ds3.path.style.access={{ .Values.s3_path_style_access }}", -{{- end }} - "-Dweb.submit.enable=false", - "-Dmetrics.reporter.prom.class=org.apache.flink.metrics.prometheus.PrometheusReporter", - "-Dmetrics.reporter.prom.host={{ .Release.Name }}-taskmanager", - "-Dmetrics.reporter.prom.port=9251-9260", - "-Djobmanager.rpc.address={{ .Release.Name }}-jobmanager", - "-Dtaskmanager.rpc.port={{ .Values.taskmanager.rpc_port }}"] - ports: - - containerPort: {{ .Values.taskmanager.rpc_port }} - name: rpc - {{- if .Values.healthcheck }} - livenessProbe: -{{ toYaml .Values.livenessProbe | indent 10 }} - {{- end }} - volumeMounts: - - name: flink-config-volume - mountPath: /opt/flink/conf/flink-conf.yaml - subPath: flink-conf.yaml - - name: flink-config-volume - mountPath: /opt/flink/conf/log4j-console.properties - subPath: log4j-console.properties \ No newline at end of file diff --git a/knowlg-automation/helm_charts/flink/values.yaml b/knowlg-automation/helm_charts/flink/values.yaml deleted file mode 100644 index 1878f07dc..000000000 --- a/knowlg-automation/helm_charts/flink/values.yaml +++ /dev/null @@ -1,183 +0,0 @@ -namespace: "flink-dev" -imagepullsecrets: "" -dockerhub: "" -repository: "docker.io/anandp504/sunbird-oneclick" -image_tag: "1.0.0" -serviceMonitor: - enabled: false -replicaCount: 1 - -jobmanager: - rpc_port: 6123 - blob_port: 6124 - query_port: 6125 - ui_port: 8081 - prom_port: 9250 - heap_memory: 1024 - -rest_port: 80 -resttcp_port: 8081 -service: - type: ClusterIP - - -taskmanager: - prom_port: 9251 - rpc_port: 6122 - heap_memory: 1024 - replicas: 1 - cpu_requests: 0.3 - -checkpoint_store_type: "" - -# AWS S3 Details -s3_access_key: "" -s3_secret_key: "" -s3_endpoint: "" -s3_path_style_access: "" - -# Azure Container Details -azure_account: "" -azure_secret: "" - -log4j_console_properties: | - # This affects logging for both user code and Flink - rootLogger.level = INFO - rootLogger.appenderRef.console.ref = ConsoleAppender - - # Uncomment this if you want to _only_ change Flink's logging - #logger.flink.name = org.apache.flink - #logger.flink.level = INFO - - # The following lines keep the log level of common libraries/connectors on - # log level INFO. The root logger does not override this. You have to manually - # change the log levels here. - logger.akka.name = akka - logger.akka.level = ERROR - logger.kafka.name= org.apache.kafka - logger.kafka.level = ERROR - logger.hadoop.name = org.apache.hadoop - logger.hadoop.level = ERROR - logger.zookeeper.name = org.apache.zookeeper - logger.zookeeper.level = ERROR - - # Log all infos to the console - appender.console.name = ConsoleAppender - appender.console.type = CONSOLE - appender.console.layout.type = PatternLayout - appender.console.layout.pattern = %d{yyyy-MM-dd HH:mm:ss,SSS} %-5p %-60c %x - %m%n - - # Suppress the irrelevant (wrong) warnings from the Netty channel handler - logger.netty.name = org.apache.flink.shaded.akka.org.jboss.netty.channel.DefaultChannelPipeline - logger.netty.level = OFF - -base_config: | - kafka { - broker-servers = "kafka-headless.kafka.svc.cluster.local:9092" - producer.broker-servers = "kafka-headless.kafka.svc.cluster.local:9092" - consumer.broker-servers = "kafka-headless.kafka.svc.cluster.local:9092" - zookeeper = "kafka-zookeeper-headless.svc.cluster.local:2181" - producer { - max-request-size = 1572864 - batch.size = 98304 - linger.ms = 10 - compression = "snappy" - } - } - job { - env = "dev" - enable.distributed.checkpointing = false - statebackend { - blob { - storage { - account = "obsrvacc.blob.core.windows.net" - container = "obsrv-blob" - checkpointing.dir = "checkpoint" - } - } - base.url = "" - } - } - task { - parallelism = 1 - consumer.parallelism = 1 - checkpointing.compressed = true - checkpointing.interval = 10 - checkpointing.pause.between.seconds = 3000 - restart-strategy.attempts = 3 - restart-strategy.delay = 30000 # in milli-seconds - } - - redisdb.connection.timeout = 100 - redis { - host = localhost - port = 6379 - } - - redis-meta { - host = localhost - port = 6379 - } - - postgres { - host = localhost - port = 5432 - maxConnections = 2 - user = "postgres" - password = "postgres" - } - - lms-cassandra { - host = "localhost" - port = "9042" - } - -druid-validator: - druid-validator: |+ - include file("/data/flink/conf/base-config.conf") - kafka { - input.topic = dev.telemetry.denorm - output.telemetry.route.topic = dev.druid.events.telemetry - output.summary.route.topic = dev.druid.events.summary - output.failed.topic = dev.telemetry.failed - output.duplicate.topic = dev.telemetry.duplicate - groupId = dev-druid-validator-group - } - task { - consumer.parallelism = 1 - downstream.operators.parallelism = 1 - druid.validation.enabled = true - druid.deduplication.enabled = false - } - schema { - path { - telemetry = "schemas/telemetry" - summary = "schemas/summary" - } - file { - default = envelope.json - summary = me_workflow_summary.json - search = search.json - } - } - redis { - database { - duplicationstore.id = 8 - key.expiry.seconds = 3600 - } - } - - flink-conf: |+ - jobmanager.memory.flink.size: 1024m - taskmanager.memory.flink.size: 1024m - taskmanager.numberOfTaskSlots: 1 - parallelism.default: 1 - jobmanager.execution.failover-strategy: region - taskmanager.memory.network.fraction: 0.1 - scheduler-mode: reactive - heartbeat.timeout: 8000 - heartbeat.interval: 5000 - taskmanager.memory.process.size: 1700m - jobmanager.memory.process.size: 1600m - -job_classname: org.sunbird.dp.validator.task.DruidValidatorStreamTask \ No newline at end of file diff --git a/knowlg-automation/helm_charts/kafka/kafka b/knowlg-automation/helm_charts/kafka/kafka new file mode 100644 index 000000000..7e1f5439b --- /dev/null +++ b/knowlg-automation/helm_charts/kafka/kafka @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Pod +metadata: + name: testclient + namespace: knowlg-db +spec: + containers: + - name: kafka + image: solsson/kafka:latest + command: + - sh + - -c + - "exec tail -f /dev/null" \ No newline at end of file diff --git a/knowlg-automation/helm_charts/kafka/values.yaml b/knowlg-automation/helm_charts/kafka/values.yaml index 244d96423..86136c97d 100644 --- a/knowlg-automation/helm_charts/kafka/values.yaml +++ b/knowlg-automation/helm_charts/kafka/values.yaml @@ -1486,42 +1486,76 @@ provisioning: ## @param provisioning.topics Kafka topics to provision topics: - - name: "${input_topic}" + - name: "${content_publish_topic}" partitions: 1 replicationFactor: 1 # https://kafka.apache.org/documentation/#topicconfigs config: max.message.bytes: 64000 flush.messages: 1 - - name: "${output_telemetry_route_topic}" + - name: "${content_postpublish_topic}" partitions: 1 replicationFactor: 1 # https://kafka.apache.org/documentation/#topicconfigs config: max.message.bytes: 64000 flush.messages: 1 - - name: "${output_summary_route_topic}" + - name: "${learning_job_request_topic}" partitions: 1 replicationFactor: 1 # https://kafka.apache.org/documentation/#topicconfigs config: max.message.bytes: 64000 flush.messages: 1 - - name: "${output_failed_topic}" + - name: "${learning_graph_events_topic}" partitions: 1 replicationFactor: 1 # https://kafka.apache.org/documentation/#topicconfigs config: max.message.bytes: 64000 flush.messages: 1 - - name: "${output_duplicate_topic}" + - name: "${learning_events_failed_topic}" + partitions: 1 + replicationFactor: 1 + # https://kafka.apache.org/documentation/#topicconfigs + config: + max.message.bytes: 64000 + flush.messages: 1 + - name: "${search_indexer_group_topic}" + partitions: 1 + replicationFactor: 1 + # https://kafka.apache.org/documentation/#topicconfigs + config: + max.message.bytes: 64000 + flush.messages: 1 + - name: "${qrimage_request_topic}" + partitions: 1 + replicationFactor: 1 + # https://kafka.apache.org/documentation/#topicconfigs + config: + max.message.bytes: 64000 + flush.messages: 1 + - name: "${telemetry_raw_topic}" + partitions: 1 + replicationFactor: 1 + # https://kafka.apache.org/documentation/#topicconfigs + config: + max.message.bytes: 64000 + flush.messages: 1 + - name: "${dialcode_context_job_request_topic}" + partitions: 1 + replicationFactor: 1 + # https://kafka.apache.org/documentation/#topicconfigs + config: + max.message.bytes: 64000 + flush.messages: 1 + - name: "${dialcode_context_job_request_failed_topic}" partitions: 1 replicationFactor: 1 # https://kafka.apache.org/documentation/#topicconfigs config: max.message.bytes: 64000 flush.messages: 1 - ## @param provisioning.nodeSelector Node labels for pod assignment ## Ref: https://kubernetes.io/docs/user-guide/node-selection/ ## diff --git a/knowlg-automation/helm_charts/neo4j/templates/logstash-configmap.yaml b/knowlg-automation/helm_charts/neo4j/templates/logstash-configmap.yaml index 1f14970e5..58add412b 100644 --- a/knowlg-automation/helm_charts/neo4j/templates/logstash-configmap.yaml +++ b/knowlg-automation/helm_charts/neo4j/templates/logstash-configmap.yaml @@ -32,7 +32,7 @@ data: format => "%{message}" } message_key => "%{nodeUniqueId}" - topic_id => "{{ .Values.input_topic}}" + topic_id => "{{ .Values.learning_graph_events}}" retries => 20 retry_backoff_ms => 180000 } diff --git a/knowlg-automation/helm_charts/neo4j/values.yaml b/knowlg-automation/helm_charts/neo4j/values.yaml index e4c6eadac..79669627c 100644 --- a/knowlg-automation/helm_charts/neo4j/values.yaml +++ b/knowlg-automation/helm_charts/neo4j/values.yaml @@ -17,4 +17,4 @@ volumes: requests: storage: 2Gi kafka_connection: kafka-headless.knowlg-db.svc.cluster.local:9092 -input_topic: graph-events \ No newline at end of file +learning_graph_events: dev.learning.graph.events \ No newline at end of file diff --git a/knowlg-automation/terraform/aks.yaml b/knowlg-automation/terraform/aks.yaml index f2297ea44..599468588 100644 --- a/knowlg-automation/terraform/aks.yaml +++ b/knowlg-automation/terraform/aks.yaml @@ -1,7 +1,4 @@ -╷ │ Warning: No outputs found │  -│ The state file either has no outputs defined, or all the defined outputs are empty. Please define an output in your -│ configuration with the `output` keyword and run `terraform refresh` for it to become available. If you are using -│ interpolation, please verify the interpolated value is not empty. You can use the `terraform console` command to assist. -╵ +│ The state file either has no outputs defined, or all the defined outputs are empty. Please define an output in your configuration with the `output` keyword and run `terraform refresh` for it to become available. If you are using interpolation, please +│ verify the interpolated value is not empty. You can use the `terraform console` command to assist. diff --git a/knowlg-automation/terraform/azure/aks.tf b/knowlg-automation/terraform/azure/aks.tf new file mode 100644 index 000000000..a8c1ac1f7 --- /dev/null +++ b/knowlg-automation/terraform/azure/aks.tf @@ -0,0 +1,21 @@ +resource "azurerm_kubernetes_cluster" "aks" { + name = "${local.environment_name}" + location = var.location + resource_group_name = data.azurerm_resource_group.rg.name + dns_prefix = "${local.environment_name}" + + default_node_pool { + name = var.aks_nodepool_name + node_count = var.aks_node_count + vm_size = var.aks_node_size + } + + identity { + type = var.aks_cluster_identity + } + + tags = merge( + local.common_tags, + var.additional_tags + ) +} \ No newline at end of file diff --git a/knowlg-automation/terraform/azure/cassandra-provision.tf b/knowlg-automation/terraform/azure/cassandra-provision.tf new file mode 100644 index 000000000..c09ea8a3b --- /dev/null +++ b/knowlg-automation/terraform/azure/cassandra-provision.tf @@ -0,0 +1,11 @@ +resource "helm_release" "cassandra" { + name = "cassandra" + chart = var.CASSANDRA_CHART + namespace = var.CASSANDRA_NAMESPACE + create_namespace = true + dependency_update = true + depends_on = [helm_release.neo4j] + wait_for_jobs = true + +} + \ No newline at end of file diff --git a/knowlg-automation/terraform/azure/content-provision.tf b/knowlg-automation/terraform/azure/content-provision.tf new file mode 100644 index 000000000..02a5c9fd2 --- /dev/null +++ b/knowlg-automation/terraform/azure/content-provision.tf @@ -0,0 +1,11 @@ +resource "helm_release" "content" { + name = "content" + chart = var.CONTENT_CHART + namespace = var.CONTENT_NAMESPACE + create_namespace = true + dependency_update = true + depends_on = [helm_release.redis] + wait_for_jobs = true + +} + \ No newline at end of file diff --git a/knowlg-automation/terraform/azure/data.tf b/knowlg-automation/terraform/azure/data.tf new file mode 100644 index 000000000..536d1af11 --- /dev/null +++ b/knowlg-automation/terraform/azure/data.tf @@ -0,0 +1 @@ +data "azurerm_subscription" "current" {} \ No newline at end of file diff --git a/knowlg-automation/terraform/azure/elasticsearch-provision.tf b/knowlg-automation/terraform/azure/elasticsearch-provision.tf new file mode 100644 index 000000000..8c9b37a7b --- /dev/null +++ b/knowlg-automation/terraform/azure/elasticsearch-provision.tf @@ -0,0 +1,11 @@ + +resource "helm_release" "elasticsearch" { + name = "elasticsearch" + chart = var.ELASTICSEARCH_CHART + namespace = var.ELASTICSEARCH_NAMESPACE + create_namespace = true + dependency_update = true + depends_on = [helm_release.cassandra] + wait_for_jobs = true + +} diff --git a/knowlg-automation/terraform/azure/kafka.tf b/knowlg-automation/terraform/azure/kafka.tf new file mode 120000 index 000000000..b0001ab94 --- /dev/null +++ b/knowlg-automation/terraform/azure/kafka.tf @@ -0,0 +1 @@ +../helm/kafka.tf \ No newline at end of file diff --git a/knowlg-automation/terraform/azure/kafka_vars.tf b/knowlg-automation/terraform/azure/kafka_vars.tf new file mode 120000 index 000000000..b97fea54a --- /dev/null +++ b/knowlg-automation/terraform/azure/kafka_vars.tf @@ -0,0 +1 @@ +../helm/kafka_vars.tf \ No newline at end of file diff --git a/knowlg-automation/terraform/azure/learning-provision.tf b/knowlg-automation/terraform/azure/learning-provision.tf new file mode 100644 index 000000000..c01f0c6f4 --- /dev/null +++ b/knowlg-automation/terraform/azure/learning-provision.tf @@ -0,0 +1,10 @@ +resource "helm_release" "learning" { + name = "learning" + chart = var.LEARNING_CHART + namespace = var.LEARNING_NAMESPACE + create_namespace = true + dependency_update = true + depends_on = [helm_release.taxonomy] + wait_for_jobs = true + +} \ No newline at end of file diff --git a/knowlg-automation/terraform/azure/local.tf b/knowlg-automation/terraform/azure/local.tf new file mode 100644 index 000000000..72aeb9d5f --- /dev/null +++ b/knowlg-automation/terraform/azure/local.tf @@ -0,0 +1,18 @@ + +locals { + common_tags = { + Environment = "${var.env}" + BuildingBlock = "${var.building_block}" + } + subid = split("-", "${data.azurerm_subscription.current.subscription_id}") + environment_name = "${var.building_block}-${var.env}" + uid = local.subid[0] + env_name_without_dashes = replace(local.environment_name, "-", "") + storage_account_name = "${local.env_name_without_dashes}${local.uid}" + + storage = { + azure_storage_account_name = azurerm_storage_account.storage_account.name + azure_storage_account_key = azurerm_storage_account.storage_account.primary_access_key + azure_storage_container = azurerm_storage_container.storage_container.name + } +} \ No newline at end of file diff --git a/knowlg-automation/terraform/azure/local_file.tf b/knowlg-automation/terraform/azure/local_file.tf new file mode 100644 index 000000000..1281a0361 --- /dev/null +++ b/knowlg-automation/terraform/azure/local_file.tf @@ -0,0 +1,4 @@ +resource "local_file" "kubeconfig" { + content = azurerm_kubernetes_cluster.aks.kube_config_raw + filename = "${local.environment_name}-kubeconfig.yaml" +} diff --git a/knowlg-automation/terraform/azure/neo4j-provision.tf b/knowlg-automation/terraform/azure/neo4j-provision.tf new file mode 100644 index 000000000..60f279c87 --- /dev/null +++ b/knowlg-automation/terraform/azure/neo4j-provision.tf @@ -0,0 +1,10 @@ +resource "helm_release" "neo4j" { + name = "neo4j" + chart = var.NEO4J_CHART + namespace = var.NEO4J_NAMESPACE + create_namespace = true + dependency_update = true + depends_on = [helm_release.kafka] + wait_for_jobs = true + +} \ No newline at end of file diff --git a/knowlg-automation/terraform/azure/provider.tf b/knowlg-automation/terraform/azure/provider.tf new file mode 100644 index 000000000..15eae68fc --- /dev/null +++ b/knowlg-automation/terraform/azure/provider.tf @@ -0,0 +1,17 @@ +provider "azurerm" { + features {} + skip_provider_registration = true +} + +data "azurerm_resource_group" "rg" { + name = "knowlg-testing" +} + +provider "helm" { + kubernetes { + host = azurerm_kubernetes_cluster.aks.kube_config.0.host + client_certificate = base64decode(azurerm_kubernetes_cluster.aks.kube_config.0.client_certificate) + client_key = base64decode(azurerm_kubernetes_cluster.aks.kube_config.0.client_key) + cluster_ca_certificate = base64decode(azurerm_kubernetes_cluster.aks.kube_config.0.cluster_ca_certificate) + } +} \ No newline at end of file diff --git a/knowlg-automation/terraform/azure/redis-provision.tf b/knowlg-automation/terraform/azure/redis-provision.tf new file mode 100644 index 000000000..d96b453ae --- /dev/null +++ b/knowlg-automation/terraform/azure/redis-provision.tf @@ -0,0 +1,10 @@ +resource "helm_release" "redis" { + name = "redis" + chart = var.REDIS_CHART + namespace = var.REDIS_NAMESPACE + create_namespace = true + dependency_update = true + depends_on = [helm_release.elasticsearch] + wait_for_jobs = true + +} \ No newline at end of file diff --git a/knowlg-automation/terraform/azure/search-provision.tf b/knowlg-automation/terraform/azure/search-provision.tf new file mode 100644 index 000000000..3f3ea7cc2 --- /dev/null +++ b/knowlg-automation/terraform/azure/search-provision.tf @@ -0,0 +1,10 @@ +resource "helm_release" "search" { + name = "search" + chart = var.SEARCH_CHART + namespace = var.SEARCH_NAMESPACE + create_namespace = true + dependency_update = true + depends_on = [helm_release.content] + wait_for_jobs = true + +} \ No newline at end of file diff --git a/knowlg-automation/terraform/azure/storage_account.tf b/knowlg-automation/terraform/azure/storage_account.tf new file mode 100644 index 000000000..8baac6a18 --- /dev/null +++ b/knowlg-automation/terraform/azure/storage_account.tf @@ -0,0 +1,12 @@ +resource "azurerm_storage_account" "storage_account" { + name = "${local.storage_account_name}" + resource_group_name = data.azurerm_resource_group.rg.name + location = var.location + account_tier = var.azure_storage_tier + account_replication_type = var.azure_storage_replication + + tags = merge( + local.common_tags, + var.additional_tags + ) +} \ No newline at end of file diff --git a/knowlg-automation/terraform/azure/storage_container.tf b/knowlg-automation/terraform/azure/storage_container.tf new file mode 100644 index 000000000..811955bb8 --- /dev/null +++ b/knowlg-automation/terraform/azure/storage_container.tf @@ -0,0 +1,5 @@ +resource "azurerm_storage_container" "storage_container" { + name = "${local.environment_name}" + storage_account_name = azurerm_storage_account.storage_account.name + container_access_type = "private" +} \ No newline at end of file diff --git a/knowlg-automation/terraform/azure/subnets.tf b/knowlg-automation/terraform/azure/subnets.tf new file mode 100644 index 000000000..657471fa0 --- /dev/null +++ b/knowlg-automation/terraform/azure/subnets.tf @@ -0,0 +1,7 @@ +resource "azurerm_subnet" "aks_subnet" { + name = "${local.environment_name}-aks" + resource_group_name = data.azurerm_resource_group.rg.name + virtual_network_name = azurerm_virtual_network.vnet.name + address_prefixes = var.aks_subnet_cidr + service_endpoints = var.aks_subnet_service_endpoints +} \ No newline at end of file diff --git a/knowlg-automation/terraform/azure/taxonomy-provision.tf b/knowlg-automation/terraform/azure/taxonomy-provision.tf new file mode 100644 index 000000000..1284df7ae --- /dev/null +++ b/knowlg-automation/terraform/azure/taxonomy-provision.tf @@ -0,0 +1,11 @@ +resource "helm_release" "taxonomy" { + name = "taxonomy" + chart = var.TAXONOMY_CHART + namespace = var.TAXONOMY_NAMESPACE + create_namespace = true + dependency_update = true + depends_on = [helm_release.search] + wait_for_jobs = true + +} + \ No newline at end of file diff --git a/knowlg-automation/terraform/azure/variables.tf b/knowlg-automation/terraform/azure/variables.tf new file mode 100644 index 000000000..586f84cea --- /dev/null +++ b/knowlg-automation/terraform/azure/variables.tf @@ -0,0 +1,189 @@ +variable "env" { + type = string + description = "Environment name. All resources will be prefixed with this value." + default = "dev" +} + +variable "building_block" { + type = string + description = "Building block name. All resources will be prefixed with this value." + default = "knowlg" +} + +variable "location" { + type = string + description = "Azure location to create the resources." + default = "East US 2" +} + +variable "additional_tags" { + type = map(string) + description = "Additional tags for the resources. These tags will be applied to all the resources." + default = {} +} + +variable "vnet_cidr" { + type = list(string) + description = "Azure vnet CIDR range." + default = ["10.0.0.0/16"] +} + +variable "aks_subnet_cidr" { + type = list(string) + description = "Azure AKS subnet CIDR range." + default = ["10.0.0.0/22"] +} + +variable "aks_subnet_service_endpoints" { + type = list(string) + description = "Azure AKS subnet service endpoints." + default = ["Microsoft.Sql", "Microsoft.Storage"] +} + +variable "azure_storage_tier" { + type = string + description = "Azure storage tier - Standard / Premium." + default = "Standard" +} + +variable "azure_storage_replication" { + type = string + description = "Azure storage replication - LRS / ZRS / GRS etc." + default = "LRS" +} + +variable "aks_nodepool_name" { + type = string + description = "AKS node pool name." + default = "aksnodepool1" +} + +variable "aks_node_count" { + type = number + description = "AKS node count." + default = 4 +} + +variable "aks_node_size" { + type = string + description = "AKS node size." + default = "Standard_D2s_v4" +} + +variable "aks_cluster_identity" { + type = string + description = "AKS cluster identity." + default = "SystemAssigned" +} + +variable "kubernetes_storage_class" { + type = string + description = "Storage class name for the AKS cluster" + default = "default" +} + +variable "druid_deepstorage_type" { + type = string + description = "Druid deep strorage type." + default = "azure" +} + +variable "flink_checkpoint_store_type" { + type = string + description = "Flink checkpoint store type." + default = "azure" +} + +variable "RESOURCE_GROUP" { + type = string + description = "RESOURCE GROUP name" + default = "knowlg-testing" +} + +#NEO4J +variable "NEO4J_CHART" { + description = "Neo4j Instance Running Namespace" + default = "../../helm_charts/neo4j" +} + +variable "NEO4J_NAMESPACE" { + description = "NEO4J Instance Running Namespace" + default = "knowlg-db" +} + +#CASSANDRA +variable "CASSANDRA_CHART" { + description = "Cassandra Instance Running Namespace" + default = "../../helm_charts/cassandra" +} + +variable "CASSANDRA_NAMESPACE" { + description = "CASSANDRA Instance Running Namespace" + default = "knowlg-db" +} + +#ELASTICSEARCH +variable "ELASTICSEARCH_CHART" { + description = "Elasticsearch Instance Running Namespace" + default = "../../helm_charts/elasticsearch" +} + +variable "ELASTICSEARCH_NAMESPACE" { + description = "Elasticsearch Instance Running Namespace" + default = "knowlg-db" +} + +#REDIS +variable "REDIS_CHART" { + description = "Redis Instance Running Namespace" + default = "../../helm_charts/redis" +} + +variable "REDIS_NAMESPACE" { + description = "Redis Instance Running Namespace" + default = "knowlg-db" +} + +#CONTENT +variable "CONTENT_CHART" { + description = "Content Instance Running Namespace" + default = "../../helm_charts/content" +} + +variable "CONTENT_NAMESPACE" { + description = "Content Instance Running Namespace" + default = "knowlg-api" +} + +#SEARCH +variable "SEARCH_CHART" { + description = "Search Instance Running Namespace" + default = "../../helm_charts/search" +} + +variable "SEARCH_NAMESPACE" { + description = "Search Instance Running Namespace" + default = "knowlg-api" +} + +#TAXONOMY +variable "TAXONOMY_CHART" { + description = "Taxonomy Instance Running Namespace" + default = "../../helm_charts/taxonomy" +} + +variable "TAXONOMY_NAMESPACE" { + description = "Taxonomy Instance Running Namespace" + default = "knowlg-api" +} + +#LEARNING +variable "LEARNING_CHART" { + description = "Learning Instance Running Namespace" + default = "../../helm_charts/learning" +} + +variable "LEARNING_NAMESPACE" { + description = "Learning Instance Running Namespace" + default = "knowlg-api" +} \ No newline at end of file diff --git a/knowlg-automation/terraform/azure/virtual_network.tf b/knowlg-automation/terraform/azure/virtual_network.tf new file mode 100644 index 000000000..e617adbd4 --- /dev/null +++ b/knowlg-automation/terraform/azure/virtual_network.tf @@ -0,0 +1,10 @@ +resource "azurerm_virtual_network" "vnet" { + name = "${local.environment_name}" + location = var.location + resource_group_name = data.azurerm_resource_group.rg.name + address_space = var.vnet_cidr + tags = merge( + local.common_tags, + var.additional_tags + ) +} \ No newline at end of file diff --git a/knowlg-automation/terraform/helm/kafka.tf b/knowlg-automation/terraform/helm/kafka.tf new file mode 100644 index 000000000..c0c21f87f --- /dev/null +++ b/knowlg-automation/terraform/helm/kafka.tf @@ -0,0 +1,36 @@ +resource "helm_release" "kafka" { + name = var.kafka_release_name + chart = var.kafka_chart_path + namespace = var.kafka_namespace + create_namespace = var.kafka_create_namespace + dependency_update = var.kafka_chart_dependecy_update + wait_for_jobs = var.kafka_wait_for_jobs + values = [ + templatefile(var.kafka_chart_custom_values_yaml, + { + content_publish_topic = "${var.env}.${var.kafka_content_publish_topic}" + content_postpublish_topic = "${var.env}.${var.kafka_content_postpublish_topic}" + learning_job_request_topic = "${var.env}.${var.kafka_learning_job_request_topic}" + learning_graph_events_topic = "${var.env}.${var.kafka_learning_graph_events_topic}" + learning_events_failed_topic = "${var.env}.${var.kafka_learning_events_failed_topic}" + search_indexer_group_topic = "${var.env}.${var.kafka_search_indexer_group_topic}" + qrimage_request_topic = "${var.env}.${var.kafka_qrimage_request_topic}" + telemetry_raw_topic = "${var.env}.${var.kafka_telemetry_raw_topic}" + dialcode_context_job_request_topic = "${var.env}.${var.kafka_dialcode_context_job_request_topic}" + dialcode_context_job_request_failed_topic = "${var.env}.${var.kafka_dialcode_context_job_request_failed_topic}" + } + ) + ] +} + + + + + + + + + + + + diff --git a/knowlg-automation/terraform/helm/kafka_exporter.tf b/knowlg-automation/terraform/helm/kafka_exporter.tf new file mode 100644 index 000000000..626520cad --- /dev/null +++ b/knowlg-automation/terraform/helm/kafka_exporter.tf @@ -0,0 +1,15 @@ +resource "helm_release" "kafka_exporter" { + name = var.kafka_exporter_release_name + chart = var.kafka_exporter_chart_path + namespace = var.kafka_exporter_namespace + create_namespace = var.kafka_exporter_create_namespace + wait_for_jobs = var.kafka_exporter_wait_for_jobs + depends_on = [helm_release.druid_cluster,helm_release.kafka] + values = [ + templatefile(var.kafka_exporter_chart_template, + { + kafka_exporter_namespace = var.kafka_exporter_namespace + } + ) + ] +} \ No newline at end of file diff --git a/knowlg-automation/terraform/helm/kafka_exporter_vars.tf b/knowlg-automation/terraform/helm/kafka_exporter_vars.tf new file mode 100644 index 000000000..1ad0d6c13 --- /dev/null +++ b/knowlg-automation/terraform/helm/kafka_exporter_vars.tf @@ -0,0 +1,35 @@ +variable "kafka_exporter_release_name" { + type = string + description = "Kafka exporter helm release name." + default = "kafka-exporter" +} + +variable "kafka_exporter_namespace" { + type = string + description = "Kafka exporter namespace." + default = "kafka" +} + +variable "kafka_exporter_chart_path" { + type = string + description = "Kafka exporter chart path." + default = "../../helm_charts/kafka-exporter" +} + +variable "kafka_exporter_create_namespace" { + type = bool + description = "Create kakfa exporter namespace." + default = true +} + +variable "kafka_exporter_wait_for_jobs" { + type = bool + description = "Kafka exporter wait for jobs paramater." + default = true +} + +variable "kafka_exporter_chart_template" { + type = string + default = "../../helm_charts/kafka-exporter/values.yaml" + +} \ No newline at end of file diff --git a/knowlg-automation/terraform/helm/kafka_vars.tf b/knowlg-automation/terraform/helm/kafka_vars.tf new file mode 100644 index 000000000..f60314c9a --- /dev/null +++ b/knowlg-automation/terraform/helm/kafka_vars.tf @@ -0,0 +1,107 @@ +variable "kafka_release_name" { + type = string + description = "Kafka helm release name." + default = "kafka" +} + +variable "kafka_namespace" { + type = string + description = "Kafka namespace." + default = "knowlg-db" +} + +variable "kafka_chart_path" { + type = string + description = "Kafka chart path." + default = "../../helm_charts/kafka" +} + +variable "kafka_chart_install_timeout" { + type = number + description = "Kafka chart install timeout." + default = 3000 +} + +variable "kafka_create_namespace" { + type = bool + description = "Create kafka namespace." + default = true +} + +variable "kafka_wait_for_jobs" { + type = bool + description = "Kafka wait for jobs paramater." + default = true +} + +variable "kafka_chart_custom_values_yaml" { + type = string + description = "Kafka chart values.yaml path." + default = "../../helm_charts/kafka/values.yaml" +} + +variable "kafka_chart_dependecy_update" { + type = bool + description = "Kafka chart dependency update." + default = true +} + +variable "kafka_content_publish_topic" { + type = string + description = "Kafka content publish topic." + default = "publish.job.request" +} + +variable "kafka_content_postpublish_topic" { + type = string + description = "Kafka content postpublish topic" + default = "content.postpublish.request" +} + +variable "kafka_learning_job_request_topic" { + type = string + description = "Kafka learning_job_request topic" + default = "learning_job_request" +} + +variable "kafka_learning_graph_events_topic" { + type = string + description = "Kafka learning.graph.events topic" + default = "learning.graph.events" +} + +variable "kafka_learning_events_failed_topic" { + type = string + description = "Kafka learning.events.failed topic" + default = "learning.events.failed" +} + +variable "kafka_search_indexer_group_topic" { + type = string + description = "Kafka search-indexer-group topic" + default = "search-indexer-group" +} + +variable "kafka_qrimage_request_topic" { + type = string + description = "Kafka qrimage.request topic" + default = "qrimage.request" +} + +variable "kafka_telemetry_raw_topic" { + type = string + description = "Kafka telemetry.raw topic" + default = "telemetry.raw" +} + +variable "kafka_dialcode_context_job_request_topic" { + type = string + description = "Kafka dialcode.context.job.request topic" + default = "dialcode.context.job.request" +} + +variable "kafka_dialcode_context_job_request_failed_topic" { + type = string + description = "Kafka dialcode.context.job.request topic" + default = "dialcode.context.job.request.failed" +} \ No newline at end of file diff --git a/knowlg-automation/terraform/local/kafka-provision.tf b/knowlg-automation/terraform/local/kafka-provision.tf index 9fe395b1d..b393f9de6 100644 --- a/knowlg-automation/terraform/local/kafka-provision.tf +++ b/knowlg-automation/terraform/local/kafka-provision.tf @@ -32,11 +32,16 @@ resource "helm_release" "kafka" { # zookeeper_enabled: true # zookeeper_heapsize: 256 # zookeeper_replica_count: 1 - input_topic = "dev.telemetry.denorm" - output_telemetry_route_topic = "dev.druid.events.telemetry" - output_summary_route_topic = "dev.druid.events.summary" - output_failed_topic = "dev.telemetry.failed" - output_duplicate_topic = "dev.telemetry.duplicate" + content_publish_topic_topic = "dev.publish.job.request" + content_postpublish_topic = "dev.content.postpublish.request" + learning_job_request_topic = "dev.learning_job_request" + learning_graph_events_topic = "dev.learning.graph.events" + learning_events_failed_topic = "dev.learning.events.failed" + search_indexer_group_topic = "dev.search-indexer-group" + qrimage_request_topic = "dev.qrimage.request" + telemetry_raw_topic = "dev.telemetry.raw" + dialcode_context_job_request_topic = "dev.dialcode.context.job.request" + dialcode_context_job_request_failed_topic = "dev.dialcode.context.job.request.failed" } ) ] diff --git a/knowlg-automation/terraform/local/vars.tf b/knowlg-automation/terraform/local/vars.tf index 195576ad4..8829a7ebb 100644 --- a/knowlg-automation/terraform/local/vars.tf +++ b/knowlg-automation/terraform/local/vars.tf @@ -45,20 +45,18 @@ variable "KAFKA_NAMESPACE" { default = "knowlg-db" } -#ELASTICSEARCH -variable "ELASTICSEARCH_CHART" { - description = "Elasticsearch Instance Running Namespace" - default = "../../helm_charts/elasticsearch" +#NEO4J +variable "NEO4J_CHART" { + description = "Neo4j Instance Running Namespace" + default = "../../helm_charts/neo4j" } -variable "ELASTICSEARCH_NAMESPACE" { - description = "Elasticsearch Instance Running Namespace" +variable "NEO4J_NAMESPACE" { + description = "NEO4J Instance Running Namespace" default = "knowlg-db" } - #CASSANDRA - variable "CASSANDRA_CHART" { description = "Cassandra Instance Running Namespace" default = "../../helm_charts/cassandra" @@ -69,20 +67,18 @@ variable "CASSANDRA_NAMESPACE" { default = "knowlg-db" } -#NEO4J - -variable "NEO4J_CHART" { - description = "Neo4j Instance Running Namespace" - default = "../../helm_charts/neo4j" +#ELASTICSEARCH +variable "ELASTICSEARCH_CHART" { + description = "Elasticsearch Instance Running Namespace" + default = "../../helm_charts/elasticsearch" } -variable "NEO4J_NAMESPACE" { - description = "NEO4J Instance Running Namespace" +variable "ELASTICSEARCH_NAMESPACE" { + description = "Elasticsearch Instance Running Namespace" default = "knowlg-db" } #REDIS - variable "REDIS_CHART" { description = "Redis Instance Running Namespace" default = "../../helm_charts/redis" From 8f305a1ee0dd8dc6bed8da2fa014d72d2cbce324 Mon Sep 17 00:00:00 2001 From: Kartheek Palla Date: Tue, 11 Apr 2023 19:05:45 +0530 Subject: [PATCH 208/490] Issue #KN-767 fix: dial services helm charts added --- knowlg-automation/helm_charts/dial/Chart.yaml | 5 + .../dial/dial-service_application.conf | 189 ++++++++++++++ .../helm_charts/dial/dial-service_logback.xml | 28 +++ .../dial/templates/deployment.yaml | 75 ++++++ .../helm_charts/dial/templates/dial-configmap | 234 ++++++++++++++++++ .../helm_charts/dial/values.yaml | 34 +++ .../terraform/azure/dial-provision.tf | 10 + .../terraform/azure/variables.tf | 14 +- .../terraform/local/dial-provision.tf | 10 + .../terraform/local/kafka-provision.tf | 2 +- knowlg-automation/terraform/local/vars.tf | 11 + 11 files changed, 610 insertions(+), 2 deletions(-) create mode 100644 knowlg-automation/helm_charts/dial/Chart.yaml create mode 100644 knowlg-automation/helm_charts/dial/dial-service_application.conf create mode 100644 knowlg-automation/helm_charts/dial/dial-service_logback.xml create mode 100644 knowlg-automation/helm_charts/dial/templates/deployment.yaml create mode 100644 knowlg-automation/helm_charts/dial/templates/dial-configmap create mode 100644 knowlg-automation/helm_charts/dial/values.yaml create mode 100644 knowlg-automation/terraform/azure/dial-provision.tf create mode 100644 knowlg-automation/terraform/local/dial-provision.tf diff --git a/knowlg-automation/helm_charts/dial/Chart.yaml b/knowlg-automation/helm_charts/dial/Chart.yaml new file mode 100644 index 000000000..57154c0f8 --- /dev/null +++ b/knowlg-automation/helm_charts/dial/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: dial +version: 0.1.0 \ No newline at end of file diff --git a/knowlg-automation/helm_charts/dial/dial-service_application.conf b/knowlg-automation/helm_charts/dial/dial-service_application.conf new file mode 100644 index 000000000..a82686311 --- /dev/null +++ b/knowlg-automation/helm_charts/dial/dial-service_application.conf @@ -0,0 +1,189 @@ +# This is the main configuration file for the application. +# https://www.playframework.com/documentation/latest/ConfigFile +# ~~~~~ + +## Akka +# https://www.playframework.com/documentation/latest/JavaAkka#Configuration +# ~~~~~ +akka { + #loggers =["akka.event.Logging$DefaultLogger"] + #log-config-on-start = true +} + +## Secret key +# http://www.playframework.com/documentation/latest/ApplicationSecret +# ~~~~~ +#play.crypto.secret = "changeme" + + +## Internationalisation +# https://www.playframework.com/documentation/latest/JavaI18N +# ~~~~~ +play.i18n { + # The application languages + langs = [ "en" ] +} + +## Play HTTP settings +# ~~~~~ +play.http { + ## Router + # https://www.playframework.com/documentation/latest/JavaRouting + # https://www.playframework.com/documentation/latest/ScalaRouting + # ~~~~~ + # Define the Router object to use for this application. + # This router will be looked up first when the application is starting up, + # so make sure this is the entry point. + # Furthermore, it's assumed your route file is named properly. + # So for an application router like `my.application.Router`, + # you may need to define a router file `conf/my.application.routes`. + # Default to Routes in the root package (aka "apps" folder) (and conf/routes) + #router = my.application.Router + #TO allow more data in request body + parser.maxDiskBuffer=50MB + parser.maxMemoryBuffer=50MB +} + +## Netty Provider +# ~~~~~ +play.server.netty { + # If you run Play on Linux, you can use Netty's native socket transport + # for higher performance with less garbage. + #transport = "native" + maxChunkSize = 30000000 +} + +## WS (HTTP Client) +# ~~~~~ +libraryDependencies += javaWs + +## Cache +# https://www.playframework.com/documentation/latest/JavaCache +# https://www.playframework.com/documentation/latest/ScalaCache +# ~~~~~ +# Play comes with an integrated cache API that can reduce the operational +# overhead of repeated requests. You must enable this by adding to build.sbt: +# +libraryDependencies += cache +# +play.cache { + # If you want to bind several caches, you can bind the individually + #bindCaches = ["db-cache", "user-cache", "session-cache"] +} + +//play.http.filters= filters.HealthCheckFilter + +# Logger +# ~~~~~ +# You can also configure logback (http://logback.qos.ch/), +# by providing an application-logger.xml file in the conf directory. + +# Root logger: +#logger.root=ERROR + +# Logger used by the framework: +#logger.play=INFO + +# Logger provided to your application: +#logger.application=DEBUG + +# APP Specific config +# ~~~~~ +# Application specific configurations can be provided here +application.global=Global + +play.http.parser.maxMemoryBuffer = 900000K +play.http.parser.maxDiskBuffer = 900000K + +play.server.http.port = 9000 +# Logger +# ~~~~~ +# You can also configure logback (http://logback.qos.ch/), +# by providing an application-logger.xml file in the conf directory. + +# Root logger: +logger.root=ERROR + +# Logger used by the framework: +logger.play=INFO + +# Logger provided to your application: +logger.application=DEBUG + +# APP Specific config +# ~~~~~ +# Application specific configurations can be provided here + +# Cache-Manager Configuration +cache.type="redis" + + +search.es_conn_info="{{ .Values.es_connection }}" +search.fields.query=["name^100","title^100","lemma^100","code^100","tags^100","question^100","domain","subject","description^10","keywords^25","ageGroup^10","filter^10","theme^10","genre^10","objects^25","contentType^100","language^200","teachingMode^25","skills^10","learningObjective^10","curriculum^100","gradeLevel^100","developer^100","attributions^10","owner^50","text","words","releaseNotes","body"] +search.fields.date=["lastUpdatedOn","createdOn","versionDate","lastSubmittedOn","lastPublishedOn"] +search.batch.size=500 +search.connection.timeout=30 + +#Top N Config for Search Telemetry +telemetry.search.topn=5 +telemetry_env=dev +installation.id=ekstep + +# Configuration for default channel ID +channel.default="in.ekstep" + +# Redis Configuration +redis.host="{{ .Values.redis_connection}}" +redis.port=6379 +redis.maxConnections=128 + +# DIAL Code Configuration +dialcode.keyspace.name="{{ .Values.dialcode_keyspace_name }}" +dialcode.keyspace.table="{{ .Values.dialcode_keyspace_table }}" +dialcode.max_count=1000 + +# System Configuration +system.config.keyspace.name="{{ .Values.system_config_keyspace_name}}" +system.config.table="{{ .Values.system_config_table}}" + +#Publisher Configuration +publisher.keyspace.name="{{ .Values.system_config_table}}" +publisher.keyspace.table="{{ .Values.publisher_keyspace_table}}" + +#DIAL Code Generator Configuration +dialcode.strip.chars="0" +dialcode.length=6.0 +dialcode.large.prime_number=1679979167 + +#DIAL Code ElasticSearch Configuration +dialcode.index=true +dialcode.object_type="{{ .Values.dialocode_object_type}}" + +# Cassandra Configuration +cassandra.lp.connection="{{ .Values.cassandra_connection }}" +cassandra.lpa.connection="{{ .Values.cassandra_connection }}" + + +# Consistency Level for Multi Node Cassandra cluster +#cassandra.lp.consistency.level=QUORUM + +#Kafka Configuration +kafka { + urls = "{{ .Values.kafka_connection}}" + topic { + send_enable = false + graph_event = "{{ .Values.graph_event}}" + } +} + +jsonld { + basePath = "https://sunbirdstagingpublic.blob.core.windows.net/sunbird-dial-staging/jsonld-schema/local" + type = "sbed" + localPath = "/tmp" + ttl = 43200 + sb_schema = ["http://store.knowlg.sunbird.org/dial/specs/sbed/schema.jsonld"] +} + +dial_id = "https://dev.sunbirded.org/dial/{dialcode}" +dial_type = "https://dev.sunbirded.org/ns/" + diff --git a/knowlg-automation/helm_charts/dial/dial-service_logback.xml b/knowlg-automation/helm_charts/dial/dial-service_logback.xml new file mode 100644 index 000000000..73529d622 --- /dev/null +++ b/knowlg-automation/helm_charts/dial/dial-service_logback.xml @@ -0,0 +1,28 @@ + + + + + + + + + + %d %msg%n + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/knowlg-automation/helm_charts/dial/templates/deployment.yaml b/knowlg-automation/helm_charts/dial/templates/deployment.yaml new file mode 100644 index 000000000..f64a4e2a8 --- /dev/null +++ b/knowlg-automation/helm_charts/dial/templates/deployment.yaml @@ -0,0 +1,75 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Chart.Name }} + namespace: {{ .Values.api_namespace }} + annotations: + reloader.stakater.com/auto: "true" +spec: + replicas: {{ .Values.replicaCount }} + strategy: + rollingUpdate: + maxSurge: {{ .Values.strategy.maxsurge }} + maxUnavailable: {{ .Values.strategy.maxunavailable }} + selector: + matchLabels: + app: {{ .Chart.Name }} + template: + metadata: + labels: + app: {{ .Chart.Name }} + spec: + volumes: + - name: {{ .Chart.Name }}-config + configMap: + name: {{ .Chart.Name }}-config + - name: {{ .Chart.Name }}-xml-config + configMap: + name: {{ .Chart.Name }}-xml-config + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.repository }}:{{ .Values.image_tag }}" + imagePullPolicy: Always + env: + - name: JAVA_OPTIONS + value: {{ .Values.env.javaoptions | quote }} + - name: _JAVA_OPTIONS + value: -Dlog4j2.formatMsgNoLookups=true + envFrom: + - configMapRef: + name: {{ .Chart.Name }}-config + resources: +{{ toYaml .Values.resources | indent 10 }} + ports: + - containerPort: {{ .Values.network.port }} + {{- if .Values.healthcheck }} + livenessProbe: +{{ toYaml .Values.livenessProbe | indent 10 }} + readinessProbe: +{{ toYaml .Values.readinessProbe | indent 10 }} + {{- end }} + volumeMounts: + - name: {{ .Chart.Name }}-config + mountPath: /home/sunbird/sunbird-dial-service-1.0-SNAPSHOT/config/application.conf + subPath: dial-service_application.conf + - name: {{ .Chart.Name }}-xml-config + mountPath: /home/sunbird/sunbird-dial-service-1.0-SNAPSHOT/config/logback.xml + subPath: dial-service_logback.xml + +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ .Chart.Name }}-service + namespace: {{ .Values.api_namespace }} + labels: + app: {{ .Chart.Name }} +spec: + type: {{ .Values.serviceType }} + ports: + - name: http-{{ .Chart.Name }} + protocol: TCP + port: {{ .Values.network.targetport }} + selector: + app: {{ .Chart.Name }} diff --git a/knowlg-automation/helm_charts/dial/templates/dial-configmap b/knowlg-automation/helm_charts/dial/templates/dial-configmap new file mode 100644 index 000000000..646e5f5fe --- /dev/null +++ b/knowlg-automation/helm_charts/dial/templates/dial-configmap @@ -0,0 +1,234 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: dial-xml-config + namespace: knowlg-api +data: + dial-service_logback.xml: | + + + + + + + + + + %d %msg%n + + + + + + + + + + + + + + + + + + + + +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: dial-config + namespace: knowlg-api +data: + dial-service_application.conf: |- + # This is the main configuration file for the application. + # https://www.playframework.com/documentation/latest/ConfigFile + # ~~~~~ + + ## Akka + # https://www.playframework.com/documentation/latest/JavaAkka#Configuration + # ~~~~~ + akka { + #loggers =["akka.event.Logging$DefaultLogger"] + #log-config-on-start = true + } + + ## Secret key + # http://www.playframework.com/documentation/latest/ApplicationSecret + # ~~~~~ + #play.crypto.secret = "changeme" + + + ## Internationalisation + # https://www.playframework.com/documentation/latest/JavaI18N + # ~~~~~ + play.i18n { + # The application languages + langs = [ "en" ] + } + + ## Play HTTP settings + # ~~~~~ + play.http { + ## Router + # https://www.playframework.com/documentation/latest/JavaRouting + # https://www.playframework.com/documentation/latest/ScalaRouting + # ~~~~~ + # Define the Router object to use for this application. + # This router will be looked up first when the application is starting up, + # so make sure this is the entry point. + # Furthermore, it's assumed your route file is named properly. + # So for an application router like `my.application.Router`, + # you may need to define a router file `conf/my.application.routes`. + # Default to Routes in the root package (aka "apps" folder) (and conf/routes) + #router = my.application.Router + #TO allow more data in request body + parser.maxDiskBuffer=50MB + parser.maxMemoryBuffer=50MB + } + + ## Netty Provider + # ~~~~~ + play.server.netty { + # If you run Play on Linux, you can use Netty's native socket transport + # for higher performance with less garbage. + #transport = "native" + maxChunkSize = 30000000 + } + + ## WS (HTTP Client) + # ~~~~~ + libraryDependencies += javaWs + + ## Cache + # https://www.playframework.com/documentation/latest/JavaCache + # https://www.playframework.com/documentation/latest/ScalaCache + # ~~~~~ + # Play comes with an integrated cache API that can reduce the operational + # overhead of repeated requests. You must enable this by adding to build.sbt: + # + libraryDependencies += cache + # + play.cache { + # If you want to bind several caches, you can bind the individually + #bindCaches = ["db-cache", "user-cache", "session-cache"] + } + + //play.http.filters= filters.HealthCheckFilter + + # Logger + # ~~~~~ + # You can also configure logback (http://logback.qos.ch/), + # by providing an application-logger.xml file in the conf directory. + + # Root logger: + #logger.root=ERROR + + # Logger used by the framework: + #logger.play=INFO + + # Logger provided to your application: + #logger.application=DEBUG + + # APP Specific config + # ~~~~~ + # Application specific configurations can be provided here + application.global=Global + + play.http.parser.maxMemoryBuffer = 900000K + play.http.parser.maxDiskBuffer = 900000K + + play.server.http.port = 9000 + # Logger + # ~~~~~ + # You can also configure logback (http://logback.qos.ch/), + # by providing an application-logger.xml file in the conf directory. + + # Root logger: + logger.root=ERROR + + # Logger used by the framework: + logger.play=INFO + + # Logger provided to your application: + logger.application=DEBUG + + # APP Specific config + # ~~~~~ + # Application specific configurations can be provided here + + # Cache-Manager Configuration + cache.type="redis" + + + search.es_conn_info="{{ .Values.es_connection }}" + search.fields.query=["name^100","title^100","lemma^100","code^100","tags^100","question^100","domain","subject","description^10","keywords^25","ageGroup^10","filter^10","theme^10","genre^10","objects^25","contentType^100","language^200","teachingMode^25","skills^10","learningObjective^10","curriculum^100","gradeLevel^100","developer^100","attributions^10","owner^50","text","words","releaseNotes","body"] + search.fields.date=["lastUpdatedOn","createdOn","versionDate","lastSubmittedOn","lastPublishedOn"] + search.batch.size=500 + search.connection.timeout=30 + + #Top N Config for Search Telemetry + telemetry.search.topn=5 + telemetry_env=dev + installation.id=ekstep + + # Configuration for default channel ID + channel.default="in.ekstep" + + # Redis Configuration + redis.host="{{ .Values.redis_connection}}" + redis.port=6379 + redis.maxConnections=128 + + # DIAL Code Configuration + dialcode.keyspace.name="{{ .Values.dialcode_keyspace_name }}" + dialcode.keyspace.table="{{ .Values.dialcode_keyspace_table }}" + dialcode.max_count=1000 + + # System Configuration + system.config.keyspace.name="{{ .Values.system_config_keyspace_name}}" + system.config.table="{{ .Values.system_config_table}}" + + #Publisher Configuration + publisher.keyspace.name="{{ .Values.system_config_table}}" + publisher.keyspace.table="{{ .Values.publisher_keyspace_table}}" + + #DIAL Code Generator Configuration + dialcode.strip.chars="0" + dialcode.length=6.0 + dialcode.large.prime_number=1679979167 + + #DIAL Code ElasticSearch Configuration + dialcode.index=true + dialcode.object_type="{{ .Values.dialocode_object_type}}" + + # Cassandra Configuration + cassandra.lp.connection="{{ .Values.cassandra_connection }}" + cassandra.lpa.connection="{{ .Values.cassandra_connection }}" + + + # Consistency Level for Multi Node Cassandra cluster + #cassandra.lp.consistency.level=QUORUM + + #Kafka Configuration + kafka { + urls = "{{ .Values.kafka_connection}}" + topic { + send_enable = false + graph_event = "{{ .Values.graph_event}}" + } + } + + jsonld { + basePath = "https://sunbirdstagingpublic.blob.core.windows.net/sunbird-dial-staging/jsonld-schema/local" + type = "sbed" + localPath = "/tmp" + ttl = 43200 + sb_schema = ["http://store.knowlg.sunbird.org/dial/specs/sbed/schema.jsonld"] + } + + dial_id = "https://dev.sunbirded.org/dial/{dialcode}" + dial_type = "https://dev.sunbirded.org/ns/" + diff --git a/knowlg-automation/helm_charts/dial/values.yaml b/knowlg-automation/helm_charts/dial/values.yaml new file mode 100644 index 000000000..9c8ec61ad --- /dev/null +++ b/knowlg-automation/helm_charts/dial/values.yaml @@ -0,0 +1,34 @@ +api_namespace: knowlg-api + +env: + javaoptions: -Xmx600m + +replicaCount: 1 +repository: docker.io/aimansharief/dial-service +image_tag: R5.2.0 +resources: + requests: + cpu: 100m + memory: 100Mi + limits: + cpu: 1 + memory: 1024Mi +network: + port: 9000 + targetport: 9000 +strategy: + type: RollingUpdate + maxsurge: 25% + maxunavailable: 25% +serviceType: ClusterIP +cassandra_connection: cassandra-db.knowlg-db.svc.cluster.local:9042 +redis_connection: redis-db.knowlg-db.svc.cluster.local +es_connection: elasticsearch.knowlg-db.svc.cluster.local:9200 +dialcode_keyspace_name: dialcode_store +dialcode_keyspace_table: dial_code +systemconfig_keyspace_name: dialcode_store +systemconfig_table: system_config +publisher_keyspace_name: dialcode_store +publisher_keyspace_table: publisher +dialcode_object_type: DialCode +graph_event: local.learning.graph.events \ No newline at end of file diff --git a/knowlg-automation/terraform/azure/dial-provision.tf b/knowlg-automation/terraform/azure/dial-provision.tf new file mode 100644 index 000000000..fd17c7630 --- /dev/null +++ b/knowlg-automation/terraform/azure/dial-provision.tf @@ -0,0 +1,10 @@ +resource "helm_release" "dial" { + name = "dial" + chart = var.DIAL_CHART + namespace = var.DIAL_NAMESPACE + create_namespace = true + dependency_update = true + depends_on = [helm_release.learning] + wait_for_jobs = true + +} \ No newline at end of file diff --git a/knowlg-automation/terraform/azure/variables.tf b/knowlg-automation/terraform/azure/variables.tf index 586f84cea..a3c3ccd56 100644 --- a/knowlg-automation/terraform/azure/variables.tf +++ b/knowlg-automation/terraform/azure/variables.tf @@ -186,4 +186,16 @@ variable "LEARNING_CHART" { variable "LEARNING_NAMESPACE" { description = "Learning Instance Running Namespace" default = "knowlg-api" -} \ No newline at end of file +} + +#DIAL +variable "DIAL_CHART" { + description = "DIAL Instance Running Namespace" + default = "../../helm_charts/dial" +} + +variable "DIAL_NAMESPACE" { + description = "DIAL Instance Running Namespace" + default = "knowlg-api" +} + diff --git a/knowlg-automation/terraform/local/dial-provision.tf b/knowlg-automation/terraform/local/dial-provision.tf new file mode 100644 index 000000000..f26fe3c67 --- /dev/null +++ b/knowlg-automation/terraform/local/dial-provision.tf @@ -0,0 +1,10 @@ +resource "helm_release" "dial" { + name = "dial" + chart = var.DIAL_CHART + namespace = var.DIAL_NAMESPACE + create_namespace = true + dependency_update = true + depends_on = [kind_cluster.one-click] + wait_for_jobs = true + +} \ No newline at end of file diff --git a/knowlg-automation/terraform/local/kafka-provision.tf b/knowlg-automation/terraform/local/kafka-provision.tf index b393f9de6..5d283023a 100644 --- a/knowlg-automation/terraform/local/kafka-provision.tf +++ b/knowlg-automation/terraform/local/kafka-provision.tf @@ -32,7 +32,7 @@ resource "helm_release" "kafka" { # zookeeper_enabled: true # zookeeper_heapsize: 256 # zookeeper_replica_count: 1 - content_publish_topic_topic = "dev.publish.job.request" + content_publish_topic = "dev.publish.job.request" content_postpublish_topic = "dev.content.postpublish.request" learning_job_request_topic = "dev.learning_job_request" learning_graph_events_topic = "dev.learning.graph.events" diff --git a/knowlg-automation/terraform/local/vars.tf b/knowlg-automation/terraform/local/vars.tf index 8829a7ebb..76fc791d1 100644 --- a/knowlg-automation/terraform/local/vars.tf +++ b/knowlg-automation/terraform/local/vars.tf @@ -134,3 +134,14 @@ variable "LEARNING_NAMESPACE" { default = "knowlg-api" } +#DIAL +variable "DIAL_CHART" { + description = "DIAL Instance Running Namespace" + default = "../../helm_charts/dial" +} + +variable "DIAL_NAMESPACE" { + description = "DIAL Instance Running Namespace" + default = "knowlg-api" +} + From 008483e45c97bf5ace87f18d552fdf9ad8aa3339 Mon Sep 17 00:00:00 2001 From: aimansharief <110388201+aimansharief@users.noreply.github.com> Date: Wed, 12 Apr 2023 11:58:02 +0530 Subject: [PATCH 209/490] Issue #KN-767 fix: Updated README.md file --- knowlg-automation/README.md | 68 ++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 31 deletions(-) diff --git a/knowlg-automation/README.md b/knowlg-automation/README.md index 0045039ca..021b06694 100644 --- a/knowlg-automation/README.md +++ b/knowlg-automation/README.md @@ -1,33 +1,39 @@ # sunbird-infra-provision -Infra provision on Azure - -Pre-requisites: -Install azure cli(az) tool on local machine and login to azure account with "az login" command. -Create a azure service account to be used for the infra provision. -Copy the ingestion spec to any folder and share the path in variables -Replace the default values in infra-provision/azure/vars.tf . - -Steps: -Run create shell script and provide option input as "azure" . - - -Destroy infra on Azure -Steps: -Run destroy shell script with option as "azure" and append --auto-approve to continue without confirmation. - - -Infra provision on local - -Pre-requisites: -For local provision, kind provider is used to provision the cluster. -Steps:Run shell script and provide option as "local" -Create infra on local -Steps: -cd terraform -sh create --auto-approve - -Destroy infra on local -Steps: -cd terraform -sh destroy --auto-approve +## Infra provision on azure +### Pre-requisites: +* Install azure cli(az) tool on local machine and login to azure account with "az login" command. +* Create a azure service account to be used for the infra provision. +* Replace the default values in infra-provision/azure/variables.tf . + +>*[ Go to the repository path: knowledge-platform/knowlg-automation/terraform ]* +### Create Infra on azure: +```shell +sh create +``` +> *provide option as "azure"* +### Destroy Infra on azure: +***Note:** [append --auto-approve to continue without confirmation.]* +```shell +sh destroy +``` +>*provide option as "azure"* + + +## Infra provision on local +### Pre-requisites: +* Terraform to be installed . +* For local provision, kind provider is used to provision the cluster. + +>*[ Go to the repository path: knowledge-platform/knowlg-automation/terraform ]* +### Create Infra on local: +```shell +sh create +``` +> *provide option as "local"* +### Destroy Infra on local: +***Note:** [append --auto-approve to continue without confirmation.]* +```shell +sh destroy +``` +>*provide option as "local"* From 984d1a8b083c3d54ad985fbff8b90a308fc80eb5 Mon Sep 17 00:00:00 2001 From: aimansharief <110388201+aimansharief@users.noreply.github.com> Date: Thu, 20 Apr 2023 11:13:23 +0530 Subject: [PATCH 210/490] Issue #KN-767 fix:Added Flink Helm Chart and Terraform script --- .../helm_charts/elasticsearch/values.yaml | 2 +- .../helm_charts/flink/.helmignore | 22 + .../helm_charts/flink/Chart.yaml | 21 + .../helm_charts/flink/templates/_helpers.tpl | 67 ++ .../flink/templates/flink_job_configmap.yaml | 14 + .../flink/templates/flink_job_deployment.yaml | 202 ++++++ .../helm_charts/flink/values.yaml | 651 ++++++++++++++++++ .../helm_charts/kafka/values.yaml | 2 +- .../terraform/local/flink-provision.tf | 9 + .../terraform/local/kafka-provision.tf | 7 +- knowlg-automation/terraform/local/vars.tf | 52 +- 11 files changed, 1044 insertions(+), 5 deletions(-) create mode 100644 knowlg-automation/helm_charts/flink/.helmignore create mode 100644 knowlg-automation/helm_charts/flink/Chart.yaml create mode 100644 knowlg-automation/helm_charts/flink/templates/_helpers.tpl create mode 100644 knowlg-automation/helm_charts/flink/templates/flink_job_configmap.yaml create mode 100644 knowlg-automation/helm_charts/flink/templates/flink_job_deployment.yaml create mode 100644 knowlg-automation/helm_charts/flink/values.yaml create mode 100644 knowlg-automation/terraform/local/flink-provision.tf diff --git a/knowlg-automation/helm_charts/elasticsearch/values.yaml b/knowlg-automation/helm_charts/elasticsearch/values.yaml index 190288d45..55ec460d5 100644 --- a/knowlg-automation/helm_charts/elasticsearch/values.yaml +++ b/knowlg-automation/helm_charts/elasticsearch/values.yaml @@ -186,7 +186,7 @@ useIstioLabels: true image: registry: docker.io repository: bitnami/elasticsearch - tag: 8.6.2-debian-11-r10 + tag: 6.8.22 digest: "" ## Specify a imagePullPolicy ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' diff --git a/knowlg-automation/helm_charts/flink/.helmignore b/knowlg-automation/helm_charts/flink/.helmignore new file mode 100644 index 000000000..50af03172 --- /dev/null +++ b/knowlg-automation/helm_charts/flink/.helmignore @@ -0,0 +1,22 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/knowlg-automation/helm_charts/flink/Chart.yaml b/knowlg-automation/helm_charts/flink/Chart.yaml new file mode 100644 index 000000000..31dcba4d2 --- /dev/null +++ b/knowlg-automation/helm_charts/flink/Chart.yaml @@ -0,0 +1,21 @@ +apiVersion: v2 +name: flink +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. +appVersion: 1.16.0 diff --git a/knowlg-automation/helm_charts/flink/templates/_helpers.tpl b/knowlg-automation/helm_charts/flink/templates/_helpers.tpl new file mode 100644 index 000000000..18df42085 --- /dev/null +++ b/knowlg-automation/helm_charts/flink/templates/_helpers.tpl @@ -0,0 +1,67 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "datapipeline_jobs.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{- define "name" -}} +{{- default .Release.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "datapipeline_jobs.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "datapipeline_jobs.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Common labels +*/}} +{{- define "datapipeline_jobs.labels" -}} +helm.sh/chart: {{ include "datapipeline_jobs.chart" . }} +{{ include "datapipeline_jobs.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end -}} + +{{/* +Selector labels +*/}} +{{- define "datapipeline_jobs.selectorLabels" -}} +app.kubernetes.io/name: {{ include "datapipeline_jobs.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} + +{{/* +Create the name of the service account to use +*/}} +{{- define "datapipeline_jobs.serviceAccountName" -}} +{{- if .Values.serviceAccount.create -}} + {{ default (include "datapipeline_jobs.fullname" .) .Values.serviceAccount.name }} +{{- else -}} + {{ default "default" .Values.serviceAccount.name }} +{{- end -}} +{{- end -}} diff --git a/knowlg-automation/helm_charts/flink/templates/flink_job_configmap.yaml b/knowlg-automation/helm_charts/flink/templates/flink_job_configmap.yaml new file mode 100644 index 000000000..6f3356860 --- /dev/null +++ b/knowlg-automation/helm_charts/flink/templates/flink_job_configmap.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ .Release.Name }}-config + namespace: {{ .Values.namespace }} + labels: + app: flink +data: + base-config: |+ +{{ .Values.base_config | indent 4 }} +{{- $name := .Release.Name }} +{{ index .Values $name | toYaml | indent 2 }} + log4j_console_properties: |+ +{{ .Values.log4j_console_properties | indent 4 }} \ No newline at end of file diff --git a/knowlg-automation/helm_charts/flink/templates/flink_job_deployment.yaml b/knowlg-automation/helm_charts/flink/templates/flink_job_deployment.yaml new file mode 100644 index 000000000..d1ec58710 --- /dev/null +++ b/knowlg-automation/helm_charts/flink/templates/flink_job_deployment.yaml @@ -0,0 +1,202 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ .Release.Name }}-jobmanager + namespace: {{ .Values.namespace }} + labels: + app: flink + component: {{ .Release.Name }}-jobmanager +spec: + type: ClusterIP + ports: + - name: rpc + port: {{ .Values.jobmanager.rpc_port }} + - name: blob + port: {{ .Values.jobmanager.blob_port }} + - name: query + port: {{ .Values.jobmanager.query_port }} + - name: ui + port: {{ .Values.jobmanager.ui_port }} + - name: prom + port: {{ .Values.jobmanager.prom_port }} + selector: + app: flink + component: {{ .Release.Name }}-jobmanager +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ .Release.Name }}-jobmanager-webui + namespace: {{ .Values.namespace }} + {{- if .Values.service.annotations }} + {{- with .Values.service.annotations }} +annotations: + {{ toYaml . | indent 4 }} + {{- end }} + {{- end }} +spec: + {{- if eq .Values.service.type "ClusterIP" }} + type: ClusterIP + {{- end }} + {{- if eq .Values.service.type "LoadBalancer" }} + type: LoadBalancer + {{- end }} + ports: + - name: rest + port: {{ .Values.rest_port }} + protocol: TCP + targetPort: {{ .Values.resttcp_port }} + selector: + app: flink + component: {{ .Release.Name }}-jobmanager +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ .Release.Name }}-jobmanager + namespace: {{ .Values.namespace }} +spec: + template: + metadata: + labels: + app: flink + component: {{ .Release.Name }}-jobmanager + annotations: + prometheus.io/scrape: 'true' + prometheus.io/port: "{{ .Values.jobmanager.prom_port }}" + spec: + volumes: + - name: flink-config-volume + configMap: + name: {{ .Release.Name }}-config + items: + - key: flink-conf + path: flink-conf.yaml + - key: base-config + path: base-config.conf + - key: {{ .Release.Name }} + path: {{ .Release.Name }}.conf + - key: log4j_console_properties + path: log4j-console.properties + restartPolicy: OnFailure +# imagePullSecrets: +# - name: {{ .Values.imagepullsecrets }} + containers: + - name: {{ .Release.Name }}-jobmanager + image: "{{ .Values.repository }}:{{ .Values.image_tag }}" + imagePullPolicy: Always + workingDir: /opt/flink + command: ["/opt/flink/bin/standalone-job.sh"] + args: ["start-foreground", +{{- if eq .Values.checkpoint_store_type "azure" }} + "-Dfs.azure.account.key.{{ .Values.azure_account }}.blob.core.windows.net={{ .Values.azure_secret }}", +{{- end }} +{{- if eq .Values.checkpoint_store_type "s3" }} + "-Ds3.access-key={{ .Values.s3_access_key }}", + "-Ds3.secret-key={{ .Values.s3_secret_key }}", + "-Ds3.endpoint={{ .Values.s3_endpoint }}", + "-Ds3.path.style.access={{ .Values.s3_path_style_access }}", +{{- end }} + "--job-classname={{ (index .Values $.Release.Name).job_classname }}", + "-Dweb.submit.enable=false", + "-Dmetrics.reporter.prom.class=org.apache.flink.metrics.prometheus.PrometheusReporter", + "-Dmetrics.reporter.prom.port={{ .Values.jobmanager.prom_port }}", + "-Djobmanager.rpc.address={{ .Release.Name }}-jobmanager", + "-Djobmanager.rpc.port={{ .Values.jobmanager.rpc_port }}", + "-Dparallelism.default=1", + "-Dblob.server.port={{ .Values.jobmanager.blob_port }}", + "-Dqueryable-state.server.ports={{ .Values.jobmanager.query_port }}", + "--config.file.path", + "/data/flink/conf/{{ .Release.Name }}.conf"] + ports: + - containerPort: {{ .Values.jobmanager.rpc_port }} + name: rpc + - containerPort: {{ .Values.jobmanager.blob_port }} + name: blob + - containerPort: {{ .Values.jobmanager.query_port }} + name: query + - containerPort: {{ .Values.jobmanager.ui_port }} + name: ui + volumeMounts: + - name: flink-config-volume + mountPath: /opt/flink/conf/flink-conf.yaml + subPath: flink-conf.yaml + - name: flink-config-volume + mountPath: /data/flink/conf/base-config.conf + subPath: base-config.conf + - name: flink-config-volume + mountPath: /data/flink/conf/{{ .Release.Name }}.conf + subPath: {{ .Release.Name }}.conf + - name: flink-config-volume + mountPath: /opt/flink/conf/log4j-console.properties + subPath: log4j-console.properties + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Release.Name }}-taskmanager + namespace: {{ .Values.namespace }} +spec: + replicas: {{ .Values.taskmanager.replicas }} + selector: + matchLabels: + app: flink + component: {{ .Release.Name }}-taskmanager + template: + metadata: + labels: + app: flink + component: {{ .Release.Name }}-taskmanager + spec: + volumes: + - name: flink-config-volume + configMap: + name: {{ .Release.Name }}-config + items: + - key: flink-conf + path: flink-conf.yaml + - key: log4j_console_properties + path: log4j-console.properties +# imagePullSecrets: +# - name: {{ .Values.imagepullsecrets }} + containers: + - name: {{ .Release.Name }}-taskmanager + image: "{{ .Values.repository }}:{{ .Values.image_tag }}" + imagePullPolicy: Always + resources: + requests: + cpu: "{{ .Values.taskmanager.cpu_requests }}" + workingDir: {{ .Values.taskmanager.flink_work_dir }} + command: ["/opt/flink/bin/taskmanager.sh"] + args: ["start-foreground", +{{- if eq .Values.checkpoint_store_type "azure" }} + "-Dfs.azure.account.key.{{ .Values.azure_account }}.blob.core.windows.net={{ .Values.azure_secret }}", +{{- end }} +{{- if eq .Values.checkpoint_store_type "s3" }} + "-Ds3.access-key={{ .Values.s3_access_key }}", + "-Ds3.secret-key={{ .Values.s3_secret_key }}", + "-Ds3.endpoint={{ .Values.s3_endpoint }}", + "-Ds3.path.style.access={{ .Values.s3_path_style_access }}", +{{- end }} + "-Dweb.submit.enable=false", + "-Dmetrics.reporter.prom.class=org.apache.flink.metrics.prometheus.PrometheusReporter", + "-Dmetrics.reporter.prom.host={{ .Release.Name }}-taskmanager", + "-Dmetrics.reporter.prom.port=9251-9260", + "-Djobmanager.rpc.address={{ .Release.Name }}-jobmanager", + "-Dtaskmanager.rpc.port={{ .Values.taskmanager.rpc_port }}"] + ports: + - containerPort: {{ .Values.taskmanager.rpc_port }} + name: rpc + {{- if .Values.healthcheck }} + livenessProbe: +{{ toYaml .Values.livenessProbe | indent 10 }} + {{- end }} + volumeMounts: + - name: flink-config-volume + mountPath: /opt/flink/conf/flink-conf.yaml + subPath: flink-conf.yaml + - name: flink-config-volume + mountPath: /opt/flink/conf/log4j-console.properties + subPath: log4j-console.properties \ No newline at end of file diff --git a/knowlg-automation/helm_charts/flink/values.yaml b/knowlg-automation/helm_charts/flink/values.yaml new file mode 100644 index 000000000..a993d6c53 --- /dev/null +++ b/knowlg-automation/helm_charts/flink/values.yaml @@ -0,0 +1,651 @@ +namespace: "knowlg-job" +imagepullsecrets: "" +dockerhub: "" +repository: "aimansharief/qrcode-image-generator-job" +# image_tag: "1.0.0" +image_tag: "latest" +serviceMonitor: + enabled: false +replicaCount: 1 + +jobmanager: + rpc_port: 6123 + blob_port: 6124 + query_port: 6125 + ui_port: 8081 + prom_port: 9250 + heap_memory: 1024 + +rest_port: 80 +resttcp_port: 8081 +service: + type: ClusterIP + +taskmanager: + prom_port: 9251 + rpc_port: 6122 + heap_memory: 1024 + replicas: 1 + cpu_requests: 0.3 + +checkpoint_store_type: "" + +# AWS S3 Details +s3_access_key: "" +s3_secret_key: "" +s3_endpoint: "" +s3_path_style_access: "" + +# Azure Container Details +azure_account: "" +azure_secret: "" + + +log4j_console_properties: | + # This affects logging for both user code and Flink + rootLogger.level = INFO + rootLogger.appenderRef.console.ref = ConsoleAppender + + # Uncomment this if you want to _only_ change Flink's logging + #logger.flink.name = org.apache.flink + #logger.flink.level = INFO + + # The following lines keep the log level of common libraries/connectors on + # log level INFO. The root logger does not override this. You have to manually + # change the log levels here. + logger.akka.name = akka + logger.akka.level = ERROR + logger.kafka.name= org.apache.kafka + logger.kafka.level = ERROR + logger.hadoop.name = org.apache.hadoop + logger.hadoop.level = ERROR + logger.zookeeper.name = org.apache.zookeeper + logger.zookeeper.level = ERROR + + # Log all infos to the console + appender.console.name = ConsoleAppender + appender.console.type = CONSOLE + appender.console.layout.type = PatternLayout + appender.console.layout.pattern = %d{yyyy-MM-dd HH:mm:ss,SSS} %-5p %-60c %x - %m%n + + # Suppress the irrelevant (wrong) warnings from the Netty channel handler + logger.netty.name = org.apache.flink.shaded.akka.org.jboss.netty.channel.DefaultChannelPipeline + logger.netty.level = OFF + +base_config: | + kafka { + broker-servers = "kafka-headless.knowlg-db.svc.cluster.local:9092" + producer.broker-servers = "kafka-headless.knowlg-db.svc.cluster.local:9092" + consumer.broker-servers = "kafka-headless.knowlg-db.svc.cluster.local:9092" + zookeeper = "kafka-zookeeper-headless.knowlg-db.svc.cluster.local:2181" + producer { + max-request-size = 1572864 + batch.size = 98304 + linger.ms = 10 + compression = "snappy" + } + output.system.event.topic = "dev.system.events" + } + job { + env = "local" + enable.distributed.checkpointing = false + statebackend { + blob { + storage { + account = "blob.storage.account" + container = "kp-checkpoints" + checkpointing.dir = "flink-jobs" + } + } + base.url = "" + } + } + task { + parallelism = 1 + consumer.parallelism = 1 + checkpointing.compressed = true + checkpointing.interval = 10 + checkpointing.pause.between.seconds = 3000 + restart-strategy.attempts = 3 + restart-strategy.delay = 30000 # in milli-seconds + } + + + redis { + host = redis-db.knowlg-db.svc.cluster.local + port = 6379 + connection { + max = 2 + idle.min = 1 + idle.max = 2 + minEvictableIdleTimeSeconds = 120 + timeBetweenEvictionRunsSeconds = 300 + } + } + lms-cassandra { + host = cassandra-db.knowlg-db.svc.cluster.local + port = "9042" + } + + neo4j { + routePath = "bolt://neo4j-db.knowlg-db.svc.cluster.local:7687" + graph = "domain" + } + + es { + basePath = "elasticsearch.knowlg-db.svc.cluster.local:9200" + } + +search-indexer: + search-indexer: |+ + include file("/data/flink/conf/base-config.conf") + job { + env = "sunbirddev" + } + kafka { + event.max.size = "1048576" # Max is only 1MB + input.topic = "sunbirddev.learning.graph.events" + error.topic = "sunbirddev.learning.events.failed" + groupId = "local-search-indexer-group" + producer { + max-request-size = 5242880 + } + } + task { + consumer.parallelism = 1 + router.parallelism = 1 + compositeSearch.parallelism = 1 + dialcodeIndexer.parallelism = 1 + dialcodemetricsIndexer.parallelism = 1 + } + compositesearch.index.name = "compositesearch" + nested.fields = ["badgeAssertions", "targets", "badgeAssociations", "plugins", "me_totalTimeSpent", "me_totalPlaySessionCount", "me_totalTimeSpentInSec", "batches", "trackable", "credentials", "discussionForum", "provider", "osMetadata", "actions"] + schema.definition_cache.expiry = 14400 + restrict { + metadata.objectTypes = [] + objectTypes = ["EventSet", "Questionnaire", "Misconception", "FrameworkType", "EventSet", "Event"] + } + cloudstorage.metadata.replace_absolute_path=false + cloudstorage.relative_path_prefix= "CONTENT_STORAGE_BASE_PATH" + cloudstorage.read_base_path="https://sunbirddev.blob.core.windows.net" + cloudstorage.mecloudstorage.metadata.list=["appIcon","posterImage","artifactUrl","downloadUrl","variants","previewUrl","pdfUrl", "streamingUrl", "toc_url"] + cloud_storage_container="sunbird-content-dev" + + flink-conf: |+ + jobmanager.memory.flink.size: 1024m + taskmanager.memory.flink.size: 1024m + taskmanager.numberOfTaskSlots: 1 + jobManager.numberOfTaskSlots: 1 + parallelism.default: 1 + jobmanager.execution.failover-strategy: region + taskmanager.memory.network.fraction: 0.1 + scheduler-mode: reactive + heartbeat.timeout: 8000 + heartbeat.interval: 5000 + taskmanager.memory.process.size: 1700m + jobmanager.memory.process.size: 1600m + # classloader.resolve-order: "parent-first" + # state.savepoints.dir: file:///tmp + + job_classname: org.sunbird.job.searchindexer.task.SearchIndexerStreamTask + +audit-event-generator: + audit-event-generator: |+ + include file("/data/flink/conf/base-config.conf") + kafka { + input.topic = "sunbirddev.learning.graph.events" + output.topic = "sunbirddev.telemetry.raw" + groupId = "sunbirddev-audit-event-generator-group" + } + + task { + consumer.parallelism = 1 + parallelism = 1 + producer.parallelism = 1 + window.time = 60 + } + + schema { + basePath = "https://sunbirddev.blob.core.windows.net/sunbird-content-dev/schemas/local" + } + + channel.default = "org.sunbird" + + + flink-conf: |+ + jobmanager.memory.flink.size: 1024m + taskmanager.memory.flink.size: 1024m + taskmanager.numberOfTaskSlots: 1 + jobManager.numberOfTaskSlots: 1 + parallelism.default: 1 + jobmanager.execution.failover-strategy: region + taskmanager.memory.network.fraction: 0.1 + scheduler-mode: reactive + heartbeat.timeout: 8000 + heartbeat.interval: 5000 + taskmanager.memory.process.size: 1700m + jobmanager.memory.process.size: 1600m + # classloader.resolve-order: "parent-first" + # state.savepoints.dir: file:///tmp + + job_classname: org.sunbird.job.auditevent.task.AuditEventGeneratorStreamTask + + +asset-enrichment: + asset-enrichment: |+ + include file("/data/flink/conf/base-config.conf") + job { + env = "sunbirddev" + } + + kafka { + input.topic = "sunbirddev.learning.job.request" + groupId = "sunbirddev-asset-enrichment-group" + video_stream.topic = "sunbirddev.content.postpublish.request" + } + + task { + consumer.parallelism = 1 + router.parallelism = 1 + videoEnrichment.parallelism = 1 + imageEnrichment.parallelism = 1 + } + + content { + stream { + enabled = true + mimeType = ["video/mp4", "video/webm"] + } + youtube { + applicationName = "fetch-youtube-license" + regexPattern = ["\\?vi?=([^&]*)", "watch\\?.*v=([^&]*)", "(?:embed|vi?)/([^/?]*)", "^([A-Za-z0-9\\-\\_]*)"] + } + upload.context.driven = true + max.iteration.count = 2 + } + + thumbnail.max { + sample = 5 + size.pixel = 150 + } + + cloudstorage.metadata.replace_absolute_path=false + cloudstorage.relative_path_prefix= "CONTENT_STORAGE_BASE_PATH" + cloudstorage.read_base_path="https://sunbirddev.blob.core.windows.net" + cloudstorage.write_base_path=["https://sunbirddev.blob.core.windows.net","https://obj.dev.sunbird.org"] + cloudstorage.metadata.list=["appIcon","posterImage","artifactUrl","downloadUrl","variants","previewUrl","pdfUrl", "streamingUrl", "toc_url"] + + cloud_storage_type="" + cloud_storage_key="" + cloud_storage_secret="" + cloud_storage_container="" + cloud_storage_endpoint="" + + + flink-conf: |+ + jobmanager.memory.flink.size: 1024m + taskmanager.memory.flink.size: 1024m + taskmanager.numberOfTaskSlots: 1 + jobManager.numberOfTaskSlots: 1 + parallelism.default: 1 + jobmanager.execution.failover-strategy: region + taskmanager.memory.network.fraction: 0.1 + scheduler-mode: reactive + heartbeat.timeout: 8000 + heartbeat.interval: 5000 + taskmanager.memory.process.size: 1700m + jobmanager.memory.process.size: 1600m + # classloader.resolve-order: "parent-first" + # state.savepoints.dir: file:///tmp + + job_classname: org.sunbird.job.assetenricment.task.AssetEnrichmentStreamTask + + +post-publish-processor: + post-publish-processor: |+ + include file("/data/flink/conf/base-config.conf") + job { + env = "sunbirddev" + } + + kafka { + input.topic = "sunbirddev.content.postpublish.request" + groupId = "local-post-publish-processor-group" + publish.topic = "sunbirddev.learning.job.request" + qrimage.topic = "sunbirddev.qrimage.request" + dialcode.context.topic = "sunbirddev.dialcode.context.job.request" + } + + task { + consumer.parallelism = 1 + router.parallelism = 1 + shallow_copy.parallelism = 1 + link_dialcode.parallelism = 1 + batch_create.parallelism = 1 + dialcode_context_updater.parallelism = 1 + } + + lms-cassandra { + keyspace = "sunbird_courses" + batchTable = "course_batch" + } + + dialcode-cassandra { + keyspace = "dialcodes" + imageTable = "dialcode_images" + } + + service { + search.basePath = "http://localhost:9000/search" + lms.basePath = "http://localhost:9000/lms" + learning_service.basePath = "http://localhost:8080/learning-service" + dial.basePath = "https://dev.sunbirded.org/dial/" + } + + dialcode { + linkable.primaryCategory = ["Course"] + } + + cloudstorage.metadata.replace_absolute_path=false + cloudstorage.read_base_path="https://sunbirddev.blob.core.windows.net" + cloudstorage.write_base_path=["https://sunbirddev.blob.core.windows.net","https://obj.dev.sunbird.org"] + cloudstorage.metadata.list=["appIcon","posterImage","artifactUrl","downloadUrl","variants","previewUrl","pdfUrl", "streamingUrl", "toc_url"] + + cloud_storage_type="azure" + cloud_storage_key="" + cloud_storage_secret="" + cloud_storage_container="" + cloud_storage_endpoint="" + + + flink-conf: |+ + jobmanager.memory.flink.size: 1024m + taskmanager.memory.flink.size: 1024m + taskmanager.numberOfTaskSlots: 1 + jobManager.numberOfTaskSlots: 1 + parallelism.default: 1 + jobmanager.execution.failover-strategy: region + taskmanager.memory.network.fraction: 0.1 + scheduler-mode: reactive + heartbeat.timeout: 8000 + heartbeat.interval: 5000 + taskmanager.memory.process.size: 1700m + jobmanager.memory.process.size: 1600m + # classloader.resolve-order: "parent-first" + # state.savepoints.dir: file:///tmp + + job_classname: org.sunbird.job.postpublish.task.PostPublishProcessorStreamTask + + +dialcode-context-updater: + dialcode-context-updater: |+ + include file("/data/flink/conf/base-config.conf") + kafka { + input.topic = "sunbirddev.dialcode.context.job.request" + failed.topic = "sunbirddev.dialcode.context.job.request.failed" + groupId = "sunbirddev-dialcode-group" + } + + task { + consumer.parallelism = 1 + parallelism = 1 + dialcode-context-updater.parallelism = 1 + } + + dialcode_context_updater { + actions="dialcode-context-update" + search_mode="Collection" + context_map_path = "https://raw.githubusercontent.com/project-sunbird/knowledge-platform-jobs/release-5.0.0/dialcode-context-updater/src/main/resources/contextMapping.json" + identifier_search_fields = ["identifier", "primaryCategory", "channel"] + dial_code_context_read_api_path = "/dialcode/v4/read/" + dial_code_context_update_api_path = "/dialcode/v4/update/" + } + + service { + search.basePath = "http://11.2.6.6/search" + dial_service.basePath = "http://11.2.6.6/dial" + } + + es_sync_wait_time = 5000 + + + flink-conf: |+ + jobmanager.memory.flink.size: 1024m + taskmanager.memory.flink.size: 1024m + taskmanager.numberOfTaskSlots: 1 + jobManager.numberOfTaskSlots: 1 + parallelism.default: 1 + jobmanager.execution.failover-strategy: region + taskmanager.memory.network.fraction: 0.1 + scheduler-mode: reactive + heartbeat.timeout: 8000 + heartbeat.interval: 5000 + taskmanager.memory.process.size: 1700m + jobmanager.memory.process.size: 1600m + # classloader.resolve-order: "parent-first" + # state.savepoints.dir: file:///tmp + + job_classname: org.sunbird.job.dialcodecontextupdater.task.DialcodeContextUpdaterStreamTask + + +qrcode-image-generator: + qrcode-image-generator: |+ + include file("/data/flink/conf/base-config.conf") + + job { + env = "sunbirddev" + } + + kafka { + input.topic = "sunbirddev.qrimage.request" + groupId = "sunbirddev-qrcode-image-generator-group" + } + + task { + consumer.parallelism = 1 + parallelism = 1 + window.time = 60 + } + + lp.tmp.file.location="/tmp" + + qr.image { + imageFormat="png" + bottomMargin=0 + margin=1 + } + + lms-cassandra { + keyspace = "dialcodes" + table { + image = "dialcode_images" + batch = "dialcode_batch" + } + } + + # Default value is 120 + max_allowed_character_for_file_name = 120 + + cloudstorage.metadata.replace_absolute_path=false + cloudstorage.relative_path_prefix= "DIAL_STORAGE_BASE_PATH" + cloudstorage.read_base_path="https://sunbirddev.blob.core.windows.net" + cloudstorage.write_base_path=["https://sunbirddev.blob.core.windows.net/dial","https://obj.dev.sunbird.org/dial"] + cloudstorage.metadata.list=["appIcon","posterImage","artifactUrl","downloadUrl","variants","previewUrl","pdfUrl", "streamingUrl", "toc_url"] + + cloud_storage_type="" + cloud_storage_key="" + cloud_storage_secret="" + cloud_storage_container="" + cloud_storage_endpoint="" + + + flink-conf: |+ + jobmanager.memory.flink.size: 1024m + taskmanager.memory.flink.size: 1024m + taskmanager.numberOfTaskSlots: 1 + jobManager.numberOfTaskSlots: 1 + parallelism.default: 1 + jobmanager.execution.failover-strategy: region + taskmanager.memory.network.fraction: 0.1 + scheduler-mode: reactive + heartbeat.timeout: 8000 + heartbeat.interval: 5000 + taskmanager.memory.process.size: 1700m + jobmanager.memory.process.size: 1600m + # classloader.resolve-order: "parent-first" + # state.savepoints.dir: file:///tmp + + job_classname: org.sunbird.job.qrimagegenerator.task.QRCodeImageGeneratorTask + +video-stream-generator: + video-stream-generator: |+ + include file("/data/flink/conf/base-config.conf") + + kafka { + input.topic = "sunbirddev.content.postpublish.request" + groupId = "sunbirddev-video-stream-generator-group" + } + + task { + consumer.parallelism = 1 + parallelism = 1 + timer.duration = 60 + max.retries = 10 + } + + lms-cassandra { + keyspace = "sunbirddev_platform_db" + table = "job_request" + } + + service { + content { + basePath = "http://11.2.6.6/content" + } + } + + # Azure Media Service Config + azure { + location = "centralindia" + tenant = "tenant" + subscription_id = "subscription id " + + login { + endpoint="https://login.microsoftonline.com" + } + + api { + endpoint="https://management.azure.com" + version = "2018-07-01" + } + + account_name = "account name" + resource_group_name = "group name" + + transform { + default = "media_transform_default" + hls = "media_transform_hls" + } + + stream { + base_url = "https://sunbirdspikemedia-inct.streaming.media.azure.net" + endpoint_name = "default" + protocol = "Hls" + policy_name = "Predefined_ClearStreamingOnly" + } + + token { + client_key = "client key" + client_secret = "client secret" + } + } + + azure_tenant="tenant" + azure_subscription_id="subscription id" + azure_account_name="account name" + azure_resource_group_name="group name" + azure_token_client_key="client key" + azure_token_client_secret="client secret" + + # CSP Name. e.g: aws or azure + media_service_type="aws" + + #AWS Elemental Media Convert Config + aws { + region="ap-south-1" + content_bucket_name="awsmedia-spike" + token { + access_key="access key" + access_secret="access secret" + } + api { + endpoint="API Endpoint for media convert" + version="2017-08-29" + } + service { + name="mediaconvert" + queue="Media Convert Queue Id" + role="Media Convert Role Name" + } + stream { + protocol="Hls" + } + } + + media_service_job_success_status=["FINISHED", "COMPLETE"] + + + + flink-conf: |+ + jobmanager.memory.flink.size: 1024m + taskmanager.memory.flink.size: 1024m + taskmanager.numberOfTaskSlots: 1 + jobManager.numberOfTaskSlots: 1 + parallelism.default: 1 + jobmanager.execution.failover-strategy: region + taskmanager.memory.network.fraction: 0.1 + scheduler-mode: reactive + heartbeat.timeout: 8000 + heartbeat.interval: 5000 + taskmanager.memory.process.size: 1700m + jobmanager.memory.process.size: 1600m + # classloader.resolve-order: "parent-first" + # state.savepoints.dir: file:///tmp + + job_classname: org.sunbird.job.videostream.task.VideoStreamGeneratorStreamTask + +audit-history-indexer: + audit-history-indexer: |+ + include file("/data/flink/conf/base-config.conf") + kafka { + input.topic = "sunbirddev.learning.graph.events" + groupId = "sunbirddev-audit-history-indexer-group" + } + + task { + consumer.parallelism = 1 + parallelism = 1 + window.time = 60 + } + + timezone = "IST" + + + + flink-conf: |+ + jobmanager.memory.flink.size: 1024m + taskmanager.memory.flink.size: 1024m + taskmanager.numberOfTaskSlots: 1 + jobManager.numberOfTaskSlots: 1 + parallelism.default: 1 + jobmanager.execution.failover-strategy: region + taskmanager.memory.network.fraction: 0.1 + scheduler-mode: reactive + heartbeat.timeout: 8000 + heartbeat.interval: 5000 + taskmanager.memory.process.size: 1700m + jobmanager.memory.process.size: 1600m + # classloader.resolve-order: "parent-first" + # state.savepoints.dir: file:///tmp + + job_classname: org.sunbird.job.audithistory.task.AuditHistoryIndexerStreamTask \ No newline at end of file diff --git a/knowlg-automation/helm_charts/kafka/values.yaml b/knowlg-automation/helm_charts/kafka/values.yaml index 86136c97d..8cee23f3a 100644 --- a/knowlg-automation/helm_charts/kafka/values.yaml +++ b/knowlg-automation/helm_charts/kafka/values.yaml @@ -1549,7 +1549,7 @@ provisioning: config: max.message.bytes: 64000 flush.messages: 1 - - name: "${dialcode_context_job_request_failed_topic}" + - name: "${error_topic}" partitions: 1 replicationFactor: 1 # https://kafka.apache.org/documentation/#topicconfigs diff --git a/knowlg-automation/terraform/local/flink-provision.tf b/knowlg-automation/terraform/local/flink-provision.tf new file mode 100644 index 000000000..5078c8319 --- /dev/null +++ b/knowlg-automation/terraform/local/flink-provision.tf @@ -0,0 +1,9 @@ +resource "helm_release" "flink" { + name = var.flink_release_name + chart = var.flink_chart_path + namespace = "${var.flink_namespace}" + create_namespace = var.flink_create_namespace + depends_on = [helm_release.kafka] + wait_for_jobs = var.flink_wait_for_jobs + timeout = var.flink_chart_install_timeout +} \ No newline at end of file diff --git a/knowlg-automation/terraform/local/kafka-provision.tf b/knowlg-automation/terraform/local/kafka-provision.tf index 5d283023a..fad0ed369 100644 --- a/knowlg-automation/terraform/local/kafka-provision.tf +++ b/knowlg-automation/terraform/local/kafka-provision.tf @@ -8,7 +8,7 @@ provider "helm" { resource "helm_release" "kafka" { name = "kafka" - chart = var.KAFKA_CHART + chart = var.KAFKA_CHART namespace = var.KAFKA_NAMESPACE create_namespace = true dependency_update = true @@ -42,6 +42,11 @@ resource "helm_release" "kafka" { telemetry_raw_topic = "dev.telemetry.raw" dialcode_context_job_request_topic = "dev.dialcode.context.job.request" dialcode_context_job_request_failed_topic = "dev.dialcode.context.job.request.failed" + input_topic = "sunbirddev.learning.graph.events" + output_telemetry_route_topic = "dev.druid.events.telemetry" + output_summary_route_topic = "dev.druid.events.summary" + error_topic = "sunbirddev.learning.events.failed" + output_duplicate_topic = "dev.telemetry.duplicate" } ) ] diff --git a/knowlg-automation/terraform/local/vars.tf b/knowlg-automation/terraform/local/vars.tf index 76fc791d1..fe950fb7d 100644 --- a/knowlg-automation/terraform/local/vars.tf +++ b/knowlg-automation/terraform/local/vars.tf @@ -136,12 +136,60 @@ variable "LEARNING_NAMESPACE" { #DIAL variable "DIAL_CHART" { - description = "DIAL Instance Running Namespace" + description = "Dial Instance Running Namespace" default = "../../helm_charts/dial" } variable "DIAL_NAMESPACE" { - description = "DIAL Instance Running Namespace" + description = "Dial Instance Running Namespace" default = "knowlg-api" } +#FLINK +variable "flink_release_name" { + type = string + description = "Flink helm release name." + default = "qrcode-image-generator" +} + +variable "flink_namespace" { + type = string + description = "Flink namespace." + default = "knowlg-job" +} + +variable "flink_chart_path" { + type = string + description = "Flink chart path." + default = "../../helm_charts/flink" +} + +variable "flink_chart_install_timeout" { + type = number + description = "Flink chart install timeout." + default = 900 +} + +variable "flink_create_namespace" { + type = bool + description = "Create flink namespace." + default = true +} + +variable "flink_wait_for_jobs" { + type = bool + description = "Flink wait for jobs paramater." + default = false +} + +variable "flink_chart_template" { + type = string + description = "Flink chart values.yaml path." + default = "../terraform_helm_templates/flink.yaml.tfpl" +} + +variable "flink_kubernetes_service_name" { + type = string + description = "Flink kubernetes service name." + default = "qrcode-image-generator-jobmanager" +} \ No newline at end of file From 09b4863cbb79a5fd5bcfd63c38fb40cfe17b59ce Mon Sep 17 00:00:00 2001 From: Kartheek Palla Date: Thu, 20 Apr 2023 16:01:46 +0530 Subject: [PATCH 211/490] Issue #KN-767 fix: knowlg one click installation for flink jobs in azure added --- .../helm_charts/flink/values.yaml | 79 +++++++++++-------- .../helm_charts/kafka/values.yaml | 9 +-- .../terraform/azure/flink-provision.tf | 10 +++ .../terraform/azure/variables.tf | 49 ++++++++++++ .../terraform/local/kafka-provision.tf | 7 +- 5 files changed, 105 insertions(+), 49 deletions(-) create mode 100644 knowlg-automation/terraform/azure/flink-provision.tf diff --git a/knowlg-automation/helm_charts/flink/values.yaml b/knowlg-automation/helm_charts/flink/values.yaml index a993d6c53..1a6b6b81d 100644 --- a/knowlg-automation/helm_charts/flink/values.yaml +++ b/knowlg-automation/helm_charts/flink/values.yaml @@ -1,7 +1,7 @@ namespace: "knowlg-job" imagepullsecrets: "" dockerhub: "" -repository: "aimansharief/qrcode-image-generator-job" +repository: "aimansharief/kp-jobs" # image_tag: "1.0.0" image_tag: "latest" serviceMonitor: @@ -140,13 +140,13 @@ search-indexer: search-indexer: |+ include file("/data/flink/conf/base-config.conf") job { - env = "sunbirddev" + env = "dev" } kafka { event.max.size = "1048576" # Max is only 1MB - input.topic = "sunbirddev.learning.graph.events" - error.topic = "sunbirddev.learning.events.failed" - groupId = "local-search-indexer-group" + input.topic = "dev.learning.graph.events" + error.topic = "dev.learning.events.failed" + groupId = "dev-search-indexer-group" producer { max-request-size = 5242880 } @@ -192,10 +192,14 @@ search-indexer: audit-event-generator: audit-event-generator: |+ include file("/data/flink/conf/base-config.conf") + job { + env = "dev" + } + kafka { - input.topic = "sunbirddev.learning.graph.events" - output.topic = "sunbirddev.telemetry.raw" - groupId = "sunbirddev-audit-event-generator-group" + input.topic = "dev.learning.graph.events" + output.topic = "dev.telemetry.raw" + groupId = "dev-audit-event-generator-group" } task { @@ -211,7 +215,6 @@ audit-event-generator: channel.default = "org.sunbird" - flink-conf: |+ jobmanager.memory.flink.size: 1024m taskmanager.memory.flink.size: 1024m @@ -235,13 +238,13 @@ asset-enrichment: asset-enrichment: |+ include file("/data/flink/conf/base-config.conf") job { - env = "sunbirddev" + env = "dev" } kafka { - input.topic = "sunbirddev.learning.job.request" - groupId = "sunbirddev-asset-enrichment-group" - video_stream.topic = "sunbirddev.content.postpublish.request" + input.topic = "dev.learning.job.request" + groupId = "dev-asset-enrichment-group" + video_stream.topic = "dev.content.postpublish.request" } task { @@ -305,15 +308,15 @@ post-publish-processor: post-publish-processor: |+ include file("/data/flink/conf/base-config.conf") job { - env = "sunbirddev" + env = "dev" } kafka { - input.topic = "sunbirddev.content.postpublish.request" + input.topic = "dev.content.postpublish.request" groupId = "local-post-publish-processor-group" - publish.topic = "sunbirddev.learning.job.request" - qrimage.topic = "sunbirddev.qrimage.request" - dialcode.context.topic = "sunbirddev.dialcode.context.job.request" + publish.topic = "dev.learning.job.request" + qrimage.topic = "dev.qrimage.request" + dialcode.context.topic = "dev.dialcode.context.job.request" } task { @@ -380,10 +383,14 @@ post-publish-processor: dialcode-context-updater: dialcode-context-updater: |+ include file("/data/flink/conf/base-config.conf") + job { + env = "dev" + } + kafka { - input.topic = "sunbirddev.dialcode.context.job.request" - failed.topic = "sunbirddev.dialcode.context.job.request.failed" - groupId = "sunbirddev-dialcode-group" + input.topic = "dev.dialcode.context.job.request" + failed.topic = "dev.dialcode.context.job.request.failed" + groupId = "dev-dialcode-group" } task { @@ -431,14 +438,13 @@ dialcode-context-updater: qrcode-image-generator: qrcode-image-generator: |+ include file("/data/flink/conf/base-config.conf") - job { - env = "sunbirddev" + env = "dev" } kafka { - input.topic = "sunbirddev.qrimage.request" - groupId = "sunbirddev-qrcode-image-generator-group" + input.topic = "dev.qrimage.request" + groupId = "dev-qrcode-image-generator-group" } task { @@ -500,10 +506,13 @@ qrcode-image-generator: video-stream-generator: video-stream-generator: |+ include file("/data/flink/conf/base-config.conf") - + job { + env = "dev" + } + kafka { - input.topic = "sunbirddev.content.postpublish.request" - groupId = "sunbirddev-video-stream-generator-group" + input.topic = "dev.content.postpublish.request" + groupId = "dev-video-stream-generator-group" } task { @@ -514,7 +523,7 @@ video-stream-generator: } lms-cassandra { - keyspace = "sunbirddev_platform_db" + keyspace = "dev_platform_db" table = "job_request" } @@ -594,8 +603,6 @@ video-stream-generator: media_service_job_success_status=["FINISHED", "COMPLETE"] - - flink-conf: |+ jobmanager.memory.flink.size: 1024m taskmanager.memory.flink.size: 1024m @@ -617,9 +624,13 @@ video-stream-generator: audit-history-indexer: audit-history-indexer: |+ include file("/data/flink/conf/base-config.conf") + job { + env = "dev" + } + kafka { - input.topic = "sunbirddev.learning.graph.events" - groupId = "sunbirddev-audit-history-indexer-group" + input.topic = "dev.learning.graph.events" + groupId = "dev-audit-history-indexer-group" } task { @@ -630,8 +641,6 @@ audit-history-indexer: timezone = "IST" - - flink-conf: |+ jobmanager.memory.flink.size: 1024m taskmanager.memory.flink.size: 1024m diff --git a/knowlg-automation/helm_charts/kafka/values.yaml b/knowlg-automation/helm_charts/kafka/values.yaml index 8cee23f3a..5baca149a 100644 --- a/knowlg-automation/helm_charts/kafka/values.yaml +++ b/knowlg-automation/helm_charts/kafka/values.yaml @@ -1548,14 +1548,7 @@ provisioning: # https://kafka.apache.org/documentation/#topicconfigs config: max.message.bytes: 64000 - flush.messages: 1 - - name: "${error_topic}" - partitions: 1 - replicationFactor: 1 - # https://kafka.apache.org/documentation/#topicconfigs - config: - max.message.bytes: 64000 - flush.messages: 1 + flush.messages: 1 ## @param provisioning.nodeSelector Node labels for pod assignment ## Ref: https://kubernetes.io/docs/user-guide/node-selection/ ## diff --git a/knowlg-automation/terraform/azure/flink-provision.tf b/knowlg-automation/terraform/azure/flink-provision.tf new file mode 100644 index 000000000..213d4345d --- /dev/null +++ b/knowlg-automation/terraform/azure/flink-provision.tf @@ -0,0 +1,10 @@ +resource "helm_release" "flink" { + count = length(var.flink_release_name) + name = var.flink_release_name[count.index] + chart = var.flink_chart_path + namespace = "${var.flink_namespace}" + create_namespace = var.flink_create_namespace + depends_on = [helm_release.elasticsearch] + wait_for_jobs = var.flink_wait_for_jobs + timeout = var.flink_chart_install_timeout +} \ No newline at end of file diff --git a/knowlg-automation/terraform/azure/variables.tf b/knowlg-automation/terraform/azure/variables.tf index a3c3ccd56..d2d582d77 100644 --- a/knowlg-automation/terraform/azure/variables.tf +++ b/knowlg-automation/terraform/azure/variables.tf @@ -199,3 +199,52 @@ variable "DIAL_NAMESPACE" { default = "knowlg-api" } +#FLINK +variable "flink_release_name" { + type = list(string) + description = "Flink helm release name." + default = ["search-indexer","audit-event-generator","asset-enrichment","post-publish-processor","dialcode-context-updater", "qrcode-image-generator","video-stream-generator","audit-history-indexer"] + # default = ["merged-pipeline"] +} + +variable "flink_namespace" { + type = string + description = "Flink namespace." + default = "knowlg-job" +} + +variable "flink_chart_path" { + type = string + description = "Flink chart path." + default = "../../helm_charts/flink" +} + +variable "flink_chart_install_timeout" { + type = number + description = "Flink chart install timeout." + default = 900 +} + +variable "flink_create_namespace" { + type = bool + description = "Create flink namespace." + default = true +} + +variable "flink_wait_for_jobs" { + type = bool + description = "Flink wait for jobs paramater." + default = false +} + +variable "flink_chart_template" { + type = string + description = "Flink chart values.yaml path." + default = "../terraform_helm_templates/flink.yaml.tfpl" +} + +variable "flink_kubernetes_service_name" { + type = string + description = "Flink kubernetes service name." + default = "asset-enrichment-jobmanager" +} diff --git a/knowlg-automation/terraform/local/kafka-provision.tf b/knowlg-automation/terraform/local/kafka-provision.tf index fad0ed369..6c36803dd 100644 --- a/knowlg-automation/terraform/local/kafka-provision.tf +++ b/knowlg-automation/terraform/local/kafka-provision.tf @@ -34,7 +34,7 @@ resource "helm_release" "kafka" { # zookeeper_replica_count: 1 content_publish_topic = "dev.publish.job.request" content_postpublish_topic = "dev.content.postpublish.request" - learning_job_request_topic = "dev.learning_job_request" + learning_job_request_topic = "dev.learning.job.request" learning_graph_events_topic = "dev.learning.graph.events" learning_events_failed_topic = "dev.learning.events.failed" search_indexer_group_topic = "dev.search-indexer-group" @@ -42,11 +42,6 @@ resource "helm_release" "kafka" { telemetry_raw_topic = "dev.telemetry.raw" dialcode_context_job_request_topic = "dev.dialcode.context.job.request" dialcode_context_job_request_failed_topic = "dev.dialcode.context.job.request.failed" - input_topic = "sunbirddev.learning.graph.events" - output_telemetry_route_topic = "dev.druid.events.telemetry" - output_summary_route_topic = "dev.druid.events.summary" - error_topic = "sunbirddev.learning.events.failed" - output_duplicate_topic = "dev.telemetry.duplicate" } ) ] From 9966f93bd0387140c85d0e39f1f782f26739f394 Mon Sep 17 00:00:00 2001 From: aimansharief <110388201+aimansharief@users.noreply.github.com> Date: Tue, 25 Apr 2023 15:43:40 +0530 Subject: [PATCH 212/490] Revert "Merge branch 'Sunbird-Knowlg:master' into KN-848" This reverts commit 9c7c619b585ebea9cdd3a82ed92e36db73446551, reversing changes made to 59e42ecb04d66c53f170dff329ccc3d5676f12cb. --- schemas/content/1.0/schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/schemas/content/1.0/schema.json b/schemas/content/1.0/schema.json index a3efb2b7b..38e82681d 100644 --- a/schemas/content/1.0/schema.json +++ b/schemas/content/1.0/schema.json @@ -148,7 +148,7 @@ }, "compatibilityLevel": { "type": "number", - "default": 2 + "default": 1 }, "minGenieVersion": { "type": "string" From 6986d028beec82ee98325a7f31684314da65c067 Mon Sep 17 00:00:00 2001 From: Aiman Sharief Date: Wed, 26 Apr 2023 15:57:21 +0530 Subject: [PATCH 213/490] Delete schema.json --- schemas/content/1.0/schema.json | 1416 ------------------------------- 1 file changed, 1416 deletions(-) delete mode 100644 schemas/content/1.0/schema.json diff --git a/schemas/content/1.0/schema.json b/schemas/content/1.0/schema.json deleted file mode 100644 index 38e82681d..000000000 --- a/schemas/content/1.0/schema.json +++ /dev/null @@ -1,1416 +0,0 @@ -{ - "$id": "content-schema.json", - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Content", - "type": "object", - "required": [ - "name", - "status", - "mimeType", - "channel", - "contentType", - "code", - "contentEncoding", - "contentDisposition", - "mediaType", - "primaryCategory" - ], - "properties": { - "name": { - "type": "string", - "minLength": 5 - }, - "code": { - "type": "string" - }, - "createdOn": { - "type": "string" - }, - "lastUpdatedOn": { - "type": "string" - }, - "status": { - "type": "string", - "enum": [ - "Draft", - "Review", - "Redraft", - "Flagged", - "Live", - "Unlisted", - "Retired", - "Mock", - "Processing", - "FlagDraft", - "FlagReview", - "Failed" - ], - "default": "Draft" - }, - "channel": { - "type": "string" - }, - "mimeType": { - "type": "string", - "enum": [ - "application/vnd.ekstep.ecml-archive", - "application/vnd.ekstep.html-archive", - "application/vnd.android.package-archive", - "application/vnd.ekstep.content-archive", - "application/vnd.ekstep.content-collection", - "application/vnd.ekstep.plugin-archive", - "application/vnd.ekstep.h5p-archive", - "application/epub", - "text/x-url", - "video/x-youtube", - "application/octet-stream", - "application/msword", - "application/pdf", - "image/jpeg", - "image/jpg", - "image/png", - "image/tiff", - "image/bmp", - "image/gif", - "image/svg+xml", - "video/avi", - "video/mpeg", - "video/quicktime", - "video/3gpp", - "video/mp4", - "video/ogg", - "video/webm", - "audio/mp3", - "audio/mp4", - "audio/mpeg", - "audio/ogg", - "audio/webm", - "audio/x-wav", - "audio/wav", - "application/json", - "application/quiz" - ] - }, - "osId": { - "type": "string", - "default": "org.ekstep.launcher" - }, - "contentEncoding": { - "type": "string", - "enum": [ - "gzip", - "identity" - ], - "default": "gzip" - }, - "contentDisposition": { - "type": "string", - "enum": [ - "inline", - "online", - "attachment", - "online-only" - ], - "default": "inline" - }, - "mediaType": { - "type": "string", - "enum": [ - "content", - "collection", - "image", - "video", - "audio", - "voice", - "ecml", - "document", - "pdf", - "text", - "other" - ], - "default": "content" - }, - "os": { - "type": "array", - "items": { - "type" : "string", - "enum": [ - "All", - "Android", - "iOS", - "Windows" - ] - }, - "default": ["All"] - }, - "minOsVersion": { - "type": "string" - }, - "compatibilityLevel": { - "type": "number", - "default": 1 - }, - "minGenieVersion": { - "type": "string" - }, - "minSupportedVersion": { - "type": "string" - }, - "filter": { - "type": "array", - "items": { - "type": "string" - } - }, - "variants": { - "type": "object" - }, - "config": { - "type": "object" - }, - "visibility": { - "type": "string", - "enum": [ - "Default", - "Parent", - "Private", - "Protected" - ], - "default": "Default" - }, - "audience": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "Student", - "Teacher", - "Administrator", - "Parent", - "Others", - "Other" - ] - }, - "default": ["Student"] - }, - "posterImage": { - "type": "string", - "format": "url" - }, - "badgeAssertions": { - "type": "array", - "items": { - "type": "object" - } - }, - "targets": { - "type": "array", - "items": { - "type": "object" - } - }, - "contentCredits": { - "type": "array", - "items": { - "type": "object" - } - }, - "appIcon": { - "type": "string", - "format": "url" - }, - "grayScaleAppIcon": { - "type": "string", - "format": "url" - }, - "thumbnail": { - "type": "string", - "format": "url" - }, - "screenshots": { - "type": "string" - }, - "format": { - "type": "string" - }, - "duration": { - "type": "string" - }, - "size": { - "type": "number" - }, - "idealScreenSize": { - "type": "string", - "enum": [ - "small", - "normal", - "large", - "xlarge", - "other" - ], - "default": "normal" - }, - "idealScreenDensity": { - "type": "string", - "enum": [ - "ldpi", - "mdpi", - "hdpi", - "xhdpi", - "xxhdpi", - "xxxhdpi" - ], - "default": "hdpi" - }, - "releaseNotes": { - "type": "array" - }, - "pkgVersion": { - "type": "number" - }, - "semanticVersion": { - "type": "string" - }, - "versionKey": { - "type": "string" - }, - "resources": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "Speaker", - "Touch", - "Microphone", - "GPS", - "Motion Sensor", - "Compass" - ] - } - }, - "downloadUrl": { - "type": "string", - "format": "url" - }, - "artifactUrl": { - "type": "string", - "format": "url" - }, - "previewUrl": { - "type": "string", - "format": "url" - }, - "streamingUrl": { - "type": "string", - "format": "url" - }, - "objects": { - "type": "array" - }, - "organization": { - "type": "array" - }, - "createdFor": { - "type": "array" - }, - "developer": { - "type": "string" - }, - "source": { - "type": "string" - }, - "notes": { - "type": "string" - }, - "pageNumber": { - "type": "string" - }, - "publication": { - "type": "string" - }, - "edition": { - "type": "string" - }, - "publisher": { - "type": "string" - }, - "author": { - "type": "string" - }, - "owner": { - "type": "string" - }, - "attributions": { - "type": "array" - }, - "collaborators": { - "type": "array" - }, - "creators": { - "type": "string" - }, - "contributors": { - "type": "string" - }, - "voiceCredits": { - "type": "array" - }, - "soundCredits": { - "type": "array" - }, - "imageCredits": { - "type": "array" - }, - "copyright": { - "type": "string" - }, - "license": { - "type": "string", - "default": "CC BY 4.0" - }, - "language": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "English", - "Hindi", - "Assamese", - "Bengali", - "Gujarati", - "Kannada", - "Malayalam", - "Marathi", - "Nepali", - "Odia", - "Punjabi", - "Tamil", - "Telugu", - "Urdu", - "Sanskrit", - "Maithili", - "Munda", - "Santali", - "Juang", - "Ho", - "Other" - ] - }, - "default": ["English"] - }, - "languageCode": { - "type": "array" - }, - "words": { - "type": "array" - }, - "text": { - "type": "array" - }, - "forkable": { - "type": "boolean" - }, - "translatable": { - "type": "boolean" - }, - "ageGroup": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "<5", - "5-6", - "6-7", - "7-8", - "8-10", - ">10", - "Other" - ] - } - }, - "interactivityLevel": { - "type": "string", - "enum": [ - "High", - "Medium", - "Low" - ] - }, - "contentType": { - "type": "string", - "enum": [ - "Resource", - "Collection", - "TextBook", - "LessonPlan", - "Course", - "Template", - "Asset", - "Plugin", - "LessonPlanUnit", - "CourseUnit", - "TextBookUnit", - "TeachingMethod", - "PedagogyFlow", - "FocusSpot", - "LearningOutcomeDefinition", - "PracticeQuestionSet", - "CuriosityQuestionSet", - "MarkingSchemeRubric", - "ExplanationResource", - "ExperientialResource", - "ConceptMap", - "SelfAssess", - "PracticeResource", - "eTextBook", - "OnboardingResource", - "ExplanationVideo", - "ClassroomTeachingVideo", - "ExplanationReadingMaterial", - "LearningActivity", - "PreviousBoardExamPapers", - "LessonPlanResource", - "TVLesson" - ] - }, - "resourceType": { - "type": "string", - "enum": [ - "Read", - "Learn", - "Teach", - "Play", - "Test", - "Practice", - "Experiment", - "Collection", - "Book", - "Lesson Plan", - "Course", - "Theory", - "Worksheet", - "Practical" - ] - }, - "category": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "core", - "learning", - "literacy", - "math", - "science", - "time", - "wordnet", - "game", - "mcq", - "mtf", - "ftb", - "library" - ] - } - }, - "templateType": { - "type": "string", - "enum": [ - "story", - "worksheet", - "mcq", - "ftb", - "mtf", - "recognition", - "activity", - "widget", - "other" - ] - }, - "genre": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "Picture Books", - "Chapter Books", - "Flash Cards", - "Serial Books", - "Alphabet Books", - "Folktales", - "Fiction", - "Non-Fiction", - "Poems/Rhymes", - "Plays", - "Comics", - "Words" - ] - } - }, - "theme": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "History", - "Adventure", - "Mystery", - "Science", - "Nature", - "Art", - "Music", - "Funny", - "Family", - "Life Skills", - "Scary", - "School Stories", - "Holidays", - "Hobby", - "Geography", - "Rural", - "Urban" - ] - } - }, - "themes": { - "type": "array" - }, - "rating": { - "type": "number" - }, - "rating_a": { - "type": "number" - }, - "quality": { - "type": "number" - }, - "genieScore": { - "type": "number" - }, - "authoringScore": { - "type": "number" - }, - "popularity": { - "type": "number" - }, - "downloads": { - "type": "number" - }, - "launchUrl": { - "type": "string" - }, - "activity_class": { - "type": "string" - }, - "draftImage": { - "type": "string" - }, - "scaffolding": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "Tutorial", - "Help", - "Practice" - ] - } - }, - "interceptionPoints": { - "type": "object", - "items": { - "type": "array" - }, - "default": {} - }, - "interceptionType": { - "type": "string", - "enum": [ - "Timestamp" - ] - }, - "feedback": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "Right/Wrong", - "Reflection", - "Guidance", - "Learn from Mistakes", - "Adaptive Feedback", - "Interrupts", - "Rich Feedback" - ] - } - }, - "feedbackType": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "Audio", - "Visual", - "Textual", - "Tactile" - ] - } - }, - "teachingMode": { - "type": "string", - "enum": [ - "Abstract", - "Concrete", - "Pictorial" - ] - }, - "skills": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "Listening", - "Speaking", - "Reading", - "Writing", - "Touch", - "Gestures", - "Draw" - ] - } - }, - "keywords": { - "type": "array" - }, - "domain": { - "type": "array" - }, - "dialcodes": { - "type": "array" - }, - "optStatus": { - "type": "string", - "enum": [ - "Pending", - "Processing", - "Error", - "Complete" - ] - }, - "description": { - "type": "string" - }, - "instructions": { - "type": "string" - }, - "body": { - "type": "string" - }, - "oldBody": { - "type": "string" - }, - "stageIcons": { - "type": "string" - }, - "editorState": { - "type": "string" - }, - "data": { - "type": "array" - }, - "loadingMessage": { - "type": "string" - }, - "checksum": { - "type": "string" - }, - "learningObjective": { - "type": "array" - }, - "createdBy": { - "type": "string" - }, - "creator": { - "type": "string" - }, - "reviewer": { - "type": "string" - }, - "lastUpdatedBy": { - "type": "string" - }, - "lastSubmittedBy": { - "type": "string" - }, - "lastSubmittedOn": { - "type": "string" - }, - "lastPublishedBy": { - "type": "string" - }, - "lastPublishedOn": { - "type": "string" - }, - "versionDate": { - "type": "string" - }, - "origin": { - "type": "string" - }, - "originData": { - "type": "object" - }, - "versionCreatedBy": { - "type": "string" - }, - "me_totalSessionsCount": { - "type": "number" - }, - "me_creationSessions": { - "type": "number" - }, - "me_creationTimespent": { - "type": "number" - }, - "me_totalTimespent": { - "type": "number" - }, - "me_totalInteractions": { - "type": "number" - }, - "me_averageInteractionsPerMin": { - "type": "number" - }, - "me_averageSessionsPerDevice": { - "type": "number" - }, - "me_totalDevices": { - "type": "number" - }, - "me_averageTimespentPerSession": { - "type": "number" - }, - "me_averageRating": { - "type": "number" - }, - "me_totalDownloads": { - "type": "number" - }, - "me_totalSideloads": { - "type": "number" - }, - "me_totalRatings": { - "type": "number" - }, - "me_totalComments": { - "type": "number" - }, - "me_totalUsage": { - "type": "number" - }, - "me_totalLiveContentUsage": { - "type": "number" - }, - "me_usageLastWeek": { - "type": "number" - }, - "me_deletionsLastWeek": { - "type": "number" - }, - "me_lastUsedOn": { - "type": "string" - }, - "me_lastRemovedOn": { - "type": "string" - }, - "me_hierarchyLevel": { - "type": "number" - }, - "me_totalDialcodeAttached": { - "type": "number" - }, - "me_totalDialcodeLinkedToContent": { - "type": "number" - }, - "me_totalDialcode": { - "type": "array" - }, - "flagReasons": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "Inappropriate Content", - "Copyright Violation", - "Privacy Violation", - "Other" - ] - } - }, - "flaggedBy": { - "type": "array" - }, - "flags": { - "type": "array" - }, - "lastFlaggedOn": { - "type": "string" - }, - "tempData": { - "type": "string" - }, - "copyType": { - "type": "string" - }, - "pragma": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "external", - "ads" - ] - } - }, - "publishChecklist": { - "type": "array" - }, - "publishComment": { - "type": "string" - }, - "rejectReasons": { - "type": "array" - }, - "rejectComment": { - "type": "string" - }, - "totalQuestions": { - "type": "number" - }, - "totalScore": { - "type": "number" - }, - "ownershipType": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "createdBy", - "createdFor" - ] - }, - "default": ["createdBy"] - }, - "reservedDialcodes": { - "type": "object" - }, - "dialcodeRequired": { - "type": "string", - "enum": [ - "Yes", - "No" - ], - "default": "No" - }, - "lockKey": { - "type": "string" - }, - "badgeAssociations": { - "type": "object" - }, - "framework": { - "type": "string", - "default": "NCF" - }, - "lastStatusChangedOn": { - "type": "string" - }, - "uploadError": { - "type": "string" - }, - "appId": { - "type": "string" - }, - "s3Key": { - "type": "string" - }, - "consumerId": { - "type": "string" - }, - "organisation": { - "type": "array" - }, - "nodeType": { - "type": "string" - }, - "prevState": { - "type": "string" - }, - "publishError": { - "type": "string" - }, - "publish_type": { - "type": "string" - }, - "ownedBy": { - "type": "string" - }, - "purpose": { - "type": "string" - }, - "toc_url": { - "type": "string", - "format": "url" - }, - "reviewError": { - "type": "string" - }, - "mimeTypesCount": { - "type": "string" - }, - "contentTypesCount": { - "type": "string" - }, - "childNodes": { - "type": "array" - }, - "leafNodesCount": { - "type": "number" - }, - "depth": { - "type": "number" - }, - "SYS_INTERNAL_LAST_UPDATED_ON": { - "type": "string" - }, - "assets": { - "type": "array" - }, - "version": { - "type": "number", - "default": 2 - }, - "qrCodeProcessId": { - "type": "string" - }, - "migratedUrl": { - "type": "string", - "format": "url" - }, - "totalCompressedSize": { - "type": "number" - }, - "programId": { - "type": "string" - }, - "leafNodes": { - "type": "array" - }, - "editorVersion": { - "type": "number" - }, - "unitIdentifiers": { - "type": "array" - }, - "questionCategories": { - "type": "array" - }, - "certTemplate": { - "type": "array", - "items": { - "type": "object" - } - }, - "subject" : { - "type": "array" - }, - "medium" : { - "type": "array" - }, - "gradeLevel" : { - "type": "array" - }, - "topic" : { - "type": "array" - }, - "subDomains" : { - "type": "array" - }, - "subjectCodes" : { - "type": "array" - }, - "difficultyLevel" : { - "type": "string" - }, - "board" : { - "type": "string" - }, - "licenseterms" : { - "type": "string" - }, - "copyrightYear" : { - "type": "number", - "minimum": 2020, - "exclusiveMaximum": 3000 - }, - "organisationId" : { - "type": "string" - }, - "itemSetPreviewUrl": { - "type": "string" - }, - "textbook_name" : { - "type": "array" - }, - "level1Name" : { - "type": "array" - }, - "level1Concept" : { - "type": "array" - }, - "level2Name" : { - "type": "array" - }, - "level2Concept" : { - "type": "array" - }, - "level3Name" : { - "type": "array" - }, - "level3Concept" : { - "type": "array" - }, - "sourceURL" : { - "type": "string" - }, - "me_totalTimeSpentInSec": { - "type": "object" - }, - "me_totalPlaySessionCount": { - "type": "object" - }, - "me_totalRatingsCount": { - "type": "number" - }, - "monitorable": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "progress-report", - "score-report" - ] - } - }, - "userConsent": { - "type": "string", - "enum": [ - "Yes", - "No" - ] - }, - "trackable": { - "type": "object", - "properties": { - "enabled": { - "type": "string", - "enum": ["Yes","No"], - "default": "No" - }, - "autoBatch": { - "type": "string", - "enum": ["Yes","No"], - "default": "No" - } - }, - "default": { - "enabled": "No", - "autoBatch": "No" - }, - "additionalProperties": false - }, - "credentials": { - "type": "object", - "properties": { - "enabled": { - "type": "string", - "enum": ["Yes","No"], - "default": "No" - } - }, - "default": { - "enabled": "No" - }, - "additionalProperties": false - }, - "discussionForum":{ - "type": "object", - "properties": - { - "enabled": - { - "type": "string", - "enum": ["Yes", "No"], - "default": "No" - } - }, - "default": - { - "enabled": "No" - }, - "additionalProperties": false - }, - "processId": { - "type": "string" - }, - "primaryCategory": { - "type": "string" - }, - "additionalCategories": { - "type": "array", - "items": { - "type": "string" - } - }, - "prevStatus": { - "type": "string" - }, - "cloudStorageKey": { - "type": "string" - }, - "batches": { - "type": "array", - "items": { - "type": "object" - } - }, - "year": { - "type": "string" - }, - "plugins": { - "type": "array", - "items": { - "type": "object" - } - }, - "showNotification": { - "type": "boolean" - }, - "collectionId" : { - "type": "string" - }, - "learningOutcome": { - "type": "array", - "items": { - "type": "string" - } - }, - "displayScore": { - "type": "boolean" - }, - "catalogPaths" : { - "type": "array", - "items": { - "type": "object" - } - }, - "competencies" : { - "type": "array", - "items": { - "type": "object" - } - }, - "sharedWith" : { - "type": "array" - }, - "isExternal": { - "type": "boolean" - }, - "learningMode": { - "type": "string", - "enum": [ - "Self Paced", - "Instructor Led", - "Classroom" - ] - }, - "creatorLogo" : { - "type": "string", - "format": "url" - }, - "subTitles" : { - "type": "array", - "items": { - "type": "object" - } - }, - "reviewStatus" : { - "type" : "string", - "enum": [ - "InReview", - "Reviewed" - ] - }, - "boardIds": { - "type": "array", - "items": { - "type": "string" - } - }, - "gradeLevelIds": { - "type": "array", - "items": { - "type": "string" - } - }, - "subjectIds": { - "type": "array", - "items": { - "type": "string" - } - }, - "mediumIds": { - "type": "array", - "items": { - "type": "string" - } - }, - "topicsIds": { - "type": "array", - "items": { - "type": "string" - } - }, - "targetFWIds":{ - "type": "array", - "items": { - "type": "string" - } - }, - "targetBoardIds":{ - "type": "array", - "items": { - "type": "string" - } - }, - "targetGradeLevelIds": { - "type": "array", - "items": { - "type": "string" - } - }, - "targetSubjectIds": { - "type": "array", - "items": { - "type": "string" - } - }, - "targetMediumIds": { - "type": "array", - "items": { - "type": "string" - } - }, - "targetTopicIds": { - "type": "array", - "items": { - "type": "string" - } - }, - "maxAttempts": { - "type": "number" - }, - "se_FWIds":{ - "type": "array", - "items": { - "type": "string" - } - }, - "se_boardIds":{ - "type": "array", - "items": { - "type": "string" - } - }, - "se_subjectIds": { - "type": "array", - "items": { - "type": "string" - } - }, - "se_mediumIds": { - "type": "array", - "items": { - "type": "string" - } - }, - "se_topicIds": { - "type": "array", - "items": { - "type": "string" - } - }, - "se_gradeLevelIds": { - "type": "array", - "items": { - "type": "string" - } - }, - "se_boards":{ - "type": "array", - "items": { - "type": "string" - } - }, - "se_subjects":{ - "type": "array", - "items": { - "type": "string" - } - }, - "se_mediums": { - "type": "array", - "items": { - "type": "string" - } - }, - "se_topics": { - "type": "array", - "items": { - "type": "string" - } - }, - "se_gradeLevels": { - "type": "array", - "items": { - "type": "string" - } - }, - "transcripts": { - "type": "array", - "items": { - "type": "object" - } - }, - "accessibility": { - "type": "array", - "items": { - "type": "object" - } - }, - "taxonomyPaths_v2": { - "type": "array", - "items": { - "type": "object" - } - }, - "competencies_v3": { - "type": "array", - "items": { - "type": "object" - } - }, - "migrationVersion": { - "type": "number" - } - } -} From 35fde5c7feed5c847964f1253a6bf2a43e46bea6 Mon Sep 17 00:00:00 2001 From: aimansharief <110388201+aimansharief@users.noreply.github.com> Date: Wed, 26 Apr 2023 16:08:05 +0530 Subject: [PATCH 214/490] Issue KN-848 fix: Removed the deprecated import and added the alternative --- .../src/test/scala/org/sunbird/managers/BaseSpec.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content-api/hierarchy-manager/src/test/scala/org/sunbird/managers/BaseSpec.scala b/content-api/hierarchy-manager/src/test/scala/org/sunbird/managers/BaseSpec.scala index 361d12c04..eb9b6cc5f 100644 --- a/content-api/hierarchy-manager/src/test/scala/org/sunbird/managers/BaseSpec.scala +++ b/content-api/hierarchy-manager/src/test/scala/org/sunbird/managers/BaseSpec.scala @@ -1,7 +1,6 @@ package org.sunbird.managers import java.io.{File, IOException} - import com.datastax.driver.core.{ResultSet, Session} import org.apache.commons.io.FileUtils import org.cassandraunit.utils.EmbeddedCassandraServerHelper @@ -9,7 +8,8 @@ import org.neo4j.graphdb.GraphDatabaseService import org.neo4j.graphdb.factory.GraphDatabaseFactory import org.neo4j.graphdb.factory.GraphDatabaseSettings.Connector.ConnectorType import org.neo4j.kernel.configuration.BoltConnector -import org.scalatest.{AsyncFlatSpec, BeforeAndAfterAll, BeforeAndAfterEach, Matchers} +import org.scalatest.flatspec.AsyncFlatSpec +import org.scalatest.{BeforeAndAfterAll, BeforeAndAfterEach, Matchers} import org.sunbird.cassandra.CassandraConnector import org.sunbird.common.Platform From 2d52ad0fe52bfedb43fab191a41b8b49059613bb Mon Sep 17 00:00:00 2001 From: Kartheek Palla Date: Fri, 28 Apr 2023 15:36:52 +0530 Subject: [PATCH 215/490] Issue #KN-767 fix: search service config updated --- .../search/templates/search-configmap.yaml | 70 ++++++++++++++----- 1 file changed, 52 insertions(+), 18 deletions(-) diff --git a/knowlg-automation/helm_charts/search/templates/search-configmap.yaml b/knowlg-automation/helm_charts/search/templates/search-configmap.yaml index 54fb0cd78..f4f92a3ed 100644 --- a/knowlg-automation/helm_charts/search/templates/search-configmap.yaml +++ b/knowlg-automation/helm_charts/search/templates/search-configmap.yaml @@ -77,6 +77,49 @@ data: # And then uncomment this line to debug the configuration. # #log-config-on-start = true + default-dispatcher { + # This will be used if you have set "executor = "fork-join-executor"" + fork-join-executor { + # Min number of threads to cap factor-based parallelism number to + parallelism-min = 8 + # The parallelism factor is used to determine thread pool size using the + # following formula: ceil(available processors * factor). Resulting size + # is then bounded by the parallelism-min and parallelism-max values. + parallelism-factor = 32.0 + # Max number of threads to cap factor-based parallelism number to + parallelism-max = 64 + # Setting to "FIFO" to use queue like peeking mode which "poll" or "LIFO" to use stack + # like peeking mode which "pop". + task-peeking-mode = "FIFO" + } + } + actors-dispatcher { + type = "Dispatcher" + executor = "fork-join-executor" + fork-join-executor { + parallelism-min = 8 + parallelism-factor = 32.0 + parallelism-max = 64 + } + # Throughput for default Dispatcher, set to 1 for as fair as possible + throughput = 1 + } + actor { + deployment { + /searchActor + { + router = smallest-mailbox-pool + nr-of-instances = 10 + dispatcher = actors-dispatcher + } + /healthActor + { + router = smallest-mailbox-pool + nr-of-instances = 10 + dispatcher = actors-dispatcher + } + } + } } ## Secret key @@ -325,35 +368,26 @@ data: akka.http.parsing.max-content-length = 50MB schema.base_path = "../../schemas/" + #schema.base_path="https://sunbirdstagingpublic.blob.core.windows.net/sunbird-content-staging/schemas/local" + telemetry_env="sunbird" + installation.id="Sunbird_Stage" - content.relatedBoards.properties=["channel","board","subject","medium"] - - - # Path for elasticsearch.properties file - search.config.path=/home/learning/platform/search - - # Cache-Manager Configuration - cache.type="redis" - + # ElasticSearch Configuration + ekstepPlatformApiUserId="search-service" search.es_conn_info="{{ .Values.es_connection }}" search.fields.query=["name^100","title^100","lemma^100","code^100","domain","subject","description^10","keywords^100","ageGroup^10","filter^10","theme^10","genre^10","objects^25","contentType^100","language^200","teachingMode^25","skills^10","learningObjective^10","curriculum^100","gradeLevel^100","developer^100","attributions^10","identifier^100","IL_UNIQUE_ID^100","owner^50","board^100","relatedBoards^100","creator^100", "dialcodes^100","text","words","releaseNotes"] search.fields.date=["lastUpdatedOn","createdOn","versionDate","lastSubmittedOn","lastPublishedOn"] - search.fields.mode_collection=["identifier","name","objectType","contentType","mimeType","size","childNodes","board","subject","medium","gradeLevel","appIcon", "origin", "originData"] + search.fields.mode_collection=["identifier","name","objectType","contentType","mimeType","size","childNodes","board","subject","medium","gradeLevel","appIcon","resourceType","origin","originData"] search.batch.size=500 search.connection.timeout=30 - platform-api-url="http://localhost:8080/learning-service" - language.map={"Hindi":"hi", "English":"en", "Telugu":"te", "Kannada":"ka", "Tamil":"ta", "Assamese":"as", "Bengali":"bn", "Bodo":"bo", "Gujarati":"gu", "Konkani":"ko", "Malayalam":"ml", "Marathi":"mr", "Nepali":"ne", "Odia":"or", "Punjabi":"pj", "Sanskrit":"sk"} - #Top N Config for Search Telemetry - telemetry.search.topn=5 - telemetry_env=dev - installation.id=ekstep + language.map={"Hindi":"hi", "English":"en", "Telugu":"te", "Kannada":"ka", "Tamil":"ta", "Assamese":"as", "Bengali":"bn", "Bodo":"bo", "Gujarati":"gu", "Konkani":"ko", "Malayalam":"ml", "Marathi":"mr", "Nepali":"ne", "Odia":"or", "Punjabi":"pj", "Sanskrit":"san"} # Configuration for default channel ID channel.default="in.ekstep" - ekstepPlatformApiUserId="search-service" - + compositesearch.index.name="compositesearch" + content.tagging.backward_enable=false content.tagging.property=["subject","medium"] search.payload.log_enable=true From 25d66bd7f88b6e038be503efd7bfba74571602ae Mon Sep 17 00:00:00 2001 From: Aiman Sharief Date: Tue, 9 May 2023 13:39:15 +0530 Subject: [PATCH 216/490] Issue #KN-10 feat: Added create framework API --- .../org/sunbird/actors/FrameworkActor.scala | 54 +++++++++++++++++++ .../scala/org/sunbird/utils/Constants.scala | 7 +++ .../controllers/v3/FrameworkController.scala | 25 ++++++--- .../app/modules/TaxonomyModule.scala | 3 +- .../app/utils/ActorNames.scala | 1 + .../taxonomy-service/app/utils/ApiId.scala | 1 + 6 files changed, 82 insertions(+), 9 deletions(-) create mode 100644 taxonomy-api/taxonomy-actors/src/main/scala/org/sunbird/actors/FrameworkActor.scala diff --git a/taxonomy-api/taxonomy-actors/src/main/scala/org/sunbird/actors/FrameworkActor.scala b/taxonomy-api/taxonomy-actors/src/main/scala/org/sunbird/actors/FrameworkActor.scala new file mode 100644 index 000000000..b41f0fd0e --- /dev/null +++ b/taxonomy-api/taxonomy-actors/src/main/scala/org/sunbird/actors/FrameworkActor.scala @@ -0,0 +1,54 @@ +package org.sunbird.actors + +import org.apache.commons.lang3.StringUtils +import org.sunbird.actor.core.BaseActor +import org.sunbird.common.Platform +import org.sunbird.common.dto.{Request, Response, ResponseHandler} +import org.sunbird.common.exception.ClientException +import org.sunbird.graph.OntologyEngineContext +import org.sunbird.graph.nodes.DataNode +import org.sunbird.utils.{Constants, RequestUtil} + +import java.util +import java.util.Optional +import javax.inject.Inject +import scala.collection.JavaConverters.mapAsScalaMapConverter +import scala.concurrent.{ExecutionContext, Future} + +class FrameworkActor @Inject()(implicit oec: OntologyEngineContext) extends BaseActor { + + implicit val ec: ExecutionContext = getContext().dispatcher + + override def onReceive(request: Request): Future[Response] = { + request.getOperation match { + case Constants.CREATE_FRAMEWORK => create(request) + case _ => ERROR(request.getOperation) + } + } + + + @throws[Exception] + private def create(request: Request): Future[Response] = { + RequestUtil.restrictProperties(request) + val code = request.getRequest.getOrDefault(Constants.CODE, "").asInstanceOf[String] + if (StringUtils.isBlank(code)) + throw new ClientException("ERR_FRAMEWORK_CODE_REQUIRED", "Unique code is mandatory for framework") + request.getRequest.put(Constants.IDENTIFIER ,code) + if (request.getRequest.containsKey("translations")) validateTranslations(request) + DataNode.create(request).map(node => { + ResponseHandler.OK.put(Constants.IDENTIFIER, node.getIdentifier) + }) + } + + private def validateTranslations(request: Request) = { + val translations: util.Map[String, AnyRef] = Optional.ofNullable(request.get("translations").asInstanceOf[util.HashMap[String, AnyRef]]).orElse(new util.HashMap[String, AnyRef]()) + if (translations.isEmpty) request.getRequest.remove("translations") + else { + val languageCodes = Platform.getStringList("platform.language.codes", new util.ArrayList[String]()) + if (translations.asScala.exists(entry => !languageCodes.contains(entry._1))) + throw new ClientException("ERR_INVALID_LANGUAGE_CODE", "Please Provide Valid Language Code For translations. Valid Language Codes are : " + languageCodes) + } + } + + +} \ No newline at end of file diff --git a/taxonomy-api/taxonomy-actors/src/main/scala/org/sunbird/utils/Constants.scala b/taxonomy-api/taxonomy-actors/src/main/scala/org/sunbird/utils/Constants.scala index 29f875655..8793fc947 100644 --- a/taxonomy-api/taxonomy-actors/src/main/scala/org/sunbird/utils/Constants.scala +++ b/taxonomy-api/taxonomy-actors/src/main/scala/org/sunbird/utils/Constants.scala @@ -24,4 +24,11 @@ object Constants { val CHANNEL = "channel"; val TARGET_OBJECT_TYPE = "targetObjectType" val OBJECT_CATEGORY_DEFINITION: String = "objectCategoryDefinition" + + // Constants For Framework + val FRAMEWORK: String = "framework"; + val CREATE_FRAMEWORK: String = "createFramework"; + val FRAMEWORK_SCHEMA_VERSION = "1.0"; + val FRAMEWORK_SCHEMA_NAME="framework" + val CODE: String = "code"; } diff --git a/taxonomy-api/taxonomy-service/app/controllers/v3/FrameworkController.scala b/taxonomy-api/taxonomy-service/app/controllers/v3/FrameworkController.scala index 361403b1c..3d5974e54 100644 --- a/taxonomy-api/taxonomy-service/app/controllers/v3/FrameworkController.scala +++ b/taxonomy-api/taxonomy-service/app/controllers/v3/FrameworkController.scala @@ -1,19 +1,28 @@ package controllers.v3 +import akka.actor.{ActorRef, ActorSystem} import scala.concurrent.{ExecutionContext, Future} import controllers.BaseController -import javax.inject.{Inject, Singleton} +import javax.inject.{Inject, Named, Singleton} import org.sunbird.common.dto.ResponseHandler +import org.sunbird.utils.Constants import play.api.mvc.ControllerComponents -import utils.JavaJsonUtils +import utils.{ActorNames, ApiId, JavaJsonUtils} +import java.util @Singleton -class FrameworkController @Inject()(cc: ControllerComponents)(implicit exec: ExecutionContext) extends BaseController(cc) { - - def createFramework() = Action.async { implicit request => - val result = ResponseHandler.OK() - val response = JavaJsonUtils.serialize(result) - Future(Ok(response).as("application/json")) +class FrameworkController @Inject()(@Named(ActorNames.FRAMEWORK_ACTOR) frameworkActor: ActorRef, cc: ControllerComponents, actorSystem: ActorSystem)(implicit exec: ExecutionContext) extends BaseController(cc) { + + val objectType = "Framework" + + def createFramework()= Action.async { implicit request => + val headers = commonHeaders() + val body = requestBody() + val framework = body.getOrDefault(Constants.FRAMEWORK, new util.HashMap()).asInstanceOf[java.util.Map[String, Object]] + framework.putAll(headers) + val frameworkRequest = getRequest(framework, headers, Constants.CREATE_FRAMEWORK) + setRequestContext(frameworkRequest, Constants.FRAMEWORK_SCHEMA_VERSION, objectType, Constants.FRAMEWORK_SCHEMA_NAME) + getResult(ApiId.CREATE_FRAMEWORK, frameworkActor, frameworkRequest) } def readFramework(identifier: String, categories: Option[String]) = Action.async { implicit request => diff --git a/taxonomy-api/taxonomy-service/app/modules/TaxonomyModule.scala b/taxonomy-api/taxonomy-service/app/modules/TaxonomyModule.scala index 25f2cd660..03c96326d 100644 --- a/taxonomy-api/taxonomy-service/app/modules/TaxonomyModule.scala +++ b/taxonomy-api/taxonomy-service/app/modules/TaxonomyModule.scala @@ -1,7 +1,7 @@ package modules import com.google.inject.AbstractModule -import org.sunbird.actors.{HealthActor, ObjectCategoryActor, ObjectCategoryDefinitionActor} +import org.sunbird.actors.{HealthActor, ObjectCategoryActor, ObjectCategoryDefinitionActor, FrameworkActor} import play.libs.akka.AkkaGuiceSupport import utils.ActorNames @@ -12,6 +12,7 @@ class TaxonomyModule extends AbstractModule with AkkaGuiceSupport { bindActor(classOf[HealthActor], ActorNames.HEALTH_ACTOR) bindActor(classOf[ObjectCategoryActor], ActorNames.OBJECT_CATEGORY_ACTOR) bindActor(classOf[ObjectCategoryDefinitionActor], ActorNames.OBJECT_CATEGORY_DEFINITION_ACTOR) + bindActor(classOf[FrameworkActor], ActorNames.FRAMEWORK_ACTOR) println("Initialized application actors for taxonomy service") } } diff --git a/taxonomy-api/taxonomy-service/app/utils/ActorNames.scala b/taxonomy-api/taxonomy-service/app/utils/ActorNames.scala index 0d3a17695..8619fe10e 100644 --- a/taxonomy-api/taxonomy-service/app/utils/ActorNames.scala +++ b/taxonomy-api/taxonomy-service/app/utils/ActorNames.scala @@ -5,5 +5,6 @@ object ActorNames { final val HEALTH_ACTOR = "healthActor" final val OBJECT_CATEGORY_ACTOR = "objectCategoryActor" final val OBJECT_CATEGORY_DEFINITION_ACTOR = "objectCategoryDefinitionActor" + final val FRAMEWORK_ACTOR = "frameworkActor" } diff --git a/taxonomy-api/taxonomy-service/app/utils/ApiId.scala b/taxonomy-api/taxonomy-service/app/utils/ApiId.scala index 2947f8da6..f81fcd583 100644 --- a/taxonomy-api/taxonomy-service/app/utils/ApiId.scala +++ b/taxonomy-api/taxonomy-service/app/utils/ApiId.scala @@ -9,5 +9,6 @@ object ApiId { final val CREATE_OBJECT_CATEGORY_DEFINITION = "api.object.category.definition.create" final val READ_OBJECT_CATEGORY_DEFINITION = "api.object.category.definition.read" final val UPDATE_OBJECT_CATEGORY_DEFINITION = "api.object.category.definition.update" + final val CREATE_FRAMEWORK = "ekstep.learning.framework.create" } From 3d9900ca341a104610115e0ae9c6f2f28ece102f Mon Sep 17 00:00:00 2001 From: Aiman Sharief Date: Tue, 9 May 2023 16:11:28 +0530 Subject: [PATCH 217/490] Issue #KN-10 fix: Updated youtubeurl --- .../src/main/scala/org/sunbird/cloudstore/StorageService.scala | 2 +- .../org/sunbird/url/mgr/impl/YouTubeURLManagerImplTest.java | 2 +- .../src/test/java/org/sunbird/url/util/YouTubeUrlUtilTest.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala index 0eea29c95..0fad2e4d8 100644 --- a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala +++ b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala @@ -22,7 +22,7 @@ class StorageService { val storageSecret = Platform.config.getString("cloud_storage_secret") // TODO: endPoint defined to support "cephs3". Make code changes after cloud-store-sdk 2.11 support it. val endPoint = if (Platform.config.hasPath("cloud_storage_endpoint")) Option(Platform.config.getString("cloud_storage_endpoint")) else None - storageService = StorageServiceFactory.getStorageService(new StorageConfig(storageType, storageKey, storageSecret)) + storageService = StorageServiceFactory.getStorageService(new StorageConfig(storageType, storageKey, storageSecret, endPoint)) } storageService } diff --git a/platform-modules/url-manager/src/test/java/org/sunbird/url/mgr/impl/YouTubeURLManagerImplTest.java b/platform-modules/url-manager/src/test/java/org/sunbird/url/mgr/impl/YouTubeURLManagerImplTest.java index 4dccab286..adcefd43f 100644 --- a/platform-modules/url-manager/src/test/java/org/sunbird/url/mgr/impl/YouTubeURLManagerImplTest.java +++ b/platform-modules/url-manager/src/test/java/org/sunbird/url/mgr/impl/YouTubeURLManagerImplTest.java @@ -26,7 +26,7 @@ public class YouTubeURLManagerImplTest { @Test public void testValidateUrlWithValidUrlValidCriteria() { - String videoUrl = "https://www.youtube.com/watch?v=owr198WQpM8"; + String videoUrl = "https://www.youtube.com/watch?v=GHmQ8euNwv8"; Map result = youtubeMgr.validateURL(videoUrl, "license"); assertTrue(MapUtils.isNotEmpty(result)); assertTrue(result.size() == 2); diff --git a/platform-modules/url-manager/src/test/java/org/sunbird/url/util/YouTubeUrlUtilTest.java b/platform-modules/url-manager/src/test/java/org/sunbird/url/util/YouTubeUrlUtilTest.java index 913e2bb0b..037fdcaa5 100644 --- a/platform-modules/url-manager/src/test/java/org/sunbird/url/util/YouTubeUrlUtilTest.java +++ b/platform-modules/url-manager/src/test/java/org/sunbird/url/util/YouTubeUrlUtilTest.java @@ -26,7 +26,7 @@ public class YouTubeUrlUtilTest { @Test public void testGetLicenseWithValidUrlPattern1() { - String videoUrl = "https://www.youtube.com/watch?v=owr198WQpM8"; + String videoUrl = "https://www.youtube.com/watch?v=GHmQ8euNwv8"; String result = YouTubeUrlUtil.getLicense(videoUrl); assertEquals("creativeCommon", result); } From f633e69a66451f79d4cffb2837576b41068290bf Mon Sep 17 00:00:00 2001 From: Aiman Sharief Date: Tue, 9 May 2023 16:15:17 +0530 Subject: [PATCH 218/490] Delete StorageService.scala --- .../sunbird/cloudstore/StorageService.scala | 89 ------------------- 1 file changed, 89 deletions(-) delete mode 100644 platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala diff --git a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala deleted file mode 100644 index 0fad2e4d8..000000000 --- a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala +++ /dev/null @@ -1,89 +0,0 @@ -package org.sunbird.cloudstore - -import org.apache.commons.lang3.StringUtils -import org.apache.tika.Tika -import org.sunbird.cloud.storage.BaseStorageService -import org.sunbird.cloud.storage.factory.{StorageConfig, StorageServiceFactory} -import org.sunbird.common.{Platform, Slug} -import org.sunbird.common.exception.ServerException - -import java.io.File -import scala.concurrent.{ExecutionContext, Future} - -class StorageService { - - val storageType: String = if (Platform.config.hasPath("cloud_storage_type")) Platform.config.getString("cloud_storage_type") else "" - var storageService: BaseStorageService = null - - @throws[Exception] - def getService: BaseStorageService = { - if (null == storageService) { - val storageKey = Platform.config.getString("cloud_storage_key") - val storageSecret = Platform.config.getString("cloud_storage_secret") - // TODO: endPoint defined to support "cephs3". Make code changes after cloud-store-sdk 2.11 support it. - val endPoint = if (Platform.config.hasPath("cloud_storage_endpoint")) Option(Platform.config.getString("cloud_storage_endpoint")) else None - storageService = StorageServiceFactory.getStorageService(new StorageConfig(storageType, storageKey, storageSecret, endPoint)) - } - storageService - } - - def getContainerName: String = { - if(Platform.config.hasPath("cloud_storage_container")) - Platform.config.getString("cloud_storage_container") - else - throw new ServerException("ERR_INVALID_CLOUD_STORAGE", "Cloud Storage Container name not configured.") - } - - def uploadFile(folderName: String, file: File, slug: Option[Boolean] = Option(true)): Array[String] = { - val slugFile = if (slug.getOrElse(true)) Slug.createSlugFile(file) else file - val objectKey = folderName + "/" + slugFile.getName - val url = getService.upload(getContainerName, slugFile.getAbsolutePath, objectKey, Option.apply(false), Option.apply(1), Option.apply(5), Option.empty) - Array[String](objectKey, url) - } - - def uploadDirectory(folderName: String, directory: File, slug: Option[Boolean] = Option(true)): Array[String] = { - val slugFile = if (slug.getOrElse(true)) Slug.createSlugFile(directory) else directory - val objectKey = folderName + File.separator - val url = getService.upload(getContainerName, slugFile.getAbsolutePath, objectKey, Option.apply(true), Option.apply(1), Option.apply(5), Option.empty) - Array[String](objectKey, url) - } - - def uploadDirectoryAsync(folderName: String, directory: File, slug: Option[Boolean] = Option(true))(implicit ec: ExecutionContext): Future[List[String]] = { - val slugFile = if (slug.getOrElse(true)) Slug.createSlugFile(directory) else directory - val objectKey = folderName + File.separator - getService.uploadFolder(getContainerName, slugFile.getAbsolutePath, objectKey, Option.apply(false), None, None, 1) - } - - def getObjectSize(key: String): Double = { - val blob = getService.getObject(getContainerName, key, Option.apply(false)) - blob.contentLength - } - - def copyObjectsByPrefix(source: String, destination: String): Unit = { - getService.copyObjects(getContainerName, source, getContainerName, destination, Option.apply(true)) - } - - def deleteFile(key: String, isDirectory: Option[Boolean] = Option(false)): Unit = { - getService.deleteObject(getContainerName, key, isDirectory) - } - - def getSignedURL(key: String, ttl: Option[Int], permission: Option[String]): String = { - getService.getPutSignedURL(getContainerName, key, ttl, permission, Option.apply(getMimeType(key))) - } - - def getUri(key: String): String = { - try { - getService.getUri(getContainerName, key, Option.apply(false)) - } catch { - case e:Exception => - println("StorageService --> getUri --> Exception: " + e.getMessage) - "" - } - } - - def getMimeType(fileName: String): String = { - val tika: Tika = new Tika() - tika.detect(fileName) - } - -} From e8755902d1c9dee6815b9c166815a083d0fd403c Mon Sep 17 00:00:00 2001 From: Aiman Sharief Date: Tue, 9 May 2023 16:20:03 +0530 Subject: [PATCH 219/490] Revert "Delete StorageService.scala" This reverts commit f633e69a66451f79d4cffb2837576b41068290bf. --- .../sunbird/cloudstore/StorageService.scala | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala diff --git a/platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala new file mode 100644 index 000000000..0fad2e4d8 --- /dev/null +++ b/platform-modules/mimetype-manager/src/main/scala/org/sunbird/cloudstore/StorageService.scala @@ -0,0 +1,89 @@ +package org.sunbird.cloudstore + +import org.apache.commons.lang3.StringUtils +import org.apache.tika.Tika +import org.sunbird.cloud.storage.BaseStorageService +import org.sunbird.cloud.storage.factory.{StorageConfig, StorageServiceFactory} +import org.sunbird.common.{Platform, Slug} +import org.sunbird.common.exception.ServerException + +import java.io.File +import scala.concurrent.{ExecutionContext, Future} + +class StorageService { + + val storageType: String = if (Platform.config.hasPath("cloud_storage_type")) Platform.config.getString("cloud_storage_type") else "" + var storageService: BaseStorageService = null + + @throws[Exception] + def getService: BaseStorageService = { + if (null == storageService) { + val storageKey = Platform.config.getString("cloud_storage_key") + val storageSecret = Platform.config.getString("cloud_storage_secret") + // TODO: endPoint defined to support "cephs3". Make code changes after cloud-store-sdk 2.11 support it. + val endPoint = if (Platform.config.hasPath("cloud_storage_endpoint")) Option(Platform.config.getString("cloud_storage_endpoint")) else None + storageService = StorageServiceFactory.getStorageService(new StorageConfig(storageType, storageKey, storageSecret, endPoint)) + } + storageService + } + + def getContainerName: String = { + if(Platform.config.hasPath("cloud_storage_container")) + Platform.config.getString("cloud_storage_container") + else + throw new ServerException("ERR_INVALID_CLOUD_STORAGE", "Cloud Storage Container name not configured.") + } + + def uploadFile(folderName: String, file: File, slug: Option[Boolean] = Option(true)): Array[String] = { + val slugFile = if (slug.getOrElse(true)) Slug.createSlugFile(file) else file + val objectKey = folderName + "/" + slugFile.getName + val url = getService.upload(getContainerName, slugFile.getAbsolutePath, objectKey, Option.apply(false), Option.apply(1), Option.apply(5), Option.empty) + Array[String](objectKey, url) + } + + def uploadDirectory(folderName: String, directory: File, slug: Option[Boolean] = Option(true)): Array[String] = { + val slugFile = if (slug.getOrElse(true)) Slug.createSlugFile(directory) else directory + val objectKey = folderName + File.separator + val url = getService.upload(getContainerName, slugFile.getAbsolutePath, objectKey, Option.apply(true), Option.apply(1), Option.apply(5), Option.empty) + Array[String](objectKey, url) + } + + def uploadDirectoryAsync(folderName: String, directory: File, slug: Option[Boolean] = Option(true))(implicit ec: ExecutionContext): Future[List[String]] = { + val slugFile = if (slug.getOrElse(true)) Slug.createSlugFile(directory) else directory + val objectKey = folderName + File.separator + getService.uploadFolder(getContainerName, slugFile.getAbsolutePath, objectKey, Option.apply(false), None, None, 1) + } + + def getObjectSize(key: String): Double = { + val blob = getService.getObject(getContainerName, key, Option.apply(false)) + blob.contentLength + } + + def copyObjectsByPrefix(source: String, destination: String): Unit = { + getService.copyObjects(getContainerName, source, getContainerName, destination, Option.apply(true)) + } + + def deleteFile(key: String, isDirectory: Option[Boolean] = Option(false)): Unit = { + getService.deleteObject(getContainerName, key, isDirectory) + } + + def getSignedURL(key: String, ttl: Option[Int], permission: Option[String]): String = { + getService.getPutSignedURL(getContainerName, key, ttl, permission, Option.apply(getMimeType(key))) + } + + def getUri(key: String): String = { + try { + getService.getUri(getContainerName, key, Option.apply(false)) + } catch { + case e:Exception => + println("StorageService --> getUri --> Exception: " + e.getMessage) + "" + } + } + + def getMimeType(fileName: String): String = { + val tika: Tika = new Tika() + tika.detect(fileName) + } + +} From 1d04d265f9ed7586b767daec46f3eac5871e59c9 Mon Sep 17 00:00:00 2001 From: Aiman Sharief Date: Tue, 9 May 2023 16:30:08 +0530 Subject: [PATCH 220/490] Updated Cloud-store-sdk version --- platform-modules/mimetype-manager/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform-modules/mimetype-manager/pom.xml b/platform-modules/mimetype-manager/pom.xml index ea978cbd5..cae89374c 100644 --- a/platform-modules/mimetype-manager/pom.xml +++ b/platform-modules/mimetype-manager/pom.xml @@ -30,7 +30,7 @@ org.sunbird cloud-store-sdk - 1.4.3 + 1.4.6 org.scala-lang From 8a7a3bc699784088eba6d6d5fc4a146e558823ca Mon Sep 17 00:00:00 2001 From: Aiman Sharief Date: Tue, 9 May 2023 17:17:56 +0530 Subject: [PATCH 221/490] Update YouTubeUrlUtilTest.java --- .../src/test/java/org/sunbird/url/util/YouTubeUrlUtilTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform-modules/url-manager/src/test/java/org/sunbird/url/util/YouTubeUrlUtilTest.java b/platform-modules/url-manager/src/test/java/org/sunbird/url/util/YouTubeUrlUtilTest.java index 037fdcaa5..7f72e5f48 100644 --- a/platform-modules/url-manager/src/test/java/org/sunbird/url/util/YouTubeUrlUtilTest.java +++ b/platform-modules/url-manager/src/test/java/org/sunbird/url/util/YouTubeUrlUtilTest.java @@ -92,7 +92,7 @@ public void testIsValidLicenseWithInvalidLicense() { @Test public void testGetVideoListWithValidVideoId(){ - String videoId = "owr198WQpM8"; + String videoId = "GHmQ8euNwv8"; List