Skip to content

Commit

Permalink
var support json substituion and expression substition
Browse files Browse the repository at this point in the history
  • Loading branch information
cedric05 committed Dec 31, 2024
1 parent 4e56122 commit 764d1cf
Show file tree
Hide file tree
Showing 10 changed files with 303 additions and 11 deletions.
4 changes: 2 additions & 2 deletions dothttp/http.tx
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ TOFILE:
;

JSON:
array=Array | object=Object
array=Array | object=Object | var = VarString
;

Array:
Expand All @@ -237,7 +237,7 @@ Factor:
;

Number:
/\d+/
/\d+(\.)?\d*/ | ID
;


Expand Down
2 changes: 1 addition & 1 deletion dothttp/parse/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ def load_properties_from_var(model:MultidefHttp, property_util: PropertyProvider
## but it will complicate the code
for variable in model.variables:
if variable.value:
var_value = jsonmodel_to_json(variable.value)
var_value = jsonmodel_to_json(variable.value, property_util=property_util)
property_util.add_infile_property_from_var(variable.name, var_value)
elif variable.func:
func_name = f"${variable.func.name}"
Expand Down
23 changes: 21 additions & 2 deletions dothttp/parse/dsl_jsonparser.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import json
import logging
from typing import Dict, List, Optional, Union

from ..utils.property_util import PropertyProvider, get_no_replace_property_provider
from ..utils.common import triple_or_double_tostring
from .expression import Token, TokenType

base_logger = logging.getLogger("dothttp")


class JsonParser:
Expand All @@ -19,6 +22,8 @@ def json_or_array_to_json(self, model) -> Union[Dict, List]:
self.get_key(member): self.jsonmodel_to_json(member.value)
for member in json_object.members
}
elif var_value := model.var:
return self.property_util.get_updated_obj_content(var_value)

def get_key(self, member):
if member.key:
Expand Down Expand Up @@ -47,7 +52,21 @@ def jsonmodel_to_json(self, model):
elif model == "null":
return None
elif expr := model.expr:
return eval(expr)
try:
expression = Token.parse_expr(expr)
new_expression = ""
for token in expression:
if token.token_type == TokenType.VAR:
value = self.property_util.resolve_property_string(token.value)
else:
value = token.value
if not isinstance(value, str):
value = str(value)
new_expression += value
return eval(new_expression)
except:
base_logger.error(f"error in evaluating expression {expr}, new expression {new_expression}")
return 0


# Supporting function
Expand Down
85 changes: 85 additions & 0 deletions dothttp/parse/expression.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@

from enum import Enum


class TokenType(Enum):
VAR = 1
OPERATOR = 2
NUMBER = 3
PARENTHESES = 4

class Token():
def __init__(self, token_type, value):
self.token_type = token_type
self.value = value

def __repr__(self):
return f"{self.token_type}({self.value})"

def __eq__(self, compr):
if self.value == compr.value and self.token_type == compr.token_type:
return True
return False

@staticmethod
def var(value):
return Token(TokenType.VAR, value)

@staticmethod
def operator(value):
return Token(TokenType.OPERATOR, value)

@staticmethod
def number(value):
return Token(TokenType.NUMBER, value)

@staticmethod
def parentheses(value):
return Token(TokenType.PARENTHESES, value)

@staticmethod
def parse_expr(expr):
current_var = ""
numeric = ""
for i in expr:
if current_var:
if i in ['\t', '\n', ' ']:
continue
elif i in ['+', '-', '*', '/']:
yield Token.var(current_var)
yield Token.operator(i)
current_var = ""
continue
elif i in ['(', ')']:
yield Token.var(current_var)
yield Token.parentheses(i)
current_var = ""
else:
current_var += i
elif numeric:
if i in ['\t', '\n', ' ']:
continue
elif i in ['+', '-', '*', '/']:
yield Token.number(numeric)
yield Token.operator(i)
numeric = ""
continue
elif i in ['(', ')']:
yield Token.number(numeric)
yield Token.parentheses(i)
numeric = ""
else:
numeric += i
elif i in ['+', '-', '*', '/']:
yield Token.operator(i)
elif i in ['(', ')']:
yield Token.parentheses(i)
elif i.isalpha():
current_var = i
elif i.isnumeric():
numeric = i
if current_var:
yield Token.var(current_var)
if numeric:
yield Token.number(numeric)

1 change: 0 additions & 1 deletion dothttp/utils/property_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,6 @@ def check_properties_for_content(self, content):
)
return content_prop_needed, props_needed

