@skyramp/skyramp 0.4.106 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Binary file
@@ -40,6 +40,11 @@ struct worker_info {
40
40
  char *error;
41
41
  };
42
42
 
43
+ struct response_wrapper {
44
+ char *response;
45
+ char *error;
46
+ };
47
+
43
48
  #line 1 "cgo-generated-wrapper"
44
49
 
45
50
  #line 7 "endpoint.go"
@@ -127,6 +132,18 @@ extern char* generateUserTokenWrapper(char* provider, char* email, char* oauthTo
127
132
  extern struct worker_info newStartDockerSkyrampWorkerWrapper(char* image, char* tag, GoInt hostPort, char* targetNetworkName, char* testServiceAlias);
128
133
  extern char* newDeleteDockerSkyrampWorkerWrapper(char* containerName);
129
134
  extern char* runTesterCurlWrapper(char* namespace, char* kubePath, char* kubeContext, char* clusterName, char* address, char* request);
135
+ extern struct response_wrapper sendRequestWrapper(char* address, char* namespace, char* kubePath, char* kubeContext, char* clusterName, char* request);
136
+
137
+ /* Return type for getJSONValueWrapper */
138
+ struct getJSONValueWrapper_return {
139
+ char* r0;
140
+ char* r1;
141
+ };
142
+ extern struct getJSONValueWrapper_return getJSONValueWrapper(char* jsonS, char* path);
143
+ extern char* checkStatusCodeWrapper(char* response, char* expectedStatus);
144
+ extern char* deployDockerDashboardWrapper(char* networkName);
145
+ extern char* deployK8sDashboardWrapper(char* networkName);
146
+ extern struct response_wrapper checkSchemaWrapper(char* requestBody, char* sampleSchema);
130
147
  extern char* newGrpcEndpointWrapper(char* name, char* serviceName, GoInt port, char* inputFile);
131
148
  extern char* newRestEndpointWrapper(char* name, char* openApiTag, GoInt port, char* inputFile, char* restPath);
132
149
  extern char* getEndpointWrapper(char* service, GoInt port, char* restPath);
Binary file
@@ -40,6 +40,11 @@ struct worker_info {
40
40
  char *error;
41
41
  };
42
42
 
43
+ struct response_wrapper {
44
+ char *response;
45
+ char *error;
46
+ };
47
+
43
48
  #line 1 "cgo-generated-wrapper"
44
49
 
45
50
  #line 7 "endpoint.go"
@@ -127,6 +132,18 @@ extern char* generateUserTokenWrapper(char* provider, char* email, char* oauthTo
127
132
  extern struct worker_info newStartDockerSkyrampWorkerWrapper(char* image, char* tag, GoInt hostPort, char* targetNetworkName, char* testServiceAlias);
128
133
  extern char* newDeleteDockerSkyrampWorkerWrapper(char* containerName);
129
134
  extern char* runTesterCurlWrapper(char* namespace, char* kubePath, char* kubeContext, char* clusterName, char* address, char* request);
135
+ extern struct response_wrapper sendRequestWrapper(char* address, char* namespace, char* kubePath, char* kubeContext, char* clusterName, char* request);
136
+
137
+ /* Return type for getJSONValueWrapper */
138
+ struct getJSONValueWrapper_return {
139
+ char* r0;
140
+ char* r1;
141
+ };
142
+ extern struct getJSONValueWrapper_return getJSONValueWrapper(char* jsonS, char* path);
143
+ extern char* checkStatusCodeWrapper(char* response, char* expectedStatus);
144
+ extern char* deployDockerDashboardWrapper(char* networkName);
145
+ extern char* deployK8sDashboardWrapper(char* networkName);
146
+ extern struct response_wrapper checkSchemaWrapper(char* requestBody, char* sampleSchema);
130
147
  extern char* newGrpcEndpointWrapper(char* name, char* serviceName, GoInt port, char* inputFile);
131
148
  extern char* newRestEndpointWrapper(char* name, char* openApiTag, GoInt port, char* inputFile, char* restPath);
132
149
  extern char* getEndpointWrapper(char* service, GoInt port, char* restPath);
@@ -40,6 +40,11 @@ struct worker_info {
40
40
  char *error;
41
41
  };
42
42
 
43
+ struct response_wrapper {
44
+ char *response;
45
+ char *error;
46
+ };
47
+
43
48
  #line 1 "cgo-generated-wrapper"
44
49
 
45
50
  #line 7 "endpoint.go"
@@ -127,6 +132,18 @@ extern char* generateUserTokenWrapper(char* provider, char* email, char* oauthTo
127
132
  extern struct worker_info newStartDockerSkyrampWorkerWrapper(char* image, char* tag, GoInt hostPort, char* targetNetworkName, char* testServiceAlias);
128
133
  extern char* newDeleteDockerSkyrampWorkerWrapper(char* containerName);
129
134
  extern char* runTesterCurlWrapper(char* namespace, char* kubePath, char* kubeContext, char* clusterName, char* address, char* request);
135
+ extern struct response_wrapper sendRequestWrapper(char* address, char* namespace, char* kubePath, char* kubeContext, char* clusterName, char* request);
136
+
137
+ /* Return type for getJSONValueWrapper */
138
+ struct getJSONValueWrapper_return {
139
+ char* r0;
140
+ char* r1;
141
+ };
142
+ extern struct getJSONValueWrapper_return getJSONValueWrapper(char* jsonS, char* path);
143
+ extern char* checkStatusCodeWrapper(char* response, char* expectedStatus);
144
+ extern char* deployDockerDashboardWrapper(char* networkName);
145
+ extern char* deployK8sDashboardWrapper(char* networkName);
146
+ extern struct response_wrapper checkSchemaWrapper(char* requestBody, char* sampleSchema);
130
147
  extern char* newGrpcEndpointWrapper(char* name, char* serviceName, GoInt port, char* inputFile);
131
148
  extern char* newRestEndpointWrapper(char* name, char* openApiTag, GoInt port, char* inputFile, char* restPath);
132
149
  extern char* getEndpointWrapper(char* service, GoInt port, char* restPath);
Binary file
@@ -40,6 +40,11 @@ struct worker_info {
40
40
  char *error;
41
41
  };
42
42
 
43
+ struct response_wrapper {
44
+ char *response;
45
+ char *error;
46
+ };
47
+
43
48
  #line 1 "cgo-generated-wrapper"
44
49
 
45
50
  #line 7 "endpoint.go"
@@ -127,6 +132,18 @@ extern char* generateUserTokenWrapper(char* provider, char* email, char* oauthTo
127
132
  extern struct worker_info newStartDockerSkyrampWorkerWrapper(char* image, char* tag, GoInt hostPort, char* targetNetworkName, char* testServiceAlias);
128
133
  extern char* newDeleteDockerSkyrampWorkerWrapper(char* containerName);
129
134
  extern char* runTesterCurlWrapper(char* namespace, char* kubePath, char* kubeContext, char* clusterName, char* address, char* request);
135
+ extern struct response_wrapper sendRequestWrapper(char* address, char* namespace, char* kubePath, char* kubeContext, char* clusterName, char* request);
136
+
137
+ /* Return type for getJSONValueWrapper */
138
+ struct getJSONValueWrapper_return {
139
+ char* r0;
140
+ char* r1;
141
+ };
142
+ extern struct getJSONValueWrapper_return getJSONValueWrapper(char* jsonS, char* path);
143
+ extern char* checkStatusCodeWrapper(char* response, char* expectedStatus);
144
+ extern char* deployDockerDashboardWrapper(char* networkName);
145
+ extern char* deployK8sDashboardWrapper(char* networkName);
146
+ extern struct response_wrapper checkSchemaWrapper(char* requestBody, char* sampleSchema);
130
147
  extern char* newGrpcEndpointWrapper(char* name, char* serviceName, GoInt port, char* inputFile);
131
148
  extern char* newRestEndpointWrapper(char* name, char* openApiTag, GoInt port, char* inputFile, char* restPath);
132
149
  extern char* getEndpointWrapper(char* service, GoInt port, char* restPath);
Binary file
Binary file
@@ -40,6 +40,11 @@ struct worker_info {
40
40
  char *error;
41
41
  };
42
42
 
43
+ struct response_wrapper {
44
+ char *response;
45
+ char *error;
46
+ };
47
+
43
48
  #line 1 "cgo-generated-wrapper"
44
49
 
45
50
  #line 7 "endpoint.go"
@@ -127,6 +132,18 @@ extern __declspec(dllexport) char* generateUserTokenWrapper(char* provider, char
127
132
  extern __declspec(dllexport) struct worker_info newStartDockerSkyrampWorkerWrapper(char* image, char* tag, GoInt hostPort, char* targetNetworkName, char* testServiceAlias);
128
133
  extern __declspec(dllexport) char* newDeleteDockerSkyrampWorkerWrapper(char* containerName);
129
134
  extern __declspec(dllexport) char* runTesterCurlWrapper(char* namespace, char* kubePath, char* kubeContext, char* clusterName, char* address, char* request);
135
+ extern __declspec(dllexport) struct response_wrapper sendRequestWrapper(char* address, char* namespace, char* kubePath, char* kubeContext, char* clusterName, char* request);
136
+
137
+ /* Return type for getJSONValueWrapper */
138
+ struct getJSONValueWrapper_return {
139
+ char* r0;
140
+ char* r1;
141
+ };
142
+ extern __declspec(dllexport) struct getJSONValueWrapper_return getJSONValueWrapper(char* jsonS, char* path);
143
+ extern __declspec(dllexport) char* checkStatusCodeWrapper(char* response, char* expectedStatus);
144
+ extern __declspec(dllexport) char* deployDockerDashboardWrapper(char* networkName);
145
+ extern __declspec(dllexport) char* deployK8sDashboardWrapper(char* networkName);
146
+ extern __declspec(dllexport) struct response_wrapper checkSchemaWrapper(char* requestBody, char* sampleSchema);
130
147
  extern __declspec(dllexport) char* newGrpcEndpointWrapper(char* name, char* serviceName, GoInt port, char* inputFile);
131
148
  extern __declspec(dllexport) char* newRestEndpointWrapper(char* name, char* openApiTag, GoInt port, char* inputFile, char* restPath);
132
149
  extern __declspec(dllexport) char* getEndpointWrapper(char* service, GoInt port, char* restPath);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skyramp/skyramp",
3
- "version": "0.4.106",
3
+ "version": "0.5.1",
4
4
  "description": "module for leveraging skyramp cli functionality",
5
5
  "scripts": {
6
6
  "lint": "eslint 'src/**/*.js' 'src/**/*.ts' --fix",
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Represents a REST request.
3
+ */
4
+ interface RequestV2Options {
5
+ url: string;
6
+ path: string;
7
+ method: string;
8
+ body?: string;
9
+ headers?: {[headerName: string]: string};
10
+ cookies?: {[cookieName: string]: string};
11
+ dataOverride?: {[dataName: string]: string | number | boolean | object};
12
+ pathParams?: {[pathName: string]: string | number | boolean | object};
13
+ queryParams?: {[queryName: string]: string | number | boolean | object};
14
+ formParams?: {[formParamName: string]: string | number | boolean | object};
15
+ expectedCode?: string;
16
+ funcHandler?: string;
17
+ funcHandlerType?: string;
18
+ }
19
+ export declare class RequestV2 {
20
+ /**
21
+ * Creates a new instance of RequestV2.
22
+ * @param options - The options for creating the request.
23
+ */
24
+ constructor(options: RequestV2Options);
25
+ toYaml(): string;
26
+ }
@@ -0,0 +1,109 @@
1
+ const yaml = require('js-yaml');
2
+
3
+ /**
4
+ * Represents a REST request.
5
+ */
6
+ class RequestV2 {
7
+ /**
8
+ * Creates a new instance of RequestV2.
9
+ * @param {Object} [options={}] - The options for creating the request.
10
+ * @param {string} [options.url=''] - The URL of the request.
11
+ * @param {string} [options.path=''] - The REST path of the request.
12
+ * @param {string} [options.method=''] - The HTTP method of the request.
13
+ * @param {string} [options.body=''] - The request body in JSON format.
14
+ * @param {Object.<string, string>} [options.headers={}] - The HTTP headers of the request.
15
+ * @param {Object.<string, string>} [options.cookies={}] - The HTTP cookies of the request.
16
+ * @param {Object.<string, *>} [options.dataOverride={}] - The data override for the request.
17
+ * @param {Object.<string, *>} [options.pathParams={}] - The path parameters for the request.
18
+ * @param {Object.<string, *>} [options.queryParams={}] - The query parameters for the request.
19
+ * @param {Object.<string, *>} [options.formParams={}] - The form parameters of the request.
20
+ * @param {string} [options.expectedCode=''] - The expected HTTP status code of the response.
21
+ * @param {string} [options.funcHandler=''] - The dynamic handler function for the request.
22
+ * @param {string} [options.funcHandlerType=''] - The type of the dynamic handler (e.g., 'python', 'javascript').
23
+ */
24
+ constructor(options = {}) {
25
+ this.url = options.url || '';
26
+ this.path = options.path || '';
27
+ this.method = options.method || '';
28
+ this.body = options.body || '';
29
+ this.headers = options.headers || {};
30
+ this.cookies = options.cookies || {};
31
+ this.dataOverride = options.dataOverride || {};
32
+ this.pathParams = options.pathParams || {};
33
+ this.queryParams = options.queryParams || {};
34
+ this.formParams = options.formParams || {};
35
+ this.expectedCode = options.expectedCode || '';
36
+ this.funcHandler = options.funcHandler || '';
37
+ this.funcHandlerType = options.funcHandlerType || '';
38
+ }
39
+
40
+ toYaml() {
41
+ // Map of camelCase to snake_case field names
42
+ const fieldMappings = {
43
+ url: 'url',
44
+ path: 'path',
45
+ method: 'method',
46
+ body: 'body',
47
+ headers: 'headers',
48
+ cookies: 'cookies',
49
+ dataOverride: 'data_override',
50
+ pathParams: 'path_params',
51
+ queryParams: 'query_params',
52
+ formParams: 'form_params',
53
+ expectedCode: 'expected_code',
54
+ funcHandler: 'func_handler',
55
+ funcHandlerType: 'func_handler_type'
56
+ };
57
+
58
+ const { body, ...rest } = this;
59
+ // Create object with non-empty values and convert to snake_case
60
+ const yamlObject = Object.entries(rest).reduce((acc, [key, value]) => {
61
+ // Include field only if it's not empty (empty string, empty object, or empty array)
62
+ if (value && (typeof value !== 'object' || Object.keys(value).length > 0)) {
63
+ // Use snake_case key if mapping exists, otherwise use original key
64
+ const snakeKey = fieldMappings[key] || key;
65
+ acc[snakeKey] = value;
66
+ }
67
+ return acc;
68
+ }, {});
69
+
70
+ // Add body if it exists
71
+ if (body) {
72
+ yamlObject.body = JSON.stringify(body, null, 2);
73
+ }
74
+
75
+ return yaml.dump(yamlObject);
76
+ }
77
+
78
+ toJson() {
79
+ // Use the same field mappings as toYaml for consistency
80
+ const fieldMappings = {
81
+ url: 'url',
82
+ path: 'path',
83
+ method: 'method',
84
+ body: 'body',
85
+ headers: 'headers',
86
+ cookies: 'cookies',
87
+ dataOverride: 'data_override',
88
+ pathParams: 'path_params',
89
+ queryParams: 'query_params',
90
+ formParams: 'form_params',
91
+ expectedCode: 'expected_code',
92
+ funcHandler: 'func_handler',
93
+ funcHandlerType: 'func_handler_type'
94
+ };
95
+
96
+ const { ...rest } = this;
97
+ // Create object with non-empty values and convert to snake_case
98
+ const jsonObject = Object.entries(rest).reduce((acc, [key, value]) => {
99
+ if (value && (typeof value !== 'object' || Object.keys(value).length > 0)) {
100
+ const snakeKey = fieldMappings[key] || key;
101
+ acc[snakeKey] = value;
102
+ }
103
+ return acc;
104
+ }, {});
105
+ return JSON.stringify(jsonObject, null, 2);
106
+ }
107
+ }
108
+
109
+ module.exports = RequestV2;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Represents a REST response.
3
+ */
4
+ interface ResponseV2Options {
5
+ path?: string;
6
+ method?: string;
7
+ statusCode?: string;
8
+ responseHeaders?: {[headerName: string]: string};
9
+ responseBody?: string;
10
+ requestHeaders?: {[headerName: string]: string};
11
+ requestBody?: string;
12
+ duration?: string;
13
+ }
14
+
15
+ export declare class ResponseV2 {
16
+ /**
17
+ * Creates a new instance of ResponseV2.
18
+ * @param options - The options for creating the response.
19
+ */
20
+ constructor(options?: ResponseV2Options);
21
+ toYaml(): string;
22
+ }
@@ -0,0 +1,90 @@
1
+ const yaml = require('js-yaml');
2
+ /**
3
+ * Represents a REST response.
4
+ */
5
+ class ResponseV2 {
6
+
7
+ /**
8
+ * Creates a new ResponseV2 instance
9
+ * @param {Object} options - The options for creating the response
10
+ * @param {string} [options.path=''] - The request path
11
+ * @param {string} [options.method=''] - The HTTP method used
12
+ * @param {string} [options.status_code=''] - The HTTP status code of the response
13
+ * @param {Object} [options.response_headers={}] - Headers included in the response
14
+ * @param {string} [options.response_body=''] - Body of the response
15
+ * @param {Object} [options.request_headers={}] - Headers included in the request
16
+ * @param {string} [options.request_body=''] - Body of the request
17
+ * @param {string} [options.duration=''] - Duration of the request/response cycle
18
+ */
19
+ constructor(options = {}) {
20
+ this.path = options.path || '';
21
+ this.method = options.method || '';
22
+ this.statusCode = options.status_code || '';
23
+ this.responseHeaders = options.response_headers || {};
24
+ this.responseBody = options.response_body || '';
25
+ this.requestHeaders = options.request_headers || {};
26
+ this.requestBody = options.request_body || '';
27
+ this.duration = options.duration || '';
28
+ }
29
+
30
+ toYaml() {
31
+ // Map of camelCase to snake_case field names
32
+ const fieldMappings = {
33
+ path: 'path',
34
+ method: 'method',
35
+ statusCode: 'status_code',
36
+ responseHeaders: 'response_headers',
37
+ responseBody: 'response_body',
38
+ requestHeaders: 'request_headers',
39
+ requestBody: 'request_body',
40
+ duration: 'duration'
41
+ };
42
+
43
+ const { body, ...rest } = this;
44
+ // Create object with non-empty values and convert to snake_case
45
+ const yamlObject = Object.entries(rest).reduce((acc, [key, value]) => {
46
+ // Include field only if it's not empty (empty string, empty object, or empty array)
47
+ if (value && (typeof value !== 'object' || Object.keys(value).length > 0)) {
48
+ // Use snake_case key if mapping exists, otherwise use original key
49
+ const snakeKey = fieldMappings[key] || key;
50
+ acc[snakeKey] = value;
51
+ }
52
+ return acc;
53
+ }, {});
54
+
55
+ // Add body if it exists
56
+ if (body) {
57
+ yamlObject.body = JSON.stringify(body, null, 2);
58
+ }
59
+
60
+ return yaml.dump(yamlObject);
61
+ }
62
+
63
+ toJson() {
64
+ // Use the same field mappings as toYaml for consistency
65
+ const fieldMappings = {
66
+ path: 'path',
67
+ method: 'method',
68
+ statusCode: 'status_code',
69
+ responseHeaders: 'response_headers',
70
+ responseBody: 'response_body',
71
+ requestHeaders: 'request_headers',
72
+ requestBody: 'request_body',
73
+ duration: 'duration'
74
+ };
75
+
76
+ const { ...rest } = this;
77
+ // Create object with non-empty values and convert to snake_case
78
+ const jsonObject = Object.entries(rest).reduce((acc, [key, value]) => {
79
+ if (value && (typeof value !== 'object' || Object.keys(value).length > 0)) {
80
+ const snakeKey = fieldMappings[key] || key;
81
+ acc[snakeKey] = value;
82
+ }
83
+ return acc;
84
+ }, {});
85
+ return JSON.stringify(jsonObject, null, 2);
86
+ }
87
+ }
88
+
89
+
90
+ module.exports = ResponseV2;
@@ -2,7 +2,7 @@ import { ResponseValue } from '..';
2
2
  import { Endpoint } from './Endpoint';
3
3
  import { Scenario } from './Scenario';
4
4
  import {TrafficConfig} from './TrafficConfig';
5
-
5
+ import {oauthResponseType} from './SkyrampClient';
6
6
  interface testerStartV1Options {
7
7
  namespace?: string;
8
8
  kubePath?: string;
@@ -25,8 +25,35 @@ interface MockerApplyV1Options {
25
25
  response: ResponseValue | ResponseValue[];
26
26
  trafficConfig: TrafficConfig;
27
27
  }
28
+ interface SendRequestV2Options {
29
+ url: string;
30
+ path: string;
31
+ method: string;
32
+ body?: string;
33
+ headers?: {[headerName: string]: string};
34
+ cookies?: {[cookieName: string]: string};
35
+ dataOverride?: {[dataName: string]: string | number | boolean | object};
36
+ pathParams?: {[pathName: string]: string | number | boolean | object};
37
+ queryParams?: {[queryName: string]: string | number | boolean | object};
38
+ formParams?: {[formParamName: string]: string | number | boolean | object};
39
+ expectedCode?: string;
40
+ funcHandler?: string;
41
+ funcHandlerType?: string;
42
+ }
43
+
44
+ interface SkyrampClientOptions {
45
+ docker_network?: string;
46
+ docker_skyramp_port?: number;
47
+ kubeconfigPath?: string;
48
+ clusterName?: string;
49
+ context?: string;
50
+ userToken?: string;
51
+ address?: string;
52
+ }
53
+
28
54
  export declare class SkyrampClient {
29
55
  constructor(kubeconfigPath?: string, clusterName?: string, context?: string, userToken?: string);
56
+ constructor(options: SkyrampClientOptions);
30
57
  applyLocal(): Promise<void>;
31
58
  addKubeconfig(context: string, clusterName: string, kubeconfigPath: string): Promise<void>;
32
59
  removeLocal(): Promise<void>;
@@ -58,4 +85,5 @@ export declare class SkyrampClient {
58
85
  getOAuthURL(provider: string, port: number): string;
59
86
  runOAuthLoopback(provider: string, port: number): Promise<oauthResponseType>;
60
87
  registerUser(provider: string, email: string, oauthToken: string): Promise<string>;
88
+ sendRequest(options: SendRequestV2Options): Promise<void>;
61
89
  }
@@ -1,7 +1,8 @@
1
1
  const lib = require('../lib');
2
2
  const koffi = require('koffi');
3
3
  const TrafficConfig = require('./TrafficConfig');
4
-
4
+ const RequestV2 = require('./RequestV2');
5
+ const ResponseV2 = require('./ResponseV2');
5
6
  const oauthResponseType = koffi.struct({
6
7
  emails: koffi.array('char*', 10),
7
8
  num_emails: 'int',
@@ -26,6 +27,11 @@ const testerInfoType = koffi.struct({
26
27
  error: 'char*',
27
28
  });
28
29
 
30
+ const contractResponseType = koffi.struct({
31
+ response: 'char*',
32
+ error: 'char*',
33
+ });
34
+
29
35
  const WORKER_URL = "public.ecr.aws/j1n2c2p2/rampup/worker";
30
36
  const WORKER_TAG = "latest";
31
37
  const CONTAINER_PORT = 35142;
@@ -59,6 +65,9 @@ const registerUserWrapper = lib.func('registerUserWrapper', 'string', ['string',
59
65
  const getOAuthURL = lib.func('getOAuthURLWrapper', 'string', ['string', 'int']);
60
66
  const runOAuthLoopback = lib.func('runOAuthLoopback', oauthResponseType, ['string', 'int']);
61
67
 
68
+ // contract test
69
+ // func sendRequestWrapper(address, namespace, kubePath, kubeContext, clusterName, request *C.char) *C.char {
70
+ const sendRequestWrapper = lib.func('sendRequestWrapper', contractResponseType, ['string', 'string', 'string', 'string', 'string', 'string']);
62
71
  /**
63
72
  * The `SkyrampClient` class is the main client that provides methods for interacting with the Skyramp service.
64
73
  * It allows users to manage Kubernetes clusters, deploy and delete workers and targets, apply mock descriptions, and start tests.
@@ -70,18 +79,36 @@ class SkyrampClient {
70
79
  /**
71
80
  * Initializes a new instance of the SkyrampClient class.
72
81
  * @constructor
73
- * @param {string} kubeconfigPath - The path to the kubeconfig file.
74
- * @param {string} clusterName - The name of the cluster.
75
- * @param {string} context - The context of the cluster.
76
- * @param {string} userToken - The user token for authentication.
82
+ * @param {string|Object} kubeconfigPathOrOptions - Either the path to kubeconfig file or an options object
83
+ * @param {string} [kubeconfigPathOrOptions.kubeconfigPath] - The path to the kubeconfig file
84
+ * @param {string} [kubeconfigPathOrOptions.clusterName] - The name of the cluster
85
+ * @param {string} [kubeconfigPathOrOptions.context] - The context of the cluster
86
+ * @param {string} [kubeconfigPathOrOptions.userToken] - The user token for authentication
87
+ * @param {string} [kubeconfigPathOrOptions.address] - The address of the service
88
+ * @param {string} [clusterName] - The name of the cluster (when using individual parameters)
89
+ * @param {string} [context] - The context of the cluster (when using individual parameters)
90
+ * @param {string} [userToken] - The user token for authentication (when using individual parameters)
77
91
  */
78
- constructor(kubeconfigPath, clusterName, context, userToken) {
79
- if (kubeconfigPath || clusterName || context) {
80
- this.addKubeconfig(clusterName, context, kubeconfigPath);
92
+ constructor(kubeconfigPathOrOptions, clusterName, context, userToken) {
93
+ if (typeof kubeconfigPathOrOptions === 'object') {
94
+ const options = kubeconfigPathOrOptions;
95
+ if (options.kubeconfigPath || options.clusterName || options.context) {
96
+ this.addKubeconfig(options.clusterName, options.context, options.kubeconfigPath);
97
+ }
98
+ this.workerNamespaces = [];
99
+ this.userToken = options.userToken;
100
+ this.address = options.address;
101
+ if (options.docker_skyramp_port) {
102
+ this.address = `localhost:${options.docker_skyramp_port}`;
103
+ }
104
+ } else {
105
+ if (kubeconfigPathOrOptions || clusterName || context) {
106
+ this.addKubeconfig(clusterName, context, kubeconfigPathOrOptions);
81
107
  }
82
108
  this.workerNamespaces = [];
83
109
  this.userToken = userToken;
84
110
  }
111
+ }
85
112
 
86
113
  setUserToken(userToken) {
87
114
  this.userToken = userToken;
@@ -168,7 +195,7 @@ class SkyrampClient {
168
195
 
169
196
  /**
170
197
  * Applies local changes to the Kubernetes cluster configuration.
171
- *
198
+ *
172
199
  * @returns {Promise} A promise that resolves when the local changes are successfully applied, or rejects with an error if any error occurs.
173
200
  */
174
201
  async applyLocal() {
@@ -192,7 +219,7 @@ class SkyrampClient {
192
219
 
193
220
  /**
194
221
  * Adds a Kubernetes configuration to the SkyrampClient object.
195
- *
222
+ *
196
223
  * @param {string} context - The context of the Kubernetes configuration.
197
224
  * @param {string} clusterName - The name of the Kubernetes cluster.
198
225
  * @param {string} kubeConfigPath - The path to the kubeconfig file.
@@ -215,7 +242,7 @@ class SkyrampClient {
215
242
 
216
243
  /**
217
244
  * Removes the local configuration of the SkyrampClient.
218
- *
245
+ *
219
246
  * @returns {Promise<void>} A promise that resolves when the removal is successful, or rejects with an error if an error occurs during the removal process.
220
247
  */
221
248
  async removeLocal() {
@@ -234,7 +261,7 @@ class SkyrampClient {
234
261
 
235
262
  /**
236
263
  * Asynchronously removes a cluster from the configuration.
237
- *
264
+ *
238
265
  * @param {string} clusterName - The name of the cluster to be removed from the configuration.
239
266
  * @returns {Promise} A Promise that resolves if the cluster is successfully removed, or rejects with an error if the removal fails.
240
267
  */
@@ -278,7 +305,7 @@ class SkyrampClient {
278
305
 
279
306
  /**
280
307
  * Deletes a worker from a specified namespace in the SkyrampClient class.
281
- *
308
+ *
282
309
  * @param {string} namespace - The namespace from which the worker should be deleted.
283
310
  * @returns {Promise} - A promise that resolves with no value upon successful deletion.
284
311
  * @throws {Error} - If there is no cluster to delete the worker from or if there is no worker to delete from the specified namespace.
@@ -307,7 +334,7 @@ class SkyrampClient {
307
334
 
308
335
  /**
309
336
  * Asynchronous method that runs a Docker worker using the provided image, tag, port, and network name.
310
- *
337
+ *
311
338
  * @param {string} [workerImage=WORKER_URL] - The URL of the Docker worker image.
312
339
  * @param {string} [workerTag=WORKER_TAG] - The tag of the Docker worker image.
313
340
  * @param {number} [hostPort=CONTAINER_PORT] - The port on the host machine to expose for the Docker worker.
@@ -330,7 +357,7 @@ class SkyrampClient {
330
357
 
331
358
  /**
332
359
  * Asynchronous method that removes a Docker container running the Skyramp worker.
333
- *
360
+ *
334
361
  * @param {string} containerName - The name of the Docker container to be removed.
335
362
  * @returns {Promise} A promise that resolves when the container is successfully removed.
336
363
  * @throws {Error} If an error occurs during the removal process.
@@ -357,13 +384,13 @@ class SkyrampClient {
357
384
 
358
385
  /**
359
386
  * Applies a mock description to a specified namespace and address.
360
- *
387
+ *
361
388
  * @param {string} namespace - The namespace where the mock description will be applied.
362
389
  * @param {string} address - The address to which the mock description will be applied.
363
390
  * @param {object} response - The details of the mock response.
364
391
  * @param {object} trafficConfig - The traffic configuration parameters.
365
392
  * @returns {Promise} A Promise that resolves when the mock description is successfully applied.
366
- *
393
+ *
367
394
  */
368
395
  async mockerApplyV1(...args) {
369
396
  let namespace, kubeConfig, kubeContext, clusterName, address, response, trafficConfig;
@@ -385,7 +412,7 @@ class SkyrampClient {
385
412
  if (trafficConfig instanceof TrafficConfig) {
386
413
  mockDescription.mock = { ...mockDescription.mock, ...trafficConfig.toJson() }
387
414
  }
388
-
415
+
389
416
  const yamlContent = getYamlBytes(mockDescription);
390
417
  return this.applyMockDescription(namespace, kubeConfig, kubeContext, clusterName, address, yamlContent);
391
418
  }
@@ -419,7 +446,7 @@ class SkyrampClient {
419
446
 
420
447
  /**
421
448
  * Starts a test scenario in a specified namespace.
422
- *
449
+ *
423
450
  * @param {string|object} namespaceOrOptions - The namespace in which to start the test scenario or an options object.
424
451
  * @param {string} [address] - The address of the target service to test.
425
452
  * @param {object} [scenario] - The scenario object that defines the test steps and assertions.
@@ -427,7 +454,7 @@ class SkyrampClient {
427
454
  * @param {object} [globalHeaders] - Optional global headers to be included in the test requests.
428
455
  * @param {boolean} [generateTestReport] - Optional flag to generate a test report. Default is false.
429
456
  * @param {boolean} [isDockerenv] - Optional flag to indicate if the test is running in a Docker environment. Default is false.
430
- *
457
+ *
431
458
  * @returns {Promise} - A promise that resolves when the test scenario is started.
432
459
  */
433
460
  async testerStartV1(options = {}) {
@@ -596,6 +623,33 @@ class SkyrampClient {
596
623
 
597
624
  return mockDescription;
598
625
  }
626
+
627
+ /**
628
+ * Sends a request to a Skyramp worker using the V2 API
629
+ * @param {Object} options - The options for sending the request
630
+ * @param {string} [options.address=""] - The address of the worker to send the request to
631
+ * @param {string} [options.namespace=""] - The namespace where the worker is deployed
632
+ * @param {string} [options.kubePath=""] - The path to the kubeconfig file
633
+ * @param {string} [options.kubeContext=""] - The kubernetes context to use
634
+ * @param {string} [options.clusterName=""] - The name of the kubernetes cluster
635
+ * @returns {Promise<string>} A promise that resolves with the response from the worker
636
+ */
637
+ async sendRequest(options) {
638
+ // FIXME: should be sendRequest
639
+ const req = new RequestV2(options);
640
+ return new Promise((resolve, reject) => {
641
+ const jsonRequest = req.toJson();
642
+ sendRequestWrapper.async(this.address, this.namespace, this.kubePath, this.kubeContext, this.clusterName, jsonRequest, (err, res) => {
643
+ if (err) {
644
+ reject(err);
645
+ } else {
646
+ const jsonResponse = JSON.parse(res.response);
647
+ const response = new ResponseV2(jsonResponse);
648
+ resolve(response);
649
+ }
650
+ });
651
+ });
652
+ }
599
653
  }
600
654
 
601
655
  module.exports = SkyrampClient;
@@ -0,0 +1,58 @@
1
+ const ResponseV2 = require('./classes/ResponseV2');
2
+ const lib = require('./lib');
3
+
4
+ // func checkStatusCodeWrapper(response *C.char, expectedStatus *C.char) (*C.char, *C.char) {
5
+ const checkStatusCodeWrapper = lib.func('checkStatusCodeWrapper', 'string', ['string', 'string']);
6
+
7
+
8
+ /**
9
+ * Checks if the response's status code matches the expected status code (with support for wildcards).
10
+ *
11
+ * The expected status can include 'x' as a wildcard for a single digit.
12
+ * Examples:
13
+ * - "200" matches exactly 200
14
+ * - "20x" matches any status code in the range 200 to 209
15
+ * - "2xx" matches any status code in the range 200 to 299
16
+ *
17
+ * @param {ResponseV2} response - The response object containing a `status_code` attribute.
18
+ * @param {string} expectedStatus - The expected status code as a string with optional 'x' wildcards.
19
+ * @returns {boolean} True if the status code matches, false otherwise.
20
+ * @throws {Error} If the response object lacks a 'status_code' attribute.
21
+ */
22
+ function checkStatusCode(response, expectedStatus) {
23
+ if (!(response instanceof ResponseV2)) {
24
+ throw new Error('Response must be an instance of ResponseV2');
25
+ }
26
+
27
+ if (!expectedStatus || typeof expectedStatus !== 'string') {
28
+ throw new Error('Expected status must be a string');
29
+ }
30
+
31
+ try {
32
+ const responseJson = response.toJson();
33
+ const result = checkStatusCodeWrapper(responseJson, expectedStatus);
34
+ const parsed = JSON.parse(result);
35
+
36
+ if (!parsed || typeof parsed !== 'object') {
37
+ throw new Error('Invalid response format from wrapper');
38
+ }
39
+
40
+ if (Object.prototype.hasOwnProperty.call(parsed, 'error')) {
41
+ throw new Error(parsed.error);
42
+ }
43
+
44
+ if (Object.prototype.hasOwnProperty.call(parsed, 'result')) {
45
+ return parsed.result;
46
+ }
47
+
48
+ throw new Error('Response missing both result and error fields');
49
+
50
+ } catch (err) {
51
+ console.error(`Error in checkStatusCode: ${err.message}`);
52
+ throw err; // Re-throw to allow caller to handle errors
53
+ }
54
+ }
55
+
56
+ module.exports = {
57
+ checkStatusCode
58
+ };
package/src/index.d.ts CHANGED
@@ -8,4 +8,8 @@ export * from './classes/ResponseValue';
8
8
  export * from './classes/RestParam';
9
9
  export * from './classes/TrafficConfig';
10
10
  export * from './classes/DelayConfig';
11
- export * from './classes/Protocol';
11
+ export * from './classes/Protocol';
12
+ export * from './classes/RequestV2';
13
+ export * from './classes/ResponseV2';
14
+ export * from './utils';
15
+ export * from './function';
package/src/index.js CHANGED
@@ -10,9 +10,15 @@ const DelayConfig = require('./classes/DelayConfig');
10
10
  const Protocol = require('./classes/Protocol');
11
11
  const Asserts= require('./classes/Asserts');
12
12
  const Step = require('./classes/Step');
13
+ const RequestV2 = require('./classes/RequestV2');
14
+ const ResponseV2 = require('./classes/ResponseV2');
15
+ const { getValue, checkSchema } = require('./utils');
16
+ const { checkStatusCode } = require('./function');
13
17
 
14
18
  module.exports = {
15
19
  SkyrampClient,
20
+ RequestV2,
21
+ ResponseV2,
16
22
  GrpcEndpoint,
17
23
  RestEndpoint,
18
24
  Scenario,
@@ -23,5 +29,8 @@ module.exports = {
23
29
  DelayConfig,
24
30
  Protocol,
25
31
  Asserts,
26
- Step
32
+ Step,
33
+ getValue,
34
+ checkStatusCode,
35
+ checkSchema,
27
36
  }
package/src/utils.js CHANGED
@@ -1,11 +1,21 @@
1
1
  const fs = require('fs');
2
+ const koffi = require('koffi');
2
3
  const path = require('path');
3
4
  const yaml = require('js-yaml');
5
+ const lib = require('./lib');
4
6
  const SKYRAMP_YAML_VERSION = "v1"
5
7
 
8
+ const responseType = koffi.struct({
9
+ response: 'char*',
10
+ error: 'char*',
11
+ });
12
+
13
+ const getJSONValueWrapper = lib.func('getJSONValueWrapper', responseType, ['string', 'string']);
14
+ const checkSchemaWrapper = lib.func('checkSchemaWrapper', responseType, ['string', 'string']);
15
+
6
16
  function getYamlBytes(jsonObject) {
7
17
  try {
8
- const yamlContent = yaml.dump(jsonObject, {lineWidth: 100});
18
+ const yamlContent = yaml.dump(jsonObject, { lineWidth: 100 });
9
19
  return yamlContent;
10
20
  } catch (error) {
11
21
  throw new Error('Error converting to YAML bytes:', error);
@@ -25,7 +35,7 @@ function createTestDescriptionFromScenario({ scenario, globalVars }) {
25
35
  },
26
36
  services: services,
27
37
  endpoints: endpoints,
28
- scenarios: scenarios,
38
+ scenarios: scenarios,
29
39
  requests: requests
30
40
  };
31
41
 
@@ -48,14 +58,112 @@ function readDataFromFile(filename) {
48
58
  throw new Error('Unsupported file format. Only .json, .yaml, and .js files are supported.');
49
59
  }
50
60
  return [jsonData, false];
51
- } catch(error) {
61
+ } catch (error) {
52
62
  throw new Error('Error reading or parsing file: ', error);
53
63
  }
54
64
  }
55
65
 
66
+ /**
67
+ * Extracts and parses a value from a JSON response using a JSON path expression
68
+ * @param {Object} response - The response object containing a body property with JSON data
69
+ * @param {string} path - The JSON path expression to extract the value (e.g. "$.data.id")
70
+ * @returns {*} The extracted value converted to the appropriate JavaScript type:
71
+ * - For primitive types (string, number, boolean), returns the converted value
72
+ * - For null values, returns null
73
+ * - For complex types (arrays, objects) or errors, returns null
74
+ * @example
75
+ * const response = {
76
+ * body: '{"type": "string", "value": "hello"}'
77
+ * };
78
+ * etResponseValue(response, "$.value"); // Returns "hello"
79
+ */
80
+ function getValue(response, path) {
81
+ const result = getJSONValueWrapper(response.responseBody, path);
82
+
83
+ // 1. If result is null/undefined, return null.
84
+ if (!result || result.error !== null) {
85
+ return null;
86
+ }
87
+
88
+ try {
89
+ // 2. Parse the returned JSON string.
90
+ const parsed = JSON.parse(result.response);
91
+
92
+ // 3. If parsed is an object of the shape { type, value }, handle typed values:
93
+ // e.g., { "type": "string", "value": "hello" }
94
+ if (
95
+ parsed &&
96
+ typeof parsed === 'object' &&
97
+ !Array.isArray(parsed) &&
98
+ Object.prototype.hasOwnProperty.call(parsed, 'type') &&
99
+ Object.prototype.hasOwnProperty.call(parsed, 'value')
100
+ ) {
101
+ const { type: valueType, value } = parsed;
102
+
103
+ // Return null for complex types (arrays and objects)
104
+ if (['array', 'object', '[]interface {}'].includes(valueType)) {
105
+ return null;
106
+ }
107
+
108
+ // Convert to the appropriate JavaScript type
109
+ switch (valueType) {
110
+ case 'string':
111
+ return String(value);
112
+ case "float64", "float32", "int64", "int32", "int16", "int8", "uint64", "uint32", "uint16", "uint8":
113
+ return value;
114
+ case 'boolean':
115
+ return Boolean(value);
116
+ case 'null':
117
+ return null;
118
+ default:
119
+ // For unrecognized or fallback:
120
+ return value;
121
+ }
122
+ }
123
+
124
+ // 4. If the result is not of the form { type, value }, return it directly.
125
+ return parsed;
126
+ } catch (err) {
127
+ // 5. If JSON.parse fails or something else goes wrong, log the error and return null.
128
+ console.error(`Error processing JSONValue: ${err}`);
129
+ return null;
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Validates the response body against the expected schema.
135
+ *
136
+ * @param {Object} response - An object containing a `body` property (JSON string).
137
+ * @param {Object|string} expectedSchema - The JSON schema (object or JSON string) to validate against.
138
+ * @returns {boolean} True if validation succeeds, false otherwise.
139
+ */
140
+ /**
141
+ * Validates the response body against the expected schema.
142
+ *
143
+ * @param {Object} response - The response object containing the body to validate
144
+ * @param {string} response.body - The response body as a JSON string
145
+ * @param {Object|string} expectedSchema - The JSON schema to validate against. Can be either:
146
+ * - A JSON schema object with type definitions and validation rules
147
+ * - A JSON string containing a schema definition
148
+ * @returns {boolean} True if validation succeeds, false otherwise.
149
+ */
150
+ function checkSchema(response, expectedSchema) {
151
+ const result = checkSchemaWrapper(response.responseBody, expectedSchema);
152
+ if (result.error) {
153
+ return false;
154
+ }
155
+ if (!result.response) {
156
+ return false;
157
+ }
158
+ const parsed = JSON.parse(result.response);
159
+ return parsed.result;
160
+ }
161
+
56
162
  module.exports = {
57
163
  createTestDescriptionFromScenario,
58
164
  getYamlBytes,
59
165
  readDataFromFile,
166
+ getValue,
167
+ checkSchema,
60
168
  SKYRAMP_YAML_VERSION
61
169
  }