NTP-7108 | Add Money Manager feature to the SuperApp (#12804)

Signed-off-by: namankhurmi <naman.khurmi@navi.com>
Signed-off-by: kishan kumar <kishan.kumar@navi.com>
Co-authored-by: Sanjay P <sanjay.p@navi.com>
Co-authored-by: Venkat Praneeth Reddy <venkat.praneeth@navi.com>
Co-authored-by: Naman Khurmi <naman.khurmi@navi.com>
Co-authored-by: Ankit Yadav <ankit.yadav@navi.com>
Co-authored-by: Hitesh Kumar <hitesh.kumar@navi.com>
Co-authored-by: nikhil kumar <nikhil.kumar@navi.com>
Co-authored-by: Abhinav Gupta <abhinav.g@navi.com>
Co-authored-by: Kishan Kumar <kishan.kumar@navi.com>
Co-authored-by: Soumya Ranjan Patra <soumya.ranjan@navi.com>
Co-authored-by: Girish Suragani <girish.suragani@navi.com>
Co-authored-by: Aparna Vadlamani <aparna.vadlamani@navi.com>
Co-authored-by: Siddiboina Susai <siddiboina.susai@navi.com>
Co-authored-by: Ayushman Sharma <ayushman.sharma@navi.com>
Co-authored-by: Kamalesh Garnayak <kamalesh.garnayak@navi.com>
This commit is contained in:
Shivam Goyal
2024-12-17 18:26:11 +05:30
committed by GitHub
parent 67fa6618ae
commit 926a6d11c5
368 changed files with 30249 additions and 66 deletions

1
android/navi-code/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

View File

@@ -0,0 +1,17 @@
plugins {
alias libs.plugins.kotlin.jvm
}
sourceSets {
main {
java {
srcDir "${buildDir.absolutePath}/generated/ksp/"
}
}
}
dependencies {
implementation libs.ksp.symbolProcessingApi
implementation libs.squareup.kotlinpoet
implementation libs.squareup.kotlinpoet.ksp
}

View File

@@ -0,0 +1,16 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.code.annotations
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.SOURCE)
annotation class AutoGenerate
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.SOURCE)
annotation class EventName(val name: String)

View File

@@ -0,0 +1,19 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.code.generation
import com.google.devtools.ksp.processing.SymbolProcessor
import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
import com.google.devtools.ksp.processing.SymbolProcessorProvider
import com.navi.code.processors.EventTrackerProcessor
class EventTrackerProcessorProvider : SymbolProcessorProvider {
override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor {
return EventTrackerProcessor(environment.codeGenerator, environment.logger)
}
}

View File

