├── agent ├── README.md ├── src │ └── main │ │ ├── resources │ │ └── log4j2.properties │ │ └── java │ │ └── io │ │ └── keploy │ │ └── advice │ │ ├── ksql │ │ ├── SetDdlAuto_Advice.java │ │ ├── SetEnabled_Advice.java │ │ ├── RegisterDialect.java │ │ ├── HealthCheckInterceptor.java │ │ ├── DataBaseMetaData_Advice.java │ │ ├── RegisterDriverAdvice_Interceptor.java │ │ └── RegisterDriverAdvice.java │ │ ├── OkHttpAdvice_Kotlin.java │ │ └── CustomGoogleResponseAdvice.java └── pom.xml ├── integration ├── README.md ├── src │ └── main │ │ ├── java │ │ └── io │ │ │ └── keploy │ │ │ ├── googleMaps │ │ │ └── CustomHttpResponses.java │ │ │ ├── ksql │ │ │ ├── KSavepoint.java │ │ │ ├── KParameterMetaData.java │ │ │ ├── KDriver.java │ │ │ └── KResultSetMetaData.java │ │ │ ├── httpClients │ │ │ └── OkHttpInterceptor_Kotlin.java │ │ │ └── servlet │ │ │ └── KeployMiddleware.java │ │ └── resources │ │ └── log4j2.properties └── pom.xml ├── api ├── README.md ├── src │ └── main │ │ ├── resources │ │ └── log4j2.properties │ │ └── java │ │ └── io │ │ └── keploy │ │ └── service │ │ ├── mock │ │ ├── Config.java │ │ └── MockLib.java │ │ └── HttpPostMultipart.java └── pom.xml ├── core ├── README.md ├── src │ └── main │ │ ├── java │ │ └── io │ │ │ └── keploy │ │ │ └── regression │ │ │ ├── keploy │ │ │ ├── Config.java │ │ │ ├── ServerConfig.java │ │ │ ├── Filter.java │ │ │ ├── AppConfig.java │ │ │ └── Keploy.java │ │ │ ├── context │ │ │ ├── Context.java │ │ │ └── Kcontext.java │ │ │ ├── Mock.java │ │ │ ├── KeployInstance.java │ │ │ └── Mode.java │ │ └── resources │ │ └── log4j2.properties └── pom.xml ├── common ├── README.md ├── src │ └── main │ │ └── java │ │ └── io │ │ └── keploy │ │ └── utils │ │ ├── AssertKTests.java │ │ ├── MultipartContent.java │ │ ├── HaltThread.java │ │ ├── GenericResponseWrapper.java │ │ ├── ProcessSQL.java │ │ ├── HttpStatusReasons.java │ │ ├── Utility.java │ │ ├── GenericRequestWrapper.java │ │ └── MagicBytes.java └── pom.xml ├── src └── main │ └── resources │ └── Run_Configuration.png ├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── --feature-request.yaml │ └── --bug-report.yaml ├── Workflow │ └── build.yaml ├── License-Apache_2.0-blue.svg ├── workflows │ └── maven.yml ├── PULL_REQUEST_TEMPLATE.md └── slack.svg ├── SECURITY.md ├── keploy-sdk ├── .gitignore ├── src │ └── main │ │ └── resources │ │ └── META-INF │ │ └── MANIFEST.MF └── pom.xml ├── CONTRIBUTING.md ├── v2 ├── src │ └── main │ │ └── java │ │ └── io │ │ └── keploy │ │ └── JaCoCoUtil.java └── pom.xml ├── models ├── pom.xml └── src │ └── main │ └── java │ └── io │ └── keploy │ └── grpc │ └── proto │ └── service.proto ├── .gitignore ├── CODE_OF_CONDUCT.md ├── checkstyle-config.xml └── LICENSE /agent/README.md: -------------------------------------------------------------------------------- 1 | Module for java-agent. -------------------------------------------------------------------------------- /integration/README.md: -------------------------------------------------------------------------------- 1 | Module for instrumentation. -------------------------------------------------------------------------------- /api/README.md: -------------------------------------------------------------------------------- 1 | Module for GRPC or anyother Client to send data to the keploy server. -------------------------------------------------------------------------------- /core/README.md: -------------------------------------------------------------------------------- 1 | Module for core functionality like denoise, simulate, capture, etc. -------------------------------------------------------------------------------- /common/README.md: -------------------------------------------------------------------------------- 1 | Module for common classes like Exception,Serializer/Deserializer,Models, etc. -------------------------------------------------------------------------------- /src/main/resources/Run_Configuration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keploy/java-sdk/HEAD/src/main/resources/Run_Configuration.png -------------------------------------------------------------------------------- /integration/src/main/java/io/keploy/googleMaps/CustomHttpResponses.java: -------------------------------------------------------------------------------- 1 | package io.keploy.googleMaps; 2 | 3 | import okhttp3.Response; 4 | 5 | public class CustomHttpResponses { 6 | public static Response googleMapResponse; 7 | public static String googleMapResBody; 8 | } 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | contact_links: 2 | - name: Help and support 3 | url: https://github.com/keploy/keploy#community-support 4 | about: Reach out to us on our Slack channel or Discourse discussions or GitHub discussions. 5 | - name: Dedicated support 6 | url: mailto:hello@keploy.io 7 | about: Write to us if you'd like dedicated support using Keploy -------------------------------------------------------------------------------- /.github/Workflow/build.yaml: -------------------------------------------------------------------------------- 1 | name: Build Java SDK 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v3 11 | - name: Set up JDK 8 12 | uses: actions/setup-java@v3 13 | with: 14 | java-version: '8' 15 | distribution: 'adopt' 16 | - name: Build with Maven 17 | run: mvn clean install -------------------------------------------------------------------------------- /common/src/main/java/io/keploy/utils/AssertKTests.java: -------------------------------------------------------------------------------- 1 | package io.keploy.utils; 2 | 3 | import java.util.concurrent.atomic.AtomicBoolean; 4 | 5 | /** 6 | * This class help in providing the result of test recorded by keploy when run along the unit tests 7 | */ 8 | public class AssertKTests { 9 | public static final AtomicBoolean finalTestResult = new AtomicBoolean(false); 10 | 11 | public static boolean result() { 12 | return finalTestResult.get(); 13 | } 14 | } -------------------------------------------------------------------------------- /common/src/main/java/io/keploy/utils/MultipartContent.java: -------------------------------------------------------------------------------- 1 | package io.keploy.utils; 2 | 3 | 4 | public class MultipartContent { 5 | 6 | private final String fileName; 7 | private final byte[] body; 8 | 9 | public MultipartContent(String fileName, byte[] body) { 10 | this.fileName = fileName; 11 | this.body = body; 12 | } 13 | 14 | public String getFileName() { 15 | return fileName; 16 | } 17 | 18 | public byte[] getBody() { 19 | return body; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /core/src/main/java/io/keploy/regression/keploy/Config.java: -------------------------------------------------------------------------------- 1 | package io.keploy.regression.keploy; 2 | 3 | import lombok.Getter; 4 | import lombok.NoArgsConstructor; 5 | import lombok.Setter; 6 | 7 | /** 8 | * Class to store both Client and Server configurations 9 | */ 10 | @Getter 11 | @Setter 12 | @NoArgsConstructor 13 | public class Config { 14 | private AppConfig App; 15 | private ServerConfig Server; 16 | 17 | public Config(AppConfig app, ServerConfig server) { 18 | App = app; 19 | Server = server; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | We value security for the project very highly. We encourage all users to report any vulnerabilities they discover to us. 6 | If you find a security vulnerability in the Keploy project, please report it responsibly by sending an email to hello@keploy.io 7 | 8 | At this juncture, we don't have a bug bounty program. We are a small team trying to solve a big problem. We urge you to report any vulnerabilities responsibly 9 | so that we can continue building a secure application for the entire community. -------------------------------------------------------------------------------- /keploy-sdk/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /core/src/main/java/io/keploy/regression/keploy/ServerConfig.java: -------------------------------------------------------------------------------- 1 | package io.keploy.regression.keploy; 2 | 3 | import lombok.Getter; 4 | import lombok.NoArgsConstructor; 5 | import lombok.Setter; 6 | @Getter 7 | @Setter 8 | @NoArgsConstructor 9 | public class ServerConfig { 10 | // "https://api.keploy.io"; 11 | private String URL = "http://localhost:6789/api"; 12 | private String LicenseKey; 13 | private Boolean Denoise = false; 14 | 15 | public ServerConfig(String URL, String licenseKey,Boolean denoise) { 16 | this.URL = URL; 17 | LicenseKey = licenseKey; 18 | Denoise = denoise; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /agent/src/main/resources/log4j2.properties: -------------------------------------------------------------------------------- 1 | appenders=console 2 | appender.console.type=Console 3 | appender.console.name=STDOUT 4 | appender.console.layout.type=PatternLayout 5 | #appender.console.layout.pattern=%highlight{%d{HH:mm:ss.SSS}} [%thread] %-5level %logger{36} - %msg %n 6 | appender.console.layout.pattern=%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} %highlight{${LOG_LEVEL_PATTERN:-%5p}}{FATAL=red blink, ERROR=red, WARN=yellow bold, INFO=green, DEBUG=green bold, TRACE=blue} %style{}{magenta} [%M] %style{%40C}{cyan} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%xEx} 7 | rootLogger.level=info 8 | rootLogger.appenderRefs=stdout 9 | rootLogger.appenderRef.stdout.ref=STDOUT -------------------------------------------------------------------------------- /api/src/main/resources/log4j2.properties: -------------------------------------------------------------------------------- 1 | appenders=console 2 | appender.console.type=Console 3 | appender.console.name=STDOUT 4 | appender.console.layout.type=PatternLayout 5 | #appender.console.layout.pattern=%highlight{%d{HH:mm:ss.SSS}} [%thread] %-5level %logger{36} - %msg %n 6 | appender.console.layout.pattern=%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} %highlight{${LOG_LEVEL_PATTERN:-%5p}}{FATAL=red blink, ERROR=red, WARN=yellow bold, INFO=green, DEBUG=green bold, TRACE=blue} %style{}{magenta} [%M] %style{%40C}{cyan} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%xEx} 7 | rootLogger.level=info 8 | rootLogger.appenderRefs=stdout 9 | rootLogger.appenderRef.stdout.ref=STDOUT -------------------------------------------------------------------------------- /core/src/main/resources/log4j2.properties: -------------------------------------------------------------------------------- 1 | appenders=console 2 | appender.console.type=Console 3 | appender.console.name=STDOUT 4 | appender.console.layout.type=PatternLayout 5 | #appender.console.layout.pattern=%highlight{%d{HH:mm:ss.SSS}} [%thread] %-5level %logger{36} - %msg %n 6 | appender.console.layout.pattern=%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} %highlight{${LOG_LEVEL_PATTERN:-%5p}}{FATAL=red blink, ERROR=red, WARN=yellow bold, INFO=green, DEBUG=green bold, TRACE=blue} %style{}{magenta} [%M] %style{%40C}{cyan} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%xEx} 7 | rootLogger.level=info 8 | rootLogger.appenderRefs=stdout 9 | rootLogger.appenderRef.stdout.ref=STDOUT -------------------------------------------------------------------------------- /integration/src/main/resources/log4j2.properties: -------------------------------------------------------------------------------- 1 | appenders=console 2 | appender.console.type=Console 3 | appender.console.name=STDOUT 4 | appender.console.layout.type=PatternLayout 5 | #appender.console.layout.pattern=%highlight{%d{HH:mm:ss.SSS}} [%thread] %-5level %logger{36} - %msg %n 6 | appender.console.layout.pattern=%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} %highlight{${LOG_LEVEL_PATTERN:-%5p}}{FATAL=red blink, ERROR=red, WARN=yellow bold, INFO=green, DEBUG=green bold, TRACE=blue} %style{}{magenta} [%M] %style{%40C}{cyan} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%xEx} 7 | rootLogger.level=info 8 | rootLogger.appenderRefs=stdout 9 | rootLogger.appenderRef.stdout.ref=STDOUT -------------------------------------------------------------------------------- /core/src/main/java/io/keploy/regression/context/Context.java: -------------------------------------------------------------------------------- 1 | package io.keploy.regression.context; 2 | 3 | import lombok.Getter; 4 | import lombok.NoArgsConstructor; 5 | import lombok.Setter; 6 | 7 | @Getter 8 | @Setter 9 | @NoArgsConstructor 10 | public class Context { 11 | private static InheritableThreadLocal ctx = new InheritableThreadLocal<>(); 12 | 13 | public static void setCtx(Kcontext kcontext) { 14 | ctx.set(kcontext); 15 | } 16 | 17 | public static Kcontext getCtx() { 18 | return ctx.get(); 19 | } 20 | 21 | public static void cleanup() { 22 | if (ctx.get() != null) { 23 | ctx.remove(); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /core/src/main/java/io/keploy/regression/Mock.java: -------------------------------------------------------------------------------- 1 | package io.keploy.regression; 2 | 3 | import lombok.Getter; 4 | import lombok.NoArgsConstructor; 5 | import lombok.Setter; 6 | 7 | /** 8 | * Class to identify mock type 9 | */ 10 | @Getter 11 | @Setter 12 | @NoArgsConstructor 13 | public class Mock { 14 | 15 | public enum Version { 16 | V1_BETA1("api.keploy.io/v1beta1"); 17 | 18 | public final String value; 19 | 20 | Version(String value) { 21 | this.value = value; 22 | } 23 | } 24 | 25 | public enum Kind { 26 | HTTP_EXPORT("Http"), 27 | 28 | GENERIC_EXPORT("Generic"), 29 | 30 | SQL("SQL"); 31 | 32 | public final String value; 33 | 34 | Kind(String value) { 35 | this.value = value; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /core/src/main/java/io/keploy/regression/keploy/Filter.java: -------------------------------------------------------------------------------- 1 | package io.keploy.regression.keploy; 2 | import lombok.Getter; 3 | import lombok.NoArgsConstructor; 4 | import lombok.Setter; 5 | 6 | /** 7 | * Filter class to filter out few Headers and Urls while recording 8 | */ 9 | @Getter 10 | @Setter 11 | @NoArgsConstructor 12 | public class Filter { 13 | String[] acceptUrlRegex; 14 | String[] acceptHeaderRegex; 15 | String[] rejectHeaderRegex; 16 | String[] rejectUrlRegex; 17 | public Filter(String[] acceptUrlRegex, String[] acceptHeaderRegex, String[] rejectHeaderRegex, String[] rejectUrlRegex) { 18 | this.acceptUrlRegex = acceptUrlRegex; 19 | this.acceptHeaderRegex = acceptHeaderRegex; 20 | this.rejectHeaderRegex = rejectHeaderRegex; 21 | this.rejectUrlRegex = rejectUrlRegex; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /api/src/main/java/io/keploy/service/mock/Config.java: -------------------------------------------------------------------------------- 1 | package io.keploy.service.mock; 2 | 3 | import io.keploy.regression.Mode; 4 | import io.keploy.regression.context.Context; 5 | import io.keploy.regression.context.Kcontext; 6 | 7 | import java.util.Collections; 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | /** 12 | * This is a config class for mocking feature which provides all the configurations required for mocking feature. 13 | */ 14 | public class Config { 15 | 16 | public static Mode.ModeType mode; 17 | public static String Name = ""; 18 | public static Kcontext CTX = Context.getCtx(); 19 | public static String Path = ""; 20 | public static Boolean Overwrite = false; 21 | public static Map MockId = Collections.synchronizedMap(new HashMap<>()); 22 | public static String MockPath = ""; 23 | 24 | } 25 | -------------------------------------------------------------------------------- /core/src/main/java/io/keploy/regression/KeployInstance.java: -------------------------------------------------------------------------------- 1 | package io.keploy.regression; 2 | 3 | import io.keploy.regression.keploy.Keploy; 4 | 5 | /** 6 | * Keploy instance class 7 | */ 8 | public class KeployInstance { 9 | private static volatile KeployInstance keployInstance; 10 | 11 | private final Keploy keploy; 12 | 13 | 14 | private KeployInstance() { 15 | keploy = new Keploy(); 16 | } 17 | 18 | public static KeployInstance getInstance() { 19 | if (keployInstance == null) { 20 | synchronized (KeployInstance.class) { //thread safe. 21 | if (keployInstance == null) { 22 | keployInstance = new KeployInstance(); 23 | } 24 | } 25 | } 26 | return keployInstance; 27 | } 28 | 29 | public Keploy getKeploy() { 30 | return keploy; 31 | } 32 | } -------------------------------------------------------------------------------- /common/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | java-sdk 7 | io.keploy 8 | 1.0.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | common 13 | 14 | 1.8 15 | 1.8 16 | 17 | 18 | 19 | io.keploy 20 | core 21 | 1.0.0-SNAPSHOT 22 | compile 23 | 24 | 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/--feature-request.yaml: -------------------------------------------------------------------------------- 1 | name: Feature request 2 | description: Suggest a feature to improve Keploy 3 | title: "[feature]: " 4 | labels: [feature] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Thank you for taking the time to request a feature for Keploy 10 | - type: checkboxes 11 | attributes: 12 | label: Is there an existing feature request for this? 13 | description: Please search to see if an issue related to this feature request/feature request already exists 14 | options: 15 | - label: I have searched the existing issues 16 | required: true 17 | - type: textarea 18 | attributes: 19 | label: Summary 20 | description: One paragraph description of the feature 21 | validations: 22 | required: true 23 | - type: textarea 24 | attributes: 25 | label: Why should this be worked on? 26 | description: A concise description of the problems or use cases for this feature request 27 | validations: 28 | required: true -------------------------------------------------------------------------------- /core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | java-sdk 7 | io.keploy 8 | 1.0.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | core 13 | 14 | 15 | 1.8 16 | 1.8 17 | 18 | 19 | 20 | io.keploy 21 | models 22 | 1.0.0-SNAPSHOT 23 | compile 24 | 25 | 26 | -------------------------------------------------------------------------------- /common/src/main/java/io/keploy/utils/HaltThread.java: -------------------------------------------------------------------------------- 1 | package io.keploy.utils; 2 | 3 | import java.util.concurrent.CountDownLatch; 4 | 5 | /** 6 | * While Keploy is running tests in unit test file all the test recorded will be running in separate threads. Threads 7 | * are needed to be halted at the end as we need to capture the progress at once . HaltThread class is used for halting 8 | * threads 9 | */ 10 | public class HaltThread { 11 | private static volatile HaltThread haltThread; 12 | private final CountDownLatch countDownLatch; 13 | 14 | private HaltThread() { 15 | countDownLatch = new CountDownLatch(2); 16 | } 17 | 18 | public static HaltThread getInstance() { 19 | if (haltThread == null) { 20 | synchronized (HaltThread.class) { //thread safe. 21 | if (haltThread == null) { 22 | haltThread = new HaltThread(); 23 | } 24 | } 25 | } 26 | return haltThread; 27 | } 28 | 29 | public CountDownLatch getCountDownLatch() { 30 | return countDownLatch; 31 | } 32 | } -------------------------------------------------------------------------------- /keploy-sdk/src/main/resources/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Main-Class: 3 | Class-Path: perfmark-api-0.25.0.jar jline-3.21.0.jar grpc-protobuf-1.49. 4 | 2.jar javax.annotation-api-1.3.2.jar listenablefuture-9999.0-empty-to-a 5 | void-conflict-with-guava.jar j2objc-annotations-1.3.jar gson-2.9.0.jar 6 | guava-31.1-android.jar log4j-api-2.18.0.jar kotlin-stdlib-common-1.5.31 7 | .jar annotations-13.0.jar byte-buddy-agent-1.12.14.jar grpc-stub-1.49.2 8 | .jar animal-sniffer-annotations-1.21.jar byte-buddy-1.12.14.jar grpc-co 9 | re-1.49.2.jar proto-google-common-protos-2.9.0.jar progressbar-0.9.5.ja 10 | r okio-jvm-3.0.0.jar annotations-4.1.1.4.jar kotlin-stdlib-jdk7-1.5.31. 11 | jar grpc-protobuf-lite-1.49.2.jar kotlin-stdlib-1.6.20.jar log4j-core-2 12 | .18.0.jar commons-io-2.11.0.jar grpc-context-1.49.2.jar mockito-all-1.1 13 | 0.19.jar jsr305-3.0.2.jar grpc-netty-shaded-1.49.2.jar kotlin-stdlib-jd 14 | k8-1.5.31.jar error_prone_annotations-2.14.0.jar okhttp-4.10.0.jar prot 15 | obuf-java-3.21.7.jar grpc-api-1.49.2.jar failureaccess-1.0.1.jar checke 16 | r-qual-3.12.0.jar 17 | 18 | -------------------------------------------------------------------------------- /.github/License-Apache_2.0-blue.svg: -------------------------------------------------------------------------------- 1 | License: Apache 2.0LicenseApache 2.0 -------------------------------------------------------------------------------- /.github/workflows/maven.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven 3 | 4 | name: JavaSDK build check 5 | 6 | on: 7 | push: 8 | branches: [ "main" ] 9 | pull_request: 10 | branches: [ "main" ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Set up JDK 11 20 | uses: actions/setup-java@v3 21 | with: 22 | java-version: '11' 23 | distribution: 'temurin' 24 | cache: maven 25 | - name: Build with Maven 26 | run: mvn -B package --file pom.xml 27 | 28 | checkstyle: 29 | name: checkstyle linting 30 | runs-on: ubuntu-latest 31 | steps: 32 | - uses: actions/checkout@v2 33 | - uses: dbelyaev/action-checkstyle@v0.6.1 34 | with: 35 | reporter: github-pr-review 36 | checkstyle_config: checkstyle-config.xml 37 | fail_on_error: true 38 | 39 | 40 | -------------------------------------------------------------------------------- /agent/src/main/java/io/keploy/advice/ksql/SetDdlAuto_Advice.java: -------------------------------------------------------------------------------- 1 | package io.keploy.advice.ksql; 2 | 3 | import net.bytebuddy.asm.Advice; 4 | 5 | import java.lang.reflect.Method; 6 | 7 | /** 8 | * This class is used for intercepting method setDdlAuto of HibernateProperties class and to replace an argument value to 9 | * a custom value on entry of setDdlAuto method. 10 | */ 11 | public class SetDdlAuto_Advice { 12 | 13 | /** 14 | * This method gets executed before the setDdlAuto method of HibernateProperties class. According to the 15 | * Keploy mode that is present, The argument value will be changed. 16 | */ 17 | @Advice.OnMethodEnter 18 | public static void enterMethod(@Advice.Origin Method method, @Advice.Argument(value = 0, readOnly = false) String ddlAuto) { 19 | ddlAuto = (System.getenv("KEPLOY_MODE").equals("test")) ? "none" : ddlAuto; 20 | } 21 | 22 | /** 23 | * This method gets executed after the setDdlAuto method of HibernateProperties class.This does nothing as we don't 24 | * want to change anything after the completion of the setDdlAuto method of HibernateProperties class. 25 | */ 26 | @Advice.OnMethodExit 27 | public static void exitMethod(@Advice.Origin Method method) { 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /agent/src/main/java/io/keploy/advice/ksql/SetEnabled_Advice.java: -------------------------------------------------------------------------------- 1 | package io.keploy.advice.ksql; 2 | 3 | import net.bytebuddy.asm.Advice; 4 | 5 | import java.lang.reflect.Method; 6 | 7 | /** 8 | * This class is used for intercepting method setEnabled of LiquibaseProperties class and to replace an argument value to 9 | * a custom value on entry of setEnabled method. 10 | */ 11 | public class SetEnabled_Advice { 12 | 13 | /** 14 | * This method gets executed before the setEnabled method of LiquibaseProperties class. According to the 15 | * Keploy mode that is present, The argument value will be changed 16 | */ 17 | @Advice.OnMethodEnter 18 | public static void enterMethod(@Advice.Origin Method method, @Advice.Argument(value = 0, readOnly = false) boolean enabled) { 19 | enabled = (System.getenv("KEPLOY_MODE").equals("test")) ? false : enabled; 20 | } 21 | 22 | /** 23 | * This method gets executed after the setEnabled method of LiquibaseProperties class.This does nothing as we don't 24 | * want to change anything after the completion of the setEnabled method of LiquibaseProperties class. 25 | */ 26 | @Advice.OnMethodExit 27 | public static void exitMethod(@Advice.Origin Method method) { 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /integration/src/main/java/io/keploy/ksql/KSavepoint.java: -------------------------------------------------------------------------------- 1 | package io.keploy.ksql; 2 | 3 | import org.apache.logging.log4j.LogManager; 4 | 5 | import java.sql.SQLException; 6 | import java.sql.Savepoint; 7 | 8 | import static io.keploy.ksql.KResultSet.msg1; 9 | import static io.keploy.ksql.KResultSet.msg2; 10 | 11 | /** 12 | * KSavepoint is a wrapper class for Savepoint used in SQL, this class helps in recording data in record mode 13 | * and providing data in test mode 14 | */ 15 | public class KSavepoint implements Savepoint { 16 | 17 | Savepoint wrappedSavepoint; 18 | private static final org.apache.logging.log4j.Logger logger = LogManager.getLogger(KSavepoint.class); 19 | public KSavepoint(Savepoint setSavepoint) { 20 | wrappedSavepoint = setSavepoint; 21 | } 22 | 23 | @Override 24 | public int getSavepointId() throws SQLException { 25 | logger.warn("{} int getSavepointId() throws SQLException {}", msg1, msg2); 26 | return wrappedSavepoint.getSavepointId(); 27 | } 28 | 29 | @Override 30 | public String getSavepointName() throws SQLException { 31 | logger.warn("{} String getSavepointName() throws SQLException {}", msg1, msg2); 32 | return wrappedSavepoint.getSavepointName(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /core/src/main/java/io/keploy/regression/context/Kcontext.java: -------------------------------------------------------------------------------- 1 | package io.keploy.regression.context; 2 | 3 | import io.keploy.grpc.stubs.Service; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | 8 | import javax.servlet.http.HttpServletRequest; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | /** 13 | * Class for Keploy context which provides all the configuration required by keploy 14 | */ 15 | @Getter 16 | @Setter 17 | @NoArgsConstructor 18 | public class Kcontext { 19 | 20 | private HttpServletRequest Request; 21 | 22 | private io.keploy.regression.Mode.ModeType Mode = io.keploy.regression.Mode.ModeType.MODE_RECORD; 23 | 24 | private Boolean FileExport = false; 25 | 26 | private String TestId; 27 | 28 | private List Deps = new ArrayList<>(); 29 | 30 | private List Mock = new ArrayList<>(); 31 | 32 | public Kcontext(HttpServletRequest request, io.keploy.regression.Mode.ModeType mode, Boolean fileExport, String testId, List deps, List mock) { 33 | Request = request; 34 | Mode = mode; 35 | FileExport = fileExport; 36 | TestId = testId; 37 | Deps = deps; 38 | Mock = mock; 39 | } 40 | } -------------------------------------------------------------------------------- /core/src/main/java/io/keploy/regression/keploy/AppConfig.java: -------------------------------------------------------------------------------- 1 | package io.keploy.regression.keploy; 2 | 3 | 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | 8 | import java.time.Duration; 9 | 10 | /** 11 | * All the information regarding the user application are stored here 12 | */ 13 | @Getter 14 | @Setter 15 | @NoArgsConstructor 16 | public class AppConfig { 17 | 18 | private String Name = "myApp"; 19 | 20 | private String Host = "localhost"; 21 | 22 | private String Port = "8080"; 23 | 24 | private String TestPath = ""; 25 | 26 | private String MockPath = ""; 27 | 28 | private String AssetPath = ""; 29 | 30 | private Duration Delay = Duration.ofSeconds(5); 31 | 32 | private Duration Timeout = Duration.ofSeconds(60); 33 | 34 | private Filter Filter; 35 | 36 | public AppConfig(String name, String host, String port, String testPath, String mockPath, String assetPath, Duration delay, Duration timeout, io.keploy.regression.keploy.Filter filter) { 37 | Name = name; 38 | Host = host; 39 | Port = port; 40 | TestPath = testPath; 41 | MockPath = mockPath; 42 | AssetPath = assetPath; 43 | Delay = delay; 44 | Timeout = timeout; 45 | Filter = filter; 46 | } 47 | } -------------------------------------------------------------------------------- /agent/src/main/java/io/keploy/advice/ksql/RegisterDialect.java: -------------------------------------------------------------------------------- 1 | package io.keploy.advice.ksql; 2 | 3 | 4 | import io.keploy.ksql.KDriver; 5 | import net.bytebuddy.asm.Advice; 6 | 7 | import java.lang.reflect.Constructor; 8 | import java.util.Map; 9 | 10 | /** 11 | * This class is used for intercepting constructor of JpaProperties class and to modify the value of a field value of 12 | * the class on exit of that constructor method. 13 | */ 14 | public class RegisterDialect { 15 | 16 | /** 17 | * This method gets executed before the constructor of JpaProperties class.This does nothing as we don't 18 | * want to change anything before the invocation of JpaProperties constructor. 19 | */ 20 | @Advice.OnMethodEnter 21 | static void enterMethods(@Advice.Origin Constructor constructor) throws Exception { 22 | } 23 | 24 | /** 25 | * This method gets executed after constructor of JpaProperties class and modifies the value of the field - properties. 26 | * 27 | * @param properties - a field in JpaProperties class 28 | */ 29 | @Advice.OnMethodExit 30 | static void exitMethods(@Advice.Origin Constructor constructor, @Advice.FieldValue(readOnly = false, value = "properties") Map properties) throws Exception { 31 | properties.put("hibernate.dialect", KDriver.Dialect); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /agent/src/main/java/io/keploy/advice/ksql/HealthCheckInterceptor.java: -------------------------------------------------------------------------------- 1 | package io.keploy.advice.ksql; 2 | 3 | import net.bytebuddy.asm.Advice; 4 | 5 | import java.lang.reflect.Method; 6 | 7 | /** 8 | * This class is used for intercepting method withDetail of Health$Builder class and to replace an argument value to 9 | * a custom value on entry of that withDetail method. 10 | */ 11 | public class HealthCheckInterceptor { 12 | 13 | /** 14 | * This method gets executed before the withDetail method of Health$Builder class.In test mode of keploy this method 15 | * will throw error as we don't get health check from database. so we mock the health check response and feed it to 16 | * the method. Whenever the value of the health check argument is NULL we replace it with "HI" so that the method 17 | * never fails 18 | */ 19 | @Advice.OnMethodEnter 20 | public static void enterMethod(@Advice.Origin Method method, @Advice.Argument(value = 0, readOnly = false) String key, @Advice.Argument(value = 1, readOnly = false) Object value) { 21 | if (value == null) { 22 | value = "HI"; 23 | } 24 | } 25 | 26 | /** 27 | * This method gets executed after the withDetail method of Health$Builder class.This does nothing as we don't 28 | * want to change anything after the completion of withDetail method of Health$Builder class. 29 | */ 30 | @Advice.OnMethodExit 31 | public static void exitMethod(@Advice.Origin Method method) { 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /agent/src/main/java/io/keploy/advice/OkHttpAdvice_Kotlin.java: -------------------------------------------------------------------------------- 1 | package io.keploy.advice; 2 | 3 | import io.keploy.httpClients.OkHttpInterceptor_Kotlin; 4 | import net.bytebuddy.asm.Advice; 5 | import okhttp3.OkHttpClient; 6 | import org.apache.logging.log4j.LogManager; 7 | import org.apache.logging.log4j.Logger; 8 | 9 | import java.lang.reflect.Constructor; 10 | 11 | 12 | /** 13 | * This class is used for intercepting constructor of OkHttpClient$Builder class and to add an interceptor to its builder 14 | */ 15 | public class OkHttpAdvice_Kotlin { 16 | 17 | /** 18 | * This method gets executed before the constructor of OkHttpClient$Builder class.This does nothing as we don't 19 | * want to change anything before the invocation of OkHttpClient$Builder constructor. 20 | */ 21 | @Advice.OnMethodEnter 22 | static void enterMethods(@Advice.Origin Constructor constructor) throws Exception { 23 | } 24 | 25 | /** 26 | * This method gets executed after constructor of OkHttpClient$Builder class and Adds a interceptor to its builder 27 | * 28 | * @param builder - OkHttpClient.Builder 29 | */ 30 | @Advice.OnMethodExit 31 | static void exitMethods(@Advice.Origin Constructor constructor, @Advice.This OkHttpClient.Builder builder) { 32 | final Logger logger = LogManager.getLogger(OkHttpAdvice_Kotlin.class); 33 | 34 | logger.debug("inside OnMethodExitAdvice of OkHttpAdvice_Kotlin for constructor: {}", constructor); 35 | 36 | OkHttpInterceptor_Kotlin okHttpInterceptor = new OkHttpInterceptor_Kotlin(); 37 | builder.addInterceptor(okHttpInterceptor); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /core/src/main/java/io/keploy/regression/Mode.java: -------------------------------------------------------------------------------- 1 | 2 | package io.keploy.regression; 3 | 4 | import io.keploy.regression.context.Context; 5 | import org.apache.logging.log4j.LogManager; 6 | import org.apache.logging.log4j.Logger; 7 | 8 | /** 9 | * Class for Keploy Mode 10 | */ 11 | public class Mode { 12 | 13 | private static final Logger logger = LogManager.getLogger(io.keploy.regression.Mode.class); 14 | 15 | private static ModeType modeType; 16 | 17 | public Mode() { 18 | 19 | } 20 | 21 | public Mode(ModeType mode) { 22 | modeType = mode; 23 | } 24 | 25 | public static ModeType getMode() { 26 | return modeType; 27 | } 28 | 29 | public static void setMode(ModeType mode) { 30 | if (!isValid(mode)) { 31 | return; 32 | } 33 | modeType = mode; 34 | } 35 | 36 | public static boolean isValid(ModeType mode) { 37 | return mode == ModeType.MODE_RECORD || mode == ModeType.MODE_TEST || mode == ModeType.MODE_OFF; 38 | } 39 | 40 | public static void setTestMode() { 41 | setMode(ModeType.MODE_TEST); 42 | } 43 | 44 | public enum ModeType { 45 | MODE_RECORD("record"), 46 | MODE_TEST("test"), 47 | MODE_OFF("off"); 48 | 49 | public final String value; 50 | 51 | ModeType(String val) { 52 | this.value = val; 53 | } 54 | 55 | public ModeType getModeFromContext() { 56 | if (Context.getCtx() == null) { 57 | logger.error("failed to get keploy context"); 58 | return ModeType.MODE_OFF; 59 | } 60 | return Context.getCtx().getMode(); 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/--bug-report.yaml: -------------------------------------------------------------------------------- 1 | name: Bug report 2 | description: Create a bug report to help us improve Keploy 3 | title: "[bug]: " 4 | labels: [bug] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Thank you for taking the time to fill out this bug report. 10 | - type: checkboxes 11 | attributes: 12 | label: Is there an existing issue for this? 13 | description: Please search to see if an issue already exists for the bug you encountered 14 | options: 15 | - label: I have searched the existing issues 16 | required: true 17 | - type: textarea 18 | attributes: 19 | label: Current behavior 20 | description: A concise description of what you're experiencing and what you expect 21 | placeholder: | 22 | When I do , happens and I see the error message attached below: 23 | ```...``` 24 | What I expect is 25 | validations: 26 | required: true 27 | - type: textarea 28 | attributes: 29 | label: Steps to reproduce 30 | description: Add steps to reproduce this behaviour, include console or network logs and screenshots 31 | placeholder: | 32 | 1. Go to '...' 33 | 2. Click on '....' 34 | 3. Scroll down to '....' 35 | 4. See error 36 | validations: 37 | required: true 38 | - type: dropdown 39 | id: env 40 | attributes: 41 | label: Environment 42 | options: 43 | - Production 44 | - Release 45 | - Deploy preview 46 | validations: 47 | required: false 48 | - type: dropdown 49 | id: version 50 | attributes: 51 | label: Version 52 | options: 53 | - Cloud 54 | - Self-hosted 55 | - Local 56 | validations: 57 | required: true -------------------------------------------------------------------------------- /agent/src/main/java/io/keploy/advice/ksql/DataBaseMetaData_Advice.java: -------------------------------------------------------------------------------- 1 | package io.keploy.advice.ksql; 2 | 3 | import io.keploy.ksql.KDatabaseMetaData; 4 | import net.bytebuddy.asm.Advice; 5 | import org.apache.logging.log4j.LogManager; 6 | import org.apache.logging.log4j.Logger; 7 | 8 | import java.lang.reflect.Constructor; 9 | import java.sql.DatabaseMetaData; 10 | 11 | 12 | /** 13 | * This class is used for intercepting constructor of NewProxyDatabaseMetaData class and to replace value of a field to 14 | * a custom value i.e. KDatabaseMetaData on exit of that constructor method. 15 | */ 16 | public class DataBaseMetaData_Advice { 17 | 18 | /** 19 | * This method gets executed before the constructor of NewProxyDatabaseMetaData class.This does nothing as we don't 20 | * want to change anything before the invocation of NewProxyDatabaseMetaData constructor. 21 | */ 22 | @Advice.OnMethodEnter 23 | static void enterMethods(@Advice.Origin Constructor constructor) throws Exception { 24 | } 25 | 26 | /** 27 | * This method gets executed after constructor of NewProxyDatabaseMetaData class and replaces the value of field metaData 28 | * to KDatabaseMetaData . 29 | * 30 | * @param metaData - a field in NewProxyDatabaseMetaData class 31 | */ 32 | @Advice.OnMethodExit 33 | static void exitMethods(@Advice.Origin Constructor constructor, @Advice.FieldValue(value = "inner", readOnly = false) DatabaseMetaData metaData) { 34 | final Logger logger = LogManager.getLogger(DataBaseMetaData_Advice.class); 35 | logger.debug("inside OnMethodExitAdvice for constructor: {}", constructor); 36 | metaData = new KDatabaseMetaData(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /core/src/main/java/io/keploy/regression/keploy/Keploy.java: -------------------------------------------------------------------------------- 1 | package io.keploy.regression.keploy; 2 | 3 | import io.keploy.regression.Mode; 4 | import io.keploy.grpc.stubs.Service; 5 | import lombok.Getter; 6 | import lombok.Setter; 7 | 8 | import java.util.*; 9 | 10 | @Getter 11 | @Setter 12 | public class Keploy { 13 | 14 | private Config cfg; 15 | private Map resp; 16 | private Map> deps; 17 | private Map> mocks; 18 | private Map mocktime; 19 | 20 | 21 | public Keploy() { 22 | resp = Collections.synchronizedMap(new HashMap<>()); 23 | deps = Collections.synchronizedMap(new HashMap<>()); 24 | mocks = Collections.synchronizedMap(new HashMap<>()); 25 | mocktime = Collections.synchronizedMap(new HashMap<>()); 26 | 27 | initMode(); 28 | } 29 | 30 | private void initMode() { 31 | 32 | // checking because user may be setting while running in test mode 33 | if (Mode.getMode() == null) { 34 | String envMode = "off"; 35 | if (System.getenv("KEPLOY_MODE") != null) { 36 | envMode = System.getenv("KEPLOY_MODE"); 37 | } 38 | 39 | envMode = envMode.trim(); 40 | 41 | switch (envMode) { 42 | case "record": 43 | Mode.setMode(Mode.ModeType.MODE_RECORD); 44 | break; 45 | case "test": 46 | Mode.setMode(Mode.ModeType.MODE_TEST); 47 | break; 48 | case "off": 49 | Mode.setMode(Mode.ModeType.MODE_OFF); 50 | break; 51 | } 52 | } 53 | } 54 | 55 | } -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Keploy 2 | 3 | Thank you for your interest in Keploy and for taking the time to contribute to this project. 🙌 4 | Keploy is a project by developers for developers and there are a lot of ways you can contribute. 5 | If you don't know where to start contributing, ask us on our [Slack channel](https://join.slack.com/t/keploy/shared_invite/zt-12rfbvc01-o54cOG0X1G6eVJTuI_orSA). 6 | 7 | ## Code of conduct 8 | 9 | Read our [Code of Conduct](CODE_OF_CONDUCT.md) before contributing 10 | 11 | ## How can I contribute? 12 | 13 | There are many ways in which you can contribute to Keploy. 14 | 15 | #### 🐛 Report a bug 16 | Report all issues through GitHub Issues using the [Report a Bug](https://github.com/keploy/keploy/issues/new?assignees=&labels=&template=bug_report.md&title=) template. 17 | To help resolve your issue as quickly as possible, read the template and provide all the requested information. 18 | 19 | #### 🛠 File a feature request 20 | We welcome all feature requests, whether it's to add new functionality to an existing extension or to offer an idea for a brand new extension. 21 | File your feature request through GitHub Issues using the [Feature Request](https://github.com/keploy/keploy/issues/new?assignees=&labels=&template=feature_request.md&title=) template. 22 | 23 | #### 📝 Improve the documentation 24 | In the process of shipping features quickly, we may forget to keep our docs up to date. You can help by suggesting improvements to our documentation using the [Documentation Improvement](https://github.com/keploy/docs/issues) template! 25 | 26 | #### ⚙️ Close a Bug / Feature issue 27 | We welcome contributions that help make keploy bug free & improve the experience of our users. You can also find issues tagged [Good First Issues](https://github.com/keploy/keploy/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22). -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Related Issue 2 | - Info about Issue or bug 3 | 4 | Closes: #[issue number that will be closed through this PR] 5 | 6 | #### Describe the changes you've made 7 | A clear and concise description of what you have done to successfully close your assigned issue. Any new files? or anything you feel to let us know! 8 | 9 | ## Type of change 10 | 11 | 15 | - [ ] Bug fix (non-breaking change which fixes an issue) 16 | - [ ] New feature (non-breaking change which adds functionality) 17 | - [ ] Code style update (formatting, local variables) 18 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 19 | - [ ] This change requires a documentation update 20 | 21 | ## How did you test your code changes? 22 | 23 | Please describe the tests(if any). Provide instructions how its affecting the coverage. 24 | 25 | #### Describe if there is any unusual behaviour of your code(Write `NA` if there isn't) 26 | A clear and concise description of it. 27 | 28 | ## Checklist: 29 | 33 | - [ ] My code follows the style guidelines of this project. 34 | - [ ] I have performed a self-review of my own code. 35 | - [ ] I have commented my code, particularly in hard-to-understand areas and used java doc. 36 | - [ ] I have made corresponding changes to the documentation. 37 | - [ ] My changes generate no new warnings. 38 | - [ ] I have added tests that prove my fix is effective or that my feature works. 39 | - [ ] New and existing unit tests pass locally with my changes. 40 | 41 | ## Screenshots (if any) 42 | 43 | | Original | Updated | 44 | |:-----------------------:|:--------------------------:| 45 | | **original screenshot** | updated screenshot | -------------------------------------------------------------------------------- /api/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | java-sdk 7 | io.keploy 8 | 1.0.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | api 13 | 14 | 1.8 15 | 1.8 16 | 17 | 18 | 19 | io.keploy 20 | core 21 | 1.0.0-SNAPSHOT 22 | compile 23 | 24 | 25 | io.keploy 26 | models 27 | 1.0.0-SNAPSHOT 28 | compile 29 | 30 | 31 | io.keploy 32 | common 33 | 1.0.0-SNAPSHOT 34 | compile 35 | 36 | 37 | 38 | com.google.protobuf 39 | protobuf-java 40 | 3.21.7 41 | 42 | 43 | 44 | io.grpc 45 | grpc-stub 46 | 1.49.2 47 | 48 | 49 | 50 | io.grpc 51 | grpc-core 52 | 1.49.2 53 | 54 | 55 | -------------------------------------------------------------------------------- /.github/slack.svg: -------------------------------------------------------------------------------- 1 | 2 | slack 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 19 | 20 | -------------------------------------------------------------------------------- /agent/src/main/java/io/keploy/advice/ksql/RegisterDriverAdvice_Interceptor.java: -------------------------------------------------------------------------------- 1 | package io.keploy.advice.ksql; 2 | 3 | import io.keploy.ksql.KDriver; 4 | import net.bytebuddy.implementation.bind.annotation.Origin; 5 | import net.bytebuddy.implementation.bind.annotation.SuperCall; 6 | 7 | import java.lang.reflect.Method; 8 | import java.util.concurrent.Callable; 9 | 10 | 11 | /** 12 | * This class is used for intercepting method determineDriverClassName of DataSourceProperties class and returns a 13 | * custom value instead of value returned by the determineDriverClassName method . 14 | */ 15 | public class RegisterDriverAdvice_Interceptor { 16 | 17 | /** 18 | * This method will get called instead of determineDriverClassName 19 | * @param client - original method client 20 | * @param method - contains all the details regarding original method 21 | * @return - path to Driver class 22 | */ 23 | public static String execute(@SuperCall Callable client, @Origin Method method) throws Exception { 24 | 25 | // Getting actual response from original method 26 | String s = client.call(); 27 | 28 | // Changing KDriver Dialect according to the response from original method 29 | if (s != null && !s.equals("io.keploy.ksql.KDriver")) { 30 | KDriver.DriverName = s; 31 | switch (s) { 32 | case "org.postgresql.Driver": 33 | KDriver.Dialect = "org.hibernate.dialect.PostgreSQLDialect"; 34 | break; 35 | case "com.mysql.cj.jdbc.Driver": 36 | case "com.mysql.jdbc.Driver": 37 | KDriver.Dialect = "org.hibernate.dialect.MySQLDialect"; 38 | break; 39 | case "oracle.jdbc.driver.OracleDriver": 40 | case "oracle.jdbc.OracleDriver": 41 | KDriver.Dialect = "org.hibernate.dialect.Oracle10gDialect"; 42 | break; 43 | case "org.h2.Driver": 44 | KDriver.Dialect = "org.hibernate.dialect.H2Dialect"; 45 | break; 46 | default: 47 | System.out.println("Dialect for driver: " + s + " is not supported yet"); 48 | } 49 | } 50 | 51 | // returning wrapped Driver class path 52 | return "io.keploy.ksql.KDriver"; 53 | } 54 | } -------------------------------------------------------------------------------- /agent/src/main/java/io/keploy/advice/ksql/RegisterDriverAdvice.java: -------------------------------------------------------------------------------- 1 | package io.keploy.advice.ksql; 2 | 3 | import io.keploy.ksql.KDriver; 4 | import net.bytebuddy.asm.Advice; 5 | 6 | import java.lang.reflect.Method; 7 | 8 | /** 9 | * This class is used for intercepting method setDriverClassName of DataSourceProperties class and to replace an argument value to 10 | * a custom value on entry of setDriverClassName method. 11 | */ 12 | public class RegisterDriverAdvice { 13 | 14 | /** 15 | * This method gets executed before the setDriverClassName method of DataSourceProperties class. According to the 16 | * driverClassName that is present, Dialect of KDriver is changed and driverClassName value is replaced with KDriver 17 | * path 18 | */ 19 | @Advice.OnMethodEnter 20 | public static void enterMethod(@Advice.Origin Method method, @Advice.Argument(value = 0, readOnly = false) String driverClassName) { 21 | 22 | if (driverClassName != null && !driverClassName.equals("io.keploy.ksql.KDriver")) { 23 | KDriver.DriverName = driverClassName; 24 | switch (driverClassName) { 25 | case "org.postgresql.Driver": 26 | KDriver.Dialect = "org.hibernate.dialect.PostgreSQLDialect"; 27 | break; 28 | case "com.mysql.cj.jdbc.Driver": 29 | case "com.mysql.jdbc.Driver": 30 | KDriver.Dialect = "org.hibernate.dialect.MySQLDialect"; 31 | break; 32 | case "oracle.jdbc.driver.OracleDriver": 33 | case "oracle.jdbc.OracleDriver": 34 | KDriver.Dialect = "org.hibernate.dialect.Oracle10gDialect"; 35 | break; 36 | case "org.h2.Driver": 37 | KDriver.Dialect = "org.hibernate.dialect.H2Dialect"; 38 | break; 39 | default: 40 | System.out.println("Dialect for driver: " + driverClassName + " is not supported yet"); 41 | } 42 | } 43 | driverClassName = "io.keploy.ksql.KDriver"; 44 | } 45 | 46 | /** 47 | * This method gets executed after the setDriverClassName method of DataSourceProperties class.This does nothing as we don't 48 | * want to change anything after the completion of the setDriverClassName method of DataSourceProperties class. 49 | */ 50 | @Advice.OnMethodExit 51 | public static void exitMethod(@Advice.Origin Method method) { 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /v2/src/main/java/io/keploy/JaCoCoUtil.java: -------------------------------------------------------------------------------- 1 | package io.keploy; 2 | 3 | import java.io.InputStream; 4 | import java.net.URL; 5 | import java.nio.file.Files; 6 | import java.nio.file.Path; 7 | import java.nio.file.Paths; 8 | import java.nio.file.StandardCopyOption; 9 | import java.util.zip.ZipEntry; 10 | import java.util.zip.ZipInputStream; 11 | 12 | public class JaCoCoUtil { 13 | 14 | public static void downloadAndExtractJaCoCoBinaries(String version, String resourceDir) throws Exception { 15 | Path cliPath = Paths.get(resourceDir, "jacococli.jar"); 16 | Path agentPath = Paths.get(resourceDir, "jacocoagent.jar"); 17 | Files.createDirectories(cliPath.getParent()); 18 | Files.createDirectories(agentPath.getParent()); 19 | 20 | if (Files.exists(cliPath) && Files.exists(agentPath)) { 21 | return; 22 | } 23 | 24 | String downloadUrl = "https://github.com/jacoco/jacoco/releases/download/v" + version + "/jacoco-" + version + ".zip"; 25 | 26 | try (InputStream inputStream = new URL(downloadUrl).openStream(); 27 | ZipInputStream zipInputStream = new ZipInputStream(inputStream)) { 28 | 29 | ZipEntry entry; 30 | while ((entry = zipInputStream.getNextEntry()) != null) { 31 | if (entry.getName().endsWith("jacococli.jar")) { 32 | 33 | Files.copy(zipInputStream, cliPath, StandardCopyOption.REPLACE_EXISTING); 34 | 35 | } else if (entry.getName().endsWith("jacocoagent.jar")) { 36 | 37 | Files.copy(zipInputStream, agentPath, StandardCopyOption.REPLACE_EXISTING); 38 | 39 | } 40 | 41 | if (Files.exists(cliPath) && Files.exists(agentPath)) { 42 | 43 | break; // Both binaries extracted, no need to continue 44 | } 45 | } 46 | } 47 | 48 | if (!Files.exists(cliPath) || !Files.exists(agentPath)) { 49 | throw new IllegalStateException("Failed to find JaCoCo binaries in the distribution."); 50 | } 51 | 52 | } 53 | 54 | public static void main(String[] args) { 55 | if (args.length != 2) { 56 | throw new IllegalArgumentException("Expected two arguments: version and resourceDir"); 57 | } 58 | String version = args[0]; 59 | String resourceDir = args[1]; 60 | try { 61 | downloadAndExtractJaCoCoBinaries(version, resourceDir); 62 | } catch (Exception e) { 63 | e.printStackTrace(); 64 | System.exit(1); 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /agent/src/main/java/io/keploy/advice/CustomGoogleResponseAdvice.java: -------------------------------------------------------------------------------- 1 | package io.keploy.advice; 2 | 3 | import io.keploy.regression.Mode; 4 | import io.keploy.regression.context.Context; 5 | import io.keploy.regression.context.Kcontext; 6 | import io.keploy.googleMaps.CustomHttpResponses; 7 | import net.bytebuddy.asm.Advice; 8 | import okhttp3.Response; 9 | import okio.BufferedSource; 10 | import org.apache.logging.log4j.LogManager; 11 | import org.apache.logging.log4j.Logger; 12 | 13 | import java.lang.reflect.Method; 14 | import java.util.Objects; 15 | 16 | /** 17 | * This class is used for intercepting method parseResponse of OkHttpPendingResult class and to record required data from 18 | * the method 19 | */ 20 | public class CustomGoogleResponseAdvice { 21 | 22 | /** 23 | * This method gets executed before method parseResponse of OkHttpPendingResult class. In record mode it saves the 24 | * data from the parseResponse method arguments and help in recording mocks/tests. 25 | */ 26 | @Advice.OnMethodEnter 27 | static void enterMethods(@Advice.Origin Method method, @Advice.AllArguments Object[] obj) throws Exception { 28 | final Logger logger = LogManager.getLogger(CustomGoogleResponseAdvice.class); 29 | 30 | logger.debug("inside OnMethodEnterAdvice of CustomGoogleResponseAdvice for method: {}", method); 31 | Response response = (Response) obj[1]; 32 | 33 | Kcontext kctx = Context.getCtx(); 34 | 35 | if (kctx == null) { 36 | logger.debug("[CustomGoogleResponseAdvice] keploy context is null"); 37 | } else if (kctx.getMode().getModeFromContext().equals(Mode.ModeType.MODE_RECORD)) { 38 | logger.debug("[CustomGoogleResponseAdvice] keploy mode: " + kctx.getMode()); 39 | CustomHttpResponses.googleMapResponse = response; 40 | 41 | if (response.body() != null) { 42 | final BufferedSource source = Objects.requireNonNull(response.body()).source(); 43 | source.request(Integer.MAX_VALUE); 44 | okio.ByteString snapshot = source.buffer().snapshot(); 45 | String body = ""; 46 | if (!response.body().contentType().type().contains("image")) { 47 | logger.debug("not an image"); 48 | body = snapshot.utf8(); 49 | } 50 | CustomHttpResponses.googleMapResBody = body; 51 | } 52 | } else if (kctx.getMode().getModeFromContext().equals(Mode.ModeType.MODE_OFF) || kctx.getMode().getModeFromContext().equals(Mode.ModeType.MODE_TEST)) { 53 | logger.debug("[CustomGoogleResponseAdvice] keploy mode: " + kctx.getMode()); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /common/src/main/java/io/keploy/utils/GenericResponseWrapper.java: -------------------------------------------------------------------------------- 1 | package io.keploy.utils; 2 | 3 | import javax.servlet.ServletOutputStream; 4 | import javax.servlet.WriteListener; 5 | import javax.servlet.http.HttpServletResponse; 6 | import javax.servlet.http.HttpServletResponseWrapper; 7 | import java.io.*; 8 | 9 | /** 10 | * GenericResponseWrapper is a wrapper over the Response sent to the filter. So that the response data is cached/stored and 11 | * not lost 12 | */ 13 | public class GenericResponseWrapper extends HttpServletResponseWrapper { 14 | private ServletOutputStream outputStream; 15 | private PrintWriter writer; 16 | private FilterServletOutputStream copier; 17 | 18 | public GenericResponseWrapper(HttpServletResponse response) throws IOException { 19 | super(response); 20 | } 21 | 22 | @Override 23 | public ServletOutputStream getOutputStream() throws IOException { 24 | if (writer != null) { 25 | throw new IllegalStateException("getWriter() has already been called on this response."); 26 | } 27 | 28 | if (outputStream == null) { 29 | outputStream = getResponse().getOutputStream(); 30 | copier = new FilterServletOutputStream(outputStream); 31 | } 32 | 33 | return copier; 34 | } 35 | 36 | @Override 37 | public PrintWriter getWriter() throws IOException { 38 | if (outputStream != null) { 39 | throw new IllegalStateException("getOutputStream() has already been called on this response."); 40 | } 41 | 42 | if (writer == null) { 43 | copier = new FilterServletOutputStream(getResponse().getOutputStream()); 44 | writer = new PrintWriter(new OutputStreamWriter(copier, getResponse().getCharacterEncoding()), true); 45 | } 46 | 47 | return writer; 48 | } 49 | 50 | @Override 51 | public void flushBuffer() throws IOException { 52 | if (writer != null) { 53 | writer.flush(); 54 | } else if (outputStream != null) { 55 | copier.flush(); 56 | } 57 | } 58 | 59 | public byte[] getData() { 60 | if (copier != null) { 61 | return copier.getData(); 62 | } else { 63 | return new byte[0]; 64 | } 65 | } 66 | } 67 | 68 | class FilterServletOutputStream extends ServletOutputStream { 69 | private final OutputStream outputStream; 70 | private final ByteArrayOutputStream copy; 71 | 72 | public FilterServletOutputStream(OutputStream outputStream) { 73 | this.outputStream = outputStream; 74 | this.copy = new ByteArrayOutputStream(1024); 75 | } 76 | 77 | @Override 78 | public void write(int b) throws IOException { 79 | outputStream.write(b); 80 | copy.write(b); 81 | } 82 | 83 | public byte[] getData() { 84 | return copy.toByteArray(); 85 | } 86 | 87 | @Override 88 | public boolean isReady() { 89 | return false; 90 | } 91 | 92 | @Override 93 | public void setWriteListener(WriteListener writeListener) { 94 | 95 | } 96 | } -------------------------------------------------------------------------------- /v2/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | io.keploy 8 | java-sdk 9 | 1.0.0-SNAPSHOT 10 | 11 | 12 | v2 13 | 14 | 15 | 16 | 17 | org.json 18 | json 19 | 20230618 20 | 21 | 22 | com.google.code.gson 23 | gson 24 | 2.8.9 25 | compile 26 | 27 | 28 | 29 | 30 | 31 | 32 | 1.8 33 | 1.8 34 | UTF-8 35 | 36 | 37 | 38 | 39 | 40 | src/main/resources 41 | 42 | 43 | **/* 44 | 45 | 46 | 47 | 48 | 49 | 50 | org.apache.maven.plugins 51 | maven-javadoc-plugin 52 | 3.3.0 53 | 54 | 55 | ${project.basedir}/src/main/java 56 | 57 | 58 | 59 | org.apache.maven.plugins 60 | maven-compiler-plugin 61 | 62 | 9 63 | 9 64 | 65 | 66 | 67 | org.codehaus.mojo 68 | exec-maven-plugin 69 | 3.0.0 70 | 71 | 72 | process-classes 73 | 74 | java 75 | 76 | 77 | io.keploy.JaCoCoUtil 78 | 79 | 0.8.12 80 | ${project.basedir}/src/main/resources 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /common/src/main/java/io/keploy/utils/ProcessSQL.java: -------------------------------------------------------------------------------- 1 | package io.keploy.utils; 2 | 3 | import com.google.protobuf.InvalidProtocolBufferException; 4 | import io.keploy.grpc.stubs.Service; 5 | import io.keploy.regression.Mock; 6 | import io.keploy.regression.context.Context; 7 | import io.keploy.regression.context.Kcontext; 8 | import lombok.NoArgsConstructor; 9 | import org.apache.logging.log4j.LogManager; 10 | import org.apache.logging.log4j.Logger; 11 | 12 | import java.util.ArrayList; 13 | import java.util.HashMap; 14 | import java.util.List; 15 | import java.util.Map; 16 | 17 | 18 | @NoArgsConstructor 19 | 20 | /// this is sql specific process dep 21 | public class ProcessSQL { 22 | 23 | private static final Logger logger = LogManager.getLogger(ProcessSQL.class); 24 | 25 | // @SafeVarargs 26 | public static Service.Table ProcessDep(Map meta, Service.Table table, int id) throws InvalidProtocolBufferException { 27 | 28 | Kcontext kctx = Context.getCtx(); 29 | if (kctx == null) { 30 | logger.error("dependency mocking failed: failed to get Keploy context"); 31 | return null; 32 | } 33 | switch (kctx.getMode()) { 34 | case MODE_TEST: 35 | if (kctx.getMock().size() > 0 && kctx.getMock().get(0).getKind().equals("SQL")) { 36 | List mocks = kctx.getMock(); 37 | if (mocks.size() > 0) { 38 | final Service.Table ttable = mocks.get(0).getSpec().getTable(); 39 | mocks.remove(0); 40 | return ttable; 41 | } 42 | // for int 43 | } 44 | 45 | break; 46 | case MODE_RECORD: 47 | 48 | Service.Mock.SpecSchema specSchema = null; 49 | 50 | specSchema = Service.Mock.SpecSchema.newBuilder().putAllMetadata(meta).setInt(id).setTable(table).setType("TABLE").build(); 51 | 52 | Service.Mock mock = Service.Mock.newBuilder() 53 | .setVersion(Mock.Version.V1_BETA1.value) 54 | .setName("") 55 | .setKind(Mock.Kind.SQL.value) 56 | .setSpec(specSchema) 57 | .build(); 58 | 59 | kctx.getMock().add(mock); 60 | break; 61 | } 62 | return null; 63 | } 64 | 65 | public static List toRowList(List> preTable, List columns) { 66 | List rows = new ArrayList<>(); 67 | for (Map stringStringMap : preTable) { 68 | StringBuilder row = new StringBuilder(); 69 | for (String column : columns) { 70 | if (stringStringMap.get(column) != null) { 71 | row.append("`").append(stringStringMap.get(column)).append("`|"); 72 | } else { 73 | row.append("`NA`|"); 74 | } 75 | } 76 | row.deleteCharAt(row.length() - 1); 77 | row.insert(0, "["); 78 | row.append("]"); 79 | rows.add(String.valueOf(row)); 80 | } 81 | return rows; 82 | } 83 | 84 | public static List toColumnList(List sqlColList) { 85 | List col = new ArrayList<>(); 86 | for (Service.SqlCol v : sqlColList) { 87 | col.add(v.getName()); 88 | } 89 | return col; 90 | } 91 | 92 | public static HashMap convertMap(Map s) { 93 | return new HashMap<>(s); 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /common/src/main/java/io/keploy/utils/HttpStatusReasons.java: -------------------------------------------------------------------------------- 1 | package io.keploy.utils; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public class HttpStatusReasons { 7 | 8 | private static final String UNKNOWN_STATUS = "Unknown Status"; 9 | 10 | private static final Map REASONS = new HashMap<>(); 11 | 12 | 13 | static { 14 | //informational 15 | REASONS.put(100, "Continue"); 16 | REASONS.put(101, "Switching Protocols"); 17 | REASONS.put(102, "Processing"); 18 | REASONS.put(103, "Checkpoint"); 19 | 20 | // successful 21 | REASONS.put(200, "OK"); 22 | REASONS.put(201, "Created"); 23 | REASONS.put(202, "Accepted"); 24 | REASONS.put(203, "Non-Authoritative Information"); 25 | REASONS.put(204, "No Content"); 26 | REASONS.put(205, "Reset Content"); 27 | REASONS.put(206, "Partial Content"); 28 | REASONS.put(207, "Multi-Status"); 29 | REASONS.put(208, "Already Reported"); 30 | REASONS.put(209, "IM Used"); 31 | 32 | // redirection 33 | REASONS.put(300, "Multiple Choices"); 34 | REASONS.put(301, "Moved Permanently"); 35 | REASONS.put(302, "Found"); 36 | REASONS.put(303, "See Other"); 37 | REASONS.put(304, "Not Modified"); 38 | REASONS.put(305, "Use Proxy"); 39 | REASONS.put(307, "Temporary Redirect"); 40 | REASONS.put(308, "Permanent Redirect"); 41 | 42 | // client error 43 | REASONS.put(400, "Bad Request"); 44 | REASONS.put(401, "Unauthorized"); 45 | REASONS.put(402, "Payment Required"); 46 | REASONS.put(403, "Forbidden"); 47 | REASONS.put(404, "Not Found"); 48 | REASONS.put(405, "Method Not Allowed"); 49 | REASONS.put(406, "Not Acceptable"); 50 | REASONS.put(407, "Proxy Authentication Required"); 51 | REASONS.put(408, "Request Timeout"); 52 | REASONS.put(409, "Conflict"); 53 | REASONS.put(410, "Gone"); 54 | REASONS.put(411, "Length Required"); 55 | REASONS.put(412, "Precondition Failed"); 56 | REASONS.put(413, "Payload Too Large"); 57 | REASONS.put(414, "URI Too Long"); 58 | REASONS.put(415, "Unsupported Media Type"); 59 | REASONS.put(416, "Requested range not satisfiable"); 60 | REASONS.put(417, "Expectation Failed"); 61 | REASONS.put(418, "I'm a teapot"); 62 | REASONS.put(421, "Destination Locked"); 63 | REASONS.put(422, "Unprocessable Entity"); 64 | REASONS.put(423, "Locked"); 65 | REASONS.put(424, "Failed Dependency"); 66 | REASONS.put(425, "Too Early"); 67 | REASONS.put(426, "Upgrade Required"); 68 | REASONS.put(428, "Precondition Required"); 69 | REASONS.put(429, "Too Many Requests"); 70 | REASONS.put(431, "Request Header Fields Too Large"); 71 | REASONS.put(451, "Unavailable For Legal Reasons"); 72 | 73 | //server error 74 | REASONS.put(500, "Internal Server Error"); 75 | REASONS.put(501, "Not Implemented"); 76 | REASONS.put(502, "Bad Gateway"); 77 | REASONS.put(503, "Service Unavailable"); 78 | REASONS.put(504, "Gateway Timeout"); 79 | REASONS.put(505, "HTTP Version not supported"); 80 | REASONS.put(506, "Variant Also Negotiates"); 81 | REASONS.put(507, "Insufficient Storage"); 82 | REASONS.put(508, "Loop Detected"); 83 | REASONS.put(509, "Bandwidth Limit Exceeded"); 84 | REASONS.put(510, "Not Extended"); 85 | REASONS.put(511, "Network Authentication Required"); 86 | } 87 | 88 | public static String getStatusMsg(Integer val) { 89 | return REASONS.getOrDefault(val, UNKNOWN_STATUS); 90 | } 91 | } 92 | 93 | -------------------------------------------------------------------------------- /integration/src/main/java/io/keploy/ksql/KParameterMetaData.java: -------------------------------------------------------------------------------- 1 | package io.keploy.ksql; 2 | 3 | import org.apache.logging.log4j.LogManager; 4 | 5 | import java.sql.ParameterMetaData; 6 | import java.sql.SQLException; 7 | import java.util.Objects; 8 | 9 | import static io.keploy.ksql.KDriver.mode; 10 | import static io.keploy.ksql.KDriver.testMode; 11 | 12 | /** 13 | * KParameterMetaData is a wrapper class for ParameterMetaData used in SQL, this class helps in recording data in record mode 14 | * and providing data in test mode 15 | */ 16 | public class KParameterMetaData implements ParameterMetaData { 17 | ParameterMetaData wrappedParameterMetaData; 18 | private static final org.apache.logging.log4j.Logger logger = LogManager.getLogger(ParameterMetaData.class); 19 | public KParameterMetaData(ParameterMetaData parameterMetaData) { 20 | logger.debug("Inside KParameterMetaData !!"); 21 | wrappedParameterMetaData = parameterMetaData; 22 | } 23 | 24 | @Override 25 | public int getParameterCount() throws SQLException { 26 | if (mode == testMode) { 27 | return 0; 28 | } 29 | return wrappedParameterMetaData.getParameterCount(); 30 | } 31 | 32 | @Override 33 | public int isNullable(int param) throws SQLException { 34 | if (mode == testMode) { 35 | return 0; 36 | } 37 | return wrappedParameterMetaData.isNullable(param); 38 | } 39 | 40 | @Override 41 | public boolean isSigned(int param) throws SQLException { 42 | if (mode == testMode) { 43 | return true; 44 | } 45 | return wrappedParameterMetaData.isSigned(param); 46 | } 47 | 48 | @Override 49 | public int getPrecision(int param) throws SQLException { 50 | if (mode == testMode) { 51 | return 0; 52 | } 53 | return wrappedParameterMetaData.getPrecision(param); 54 | } 55 | 56 | @Override 57 | public int getScale(int param) throws SQLException { 58 | if (mode == testMode) { 59 | return 0; 60 | } 61 | return wrappedParameterMetaData.getScale(param); 62 | } 63 | 64 | @Override 65 | public int getParameterType(int param) throws SQLException { 66 | if (mode == testMode) { 67 | return 0; 68 | } 69 | return wrappedParameterMetaData.getParameterType(param); 70 | } 71 | 72 | @Override 73 | public String getParameterTypeName(int param) throws SQLException { 74 | if (mode == testMode) { 75 | return ""; 76 | } 77 | return wrappedParameterMetaData.getParameterTypeName(param); 78 | } 79 | 80 | @Override 81 | public String getParameterClassName(int param) throws SQLException { 82 | if (mode == testMode) { 83 | return ""; 84 | } 85 | return wrappedParameterMetaData.getParameterClassName(param); 86 | } 87 | 88 | @Override 89 | public int getParameterMode(int param) throws SQLException { 90 | if (mode == testMode) { 91 | return 0; 92 | } 93 | return wrappedParameterMetaData.getParameterMode(param); 94 | } 95 | 96 | @Override 97 | public T unwrap(Class iface) throws SQLException { 98 | if (Objects.equals(System.getenv("KEPLOY_MODE"), "test")) { 99 | return null; 100 | } 101 | return wrappedParameterMetaData.unwrap(iface); 102 | } 103 | 104 | @Override 105 | public boolean isWrapperFor(Class iface) throws SQLException { 106 | if (Objects.equals(System.getenv("KEPLOY_MODE"), "test")) { 107 | return true; 108 | } 109 | return wrappedParameterMetaData.isWrapperFor(iface); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /models/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | java-sdk 7 | io.keploy 8 | 1.0.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | models 13 | 14 | 15 | javax.annotation 16 | javax.annotation-api 17 | 1.3.2 18 | 19 | 20 | io.grpc 21 | grpc-netty-shaded 22 | 1.49.2 23 | 24 | 25 | io.grpc 26 | grpc-protobuf 27 | 1.49.2 28 | 29 | 30 | io.grpc 31 | grpc-stub 32 | 1.49.2 33 | 34 | 35 | 36 | 37 | protoc 38 | 1.8 39 | 1.8 40 | 41 | 42 | 43 | 44 | clean generate-sources compile install 45 | 46 | 47 | 48 | 49 | com.github.os72 50 | protoc-jar-maven-plugin 51 | 3.11.4 52 | 53 | 54 | generate-sources 55 | 56 | run 57 | 58 | 59 | com.google.protobuf:protoc:3.21.1 60 | direct 61 | 62 | 63 | src/main/java/io/keploy/grpc/proto 64 | 65 | 66 | 67 | 68 | java 69 | src/main/java 70 | 71 | 72 | grpc-java 73 | io.grpc:protoc-gen-grpc-java:1.49.0 74 | src/main/java 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | org.apache.maven.plugins 83 | maven-compiler-plugin 84 | 85 | 1.8 86 | 1.8 87 | 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /api/src/main/java/io/keploy/service/HttpPostMultipart.java: -------------------------------------------------------------------------------- 1 | package io.keploy.service; 2 | 3 | import org.apache.logging.log4j.LogManager; 4 | import org.apache.logging.log4j.Logger; 5 | 6 | import java.io.*; 7 | import java.net.HttpURLConnection; 8 | 9 | import java.net.URLConnection; 10 | import java.util.List; 11 | import java.util.Map; 12 | import java.util.UUID; 13 | 14 | public class HttpPostMultipart { 15 | // reference: https://blog.cpming.top/p/httpurlconnection-multipart-form-data 16 | private static final Logger logger = LogManager.getLogger(HttpPostMultipart.class); 17 | 18 | private static final String CROSS = new String(Character.toChars(0x274C)); 19 | 20 | private final String boundary; 21 | private static final String LINE = "\r\n"; 22 | private final HttpURLConnection httpConn; 23 | private final String charset; 24 | private final OutputStream outputStream; 25 | private final PrintWriter writer; 26 | 27 | public HttpPostMultipart(String charset, HttpURLConnection httpConn) throws IOException { 28 | boundary = UUID.randomUUID().toString(); 29 | this.charset = charset; 30 | this.httpConn = httpConn; 31 | this.httpConn.setUseCaches(false); 32 | this.httpConn.setDoOutput(true); // indicates POST method 33 | this.httpConn.setDoInput(true); 34 | this.httpConn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary); 35 | outputStream = this.httpConn.getOutputStream(); 36 | writer = new PrintWriter(new OutputStreamWriter(outputStream, charset), true); 37 | } 38 | 39 | public void addFormField(String name, String value) { 40 | writer.append("--" + boundary).append(LINE); 41 | writer.append("Content-Disposition: form-data; name=\"" + name + "\"").append(LINE); 42 | writer.append("Content-Type: text/plain; charset=" + charset).append(LINE); 43 | writer.append(LINE); 44 | writer.append(value).append(LINE); 45 | writer.flush(); 46 | } 47 | 48 | 49 | public void addFilePart(String fieldName, File uploadFile) 50 | throws IOException { 51 | String fileName = uploadFile.getName(); 52 | writer.append("--" + boundary).append(LINE); 53 | writer.append("Content-Disposition: form-data; name=\"" + fieldName + "\"; filename=\"" + fileName + "\"").append(LINE); 54 | writer.append("Content-Type: " + URLConnection.guessContentTypeFromName(fileName)).append(LINE); 55 | writer.append("Content-Transfer-Encoding: binary").append(LINE); 56 | writer.append(LINE); 57 | writer.flush(); 58 | 59 | FileInputStream inputStream = new FileInputStream(uploadFile); 60 | byte[] buffer = new byte[4096]; 61 | int bytesRead = -1; 62 | while ((bytesRead = inputStream.read(buffer)) != -1) { 63 | outputStream.write(buffer, 0, bytesRead); 64 | } 65 | outputStream.flush(); 66 | inputStream.close(); 67 | writer.append(LINE); 68 | writer.flush(); 69 | } 70 | 71 | 72 | public void finish() throws IOException { 73 | String responseBody = ""; 74 | writer.flush(); 75 | writer.append("--" + boundary + "--").append(LINE); 76 | writer.close(); 77 | 78 | // checks server's status code first 79 | final int status = this.httpConn.getResponseCode(); 80 | logger.debug("status code got from simulate request: {}", status); 81 | 82 | final Map> responseHeaders = httpConn.getHeaderFields(); 83 | logger.debug("response headers got from simulate request: {}", responseHeaders); 84 | 85 | if (GrpcService.isSuccessfulResponse(httpConn)) { 86 | responseBody = GrpcService.getSimulateResponseBody(httpConn); 87 | logger.debug("response body got from multipart simulate request: {}", responseBody); 88 | } else { 89 | throw new IOException("Server returned non-OK status: " + status); 90 | } 91 | } 92 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/macos,linux,jetbrains,visualstudiocode 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=macos,linux,jetbrains,visualstudiocode 4 | 5 | ### JetBrains ### 6 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 7 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 8 | 9 | # User-specific stuff 10 | .idea/ 11 | .idea/**/workspace.xml 12 | .idea/**/tasks.xml 13 | .idea/**/usage.statistics.xml 14 | .idea/**/dictionaries 15 | .idea/**/shelf 16 | 17 | # AWS User-specific 18 | .idea/**/aws.xml 19 | 20 | # Generated files 21 | .idea/**/contentModel.xml 22 | 23 | # Sensitive or high-churn files 24 | .idea/**/dataSources/ 25 | .idea/**/dataSources.ids 26 | .idea/**/dataSources.local.xml 27 | .idea/**/sqlDataSources.xml 28 | .idea/**/dynamic.xml 29 | .idea/**/uiDesigner.xml 30 | .idea/**/dbnavigator.xml 31 | 32 | # Gradle 33 | .idea/**/gradle.xml 34 | .idea/**/libraries 35 | 36 | # Gradle and Maven with auto-import 37 | # When using Gradle or Maven with auto-import, you should exclude module files, 38 | # since they will be recreated, and may cause churn. Uncomment if using 39 | # auto-import. 40 | # .idea/artifacts 41 | # .idea/compiler.xml 42 | # .idea/jarRepositories.xml 43 | # .idea/modules.xml 44 | # .idea/*.iml 45 | # .idea/modules 46 | *.iml 47 | # *.ipr 48 | 49 | # CMake 50 | cmake-build-*/ 51 | 52 | # Mongo Explorer plugin 53 | .idea/**/mongoSettings.xml 54 | 55 | # File-based project format 56 | *.iws 57 | 58 | # IntelliJ 59 | out/ 60 | 61 | # mpeltonen/sbt-idea plugin 62 | .idea_modules/ 63 | 64 | # JIRA plugin 65 | atlassian-ide-plugin.xml 66 | 67 | # Cursive Clojure plugin 68 | .idea/replstate.xml 69 | 70 | # SonarLint plugin 71 | .idea/sonarlint/ 72 | 73 | # Crashlytics plugin (for Android Studio and IntelliJ) 74 | com_crashlytics_export_strings.xml 75 | crashlytics.properties 76 | crashlytics-build.properties 77 | fabric.properties 78 | 79 | # Editor-based Rest Client 80 | .idea/httpRequests 81 | 82 | # Android studio 3.1+ serialized cache file 83 | .idea/caches/build_file_checksums.ser 84 | 85 | ### JetBrains Patch ### 86 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 87 | 88 | # *.iml 89 | # modules.xml 90 | # .idea/misc.xml 91 | # *.ipr 92 | 93 | # Sonarlint plugin 94 | # https://plugins.jetbrains.com/plugin/7973-sonarlint 95 | .idea/**/sonarlint/ 96 | 97 | # SonarQube Plugin 98 | # https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin 99 | .idea/**/sonarIssues.xml 100 | 101 | # Markdown Navigator plugin 102 | # https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced 103 | .idea/**/markdown-navigator.xml 104 | .idea/**/markdown-navigator-enh.xml 105 | .idea/**/markdown-navigator/ 106 | 107 | # Cache file creation bug 108 | # See https://youtrack.jetbrains.com/issue/JBR-2257 109 | .idea/$CACHE_FILE$ 110 | 111 | # CodeStream plugin 112 | # https://plugins.jetbrains.com/plugin/12206-codestream 113 | .idea/codestream.xml 114 | 115 | ### Linux ### 116 | *~ 117 | 118 | # temporary files which can be created if a process still has a handle open of a deleted file 119 | .fuse_hidden* 120 | 121 | # KDE directory preferences 122 | .directory 123 | 124 | # Linux trash folder which might appear on any partition or disk 125 | .Trash-* 126 | 127 | # .nfs files are created when an open file is removed but is still being accessed 128 | .nfs* 129 | 130 | ### macOS ### 131 | # General 132 | .DS_Store 133 | .AppleDouble 134 | .LSOverride 135 | 136 | # Icon must end with two \r 137 | Icon 138 | 139 | 140 | # Thumbnails 141 | ._* 142 | 143 | # Files that might appear in the root of a volume 144 | .DocumentRevisions-V100 145 | .fseventsd 146 | .Spotlight-V100 147 | .TemporaryItems 148 | .Trashes 149 | .VolumeIcon.icns 150 | .com.apple.timemachine.donotpresent 151 | 152 | # Directories potentially created on remote AFP share 153 | .AppleDB 154 | .AppleDesktop 155 | Network Trash Folder 156 | Temporary Items 157 | .apdisk 158 | 159 | ### VisualStudioCode ### 160 | .vscode/* 161 | !.vscode/settings.json 162 | !.vscode/tasks.json 163 | !.vscode/launch.json 164 | !.vscode/extensions.json 165 | !.vscode/*.code-snippets 166 | 167 | # Local History for Visual Studio Code 168 | .history/ 169 | 170 | # Built Visual Studio Code Extensions 171 | *.vsix 172 | 173 | ### VisualStudioCode Patch ### 174 | # Ignore all local history of files 175 | .history 176 | .ionide 177 | 178 | # Support for Project snippet scope 179 | 180 | # End of https://www.toptal.com/developers/gitignore/api/macos,linux,jetbrains,visualstudiocode 181 | 182 | target/ 183 | dependency-reduced-pom.xml 184 | 185 | pom.xml.versionsBackup 186 | sdk.iml 187 | 188 | *.iml -------------------------------------------------------------------------------- /agent/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | java-sdk 5 | io.keploy 6 | 1.0.0-SNAPSHOT 7 | 8 | 4.0.0 9 | 10 | agent 11 | 12 | 13 | UTF-8 14 | 1.8 15 | 1.8 16 | 17 | Agent For Keploy SDK 18 | This agent is used to mock infra calls for an API 19 | 20 | https://github.com/keploy/java-sdk 21 | 22 | 23 | The Apache License, Version 2.0 24 | http://www.apache.org/licenses/LICENSE-2.0.txt 25 | 26 | 27 | 28 | 29 | sarthak160 30 | Sarthak 31 | sarthak@keploy.io 32 | 33 | 34 | gouravkrosx 35 | Gourav Kumar 36 | gourav.kumar@keploy.io 37 | 38 | 39 | 40 | scm:git@github.com:keploy/java-sdk.git 41 | scm:git@github.com:keploy/java-sdk.git 42 | https://github.com/keploy/java-sdk.git 43 | 44 | 45 | 46 | 47 | io.keploy 48 | integration 49 | 1.0.0-SNAPSHOT 50 | 51 | 52 | net.bytebuddy 53 | byte-buddy 54 | 1.12.14 55 | 56 | 57 | net.bytebuddy 58 | byte-buddy-agent 59 | 1.12.14 60 | 61 | 62 | org.apache.httpcomponents 63 | httpcore 64 | 4.4.13 65 | compile 66 | 67 | 68 | com.squareup.okhttp3 69 | okhttp 70 | 71 | 3.14.9 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | org.apache.maven.plugins 80 | maven-shade-plugin 81 | 3.2.1 82 | 83 | 84 | 85 | package 86 | 87 | shade 88 | 89 | 90 | 91 | 93 | 94 | io.keploy.agent.KAgent 95 | 96 | 97 | 98 | 99 | 100 | *:* 101 | 102 | META-INF/*.SF 103 | META-INF/*.DSA 104 | META-INF/*.RSA 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /common/src/main/java/io/keploy/utils/Utility.java: -------------------------------------------------------------------------------- 1 | package io.keploy.utils; 2 | 3 | import org.apache.logging.log4j.LogManager; 4 | import org.apache.logging.log4j.Logger; 5 | 6 | import java.io.File; 7 | import java.util.ArrayList; 8 | import java.util.Arrays; 9 | import java.util.Collections; 10 | import java.util.List; 11 | import java.util.regex.Matcher; 12 | import java.util.regex.Pattern; 13 | 14 | public class Utility { 15 | 16 | private static final Logger logger = LogManager.getLogger(Utility.class); 17 | 18 | private static final String CROSS = new String(Character.toChars(0x274C)); 19 | 20 | private static final String WARN = "\u26A0\uFE0F"; 21 | 22 | public static String getFileNameFromHeader(String header) { 23 | String fileName = ""; 24 | String regex = ".*filename=(.*)"; 25 | Pattern pattern = Pattern.compile(regex); 26 | Matcher matcher = pattern.matcher(header); 27 | if (matcher.matches()) { 28 | fileName = matcher.group(1); 29 | fileName = fileName.replaceAll("\"", ""); 30 | } 31 | return fileName; 32 | } 33 | 34 | public static String lastFileNameFromDirectory(String path) { 35 | path = path.trim(); 36 | File dir = new File(path); 37 | File[] files = dir.listFiles(); 38 | 39 | if (files == null) { 40 | logger.error(CROSS + " no directory found at location:" + path); 41 | return null; 42 | } 43 | 44 | if (files.length > 0) { 45 | Arrays.sort(files); 46 | File lastFile = files[files.length - 1]; 47 | String fileName = lastFile.getName(); 48 | logger.debug("last file of directory {}: {}", path, fileName); 49 | return fileName; 50 | } 51 | return ""; 52 | } 53 | 54 | public static String getFileNameFromPath(String filePath) { 55 | if (filePath.isEmpty()) return ""; 56 | filePath = filePath.trim(); 57 | 58 | String[] pathSplit = filePath.split("/"); 59 | String fileName = pathSplit[pathSplit.length - 1]; 60 | return fileName; 61 | } 62 | 63 | public static String getFileExtensionFromPath(String filePath) { 64 | 65 | String fileName = getFileNameFromPath(filePath); 66 | String[] split = fileName.split("\\."); 67 | String ext = split[split.length - 1]; 68 | if (split.length == 1) { 69 | logger.error("no file found at location:" + fileName); 70 | return ""; 71 | } 72 | return ext; 73 | } 74 | 75 | //it resolves the file name with path so that it can be stored in folder without naming conflict 76 | public static String resolveFileName(String folderPath) { 77 | folderPath = folderPath.trim(); 78 | 79 | List assets = new ArrayList<>(); 80 | File dir = new File(folderPath); 81 | File[] files = dir.listFiles(); 82 | 83 | if (files == null) { 84 | logger.warn(WARN + " no directory found at location:{}", folderPath); 85 | logger.warn(WARN + " hence saving in the current user directory"); 86 | return System.getProperty("user.dir") + "/asset-1"; 87 | } 88 | 89 | if (files.length == 0) { 90 | return folderPath + "/asset-1"; 91 | } 92 | 93 | for (File file : files) { 94 | boolean isPresent = file.getName().contains("asset-"); 95 | if (isPresent) { 96 | assets.add(file.getName()); 97 | } 98 | } 99 | 100 | Collections.sort(assets); 101 | 102 | String lastFileName = assets.get(assets.size() - 1); 103 | 104 | int lastFileCount; 105 | 106 | if (lastFileName != null && !lastFileName.isEmpty()) { 107 | String ext = Utility.getFileExtensionFromPath(lastFileName); 108 | int idx = lastFileName.indexOf("." + ext); 109 | if (idx == -1) { 110 | logger.debug("no extension found in last file"); 111 | return folderPath + "/asset-0"; 112 | } 113 | lastFileName = lastFileName.substring(0, idx); 114 | boolean digit = Character.isDigit(lastFileName.charAt(lastFileName.length() - 1)); 115 | if (digit) { 116 | lastFileCount = Character.getNumericValue(lastFileName.charAt(lastFileName.length() - 1)) + 1; 117 | return folderPath + "/asset-" + lastFileCount; 118 | } else { 119 | return folderPath + "/asset-1"; 120 | } 121 | } else if (lastFileName != null) { 122 | return folderPath + "/asset-1"; 123 | } else { 124 | return folderPath + "/asset-0"; 125 | } 126 | } 127 | 128 | public static void createFolder(String folderPath) { 129 | File folder = new File(folderPath); 130 | 131 | if (!folder.exists()) { 132 | boolean result = folder.mkdir(); 133 | if (!result) { 134 | logger.debug("trying again to create a directory at path: {}", folderPath); 135 | result = folder.mkdirs(); 136 | } 137 | if (result) { 138 | logger.debug("new folder created:"); 139 | } else { 140 | String WARN = "\u26A0\uFE0F"; 141 | logger.warn(WARN + " failed to create assets directory, thus saving files in user directory"); 142 | folderPath = System.getProperty("user.dir"); 143 | } 144 | } else { 145 | logger.debug("directory already exists"); 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | hello@keploy.io. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /integration/src/main/java/io/keploy/ksql/KDriver.java: -------------------------------------------------------------------------------- 1 | package io.keploy.ksql; 2 | 3 | 4 | import io.keploy.regression.Mode; 5 | import io.keploy.regression.context.Context; 6 | import io.keploy.regression.context.Kcontext; 7 | import oracle.jdbc.OracleDriver; 8 | import org.apache.logging.log4j.LogManager; 9 | 10 | import java.sql.*; 11 | import java.util.*; 12 | import java.util.logging.Logger; 13 | 14 | /** 15 | * KDriver is a wrapper class for Driver used in SQL, this class helps in recording data in record mode 16 | * and providing data in test mode 17 | */ 18 | public class KDriver implements Driver { 19 | public Driver wrappedDriver; 20 | 21 | public final Kcontext kctx = Context.getCtx(); 22 | static Mode.ModeType mode = null; 23 | public static String DriverName = ""; 24 | 25 | public static String Dialect = ""; 26 | 27 | public static Mode.ModeType testMode = Mode.ModeType.MODE_TEST; 28 | public static Mode.ModeType recordMode = Mode.ModeType.MODE_RECORD; 29 | private static final org.apache.logging.log4j.Logger logger = LogManager.getLogger(KDriver.class); 30 | 31 | private static final String CROSS = new String(Character.toChars(0x274C)); 32 | 33 | public KDriver(Driver driver) { 34 | if (Objects.equals(System.getenv("KEPLOY_MODE"), "record")) { 35 | mode = Mode.ModeType.MODE_RECORD; 36 | } else if (Objects.equals(System.getenv("KEPLOY_MODE"), "test")) { 37 | mode = Mode.ModeType.MODE_TEST; 38 | } 39 | logger.debug("KEPLOY DRIVER INITIALIZE"); 40 | 41 | this.wrappedDriver = driver; 42 | } 43 | 44 | public static void WrapDriver() throws SQLException { 45 | final Enumeration drivers = DriverManager.getDrivers(); 46 | ArrayList list = Collections.list(drivers); 47 | logger.debug("Number of Drivers to wrap:{}", list 48 | .size()); 49 | for (Driver dr : list) { 50 | logger.debug("wrapping and registering driver:{}", dr); 51 | DriverManager.deregisterDriver(dr); 52 | DriverManager.registerDriver(new KDriver(dr)); 53 | } 54 | } 55 | 56 | public KDriver() throws SQLException { 57 | if (Objects.equals(System.getenv("KEPLOY_MODE"), "record")) { 58 | mode = Mode.ModeType.MODE_RECORD; 59 | } else if (Objects.equals(System.getenv("KEPLOY_MODE"), "test")) { 60 | mode = Mode.ModeType.MODE_TEST; 61 | } 62 | wrappedDriver = getWrappedDriver(); 63 | // set record mode as 64 | if (Objects.equals(DriverName, "org.h2.Driver")) { 65 | logger.info("starting test connection for H2 "); 66 | mode = recordMode; 67 | } 68 | } 69 | 70 | private Driver getWrappedDriver() throws SQLException { 71 | String driver = DriverName; 72 | Driver d; 73 | switch (driver) { 74 | case "org.postgresql.Driver": 75 | d = new org.postgresql.Driver(); 76 | break; 77 | case "com.mysql.cj.jdbc.Driver": 78 | d = new com.mysql.cj.jdbc.Driver(); 79 | break; 80 | case "com.mysql.jdbc.Driver": 81 | d = new com.mysql.jdbc.Driver(); 82 | break; 83 | case "org.h2.Driver": 84 | d = new org.h2.Driver(); 85 | break; 86 | case "oracle.jdbc.driver.OracleDriver": 87 | d = new oracle.jdbc.driver.OracleDriver(); 88 | break; 89 | case "oracle.jdbc.OracleDriver": 90 | d = new OracleDriver(); 91 | break; 92 | case "org.mariadb.jdbc.Driver": 93 | d = new org.mariadb.jdbc.Driver(); 94 | break; 95 | default: 96 | d = null; 97 | } 98 | return d; 99 | } 100 | 101 | 102 | @Override 103 | public Connection connect(String url, Properties info) { 104 | 105 | if (mode == testMode) { 106 | try { 107 | return new KConnection(); 108 | } catch (SQLException e) { 109 | throw new RuntimeException(e); 110 | } 111 | } 112 | Connection conn = null; 113 | try { 114 | conn = wrappedDriver.connect(url, info); 115 | } catch (SQLException e) { 116 | logger.error(CROSS+ " Keploy cannot establish connection with default DB \n"+ e); 117 | } 118 | return new KConnection(conn); 119 | 120 | } 121 | 122 | @Override 123 | public boolean acceptsURL(String url) { 124 | if (mode == testMode) { 125 | return true; 126 | } 127 | try { 128 | return wrappedDriver.acceptsURL(url); 129 | } catch (SQLException e) { 130 | throw new RuntimeException(e); 131 | } 132 | } 133 | 134 | @Override 135 | public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException { 136 | return new DriverPropertyInfo[0]; 137 | } 138 | 139 | @Override 140 | public int getMajorVersion() { 141 | if (mode == testMode) { 142 | return 1; 143 | } 144 | return wrappedDriver.getMajorVersion(); 145 | } 146 | 147 | 148 | @Override 149 | public int getMinorVersion() { 150 | if (mode == testMode) { 151 | return 0; 152 | } 153 | return wrappedDriver.getMinorVersion(); 154 | } 155 | 156 | @Override 157 | public boolean jdbcCompliant() { 158 | if (mode == testMode) { 159 | return true; 160 | } 161 | return wrappedDriver.jdbcCompliant(); 162 | } 163 | 164 | @Override 165 | public Logger getParentLogger() throws SQLFeatureNotSupportedException { 166 | if (mode == testMode) { 167 | return (Logger) logger; 168 | } 169 | return wrappedDriver.getParentLogger(); 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /models/src/main/java/io/keploy/grpc/proto/service.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | option java_package = "io.keploy.grpc.stubs"; 3 | option go_package = "go.keploy.io/server/grpc/regression"; 4 | package services; 5 | 6 | message Dependency { 7 | string Name = 1; 8 | string Type = 2; 9 | map Meta = 3; 10 | repeated DataBytes Data = 4; 11 | } 12 | 13 | message DataBytes { 14 | bytes Bin = 1; 15 | } 16 | 17 | message TestCaseReq { 18 | int64 Captured = 1; 19 | string AppID = 2; 20 | string URI = 3; 21 | HttpReq HttpReq = 4; 22 | HttpResp HttpResp = 5; 23 | repeated Dependency Dependency = 6; 24 | string TestCasePath = 7; 25 | string MockPath = 8; 26 | repeated Mock Mocks = 9; 27 | repeated string Remove = 10; 28 | map Replace = 11; 29 | string Type = 12; 30 | GrpcReq GrpcReq = 13; 31 | GrpcResp GrpcResp = 14; 32 | string AppPath = 15; 33 | } 34 | 35 | 36 | message TestReq { 37 | string ID = 1; 38 | string AppID = 2; 39 | string RunID = 3; 40 | HttpResp Resp = 4; 41 | string TestCasePath = 5; 42 | string MockPath = 6; 43 | string Type = 7; 44 | GrpcResp GrpcResp = 8; 45 | } 46 | 47 | message TestCase { 48 | string id = 1; 49 | int64 created = 2; 50 | int64 updated = 3; 51 | int64 captured = 4; 52 | string CID = 5; 53 | string appID = 6; 54 | string URI = 7; 55 | HttpReq HttpReq = 8; 56 | HttpResp HttpResp = 9; 57 | repeated Dependency Deps = 10; 58 | map allKeys = 11; 59 | map anchors = 12; 60 | repeated string noise = 13; 61 | repeated Mock Mocks = 14; 62 | GrpcReq GrpcReq = 15; 63 | GrpcResp GrpcResp = 16; 64 | string Type = 17; 65 | } 66 | 67 | message Method { 68 | string Method = 1; 69 | } 70 | message HttpReq { 71 | string Method = 1; 72 | int64 ProtoMajor = 2; 73 | int64 ProtoMinor = 3; 74 | string URL = 4; 75 | map URLParams = 5; 76 | map Header = 6; 77 | string Body = 7 [deprecated = true]; 78 | bytes BodyData = 10; 79 | string Binary = 8; 80 | repeated FormData Form = 9; 81 | } 82 | 83 | //for multipart request 84 | message FormData { 85 | string Key = 1; //partName 86 | repeated string Values = 2; 87 | repeated string Paths = 3; 88 | } 89 | 90 | message StrArr { 91 | repeated string Value = 1; 92 | } 93 | 94 | message HttpResp { 95 | int64 StatusCode = 1; 96 | map Header = 2; 97 | string Body = 3 [deprecated = true]; 98 | bytes BodyData = 8; 99 | string StatusMessage = 4; 100 | int64 ProtoMajor = 5; 101 | int64 ProtoMinor = 6; 102 | string Binary = 7; 103 | } 104 | 105 | message endRequest { 106 | string status = 1; 107 | string id = 2; 108 | } 109 | 110 | message endResponse { 111 | string message = 1; 112 | } 113 | 114 | message startRequest { 115 | string total = 1; 116 | string app = 2; 117 | string TestCasePath = 3; 118 | string MockPath = 4; 119 | string AppPath = 5; 120 | } 121 | 122 | message startResponse { 123 | string id = 1; 124 | } 125 | 126 | message getTCRequest { 127 | string id = 1; 128 | string app = 2; 129 | } 130 | message getTCSRequest{ 131 | string app = 1; 132 | string offset = 2; 133 | string limit = 3; 134 | string TestCasePath = 4; 135 | string MockPath = 5; 136 | } 137 | message getTCSResponse{ 138 | repeated TestCase tcs = 1; 139 | bool eof = 2; 140 | } 141 | message postTCResponse{ 142 | map tcsId = 1; 143 | } 144 | message deNoiseResponse { 145 | string message = 1; 146 | } 147 | message testResponse{ 148 | map pass = 1; 149 | } 150 | message GrpcReq { 151 | string Body = 1; 152 | string Method = 2; 153 | } 154 | message GrpcResp { 155 | string Body = 1; 156 | string Err = 2; 157 | } 158 | 159 | message Mock { 160 | message Request { 161 | string Method = 1; 162 | int64 ProtoMajor = 2; 163 | int64 ProtoMinor = 3; 164 | string URL = 4; 165 | map Header = 5; 166 | string Body = 6; 167 | } 168 | message Object { 169 | string Type = 1; 170 | bytes Data = 2; 171 | } 172 | 173 | string Version = 1; 174 | string Name = 2; 175 | string Kind = 3; 176 | message SpecSchema { 177 | map Metadata = 1; 178 | repeated Object Objects = 2; 179 | HttpReq Req = 3; 180 | HttpResp Res = 4; 181 | repeated string Mocks = 5; 182 | map Assertions = 6; 183 | int64 Created = 7; 184 | // for sql 185 | string Type = 8; 186 | optional Table Table = 9; 187 | int64 Int = 10; // change it to rows commited 188 | repeated string Err = 11; 189 | GrpcReq GrpcRequest = 12; 190 | GrpcResp GrpcResp = 13; 191 | } 192 | SpecSchema Spec = 4; 193 | } 194 | 195 | message Table{ 196 | repeated SqlCol Cols = 1; 197 | repeated string Rows = 2; 198 | } 199 | 200 | message SqlCol { 201 | string Name = 1; 202 | string Type = 2; 203 | //optional fields 204 | int64 Precision = 3; 205 | int64 Scale = 4; 206 | } 207 | 208 | message PutMockReq { 209 | Mock Mock = 1; 210 | string Path = 2; 211 | repeated string Remove = 3; 212 | map Replace = 4; 213 | } 214 | 215 | message PutMockResp { 216 | int64 Inserted = 1; 217 | } 218 | 219 | message GetMockReq { 220 | string Path = 1; 221 | string Name = 2; 222 | } 223 | 224 | message getMockResp { 225 | repeated Mock Mocks = 1; 226 | } 227 | 228 | message StartMockReq { 229 | string Path = 1; 230 | string Mode = 2; 231 | bool OverWrite = 3; 232 | string Name = 4; 233 | } 234 | 235 | message StartMockResp { 236 | bool Exists = 1; 237 | } 238 | 239 | service RegressionService{ 240 | rpc End (endRequest) returns (endResponse); 241 | rpc Start (startRequest) returns (startResponse); 242 | rpc GetTC (getTCRequest) returns (TestCase); 243 | rpc GetTCS (getTCSRequest) returns (getTCSResponse); 244 | rpc PostTC (TestCaseReq) returns (postTCResponse); 245 | rpc DeNoise (TestReq) returns(deNoiseResponse); 246 | rpc Test (TestReq) returns (testResponse); 247 | rpc PutMock (PutMockReq) returns (PutMockResp); 248 | rpc GetMocks (GetMockReq) returns (getMockResp); 249 | rpc StartMocking (StartMockReq) returns (StartMockResp); 250 | } -------------------------------------------------------------------------------- /integration/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | java-sdk 7 | io.keploy 8 | 1.0.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | integration 13 | 14 | 15 | io.keploy 16 | core 17 | 1.0.0-SNAPSHOT 18 | compile 19 | 20 | 21 | io.keploy 22 | api 23 | 1.0.0-SNAPSHOT 24 | compile 25 | 26 | 27 | org.jline 28 | jline 29 | 3.20.0 30 | 31 | 32 | me.tongfei 33 | progressbar 34 | 0.5.5 35 | 36 | 37 | io.keploy 38 | common 39 | 1.0.0-SNAPSHOT 40 | compile 41 | 42 | 43 | io.keploy 44 | models 45 | 1.0.0-SNAPSHOT 46 | compile 47 | 48 | 49 | 50 | com.google.protobuf 51 | protobuf-java 52 | 3.21.7 53 | 54 | 55 | 56 | com.oracle.database.jdbc 57 | ojdbc8 58 | 18.3.0.0 59 | provided 60 | 61 | 62 | mysql 63 | mysql-connector-java 64 | 8.0.30 65 | provided 66 | 67 | 68 | com.h2database 69 | h2 70 | 2.1.214 71 | provided 72 | 73 | 74 | org.yaml 75 | snakeyaml 76 | 1.28 77 | 78 | 79 | org.mariadb.jdbc 80 | mariadb-java-client 81 | 2.7.1 82 | provided 83 | 84 | 85 | org.postgresql 86 | postgresql 87 | 42.5.1 88 | provided 89 | 90 | 91 | com.squareup.okhttp3 92 | okhttp 93 | 94 | 3.14.9 95 | 96 | 97 | org.apache.httpcomponents 98 | httpclient 99 | 4.5.13 100 | provided 101 | 102 | 103 | 104 | org.apache.httpcomponents 105 | httpasyncclient 106 | 4.1.4 107 | provided 108 | 109 | 110 | com.amazonaws 111 | aws-java-sdk-dynamodb 112 | 1.11.857 113 | provided 114 | 115 | 116 | org.jacoco 117 | org.jacoco.core 118 | 0.8.7 119 | 120 | 121 | com.google.maps 122 | google-maps-services 123 | 2.0.0 124 | provided 125 | 126 | 127 | commons-io 128 | commons-io 129 | 2.11.0 130 | 131 | 132 | io.btrace 133 | btrace-client 134 | 2.2.3 135 | 136 | 137 | io.btrace 138 | btrace-agent 139 | 2.2.3 140 | 141 | 142 | io.btrace 143 | btrace-boot 144 | 2.2.3 145 | 146 | 147 | 148 | 149 | 1.8 150 | 1.8 151 | 152 | 153 | -------------------------------------------------------------------------------- /checkstyle-config.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | -------------------------------------------------------------------------------- /api/src/main/java/io/keploy/service/mock/MockLib.java: -------------------------------------------------------------------------------- 1 | package io.keploy.service.mock; 2 | 3 | import io.keploy.grpc.stubs.Service; 4 | import io.keploy.regression.KeployInstance; 5 | import io.keploy.regression.Mode; 6 | import io.keploy.regression.context.Context; 7 | import io.keploy.regression.context.Kcontext; 8 | import io.keploy.regression.keploy.AppConfig; 9 | import io.keploy.regression.keploy.Keploy; 10 | import io.keploy.regression.keploy.ServerConfig; 11 | import io.keploy.service.GrpcService; 12 | import org.apache.logging.log4j.LogManager; 13 | import org.apache.logging.log4j.Logger; 14 | 15 | import java.nio.file.Path; 16 | import java.nio.file.Paths; 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | 20 | import static io.keploy.service.GrpcService.blockingStub; 21 | import static io.keploy.service.mock.Config.*; 22 | 23 | /** 24 | * This is a service class used by Mocking feature. This is the class where Java-sdk communicates with Keploy server to 25 | * record/store mocks and to perform mocking. 26 | */ 27 | public class MockLib { 28 | private static final Logger logger = LogManager.getLogger(MockLib.class); 29 | static Keploy k = null; 30 | AppConfig appConfig = new AppConfig(); 31 | 32 | /** 33 | * Initialising Keploy instance and GRPC server 34 | * @param name - App name 35 | */ 36 | public MockLib(String name) { 37 | KeployInstance ki = KeployInstance.getInstance(); 38 | k = ki.getKeploy(); 39 | io.keploy.regression.keploy.Config cfg = new io.keploy.regression.keploy.Config(); 40 | Name = name; 41 | appConfig.setName(Name); 42 | cfg.setApp(appConfig); 43 | 44 | ServerConfig serverConfig = new ServerConfig(); 45 | 46 | if (System.getenv("DENOISE") != null) { 47 | serverConfig.setDenoise(Boolean.parseBoolean(System.getenv("DENOISE"))); 48 | } 49 | 50 | if (System.getenv("KEPLOY_URL") != null) { 51 | serverConfig.setURL(System.getenv("KEPLOY_URL")); 52 | } 53 | 54 | cfg.setApp(appConfig); 55 | cfg.setServer(serverConfig); 56 | k.setCfg(cfg); 57 | new GrpcService(); 58 | 59 | Kcontext ctx = NewContext(); 60 | System.out.println(ctx); 61 | } 62 | 63 | /** 64 | * Set the context according to the Keploy mode i.e. if it is in test mode add all the mock data into the context 65 | * @return - Kcontext 66 | */ 67 | public Kcontext NewContext() { 68 | mode = Mode.ModeType.MODE_TEST; 69 | 70 | String mpath = System.getenv("KEPLOY_MOCK_PATH"); 71 | Path path = Paths.get(""); 72 | // AppConfig appConfig = new AppConfig(); 73 | if (mpath != null && mpath.length() > 0 && !Paths.get(mpath).isAbsolute()) { 74 | Path effectivePath = path.resolve(mpath).toAbsolutePath(); 75 | String absolutePath = effectivePath.normalize().toString(); 76 | appConfig.setMockPath(absolutePath); 77 | } else if (mpath == null || mpath.length() == 0) { 78 | String currDir = System.getProperty("user.dir") + "/src/test/e2e/mocks"; 79 | mpath = currDir; 80 | appConfig.setMockPath(currDir); 81 | } else { 82 | //if user gives the path 83 | appConfig.setMockPath(mpath); 84 | } 85 | MockPath = appConfig.getMockPath(); 86 | logger.debug("mock path: {}", appConfig.getMockPath()); 87 | 88 | mode = System.getenv().getOrDefault("KEPLOY_MODE", "test").equals("record") ? Mode.ModeType.MODE_RECORD : Mode.ModeType.MODE_TEST; 89 | ArrayList mocks = new ArrayList<>(); 90 | if (mode == Mode.ModeType.MODE_TEST) { 91 | if (k.getCfg().getApp().getName() == null || k.getCfg().getApp().getName().length() == 0) { 92 | logger.error("Please enter the auto generated name to mock the dependencies using Keploy !"); 93 | // return; 94 | } 95 | Service.GetMockReq request = Service.GetMockReq.newBuilder().setName(k.getCfg().getApp().getName()).setPath(mpath).build(); 96 | 97 | mocks = GetAllMocks(request); 98 | if (mocks == null) { 99 | logger.error("No mocks found for the given name: {}", k.getCfg().getApp().getName()); 100 | logger.error("Failed to get the mocks from keploy server. Please ensure that keploy server is running."); 101 | } 102 | } 103 | Kcontext kctx = new Kcontext(); 104 | Context.setCtx(kctx); 105 | kctx.setMock(mocks); 106 | kctx.setMode(mode); 107 | kctx.setTestId(appConfig.getName()); 108 | kctx.setFileExport(true); 109 | String name = ""; 110 | if (k.getCfg().getApp().getName() != null) { 111 | name = " for " + k.getCfg().getApp().getName(); 112 | } 113 | System.out.println(name + " -=-==-=-=-= " + mode.value); 114 | logger.info("Keploy created new mocking context in {} mode {}.If you dont see any logs about your dependencies below, your dependency/s are NOT wrapped.", mode, name); 115 | boolean exists = StartRecordingMocks(mpath + "/" + name + ".yaml", mode.value, name, Config.Overwrite); 116 | if (exists && !Config.Overwrite) { 117 | logger.error(" Keploy failed to record dependencies because yaml file already exists {} in directory: {}.", name, mpath); 118 | Config.MockId.put(name, true); 119 | } 120 | 121 | return kctx; 122 | } 123 | 124 | public static boolean StartRecordingMocks(String path, String mode, String name, Boolean overWrite) { 125 | Service.StartMockReq startMockReq = Service.StartMockReq.newBuilder().setMode(mode).setPath(path).setName(name).setOverWrite(overWrite).build(); 126 | Service.StartMockResp startMockResp = blockingStub.startMocking(startMockReq); 127 | if (startMockResp == null) { // TODO - check how to handle this error 128 | logger.error("Failed to make StartMocking grpc call to keploy server" + name + " mock"); 129 | return false; 130 | } 131 | return startMockResp.getExists(); 132 | } 133 | 134 | /** 135 | * Gets all the mocks in the test mode from the server 136 | * 137 | * @param getMockReq - contains mock path and app name 138 | * @return - all the mocks that are recorded 139 | */ 140 | public static ArrayList GetAllMocks(Service.GetMockReq getMockReq) { 141 | final Service.getMockResp resp = blockingStub.getMocks(getMockReq); 142 | if (resp != null) { 143 | if (resp.getMocksList().size() == 0) { 144 | logger.info("Mocklist size is zero !!"); 145 | return null; 146 | } 147 | return getM(resp.getMocksList()); 148 | } 149 | 150 | logger.error("returned nil as array mocks from keploy server"); 151 | return null; 152 | } 153 | 154 | private static ArrayList getM(List mocksList) { 155 | ArrayList mockArrayList = new ArrayList<>(); 156 | for (int i = 0; i < mocksList.size(); i++) { 157 | mockArrayList.add(mocksList.get(0)); 158 | } 159 | return mockArrayList; 160 | } 161 | 162 | /** 163 | * Send recorded mocks to the server 164 | * 165 | * @param path - folder path where mock should be stored 166 | * @param mock - mock object 167 | * @return - Boolean that determines whether mocks are stored or not 168 | */ 169 | public static boolean PutMock(String path, Service.Mock mock) { 170 | 171 | Service.PutMockReq putMockReq = Service.PutMockReq.newBuilder().setMock(mock).setPath(path).build(); 172 | Service.PutMockResp putMockResp = blockingStub.putMock(putMockReq); 173 | if (putMockResp == null) { // check iska error handle 174 | logger.error("Failed to call the putMock method"); 175 | return false; 176 | } 177 | return true; 178 | } 179 | 180 | 181 | } 182 | -------------------------------------------------------------------------------- /keploy-sdk/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | java-sdk 7 | io.keploy 8 | 1.0.0-SNAPSHOT 9 | 10 | 11 | keploy-sdk 12 | 0.0.1-SNAPSHOT 13 | keploy-sdk 14 | 15 | Client Java SDK for Keploy 16 | 17 | https://github.com/keploy/java-sdk 18 | 19 | 20 | The Apache License, Version 2.0 21 | http://www.apache.org/licenses/LICENSE-2.0.txt 22 | 23 | 24 | 25 | 26 | sarthak160 27 | Sarthak 28 | sarthak@keploy.io 29 | 30 | 31 | gouravkrosx 32 | Gourav Kumar 33 | gourav.kumar@keploy.io 34 | 35 | 36 | 37 | scm:git@github.com:keploy/java-sdk.git 38 | scm:git@github.com:keploy/java-sdk.git 39 | https://github.com/keploy/java-sdk.git 40 | 41 | 42 | 43 | 1.8 44 | 45 | 46 | 47 | 48 | io.keploy 49 | core 50 | 1.0.0-SNAPSHOT 51 | compile 52 | 53 | 54 | io.keploy 55 | api 56 | 1.0.0-SNAPSHOT 57 | compile 58 | 59 | 60 | io.keploy 61 | integration 62 | 1.0.0-SNAPSHOT 63 | compile 64 | 65 | 66 | io.keploy 67 | models 68 | 1.0.0-SNAPSHOT 69 | compile 70 | 71 | 72 | io.keploy 73 | common 74 | 1.0.0-SNAPSHOT 75 | compile 76 | 77 | 78 | javax.annotation 79 | javax.annotation-api 80 | 1.3.2 81 | 82 | 83 | io.grpc 84 | grpc-netty-shaded 85 | 1.49.2 86 | 87 | 88 | io.grpc 89 | grpc-protobuf 90 | 1.49.2 91 | 92 | 93 | io.grpc 94 | grpc-stub 95 | 1.49.2 96 | 97 | 98 | com.google.protobuf 99 | protobuf-java 100 | 3.21.7 101 | 102 | 103 | org.apache.tomcat 104 | annotations-api 105 | 6.0.53 106 | provided 107 | 108 | 109 | com.squareup.okhttp3 110 | okhttp 111 | 112 | 3.14.9 113 | provided 114 | 115 | 116 | 117 | 118 | 119 | src/main/resources 120 | 121 | 122 | 123 | 124 | org.apache.maven.plugins 125 | maven-javadoc-plugin 126 | 127 | 128 | attach-javadocs 129 | 130 | jar 131 | 132 | 133 | 134 | 135 | 136 | org.apache.maven.plugins 137 | maven-shade-plugin 138 | 3.2.4 139 | 140 | false 141 | 142 | 143 | 144 | package 145 | 146 | shade 147 | 148 | 149 | 150 | 151 | 152 | org.apache.maven.plugins 153 | maven-gpg-plugin 154 | 1.6 155 | 156 | 157 | sign-artifacts 158 | verify 159 | 160 | sign 161 | 162 | 163 | 164 | --pinentry-mode 165 | loopback 166 | 167 | 168 | 169 | 170 | 171 | 172 | org.codehaus.mojo 173 | build-helper-maven-plugin 174 | 3.5.0 175 | 176 | 177 | attach 178 | package 179 | 180 | attach-artifact 181 | 182 | 183 | 184 | 185 | 186 | ../keploy-sdk/target/javadoc-bundle-options/javadoc-options-javadoc-resources.xml 187 | 188 | jar 189 | javadoc 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | -------------------------------------------------------------------------------- /common/src/main/java/io/keploy/utils/GenericRequestWrapper.java: -------------------------------------------------------------------------------- 1 | package io.keploy.utils; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.ByteArrayOutputStream; 5 | import java.io.IOException; 6 | import java.io.InputStreamReader; 7 | import java.net.URLEncoder; 8 | import java.util.Arrays; 9 | import java.util.Enumeration; 10 | import java.util.Iterator; 11 | import java.util.List; 12 | import java.util.Map; 13 | import javax.servlet.ReadListener; 14 | import javax.servlet.ServletInputStream; 15 | import javax.servlet.http.HttpServletRequest; 16 | import javax.servlet.http.HttpServletRequestWrapper; 17 | 18 | /** 19 | * GenericRequestWrapper is a wrapper over the request sent to the filter. So that the request data is cached/stored and 20 | * not lost 21 | */ 22 | public class GenericRequestWrapper extends HttpServletRequestWrapper { 23 | private static final String FORM_CONTENT_TYPE = "application/x-www-form-urlencoded"; 24 | private final ByteArrayOutputStream cachedContent; 25 | private final Integer contentCacheLimit; 26 | private ServletInputStream inputStream; 27 | private BufferedReader reader; 28 | 29 | public GenericRequestWrapper(HttpServletRequest request) { 30 | super(request); 31 | int contentLength = request.getContentLength(); 32 | this.cachedContent = new ByteArrayOutputStream(contentLength >= 0 ? contentLength : 1024); 33 | this.contentCacheLimit = null; 34 | } 35 | 36 | public GenericRequestWrapper(HttpServletRequest request, int contentCacheLimit) { 37 | super(request); 38 | this.cachedContent = new ByteArrayOutputStream(contentCacheLimit); 39 | this.contentCacheLimit = contentCacheLimit; 40 | } 41 | 42 | public ServletInputStream getInputStream() throws IOException { 43 | if (this.inputStream == null) { 44 | this.inputStream = new ContentCachingInputStream(this.getRequest().getInputStream()); 45 | } 46 | 47 | return this.inputStream; 48 | } 49 | 50 | public String getCharacterEncoding() { 51 | String enc = super.getCharacterEncoding(); 52 | return enc != null ? enc : "ISO-8859-1"; 53 | } 54 | 55 | public BufferedReader getReader() throws IOException { 56 | if (this.reader == null) { 57 | this.reader = new BufferedReader(new InputStreamReader(this.getInputStream(), this.getCharacterEncoding())); 58 | } 59 | 60 | return this.reader; 61 | } 62 | 63 | public String getParameter(String name) { 64 | if (this.cachedContent.size() == 0 && this.isFormPost()) { 65 | this.writeRequestParametersToCachedContent(); 66 | } 67 | 68 | return super.getParameter(name); 69 | } 70 | 71 | public Map getParameterMap() { 72 | if (this.cachedContent.size() == 0 && this.isFormPost()) { 73 | this.writeRequestParametersToCachedContent(); 74 | } 75 | 76 | return super.getParameterMap(); 77 | } 78 | 79 | public Enumeration getParameterNames() { 80 | if (this.cachedContent.size() == 0 && this.isFormPost()) { 81 | this.writeRequestParametersToCachedContent(); 82 | } 83 | 84 | return super.getParameterNames(); 85 | } 86 | 87 | public String[] getParameterValues(String name) { 88 | if (this.cachedContent.size() == 0 && this.isFormPost()) { 89 | this.writeRequestParametersToCachedContent(); 90 | } 91 | 92 | return super.getParameterValues(name); 93 | } 94 | 95 | private boolean isFormPost() { 96 | String contentType = this.getContentType(); 97 | return contentType != null && contentType.contains("application/x-www-form-urlencoded") && (this.getMethod().equals("POST")); 98 | } 99 | 100 | private void writeRequestParametersToCachedContent() { 101 | try { 102 | if (this.cachedContent.size() == 0) { 103 | String requestEncoding = this.getCharacterEncoding(); 104 | Map form = super.getParameterMap(); 105 | Iterator nameIterator = form.keySet().iterator(); 106 | 107 | while (nameIterator.hasNext()) { 108 | String name = (String) nameIterator.next(); 109 | List values = Arrays.asList(form.get(name)); 110 | Iterator valueIterator = values.iterator(); 111 | 112 | while (valueIterator.hasNext()) { 113 | String value = (String) valueIterator.next(); 114 | this.cachedContent.write(URLEncoder.encode(name, requestEncoding).getBytes()); 115 | if (value != null) { 116 | this.cachedContent.write(61); 117 | this.cachedContent.write(URLEncoder.encode(value, requestEncoding).getBytes()); 118 | if (valueIterator.hasNext()) { 119 | this.cachedContent.write(38); 120 | } 121 | } 122 | } 123 | 124 | if (nameIterator.hasNext()) { 125 | this.cachedContent.write(38); 126 | } 127 | } 128 | } 129 | 130 | } catch (IOException var8) { 131 | throw new IllegalStateException("Failed to write request parameters to cached content", var8); 132 | } 133 | } 134 | 135 | public byte[] getData() { 136 | return this.cachedContent.toByteArray(); 137 | } 138 | 139 | protected void handleContentOverflow(int contentCacheLimit) { 140 | } 141 | 142 | private class ContentCachingInputStream extends ServletInputStream { 143 | private final ServletInputStream is; 144 | private boolean overflow = false; 145 | 146 | public ContentCachingInputStream(ServletInputStream is) { 147 | this.is = is; 148 | } 149 | 150 | public int read() throws IOException { 151 | int ch = this.is.read(); 152 | if (ch != -1 && !this.overflow) { 153 | if (GenericRequestWrapper.this.contentCacheLimit != null && GenericRequestWrapper.this.cachedContent.size() == GenericRequestWrapper.this.contentCacheLimit) { 154 | this.overflow = true; 155 | GenericRequestWrapper.this.handleContentOverflow(GenericRequestWrapper.this.contentCacheLimit); 156 | } else { 157 | GenericRequestWrapper.this.cachedContent.write(ch); 158 | } 159 | } 160 | 161 | return ch; 162 | } 163 | 164 | public int read(byte[] b) throws IOException { 165 | int count = this.is.read(b); 166 | this.writeToCache(b, 0, count); 167 | return count; 168 | } 169 | 170 | private void writeToCache(final byte[] b, final int off, int count) { 171 | if (!this.overflow && count > 0) { 172 | if (GenericRequestWrapper.this.contentCacheLimit != null && count + GenericRequestWrapper.this.cachedContent.size() > GenericRequestWrapper.this.contentCacheLimit) { 173 | this.overflow = true; 174 | GenericRequestWrapper.this.cachedContent.write(b, off, GenericRequestWrapper.this.contentCacheLimit - GenericRequestWrapper.this.cachedContent.size()); 175 | GenericRequestWrapper.this.handleContentOverflow(GenericRequestWrapper.this.contentCacheLimit); 176 | return; 177 | } 178 | 179 | GenericRequestWrapper.this.cachedContent.write(b, off, count); 180 | } 181 | 182 | } 183 | 184 | public int read(final byte[] b, final int off, final int len) throws IOException { 185 | int count = this.is.read(b, off, len); 186 | this.writeToCache(b, off, count); 187 | return count; 188 | } 189 | 190 | public int readLine(final byte[] b, final int off, final int len) throws IOException { 191 | int count = this.is.readLine(b, off, len); 192 | this.writeToCache(b, off, count); 193 | return count; 194 | } 195 | 196 | public boolean isFinished() { 197 | return this.is.isFinished(); 198 | } 199 | 200 | public boolean isReady() { 201 | return this.is.isReady(); 202 | } 203 | 204 | public void setReadListener(ReadListener readListener) { 205 | this.is.setReadListener(readListener); 206 | } 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /common/src/main/java/io/keploy/utils/MagicBytes.java: -------------------------------------------------------------------------------- 1 | package io.keploy.utils; 2 | 3 | import java.io.File; 4 | import java.io.FileInputStream; 5 | import java.io.FileNotFoundException; 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import java.util.ArrayList; 9 | import java.util.Arrays; 10 | import java.util.List; 11 | 12 | /** 13 | * Magic bytes can be checked at : https://en.wikipedia.org/wiki/List_of_file_signatures 14 | * And converted to integer hex params using JS in the browser console, for example : 15 | * "78 01 73 0D 62 ?? 60".split(" ").map(str => str == "??" ? "MagicBytes.ANY" : "0x"+str).join(", ") 16 | * prints => "0x78, 0x01, 0x73, 0x0D, 0x62, MagicBytes.ANY, 0x60" 17 | */ 18 | public enum MagicBytes { 19 | // Executables 20 | EXE(Header.builder() 21 | .add("EXE (includes PE32 + DOS)", 0x4D, 0x5A)), 22 | MACH_O(Header.builder() 23 | .add("MACH-O 32bit", 0xFE, 0xED, 0xFA, 0xCE) 24 | .add("MACH-O 64bit", 0xFE, 0xED, 0xFA, 0xCF)), 25 | SHEBANG(Header.builder() 26 | .add("SHEBANG (#!) script", 0x23, 0x21)), 27 | ELF(Header.builder() 28 | .add("ELF", 0x7F, 0x45, 0x4C, 0x46)), 29 | COM(Header.builder() 30 | .add("COM", 0xC9)), 31 | DALVIK(Header.builder() 32 | .add("DEX", 0x64, 0x65, 0x78, 0x0A, 0x30, 0x33, 0x35, 0x00)), 33 | DMG(Header.builder() 34 | .add("DMG", 0x78, 0x01, 0x73, 0x0D, 0x62, 0x62, 0x60)), 35 | // Archives 36 | SQLITE(Header.builder() 37 | .add("SQLITE3", 0x53, 0x51, 0x4c, 0x69, 0x74, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x20, 0x33, 0x00)), 38 | TAR_LZW(Header.builder() 39 | .add("TAR LZW", 0x1F, 0x9D) 40 | .add("TAR LZ", 0x1F, 0xA0)), 41 | BZIP2(Header.builder() 42 | .add("BZ2", 0x42, 0x5A, 0x68)), 43 | LZIP(Header.builder() 44 | .add("LZIP", 0x4C, 0x5A, 0x49, 0x50)), 45 | ZIP(Header.builder() 46 | .add("ZIP", 0x50, 0x4B, 0x03, 0x04) 47 | .add("ZIP (empty)", 0x50, 0x4B, 0x05, 0x06) 48 | .add("ZIP (spanned)", 0x50, 0x4B, 0x07, 0x08)), 49 | RAR(Header.builder() 50 | .add("RAR v1.5+", 0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x00) 51 | .add("RAR v5+", 0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x01, 0x00)), 52 | ISO(Header.builder() 53 | .add("ISO9660 CD/DVD Image File", 0x43, 0x44, 0x30, 0x30, 0x31)), 54 | VMDK(Header.builder() 55 | .add("VMDK", 0x4B, 0x44, 0x4D)), 56 | VDI(Header.builder() 57 | .add("VDI (VirtualBox)", 0x3C, 0x3C, 0x3C, 0x20, 0x4F, 0x72, 0x61, 0x63, 0x6C, 0x65, 0x20, 0x56, 0x4D, 0x20, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6C, 0x42, 0x6F, 0x78, 0x20, 0x44, 0x69, 0x73, 0x6B, 0x20, 0x49, 0x6D, 0x61, 0x67, 0x65, 0x20, 0x3E, 0x3E, 0x3E)), 58 | VHD(Header.builder() 59 | .add("VHD (Win)", 0x63, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74, 0x69, 0x78)), 60 | VHDX(Header.builder() 61 | .add("VHDX (Win8)", 0x76, 0x68, 0x64, 0x78, 0x66, 0x69, 0x6C, 0x65)), 62 | ISZ(Header.builder() 63 | .add("ISZ (compressed ISO)", 0x49, 0x73, 0x5A, 0x21)), 64 | EVT(Header.builder() 65 | .add("Windows Event Viewer", 0x4C, 0x66, 0x4C, 0x65)), 66 | XAR(Header.builder() 67 | .add("eXtensible ARchive", 0x78, 0x61, 0x72, 0x21)), 68 | TAR(Header.builder() 69 | .add("TAR (subpackage)", 0x75, 0x73, 0x74, 0x61, 0x72, 0x00, 0x30, 0x30) 70 | .add("TAR", 0x75, 0x73, 0x74, 0x61, 0x72, 0x20, 0x20, 0x00)), 71 | SEVEN_ZIP(Header.builder() 72 | .add("7Z", 0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C)), 73 | GZIP(Header.builder() 74 | .add("GZ", 0x1F, 0x8B)), 75 | MATROSKA(Header.builder() 76 | .add("MKV/WebM", 0x1A, 0x45, 0xDF, 0xA3)), 77 | DICOM(Header.builder() 78 | .add("DICOM", 0x44, 0x49, 0x43, 0x4D)), 79 | ZLIB(Header.builder() 80 | .add("ZLIB (No compression - no preset dictionary)", 0x78, 0x01) 81 | .add("ZLIB (Best speed - no preset dictionary)", 0x78, 0x5E) 82 | .add("ZLIB (Default compression - no preset dictionary)", 0x78, 0x9C) 83 | .add("ZLIB (Best compression - no preset dictionary)", 0x78, 0xDA) 84 | .add("ZLIB (No compression - with preset dictionary)", 0x78, 0x20) 85 | .add("ZLIB (Best speed - with preset dictionary)", 0x78, 0x7D) 86 | .add("ZLIB (Default compression - with preset dictionary)", 0x78, 0xBB) 87 | .add("ZLIB (Best compression - with preset dictionary)", 0x78, 0xF9)), 88 | LZFSE(Header.builder() 89 | .add("LZFSE (Apple)", 0x62, 0x76, 0x78, 0x32)), 90 | PST(Header.builder() 91 | .add("Microsoft Outlook", 0x21, 0x42, 0x44, 0x4E)), 92 | // Text 93 | REG(Header.builder() 94 | .add("Windows Registry File/DAT", 0x72, 0x65, 0x67, 0x66)), 95 | DAT(Header.builder() 96 | .add("DAT/USMT 3+", 0x50, 0x4D, 0x4F, 0x43, 0x43, 0x4D, 0x4F, 0x43)), 97 | OFFICE_OLD(Header.builder() 98 | .add("Compound File Binary Format (MS-Office)", 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1)), 99 | PDF(Header.builder() 100 | .add("PDF", 0x25, 0x50, 0x44, 0x46, 0x2d)), 101 | XML(Header.builder() 102 | .add("XML", 0x3c, 0x3f, 0x78, 0x6d, 0x6c, 0x20)), 103 | RTT(Header.builder() 104 | .add("RTT", 0x7B, 0x5C, 0x72, 0x74, 0x66, 0x31)), 105 | // Images 106 | PNG(Header.builder() 107 | .add("PNG", 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A)), 108 | PBM(Header.builder() 109 | .add("PBM", 0x50, 0x31, 0x0A)), 110 | PGM(Header.builder() 111 | .add("PGM", 0x50, 0x32, 0x0A)), 112 | PPM(Header.builder() 113 | .add("PPM", 0x50, 0x33, 0x0A)), 114 | JPG(Header.builder() 115 | .add("JPG Raw", 0xFF, 0xD8, 0xFF, 0xDB) 116 | .add("JPG Raw 2", 0xFF, 0xD8, 0xFF, 0xEE) 117 | .add("JPG JFIF", 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01) 118 | .add("JPG EXIF", 0xFF, 0xD8, 0xFF, 0xE1, MagicBytes.ANY, MagicBytes.ANY, 0x45, 0x78, 0x69, 0x66, 0x00, 0x00)), 119 | GIF(Header.builder() 120 | .add("GIF87a", 0x47, 0x49, 0x46, 0x38, 0x37, 0x61) 121 | .add("GIF89a", 0x47, 0x49, 0x46, 0x38, 0x39, 0x61)), 122 | TIFF(Header.builder() 123 | .add("TIFF LE", 0x49, 0x49, 0x2A, 0x00) 124 | .add("TIFF BE", 0x4D, 0x4D, 0x00, 0x2A)), 125 | BMP(Header.builder() 126 | .add("BMP", 0x42, 0x4D)), 127 | // Audio 128 | WAV(Header.builder() 129 | .add("WAV", 0x52, 0x49, 0x46, 0x46, MagicBytes.ANY, MagicBytes.ANY, MagicBytes.ANY, MagicBytes.ANY, 0x57, 0x41, 0x56, 0x45)), 130 | MP3(Header.builder() 131 | .add("MP3", 0x49, 0x44, 0x33)), 132 | FLAC(Header.builder() 133 | .add("FLAC", 0x66, 0x4C, 0x61, 0x43)), 134 | MIDI(Header.builder() 135 | .add("MIDI", 0x4D, 0x54, 0x68, 0x64)), 136 | // Video 137 | AVI(Header.builder() 138 | .add("AVI", 0x52, 0x49, 0x46, 0x46, MagicBytes.ANY, MagicBytes.ANY, MagicBytes.ANY, MagicBytes.ANY, 0x41, 0x56, 0x49, 0x20)), 139 | MP4(Header.builder() 140 | .add("MP4", 0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70, 0x69, 0x73, 0x6F, 0x6D)), 141 | FLV(Header.builder() 142 | .add("FLV", 0x46, 0x4C, 0x56)), 143 | ; 144 | 145 | private static final int ANY = -1; 146 | private final Header[] headers; 147 | 148 | private MagicBytes(Header.Builder builder) { 149 | this.headers = builder.build(); 150 | } 151 | 152 | public Header[] getHeaders() { 153 | return headers; 154 | } 155 | 156 | /* Checks if bytes match a specific magic bytes sequence. 157 | * Tries to match each header sequentially, the code 158 | * short-circuits on match found. */ 159 | public Header is(byte[] bytes) { 160 | boolean matches; 161 | for (Header header : headers) { 162 | matches = true; 163 | for (int i = 0; i < header.bytes.length; i++) { 164 | if (header.bytes[i] != ANY && header.bytes[i] != Byte.toUnsignedInt(bytes[i])) { 165 | matches = false; 166 | break; 167 | } 168 | } 169 | if (matches) 170 | return header; 171 | } 172 | return null; 173 | } 174 | 175 | // Extracts head bytes from any stream 176 | public static byte[] extract(InputStream is, int length) throws IOException { 177 | try { 178 | byte[] buffer = new byte[length]; 179 | is.read(buffer, 0, length); 180 | return buffer; 181 | } finally { 182 | is.close(); 183 | } 184 | } 185 | 186 | public static Header matches(byte[] bytes) { 187 | Header header; 188 | for (MagicBytes magic : MagicBytes.values()) { 189 | header = magic.is(bytes); 190 | if (header != null) 191 | return header; 192 | } 193 | return null; 194 | } 195 | 196 | //TODO: Add support for more content type. 197 | public static String getContentType(Header ct) { 198 | String contentType = ct.getName(); 199 | if (contentType.contains("PNG")) { 200 | return "png"; 201 | } else if (contentType.contains("JPG")) { 202 | return "jpg"; 203 | } else if (contentType.contains("PDF")) { 204 | return "pdf"; 205 | } else if (contentType.contains("XML")) { 206 | return "xml"; 207 | } 208 | return ""; 209 | } 210 | 211 | /* Convenience methods */ 212 | 213 | public Header is(String name) throws FileNotFoundException, IOException { 214 | return is(new File(name)); 215 | } 216 | 217 | public Header is(File file) throws FileNotFoundException, IOException { 218 | return is(new FileInputStream(file)); 219 | } 220 | 221 | public Header is(InputStream is) throws IOException { 222 | return is(extract(is, 50)); 223 | } 224 | 225 | public static Header matching(String name) throws FileNotFoundException, IOException { 226 | return matching(new File(name)); 227 | } 228 | 229 | public static Header matching(File file) throws FileNotFoundException, IOException { 230 | return matching(new FileInputStream(file)); 231 | } 232 | 233 | public static Header matching(InputStream is) throws IOException { 234 | return matches(extract(is, 50)); 235 | } 236 | 237 | public static final class Header { 238 | private final String name; 239 | private final int[] bytes; 240 | 241 | public Header(String name, int[] bytes) { 242 | this.name = name; 243 | this.bytes = bytes; 244 | } 245 | 246 | public String getName() { 247 | return name; 248 | } 249 | 250 | public int[] getBytes() { 251 | return bytes; 252 | } 253 | 254 | @Override 255 | public String toString() { 256 | return String.format("%s: %s", name, Arrays.toString(bytes)); 257 | } 258 | 259 | private static Builder builder() { 260 | return new Builder(); 261 | } 262 | 263 | private static final class Builder { 264 | private final List
headers = new ArrayList<>(); 265 | 266 | 267 | public Builder add(String name, int... bytes) { 268 | headers.add(new Header(name, bytes)); 269 | return this; 270 | } 271 | 272 | public Header[] build() { 273 | return headers.toArray(new Header[0]); 274 | } 275 | } 276 | } 277 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2022 Keploy Inc 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /integration/src/main/java/io/keploy/httpClients/OkHttpInterceptor_Kotlin.java: -------------------------------------------------------------------------------- 1 | package io.keploy.httpClients; 2 | 3 | import com.google.protobuf.ByteString; 4 | import com.google.protobuf.ProtocolStringList; 5 | import io.keploy.grpc.stubs.Service; 6 | import io.keploy.regression.KeployInstance; 7 | import io.keploy.regression.context.Context; 8 | import io.keploy.regression.context.Kcontext; 9 | import io.keploy.regression.Mock; 10 | import io.keploy.regression.Mode; 11 | import io.keploy.service.GrpcService; 12 | import io.keploy.service.mock.Config; 13 | import io.keploy.service.mock.MockLib; 14 | import io.keploy.utils.HttpStatusReasons; 15 | import okhttp3.*; 16 | import okio.Buffer; 17 | import okio.BufferedSource; 18 | import org.apache.logging.log4j.LogManager; 19 | import org.apache.logging.log4j.Logger; 20 | 21 | import java.io.IOException; 22 | import java.util.*; 23 | 24 | 25 | /** 26 | * This class is used for intercepting constructor of OkHttpClient$Builder class and the following method runs instead of 27 | * the actual method and test cases and mocks are recorded and tested via this class. 28 | */ 29 | public class OkHttpInterceptor_Kotlin implements Interceptor { 30 | private static final Logger logger = LogManager.getLogger(OkHttpInterceptor_Kotlin.class); 31 | 32 | private static final String CROSS = new String(Character.toChars(0x274C)); 33 | 34 | /** 35 | * This method will get called instead of constructor of OkHttpClient$Builder 36 | * 37 | * @param chain - original method client 38 | * @return - Response object 39 | */ 40 | @Override 41 | public Response intercept(Chain chain) throws IOException { 42 | 43 | logger.debug("inside OkHttpInterceptor"); 44 | 45 | Request request = chain.request(); 46 | 47 | Kcontext kctx = Context.getCtx(); 48 | 49 | if (kctx == null) { 50 | logger.debug("simulate call"); 51 | return chain.proceed(request); 52 | } 53 | 54 | Mode.ModeType modeFromContext = kctx.getMode().getModeFromContext(); 55 | 56 | if (modeFromContext.equals(Mode.ModeType.MODE_OFF)) { 57 | return chain.proceed(request); // calling original method 58 | } 59 | 60 | String reqBody = getRequestBody(request); 61 | 62 | Map meta = new HashMap<>(); 63 | 64 | meta.put("name", "okhttp"); 65 | meta.put("type", "HTTP_CLIENT"); 66 | meta.put("operation", request.method()); 67 | meta.put("URL", request.url().toString()); 68 | meta.put("Header", request.headers().toString()); 69 | meta.put("Body", reqBody); 70 | 71 | Response response = null; 72 | 73 | switch (modeFromContext) { 74 | case MODE_TEST: //don't call chain.proceed(request) when not in file export 75 | if (kctx.getMock().size() > 0 && kctx.getMock().get(0).getKind().equals(Mock.Kind.HTTP_EXPORT.value)) { 76 | List mocks = kctx.getMock(); 77 | if (mocks.size() > 0 && mocks.get(0).getSpec().getObjectsCount() > 0) { 78 | logger.debug("test mode"); 79 | 80 | ByteString bin = mocks.get(0).getSpec().getObjectsList().get(0).getData(); 81 | 82 | Service.HttpResp httpResp = mocks.get(0).getSpec().getRes(); 83 | String body = httpResp.getBody(); 84 | long statusCode = httpResp.getStatusCode(); 85 | Map headerMap = httpResp.getHeaderMap(); 86 | String statusMsg = httpResp.getStatusMessage(); 87 | MediaType mediaType = MediaType.parse("application/json; charset=utf-8"); 88 | 89 | // ResponseBody 90 | // resBody = ResponseBody.create(body, mediaType); 91 | ResponseBody 92 | resBody = ResponseBody.create(mediaType, body); 93 | 94 | final long protoMajor = httpResp.getProtoMajor(); 95 | final long protoMinor = httpResp.getProtoMinor(); 96 | 97 | Protocol protocol = getProtocol(protoMinor, protoMajor); 98 | 99 | Response.Builder resBuilder = new Response.Builder().body(resBody) 100 | .code((int) statusCode) 101 | .message(statusMsg) 102 | .request(request) 103 | .protocol(protocol); 104 | response = setResponseHeaders(resBuilder, headerMap); 105 | 106 | //since okhttp request doesn't give protocol hence setting here. 107 | meta.put("ProtoMajor", String.valueOf(protoMajor)); 108 | meta.put("ProtoMinor", String.valueOf(protoMinor)); 109 | 110 | mocks.remove(0); 111 | } 112 | 113 | if (response == null) { 114 | logger.error(CROSS + " unable to read response"); 115 | throw new RuntimeException("unable to read response"); 116 | } 117 | 118 | return response; 119 | } else { 120 | logger.error(CROSS + " mocks not present in " + KeployInstance.getInstance().getKeploy().getCfg().getApp().getMockPath() + " directory."); 121 | throw new RuntimeException("unable to read mocks from keploy context"); 122 | } 123 | case MODE_RECORD: 124 | logger.debug("record mode"); 125 | 126 | response = chain.proceed(request); 127 | 128 | String responseBody = getResponseBody(response); 129 | int statuscode = response.code(); 130 | String statusMsg = HttpStatusReasons.getStatusMsg(statuscode); 131 | 132 | long[] protocol = getProtoVersion(response.protocol()); 133 | long ProtoMinor = protocol[0]; 134 | long ProtoMajor = protocol[1]; 135 | 136 | Map resHeaders = getHeadersMap(response.headers()); 137 | 138 | Service.HttpResp httpResp = Service.HttpResp.newBuilder() 139 | .setBody(responseBody) 140 | .setStatusCode(statuscode) 141 | .setStatusMessage(statusMsg) 142 | .setProtoMajor(ProtoMajor) 143 | .setProtoMinor(ProtoMinor) 144 | .putAllHeader(resHeaders) 145 | .build(); 146 | 147 | Service.HttpReq httpReq = Service.HttpReq.newBuilder() 148 | .setMethod(request.method()) 149 | .setBody(reqBody) 150 | .setURL(String.valueOf(request.url())) 151 | .setProtoMajor(ProtoMajor) 152 | .setProtoMinor(ProtoMinor) 153 | .putAllHeader(getHeadersMap(request.headers())) 154 | .putAllURLParams(getUrlParams(request)) 155 | .build(); 156 | 157 | 158 | meta.put("ProtoMajor", String.valueOf(ProtoMajor)); 159 | meta.put("ProtoMinor", String.valueOf(ProtoMinor)); 160 | 161 | Service.Mock.Object obj = Service.Mock.Object.newBuilder().setType("error").setData(com.google.protobuf.ByteString.fromHex("")).build(); 162 | List lobj = new ArrayList<>(); 163 | lobj.add(obj); 164 | 165 | Service.Mock.SpecSchema specSchema = Service.Mock.SpecSchema.newBuilder() 166 | .setReq(httpReq) 167 | .setRes(httpResp) 168 | .putAllMetadata(meta) 169 | .addAllObjects(lobj) 170 | .build(); 171 | 172 | Service.Mock httpMock = Service.Mock.newBuilder() 173 | .setVersion(Mock.Version.V1_BETA1.value) 174 | .setKind(Mock.Kind.HTTP_EXPORT.value) 175 | .setName(Config.Name) 176 | .setSpec(specSchema) 177 | .build(); 178 | 179 | // for mock library to work 180 | if (GrpcService.blockingStub != null && kctx.getFileExport() && !Config.MockId.containsKey(kctx.getTestId())) { 181 | final boolean recorded = MockLib.PutMock(Config.MockPath, httpMock); 182 | String CAPTURE = "\uD83D\uDFE0"; 183 | if (recorded) { 184 | logger.info(CAPTURE + " Captured the mocked outputs for Http dependency call with meta: {}", meta); 185 | } 186 | return response; 187 | } 188 | 189 | kctx.getMock().add(httpMock); 190 | return response; 191 | default: 192 | logger.error(CROSS + " integrations: Not in a valid sdk mode"); 193 | return chain.proceed(request); 194 | } 195 | } 196 | 197 | private long[] getProtoVersion(Protocol protocol) { 198 | long[] proto = new long[2]; 199 | String pname = protocol.name(); 200 | if (pname.length() == 6) { 201 | proto[1] = Character.getNumericValue(pname.charAt(5)); 202 | } else { 203 | proto[0] = Character.getNumericValue(pname.charAt(7)); 204 | proto[1] = Character.getNumericValue(pname.charAt(5)); 205 | } 206 | return proto; 207 | } 208 | 209 | private Protocol getProtocol(long protoMinor, long protoMajor) { 210 | 211 | if (protoMajor == 2) { 212 | return Protocol.HTTP_2; 213 | } else if (protoMajor == 1 && protoMinor == 1) { 214 | return Protocol.HTTP_1_1; 215 | } else { 216 | return Protocol.HTTP_1_0; 217 | } 218 | } 219 | 220 | private Map getUrlParams(Request request) { 221 | Map map = new HashMap<>(); 222 | 223 | for (String key : request.url().queryParameterNames()) { 224 | String value = request.url().queryParameterValues(key).get(0); 225 | map.put(key, value); 226 | } 227 | return map; 228 | } 229 | 230 | private Response setResponseHeaders(Response.Builder resB, Map srcMap) { 231 | Map> headerMap = new HashMap<>(); 232 | 233 | for (String key : srcMap.keySet()) { 234 | Service.StrArr values = srcMap.get(key); 235 | ProtocolStringList valueList = values.getValueList(); 236 | List headerValues = new ArrayList<>(valueList); 237 | headerMap.put(key, headerValues); 238 | } 239 | 240 | for (String key : headerMap.keySet()) { 241 | List values = headerMap.get(key); 242 | for (String value : values) { 243 | resB.addHeader(key, value); 244 | } 245 | } 246 | return resB.build(); 247 | } 248 | 249 | 250 | // private Map> getHeadersMultimap(Headers headers) { 251 | // 252 | // Map> hmap = new HashMap<>(); 253 | // 254 | // for (Pair header : headers) { 255 | // String key = header.getFirst(); 256 | // String value = header.getSecond(); 257 | // hmap.computeIfAbsent(key, x -> new ArrayList<>()).add(value); 258 | // } 259 | // 260 | // return hmap; 261 | // } 262 | 263 | private Map getHeadersMap(Headers headers) { 264 | 265 | Map> hmap = headers.toMultimap(); 266 | 267 | Map map = new HashMap<>(); 268 | 269 | for (String name : hmap.keySet()) { 270 | 271 | List values = hmap.get(name); 272 | Service.StrArr.Builder builder = Service.StrArr.newBuilder(); 273 | 274 | for (String s : values) { 275 | builder.addValue(s); 276 | } 277 | Service.StrArr value = builder.build(); 278 | 279 | map.put(name, value); 280 | } 281 | return map; 282 | } 283 | 284 | private String getRequestBody(Request request) { 285 | if (request.body() != null) { 286 | try { 287 | final Request copy = request.newBuilder().build(); 288 | final Buffer buffer = new Buffer(); 289 | Objects.requireNonNull(copy.body()).writeTo(buffer); 290 | return buffer.readUtf8(); 291 | } catch (final IOException e) { 292 | logger.error(CROSS + " unable to read request body", e); 293 | } 294 | } 295 | return ""; 296 | } 297 | 298 | private String getResponseBody(Response response) throws IOException { 299 | final BufferedSource source = Objects.requireNonNull(response.body()).source(); 300 | source.request(Integer.MAX_VALUE); 301 | okio.ByteString snapshot = source.buffer().snapshot(); 302 | return snapshot.utf8(); 303 | } 304 | } -------------------------------------------------------------------------------- /integration/src/main/java/io/keploy/servlet/KeployMiddleware.java: -------------------------------------------------------------------------------- 1 | package io.keploy.servlet; 2 | 3 | import io.grpc.netty.shaded.io.netty.util.internal.InternalThreadLocalMap; 4 | import org.apache.logging.log4j.LogManager; 5 | import org.apache.logging.log4j.Logger; 6 | import org.jacoco.core.analysis.Analyzer; 7 | import org.jacoco.core.analysis.CoverageBuilder; 8 | import org.jacoco.core.analysis.IClassCoverage; 9 | import org.jacoco.core.analysis.ICounter; 10 | import org.jacoco.core.data.ExecutionDataWriter; 11 | import org.jacoco.core.runtime.RemoteControlReader; 12 | import org.jacoco.core.runtime.RemoteControlWriter; 13 | import org.jacoco.core.tools.ExecFileLoader; 14 | import org.yaml.snakeyaml.DumperOptions; 15 | import org.yaml.snakeyaml.Yaml; 16 | import org.yaml.snakeyaml.reader.UnicodeReader; 17 | 18 | import javax.servlet.*; 19 | import javax.servlet.http.HttpServletRequest; 20 | import javax.servlet.http.HttpServletResponse; 21 | import java.io.*; 22 | import java.net.InetAddress; 23 | import java.net.Socket; 24 | import java.util.*; 25 | import java.util.concurrent.ExecutorService; 26 | import java.util.concurrent.Executors; 27 | import java.util.concurrent.Future; 28 | import java.util.concurrent.atomic.AtomicInteger; 29 | 30 | import static java.lang.System.out; 31 | 32 | public class KeployMiddleware implements Filter { 33 | 34 | private final ExecutorService executorService = Executors.newFixedThreadPool(10); 35 | public static int Lines_covered = 0; 36 | public static int Branch_covered = 0; 37 | public static int Lines_total = 0; 38 | public static int Branch_total = 0; 39 | public static int Methods_covered = 0; 40 | public static int Methods_total = 0; 41 | public static int Classes_covered = 0; 42 | public static int Classes_total = 0; 43 | public static String Line_Path = ""; 44 | 45 | private static final Logger logger = LogManager.getLogger(KeployMiddleware.class); 46 | private static final String CROSS = new String(Character.toChars(0x274C)); 47 | public static ArrayList stackTraceArr = new ArrayList<>(); 48 | private static boolean EnableDedup = false; 49 | public static AtomicInteger metCount = new AtomicInteger(0); 50 | public static AtomicInteger reqCount = new AtomicInteger(0); 51 | public static AtomicInteger cnt = new AtomicInteger(0); 52 | // public static AtomicInteger linesCovered = new AtomicInteger(0); 53 | 54 | // private static final String DESTFILE = "jacoco-client.exec"; 55 | 56 | private static final String ADDRESS = "localhost"; 57 | 58 | private static final int PORT = 36320; 59 | 60 | @Override 61 | public void init(FilterConfig filterConfig) { 62 | logger.debug("Keploy Middleware initialized"); 63 | 64 | } 65 | 66 | private static final String SET_PLAIN_TEXT = "\033[0;0m"; 67 | private static final String SET_BOLD_TEXT = "\033[0;1m"; 68 | 69 | private static String bold(String str) { 70 | return (SET_BOLD_TEXT + str + SET_PLAIN_TEXT); 71 | } 72 | 73 | @Override 74 | public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) 75 | throws IOException, ServletException { 76 | HttpServletRequest request = (HttpServletRequest) servletRequest; 77 | HttpServletResponse response = (HttpServletResponse) servletResponse; 78 | 79 | String keploy_test_id = request.getHeader("KEPLOY-TEST-ID"); 80 | String keploy_test_set_id = request.getHeader("KEPLOY-TEST-SET-ID"); 81 | // logger.debug("KEPLOY-TEST-ID: {}", keploy_test_id); 82 | filterChain.doFilter(request, response); 83 | if (System.getenv("ENABLE_DEDUP") != null) { 84 | String bool = System.getenv("ENABLE_DEDUP").trim(); 85 | EnableDedup = bool.equals("true"); 86 | } 87 | // check if dedup is disabled then what should be the goal may be we can extract from header if dedup is enabled or not 88 | if (keploy_test_id != null && EnableDedup) { 89 | 90 | // Run getCoverage in a separate thread 91 | // Thread coverageThread = new Thread(() -> { 92 | try { 93 | getCoverage(keploy_test_id, keploy_test_set_id); 94 | } catch (InterruptedException | IOException e) { 95 | throw new RuntimeException(e); 96 | } 97 | // }); 98 | 99 | // coverageThread.start(); 100 | // try { 101 | // Thread.sleep(500); 102 | // } catch (InterruptedException e) { 103 | // throw new RuntimeException(e); 104 | // } 105 | } 106 | 107 | } 108 | 109 | @Override 110 | public void destroy() { 111 | InternalThreadLocalMap.destroy(); 112 | } 113 | 114 | public void execWriter(String keploy_test_id) throws IOException { 115 | File directory = new File( 116 | System.getProperty("user.dir") + "/target"); 117 | File file = new File(directory, "jacoco-client" + keploy_test_id + ".exec"); 118 | // File file = new File(directory, "jacoco-client.exec"); 119 | 120 | final FileOutputStream localFile = new FileOutputStream(file); 121 | 122 | final ExecutionDataWriter localWriter = new ExecutionDataWriter( 123 | localFile); 124 | 125 | // Open a socket to the coverage agent: 126 | final Socket socket = new Socket(InetAddress.getByName(ADDRESS), PORT); 127 | final RemoteControlWriter writer = new RemoteControlWriter( 128 | socket.getOutputStream()); 129 | final RemoteControlReader reader = new RemoteControlReader( 130 | socket.getInputStream()); 131 | reader.setSessionInfoVisitor(localWriter); 132 | reader.setExecutionDataVisitor(localWriter); 133 | 134 | // Send a dump command and read the response: 135 | writer.visitDumpCommand(true, true); 136 | if (!reader.read()) { 137 | throw new IOException("Socket closed unexpectedly."); 138 | } 139 | 140 | socket.close(); 141 | localFile.close(); 142 | } 143 | 144 | public synchronized void execWriter2(String keploy_test_id) throws IOException { 145 | File directory = new File(System.getProperty("user.dir")+"/target"); 146 | File file = new File(directory, "jacoco-client" + keploy_test_id + ".exec"); 147 | 148 | FileOutputStream localFile = null; 149 | ExecutionDataWriter localWriter = null; 150 | Socket socket = null; 151 | RemoteControlWriter writer = null; 152 | RemoteControlReader reader = null; 153 | 154 | try { 155 | localFile = new FileOutputStream(file); 156 | BufferedOutputStream bufferedLocalFile = new BufferedOutputStream(localFile); 157 | localWriter = new ExecutionDataWriter(bufferedLocalFile); 158 | socket = new Socket(InetAddress.getByName(ADDRESS), PORT); 159 | writer = new RemoteControlWriter(socket.getOutputStream()); 160 | reader = new RemoteControlReader(socket.getInputStream()); 161 | 162 | reader.setSessionInfoVisitor(localWriter); 163 | reader.setExecutionDataVisitor(localWriter); 164 | 165 | // Send a dump command and read the response: 166 | writer.visitDumpCommand(true, true); 167 | 168 | if (!reader.read()) { 169 | throw new IOException("Socket closed unexpectedly."); 170 | } 171 | } finally { 172 | // Close resources in a finally block to ensure they are closed even if an exception occurs 173 | 174 | if (socket != null && !socket.isClosed()) { 175 | socket.close(); 176 | } 177 | 178 | if (localFile != null) { 179 | localFile.close(); 180 | } 181 | } 182 | } 183 | 184 | public void getCoverage(String keploy_test_id, String keploy_test_set_id) throws IOException, InterruptedException { 185 | 186 | try { 187 | execWriter(keploy_test_id); 188 | } catch (IOException e) { 189 | e.printStackTrace(); 190 | } 191 | 192 | try { 193 | execReader(keploy_test_id, keploy_test_set_id); 194 | } catch (IOException e) { 195 | e.printStackTrace(); // Example: print the stack trace 196 | } 197 | 198 | } 199 | 200 | public void shutdownExecutor() { 201 | executorService.shutdown(); 202 | } 203 | 204 | private void execReader(String keploy_test_id, String keploy_test_set_id) throws IOException { 205 | // Together with the original class definition we can calculate coverage 206 | // information: 207 | out.println("------------------------------------------"); 208 | Line_Path = ""; 209 | ExecFileLoader loader = new ExecFileLoader(); 210 | 211 | List> dataList = new ArrayList<>(); 212 | // Load the coverage data file 213 | File coverageFile = new File( 214 | System.getProperty("user.dir") + 215 | "/target/jacoco-client" + keploy_test_id + ".exec"); 216 | // File coverageFile = new File( 217 | // System.getProperty("user.dir") + 218 | // "/target/jacoco-client.exec"); 219 | loader.load(coverageFile); 220 | File binDir = new File( 221 | System.getProperty("user.dir")+ "/target/classes"); 222 | final CoverageBuilder coverageBuilder = new CoverageBuilder(); 223 | final Analyzer analyzer = new Analyzer(loader.getExecutionDataStore(), coverageBuilder); 224 | analyzer.analyzeAll(binDir); 225 | int x = 0; 226 | Map> executedLinesByFile = new HashMap<>(); 227 | 228 | for (final IClassCoverage cc : coverageBuilder.getClasses()) { 229 | // out.printf("Coverage of class %s%n", cc.getName()); 230 | String ClassName = cc.getName(); // base64Encode(cc.getName()); 231 | // System.out.println(cc.getMethods()); 232 | java.util.Collection method = cc.getMethods(); 233 | 234 | cc.getInstructionCounter().getTotalCount(); 235 | List ls = new ArrayList<>(); 236 | for (int i = cc.getFirstLine(); i <= cc.getLastLine(); i++) { 237 | // out.printf("Line %s: %s%n", Integer.valueOf(i), 238 | // getColor(cc.getLine(i).getStatus())); 239 | if (getColor(cc.getLine(i).getStatus()).equals("green")) { 240 | Line_Path += ClassName + i + ","; 241 | ls.add(i); 242 | } 243 | } 244 | if (ls.size() != 0) { 245 | executedLinesByFile.put(ClassName, ls); 246 | } 247 | 248 | } 249 | 250 | // System.out.println("Line_Path: " + Line_Path); 251 | 252 | Map testData = new HashMap<>(); 253 | testData.put("id", keploy_test_set_id+ "/" + keploy_test_id); 254 | // Map test1 = createTestData("test-1",testData); 255 | testData.put("executedLinesByFile", executedLinesByFile); 256 | 257 | dataList.add(testData); 258 | 259 | List> existingData = readYamlFile("dedupData.yaml"); 260 | // Append new data to the existing data 261 | existingData.addAll(dataList); 262 | 263 | // Write data to YAML file 264 | writeYamlFile(existingData, "dedupData.yaml"); 265 | } 266 | 267 | private void printCounter(final String unit, final ICounter counter) { 268 | final Integer missed = Integer.valueOf(counter.getMissedCount()); 269 | final Integer total = Integer.valueOf(counter.getTotalCount()); 270 | out.printf("%s of %s %s missed%n", missed, total, unit); 271 | Lines_covered = total - missed; 272 | // System.out.println("Lines covered: " + Lines_covered); 273 | Lines_total = total; 274 | // System.out.println("Lines total: " + Lines_total); 275 | 276 | } 277 | 278 | private String getColor(final int status) { 279 | switch (status) { 280 | case ICounter.NOT_COVERED: 281 | return "red"; 282 | case ICounter.PARTLY_COVERED: 283 | return "yellow"; 284 | case ICounter.FULLY_COVERED: 285 | return "green"; 286 | } 287 | return ""; 288 | } 289 | 290 | private static List> readYamlFile(String fileName) { 291 | List> existingData = new ArrayList<>(); 292 | 293 | try (InputStream input = new FileInputStream(fileName); 294 | UnicodeReader reader = new UnicodeReader(input)) { 295 | 296 | Yaml yaml = new Yaml(); 297 | existingData = yaml.load(reader); 298 | 299 | } catch (IOException e) { 300 | e.printStackTrace(); 301 | } 302 | 303 | return existingData != null ? existingData : new ArrayList<>(); 304 | } 305 | 306 | public static String base64Encode(String input) { 307 | byte[] encodedBytes = Base64.getEncoder().encode(input.getBytes()); 308 | return new String(encodedBytes); 309 | } 310 | 311 | private static void writeYamlFile(List> dataList, String fileName) { 312 | DumperOptions options = new DumperOptions(); 313 | options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); 314 | 315 | Yaml yaml = new Yaml(options); 316 | 317 | try (FileWriter writer = new FileWriter(fileName)) { 318 | yaml.dump(dataList, writer); 319 | System.out.println("Dedup YAML file updated successfully:- " + fileName); 320 | } catch (IOException e) { 321 | e.printStackTrace(); 322 | } 323 | } 324 | 325 | } -------------------------------------------------------------------------------- /integration/src/main/java/io/keploy/ksql/KResultSetMetaData.java: -------------------------------------------------------------------------------- 1 | package io.keploy.ksql; 2 | 3 | import io.keploy.regression.Mode; 4 | import org.apache.logging.log4j.LogManager; 5 | 6 | import java.sql.ResultSetMetaData; 7 | import java.sql.SQLException; 8 | import java.util.HashMap; 9 | 10 | import static io.keploy.ksql.KDriver.mode; 11 | import static io.keploy.ksql.KDriver.testMode; 12 | import static io.keploy.ksql.KResultSet.*; 13 | 14 | /** 15 | * KResultSetMetaData is a wrapper class for ResultSetMetaData used in SQL, this class helps in recording data in record mode 16 | * and providing data in test mode 17 | */ 18 | public class KResultSetMetaData implements ResultSetMetaData { 19 | ResultSetMetaData wrappedResultSetMetaData; 20 | 21 | public static HashMap PrecisionDict; 22 | public static HashMap ScaleDict; 23 | 24 | private static final org.apache.logging.log4j.Logger logger = LogManager.getLogger(KResultSetMetaData.class); 25 | 26 | public KResultSetMetaData(ResultSetMetaData getMetaData) { 27 | wrappedResultSetMetaData = getMetaData; 28 | } 29 | 30 | public KResultSetMetaData() { 31 | } 32 | 33 | @Override 34 | public int getColumnCount() throws SQLException { 35 | if (mode == Mode.ModeType.MODE_TEST) { 36 | logger.debug("Stored value of getColumnCount is {} in mock metaData : {} ", meta.get("getColumnCount"), meta); 37 | int gs = 1; 38 | if (KResultSet.meta.get("getColumnCount") != null) { 39 | gs = Integer.parseInt(KResultSet.meta.get("getColumnCount")); 40 | } 41 | return gs; 42 | } 43 | int gc = wrappedResultSetMetaData.getColumnCount(); 44 | logger.debug("getColumnCount value in KResultSetMetaData {}", gc); 45 | KResultSet.meta.put("getColumnCount", Integer.toString(gc)); 46 | return gc; 47 | } 48 | 49 | @Override 50 | public boolean isAutoIncrement(int column) throws SQLException { 51 | logger.warn("{} boolean isAutoIncrement(int column) throws SQLException {}", msg1, msg2); 52 | return wrappedResultSetMetaData.isAutoIncrement(column); 53 | } 54 | 55 | @Override 56 | public boolean isCaseSensitive(int column) throws SQLException { 57 | logger.warn("{} boolean isCaseSensitive(int column) throws SQLException {}", msg1, msg2); 58 | return wrappedResultSetMetaData.isCaseSensitive(column); 59 | } 60 | 61 | @Override 62 | public boolean isSearchable(int column) throws SQLException { 63 | logger.warn("{} boolean isSearchable(int column) throws SQLException {}", msg1, msg2); 64 | return wrappedResultSetMetaData.isSearchable(column); 65 | } 66 | 67 | @Override 68 | public boolean isCurrency(int column) throws SQLException { 69 | if (mode == testMode) { 70 | return false; 71 | } 72 | return wrappedResultSetMetaData.isCurrency(column); 73 | } 74 | 75 | @Override 76 | public int isNullable(int column) throws SQLException { 77 | 78 | if (mode == Mode.ModeType.MODE_TEST) { 79 | logger.debug("Stored value of isNullable is {} in mock metaData : {} ", meta.get("isNullable"), meta); 80 | int gs = 1; 81 | if (KResultSet.meta.get("isNullable") != null) { 82 | gs = Integer.parseInt(KResultSet.meta.get("isNullable")); 83 | } 84 | return gs; 85 | } 86 | int gc = wrappedResultSetMetaData.isNullable(column); 87 | logger.debug("isNullable value in KResultSetMetaData {}", gc); 88 | KResultSet.meta.put("isNullable", Integer.toString(gc)); 89 | return gc; 90 | } 91 | 92 | @Override 93 | public boolean isSigned(int column) throws SQLException { 94 | if (mode == testMode) { 95 | return true; 96 | } 97 | return wrappedResultSetMetaData.isSigned(column); 98 | } 99 | 100 | @Override 101 | public int getColumnDisplaySize(int column) throws SQLException { 102 | if (mode == Mode.ModeType.MODE_TEST) { 103 | logger.debug("Stored value of getColumnDisplaySize is {} in mock metaData : {} ", meta.get("getColumnDisplaySize"), meta); 104 | int gs = 1; 105 | if (KResultSet.meta.get("getColumnDisplaySize") != null) { 106 | gs = Integer.parseInt(KResultSet.meta.get("getColumnDisplaySize")); 107 | } 108 | return gs; 109 | } 110 | int gc = wrappedResultSetMetaData.getColumnDisplaySize(column); 111 | logger.debug("getColumnDisplaySize value in KResultSetMetaData {}", gc); 112 | KResultSet.meta.put("getColumnDisplaySize", Integer.toString(gc)); 113 | return gc; 114 | } 115 | 116 | @Override 117 | public String getColumnLabel(int column) throws SQLException { 118 | // logger.warn("{} String getColumnLabel(int column) throws SQLException {}", msg1, msg2); 119 | if (mode == Mode.ModeType.MODE_TEST) { 120 | logger.debug("Stored value of getColumnLabel is {} in mock metaData : {} ", meta.get("getColumnLabel"), meta); 121 | String gcl = "KEPLOY_LABEL"; 122 | if (KResultSet.meta.get("getColumnLabel") != null) { 123 | gcl = meta.get("getColumnLabel"); 124 | } 125 | return gcl; 126 | } 127 | String gcl = wrappedResultSetMetaData.getColumnLabel(column); 128 | logger.debug("getColumnLabel value in KResultSetMetaData {}", gcl); 129 | KResultSet.meta.put("getColumnLabel", gcl); 130 | 131 | return gcl; 132 | } 133 | 134 | @Override 135 | public String getColumnName(int column) throws SQLException { 136 | // logger.warn("{} String getColumnLabel(int column) throws SQLException {}", msg1, msg2); 137 | if (mode == Mode.ModeType.MODE_TEST) { 138 | logger.debug("Stored value of getColumnName is {} in mock metaData : {} ", meta.get("getColumnName"), meta); 139 | String gcl = "KEPLOY_getColumnName"; 140 | if (KResultSet.meta.get("getColumnName") != null) { 141 | gcl = meta.get("getColumnName"); 142 | } 143 | return gcl; 144 | } 145 | String gcl = wrappedResultSetMetaData.getColumnName(column); 146 | logger.debug("getColumnName value in KResultSetMetaData {}", gcl); 147 | KResultSet.meta.put("getColumnName", gcl); 148 | 149 | return gcl; 150 | } 151 | 152 | @Override 153 | public String getSchemaName(int column) throws SQLException { 154 | // logger.warn("{} String getColumnLabel(int column) throws SQLException {}", msg1, msg2); 155 | if (mode == Mode.ModeType.MODE_TEST) { 156 | logger.debug("Stored value of getSchemaName is {} in mock metaData : {} ", meta.get("getSchemaName"), meta); 157 | String gcl = "KEPLOY_getSchemaName"; 158 | if (KResultSet.meta.get("getSchemaName") != null) { 159 | gcl = meta.get("getSchemaName"); 160 | } 161 | return gcl; 162 | } 163 | String gcl = wrappedResultSetMetaData.getSchemaName(column); 164 | logger.debug("getSchemaName value in KResultSetMetaData {}", gcl); 165 | KResultSet.meta.put("getSchemaName", gcl); 166 | 167 | return gcl; 168 | } 169 | 170 | @Override 171 | public int getPrecision(int column) throws SQLException { 172 | String columnLabel = getColumnLabel(column); 173 | if (mode == Mode.ModeType.MODE_TEST) { 174 | if (PrecisionDict.get(columnLabel) == null) { 175 | return 0; 176 | } 177 | int i = Integer.parseInt(PrecisionDict.get(columnLabel)); 178 | logger.debug(i + "is the precision for " + PrecisionDict.get(columnLabel)); 179 | return i; 180 | } 181 | Integer getPrecision = wrappedResultSetMetaData.getPrecision(column); 182 | PrecisionDict.put(columnLabel, String.valueOf(getPrecision)); 183 | return getPrecision; 184 | } 185 | 186 | @Override 187 | public int getScale(int column) throws SQLException { 188 | String columnLabel = getColumnLabel(column); 189 | if (mode == Mode.ModeType.MODE_TEST) { 190 | if (ScaleDict.get(columnLabel) == null) { 191 | return 0; 192 | } 193 | int i = Integer.parseInt(ScaleDict.get(columnLabel)); 194 | logger.debug(i + "is the scale for " + ScaleDict.get(columnLabel)); 195 | return i; 196 | } 197 | Integer getScale = wrappedResultSetMetaData.getScale(column); 198 | ScaleDict.put(columnLabel, String.valueOf(getScale)); 199 | return getScale; 200 | } 201 | 202 | @Override 203 | public String getTableName(int column) throws SQLException { 204 | // logger.warn("{} String getColumnLabel(int column) throws SQLException {}", msg1, msg2); 205 | if (mode == Mode.ModeType.MODE_TEST) { 206 | logger.debug("Stored value of getTableName is {} in mock metaData : {} ", meta.get("getTableName"), meta); 207 | String gcl = "KEPLOY_getTableName"; 208 | if (KResultSet.meta.get("getTableName") != null) { 209 | gcl = meta.get("getTableName"); 210 | } 211 | return gcl; 212 | } 213 | String gcl = wrappedResultSetMetaData.getTableName(column); 214 | logger.debug("getTableName value in KResultSetMetaData {}", gcl); 215 | KResultSet.meta.put("getTableName", gcl); 216 | 217 | return gcl; 218 | } 219 | 220 | @Override 221 | public String getCatalogName(int column) throws SQLException { 222 | // logger.warn("{} String getColumnLabel(int column) throws SQLException {}", msg1, msg2); 223 | if (mode == Mode.ModeType.MODE_TEST) { 224 | logger.debug("Stored value of getCatalogName is {} in mock metaData : {} ", meta.get("getCatalogName"), meta); 225 | String gcl = "KEPLOY_getCatalogName"; 226 | if (KResultSet.meta.get("getCatalogName") != null) { 227 | gcl = meta.get("getCatalogName"); 228 | } 229 | return gcl; 230 | } 231 | String gcl = wrappedResultSetMetaData.getCatalogName(column); 232 | logger.debug("getCatalogName value in KResultSetMetaData {}", gcl); 233 | KResultSet.meta.put("getCatalogName", gcl); 234 | 235 | return gcl; 236 | } 237 | 238 | @Override 239 | public int getColumnType(int column) throws SQLException { 240 | if (mode == Mode.ModeType.MODE_TEST) { 241 | logger.debug("Stored value of getColumnType is {} in mock metaData : {} ", meta.get("getColumnType"), meta); 242 | int gs = 1; 243 | if (KResultSet.meta.get("getColumnType") != null) { 244 | gs = Integer.parseInt(KResultSet.meta.get("getColumnType")); 245 | } 246 | return gs; 247 | } 248 | int gc = wrappedResultSetMetaData.getColumnType(column); 249 | logger.debug("getColumnType value in KResultSetMetaData {}", gc); 250 | KResultSet.meta.put("getColumnType", Integer.toString(gc)); 251 | return gc; 252 | } 253 | 254 | @Override 255 | public String getColumnTypeName(int column) throws SQLException { 256 | // logger.warn("{} String getColumnLabel(int column) throws SQLException {}", msg1, msg2); 257 | if (mode == Mode.ModeType.MODE_TEST) { 258 | logger.debug("Stored value of getColumnTypeName is {} in mock metaData : {} ", meta.get("getColumnTypeName"), meta); 259 | String gcl = "KEPLOY_getColumnTypeName"; 260 | if (KResultSet.meta.get("getColumnTypeName") != null) { 261 | gcl = meta.get("getColumnTypeName"); 262 | } 263 | return gcl; 264 | } 265 | String gcl = wrappedResultSetMetaData.getColumnTypeName(column); 266 | logger.debug("getColumnTypeName value in KResultSetMetaData {}", gcl); 267 | KResultSet.meta.put("getColumnTypeName", gcl); 268 | 269 | return gcl; 270 | } 271 | 272 | @Override 273 | public boolean isReadOnly(int column) throws SQLException { 274 | if (mode == testMode) { 275 | return true; 276 | } 277 | return wrappedResultSetMetaData.isReadOnly(column); 278 | } 279 | 280 | @Override 281 | public boolean isWritable(int column) throws SQLException { 282 | if (mode == testMode) { 283 | return false; 284 | } 285 | return wrappedResultSetMetaData.isWritable(column); 286 | } 287 | 288 | @Override 289 | public boolean isDefinitelyWritable(int column) throws SQLException { 290 | if (mode == testMode) { 291 | return false; 292 | } 293 | return wrappedResultSetMetaData.isDefinitelyWritable(column); 294 | } 295 | 296 | @Override 297 | public String getColumnClassName(int column) throws SQLException { 298 | if (mode == Mode.ModeType.MODE_TEST) { 299 | logger.debug("Stored value of getColumnClassName is {} in mock metaData : {} ", meta.get("getColumnClassName"), meta); 300 | String gcl = "KEPLOY_getColumnClassName"; 301 | if (KResultSet.meta.get("getColumnClassName") != null) { 302 | gcl = meta.get("getColumnClassName"); 303 | } 304 | return gcl; 305 | } 306 | String gcl = wrappedResultSetMetaData.getColumnClassName(column); 307 | logger.debug("getColumnClassName value in KResultSetMetaData {}", gcl); 308 | KResultSet.meta.put("getColumnClassName", gcl); 309 | 310 | return gcl; 311 | } 312 | 313 | @Override 314 | public T unwrap(Class iface) throws SQLException { 315 | logger.warn("{} T unwrap(Class iface) throws SQLException {}", msg1, msg2); 316 | return wrappedResultSetMetaData.unwrap(iface); 317 | } 318 | 319 | @Override 320 | public boolean isWrapperFor(Class iface) throws SQLException { 321 | logger.warn("{} boolean isWrapperFor(Class iface) throws SQLException {}", msg1, msg2); 322 | return wrappedResultSetMetaData.isWrapperFor(iface); 323 | } 324 | } 325 | --------------------------------------------------------------------------------