@lru_cache
def available_properties_list(self):
return (
set(self.env_properties.keys())
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "dothttp-req"
version = "0.0.44a2"
version = "0.0.44a3"
description = "Dothttp is Simple http client for testing and development"
authors = ["Prasanth <[email protected]>"]
license = "MIT"
Expand Down
95 changes: 95 additions & 0 deletions test/core/test_expression.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import pytest
from dothttp.parse.expression import Token, TokenType

def test_var_token():
token = Token.var("x")
assert token.token_type == TokenType.VAR
assert token.value == "x"

def test_operator_token():
token = Token.operator("+")
assert token.token_type == TokenType.OPERATOR
assert token.value == "+"

def test_number_token():
token = Token.number("123")
assert token.token_type == TokenType.NUMBER
assert token.value == "123"

def test_parentheses_token():
token = Token.parentheses("(")
assert token.token_type == TokenType.PARENTHESES
assert token.value == "("

def test_parse_expr_simple():
expr = "x + 1"
tokens = list(Token.parse_expr(expr))
print(tokens)
assert tokens == [
Token.var("x"),
Token.operator("+"),
Token.number("1")
]

def test_parse_expr_with_parentheses():
expr = "(x + 1) * y"
tokens = list(Token.parse_expr(expr))
assert tokens == [
Token.parentheses("("),
Token.var("x"),
Token.operator("+"),
Token.number("1"),
Token.parentheses(")"),
Token.operator("*"),
Token.var("y")
]

def test_parse_expr_with_spaces():
expr = " x + 1 "
tokens = list(Token.parse_expr(expr))
assert tokens == [
Token.var("x"),
Token.operator("+"),
Token.number("1")
]

def test_parse_expr_with_multiple_digits():
expr = "x + 123"
tokens = list(Token.parse_expr(expr))
assert tokens == [
Token.var("x"),
Token.operator("+"),
Token.number("123")
]

def test_parse_expr_with_multiple_vars():
expr = "x + y"
tokens = list(Token.parse_expr(expr))
assert tokens == [
Token.var("x"),
Token.operator("+"),
Token.var("y")
]

def test_parse_expr_edge_case_empty():
expr = ""
tokens = list(Token.parse_expr(expr))
assert tokens == []

def test_parse_expr_edge_case_only_operators():
expr = "+-*/"
tokens = list(Token.parse_expr(expr))
assert tokens == [
Token.operator("+"),
Token.operator("-"),
Token.operator("*"),
Token.operator("/")
]

def test_parse_expr_edge_case_only_parentheses():
expr = "()"
tokens = list(Token.parse_expr(expr))
assert tokens == [
Token.parentheses("("),
Token.parentheses(")")
]
29 changes: 29 additions & 0 deletions test/core/var/expression_var.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Setting variables
var num1 = 10.1;
var num2 = 5.2;

# Addition
var sum = (num1 + num2);

# Subtraction
var difference = (num1 - num2);

# Multiplication
var product = (num1 * num2);

# Division
var quotient = (num1 / num2);

var test = "This is a test";

# Output the results
POST http://localhost:8000/post
json(
{
"sum": {{sum}},
"difference": {{difference}},
"product": {{product}},
"quotient": {{quotient}},
"test": "{{test}}"
}
)
41 changes: 37 additions & 4 deletions test/core/var/test_var.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,19 +149,52 @@ def test_override(self):
"incorrect body",
)


def test_var_templating(self):
req = self.get_request(f"{base_dir}/templating.http")
self.assertEqual(
{
"a":10,
"a": 10,
"b": "10 + 10 = 20"
},
json.loads(req.body),
"incorrect body",
)
# parse url and get query c
parsed = urlparse(req.url)
# c=randomstrs
# c=randomstrs
# total 12 = 10 (generated) + 2 (c=)
self.assertEqual(len(parsed.query), 12)
self.assertEqual(len(parsed.query), 12)

def test_var_json_sub(self):
req = self.get_request(f"{base_dir}/var_json_sub.http")
expected = {
"a": {
"b": {
"d": {
"e": {
"a": 10,
"b": 11.1,
"c": 21.1,
"d": "hello world",
"e": "if is a=10, b=11.1 and a + b is 21.1",
"f": True,
"g": None
}
}
}
}
}

self.assertEqual(expected, json.loads(req.body), "incorrect body")

def test_var_expression(self):
req = self.get_request(f"{base_dir}/expression_var.http")
expected = {
'difference': 4.8999999999999995,
'product': 52.52,
'quotient': 1.942307692307692,
'sum': 15.3,
"test": "This is a test"
}

self.assertEqual(expected, json.loads(req.body), "incorrect body")
32 changes: 32 additions & 0 deletions test/core/var/var_json_sub.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
var a = 10;
var b = 11.1;
var c = (10 + 11.1);
var d = "hello world";
var e = $"if is a={a}, b={b} and a + b is {c}";
var f = true;
var g = null;
var h = {
"a": {{a}},
"b": {{b}},
"c": {{c}},
"d": {{d}},
"e": {{e}},
"f": {{f}},
"g": {{g}}
};
var e = {
"a":{
"b": {
"d": {
"e": {{h}}
}
}
}
};

POST "https://req.dothttp.dev/"
json(
{{e}}
)


0 comments on commit 764d1cf

Please sign in to comment.