@@ -0,0 +1,140 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.code.processors
import com.google.devtools.ksp.processing.CodeGenerator
import com.google.devtools.ksp.processing.Dependencies
import com.google.devtools.ksp.processing.KSPLogger
import com.google.devtools.ksp.processing.Resolver
import com.google.devtools.ksp.processing.SymbolProcessor
import com.google.devtools.ksp.symbol.ClassKind
import com.google.devtools.ksp.symbol.KSAnnotated
import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.google.devtools.ksp.symbol.KSFunctionDeclaration
import com.google.devtools.ksp.validate
import com.navi.code.annotations.AutoGenerate
import com.navi.code.annotations.EventName
import com.navi.code.utils.isDefaultKotlinFunction
import com.navi.code.utils.toSnakeCase
import com.squareup.kotlinpoet.FileSpec
import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.KModifier
import com.squareup.kotlinpoet.ParameterSpec
import com.squareup.kotlinpoet.TypeSpec
import com.squareup.kotlinpoet.ksp.toClassName
import java.io.OutputStreamWriter
class EventTrackerProcessor(
private val codeGenerator: CodeGenerator,
private val logger: KSPLogger
) : SymbolProcessor {
override fun process(resolver: Resolver): List<KSAnnotated> {
logger.info("Starting the processing of symbols with @AutoGenerate annotation.")
val symbols =
resolver
.getSymbolsWithAnnotation(AutoGenerate::class.qualifiedName!!)
.filterIsInstance<KSClassDeclaration>()
.validateAndGetSymbols()
symbols.forEach(::generateAnalyticsClass)
return symbols.filterNot { it.validate() }.toList()
}
private fun Sequence<KSClassDeclaration>.validateAndGetSymbols(): Sequence<KSClassDeclaration> {
return filter { classDeclaration ->
when (classDeclaration.classKind) {
ClassKind.INTERFACE -> true
else -> {
throw IllegalArgumentException(
"Don't mark ${AutoGenerate::class.simpleName} on non-interface kind: ${classDeclaration.simpleName.asString()}."
)
}
}
}
}
private fun generateAnalyticsClass(classDeclaration: KSClassDeclaration) {
val packageName = classDeclaration.packageName.asString()
val className = classDeclaration.simpleName.asString()
val generatedClassName = "${className}Impl"
val fileSpecBuilder =
FileSpec.builder(packageName, generatedClassName)
.addImport("com.navi.analytics.utils", "NaviTrackEvent")
val classBuilder =
TypeSpec.objectBuilder(generatedClassName).addModifiers(KModifier.INTERNAL)
classDeclaration
.getAllFunctions()
.filterNot { it.isDefaultKotlinFunction() }
.forEach { function -> classBuilder.addFunction(generateMethod(function)) }
fileSpecBuilder.addType(classBuilder.build())
val file =
codeGenerator.createNewFile(
dependencies = Dependencies.ALL_FILES,
packageName = packageName,
fileName = generatedClassName
)
OutputStreamWriter(file).use { writer -> fileSpecBuilder.build().writeTo(writer) }
}
private fun generateMethod(function: KSFunctionDeclaration): FunSpec {
val methodName = function.simpleName.asString()
val parameters = function.parameters
val eventName =
function.annotations
.find { it.shortName.asString() == EventName::class.simpleName }
?.arguments
?.firstOrNull()
?.value as? String
?: throw IllegalArgumentException("Event Name is missing for method $methodName.")
if (eventName.isEmpty()) {
throw IllegalArgumentException("Event Name is missing for method $methodName.")
}
val paramList =
parameters.map { param ->
val paramName =
param.name?.asString()
?: throw IllegalArgumentException(
"Parameter name is missing for method $methodName."
)
val paramType = param.type.resolve().toClassName()
ParameterSpec.builder(paramName, paramType).build()
}
val paramEntries =
parameters.joinToString(", ") { param ->
val paramName = param.name?.asString() ?: "param"
"\"${paramName.toSnakeCase()}\" to $paramName.toString()"
}
return FunSpec.builder(methodName)
.addParameters(paramList)
.addStatement(
"NaviTrackEvent.trackEventOnClickStream(eventName = %S, eventValues = mapOf($paramEntries))",
eventName
)
.build()
}
}

View File

@@ -0,0 +1,47 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.code.utils
import com.google.devtools.ksp.symbol.KSFunctionDeclaration
import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.KModifier
fun KSFunctionDeclaration.isDefaultKotlinFunction(): Boolean {
return simpleName.asString() in listOf("equals", "hashCode", "toString")
}
fun KSFunctionDeclaration.generateOverrideMethodWithDefaultSuperCall(): FunSpec {
val methodName = simpleName.asString()
val methodBuilder = FunSpec.builder(methodName).addModifiers(KModifier.OVERRIDE)
parameters.forEach { param ->
val paramName =
param.name?.asString()
?: throw IllegalArgumentException(
"Parameter name is missing for method $methodName."
)
val paramType = param.type.resolve()
methodBuilder.addParameter(
paramName,
ClassName(
paramType.declaration.packageName.asString(),
paramType.declaration.simpleName.asString()
)
)
}
methodBuilder.addCode(
"super.%L(%L)\n",
methodName,
parameters.joinToString(", ") { it.name?.asString().toString() }
)
return methodBuilder.build()
}

View File

@@ -0,0 +1,12 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.code.utils
fun String.toSnakeCase(): String {
return this.replace(Regex("([a-z])([A-Z])"), "$1_$2").lowercase()
}

View File

@@ -0,0 +1 @@
com.navi.code.generation.EventTrackerProcessorProvider