diff --git a/restler/engine/fuzzing_parameters/request_schema_parser.py b/restler/engine/fuzzing_parameters/request_schema_parser.py
index 313afbb..58cbc40 100644
--- a/restler/engine/fuzzing_parameters/request_schema_parser.py
+++ b/restler/engine/fuzzing_parameters/request_schema_parser.py
@@ -181,8 +181,8 @@ def des_param_payload(param_payload_json, tag='', body_param=True):
is_dynamic_object = False
if 'Fuzzable' in payload:
- content_type = payload['Fuzzable'][0]
- content_value = payload['Fuzzable'][1]
+ content_type = payload['Fuzzable']['primitiveType']
+ content_value = payload['Fuzzable']['defaultValue']
fuzzable = True
elif 'Constant' in payload:
content_type = payload['Constant'][0]
diff --git a/restler/unit_tests/log_baseline_test_files/test_grammar.json b/restler/unit_tests/log_baseline_test_files/test_grammar.json
index f34bb14..47bde14 100644
--- a/restler/unit_tests/log_baseline_test_files/test_grammar.json
+++ b/restler/unit_tests/log_baseline_test_files/test_grammar.json
@@ -136,12 +136,11 @@
"LeafNode": {
"name": "population",
"payload": {
- "Fuzzable": [
- "Int",
- "1000",
- null,
- "population"
- ]
+ "Fuzzable": {
+ "primitiveType": "Int",
+ "defaultValue": "1000",
+ "parameterName": "population"
+ }
},
"isRequired": false,
"isReadOnly": false
@@ -164,10 +163,11 @@
"LeafNode": {
"name": "strtest",
"payload": {
- "Fuzzable": [
- "String",
- "true"
- ]
+ "Fuzzable": {
+ "primitiveType": "String",
+ "defaultValue": "true",
+ "parameterName": "population"
+ }
},
"isRequired": true,
"isReadOnly": false
@@ -247,8 +247,8 @@
"LeafNode": {
"name": "",
"payload": {
- "Fuzzable": [
- {
+ "Fuzzable": {
+ "primitiveType": {
"Enum": [
"group",
"String",
@@ -260,8 +260,8 @@
null
]
},
- "A"
- ]
+ "defaultValue": "A"
+ }
}
}
}
@@ -482,8 +482,8 @@
"LeafNode": {
"name": "group",
"payload": {
- "Fuzzable": [
- {
+ "Fuzzable": {
+ "primitiveType": {
"Enum": [
"group",
"String",
@@ -495,8 +495,8 @@
null
]
},
- "A"
- ]
+ "defaultValue": "A"
+ }
}
}
}
@@ -1524,11 +1524,11 @@
"LeafNode": {
"name": "testbool",
"payload": {
- "Fuzzable": [
- "Bool",
- "testval",
- false
- ]
+ "Fuzzable": {
+ "primitiveType": "Bool",
+ "defaultValue": "testval",
+ "exampleValue": "false"
+ }
},
"isRequired": true,
"isReadOnly": false
@@ -2150,10 +2150,10 @@
"LeafNode": {
"name": "",
"payload": {
- "Fuzzable": [
- "DateTime",
- "2020-1-1"
- ]
+ "Fuzzable": {
+ "primitiveType": "DateTime",
+ "defaultValue": "2020-1-1"
+ }
},
"isRequired": true,
"isReadOnly": false
@@ -2185,10 +2185,10 @@
"LeafNode": {
"name": "datetest",
"payload": {
- "Fuzzable": [
- "DateTime",
- "2020-1-1"
- ]
+ "Fuzzable": {
+ "primitiveType": "DateTime",
+ "defaultValue": "2020-1-1"
+ }
},
"isRequired": false,
"isReadOnly": false
diff --git a/restler/unit_tests/log_baseline_test_files/test_grammar_body.json b/restler/unit_tests/log_baseline_test_files/test_grammar_body.json
index 65e7f54..7a8c9e0 100644
--- a/restler/unit_tests/log_baseline_test_files/test_grammar_body.json
+++ b/restler/unit_tests/log_baseline_test_files/test_grammar_body.json
@@ -114,10 +114,10 @@
"LeafNode": {
"name": "population",
"payload": {
- "Fuzzable": [
- "Int",
- "1000"
- ]
+ "Fuzzable": {
+ "primitiveType": "Int",
+ "defaultValue": "1000"
+ }
},
"isRequired": false,
"isReadOnly": false
@@ -140,10 +140,10 @@
"LeafNode": {
"name": "strtest",
"payload": {
- "Fuzzable": [
- "String",
- "true"
- ]
+ "Fuzzable": {
+ "primitiveType": "String",
+ "defaultValue": "true"
+ }
},
"isRequired": true,
"isReadOnly": false
@@ -171,10 +171,10 @@
"LeafNode": {
"name": "subtest",
"payload": {
- "Fuzzable": [
- "Bool",
- "true"
- ]
+ "Fuzzable": {
+ "primitiveType": "Bool",
+ "defaultValue": "true"
+ }
},
"isRequired": false,
"isReadOnly": false
@@ -265,8 +265,10 @@
"LeafNode": {
"name": "",
"payload": {
- "Fuzzable": [
- {
+
+ "Fuzzable": {
+ "primitiveType":
+ {
"Enum": [
"group",
"String",
@@ -278,8 +280,8 @@
null
]
},
- "A"
- ]
+ "defaultValue": "A"
+ }
}
}
}
@@ -508,7 +510,8 @@
"LeafNode": {
"name": "group",
"payload": {
- "Fuzzable": [
+ "Fuzzable": {
+ "primitiveType":
{
"Enum": [
"group",
@@ -521,8 +524,8 @@
null
]
},
- "A"
- ]
+ "defaultValue": "A"
+ }
}
}
}
diff --git a/src/compiler/Restler.Compiler.Test/DependencyTests.fs b/src/compiler/Restler.Compiler.Test/DependencyTests.fs
index 570cb4e..e2d8f42 100644
--- a/src/compiler/Restler.Compiler.Test/DependencyTests.fs
+++ b/src/compiler/Restler.Compiler.Test/DependencyTests.fs
@@ -324,7 +324,7 @@ module Dependencies =
ResolveBodyDependencies = true
UseBodyExamples = Some true
SwaggerSpecFilePath = Some [(Path.Combine(Environment.CurrentDirectory, @"swagger\dependencyTests\input_producer_spec.json"))]
- CustomDictionaryFilePath = None
+ CustomDictionaryFilePath = Some (Path.Combine(Environment.CurrentDirectory, @"swagger\dependencyTests\input_producer_dict.json"))
AnnotationFilePath = Some (Path.Combine(Environment.CurrentDirectory, @"swagger\dependencyTests\input_producer_annotations.json"))
AllowGetProducers = true
}
@@ -337,6 +337,20 @@ module Dependencies =
Assert.True(grammar.Contains("""restler_custom_payload_uuid4_suffix("fileId", writer=_file__fileId__post_fileId_path.writer())"""))
Assert.True(grammar.Contains("""restler_static_string(_file__fileId__post_fileId_path.reader(), quoted=False)"""))
+ // Validate (tag, label) annotation. tag - body producer (jsonpath), label: path parameter.
+ Assert.True(grammar.Contains("""primitives.restler_custom_payload("tag", quoted=True, writer=_archive_post_tag.writer())"""))
+ Assert.True(grammar.Contains("""restler_static_string(_archive_post_tag.reader(), quoted=True)"""))
+
+ // Validate (name, name) annotation. name - body producer (POST) and consumer (PUT).
+ Assert.True(grammar.Contains("""primitives.restler_fuzzable_object("{ \"fuzz\": false }", writer=_archive_post_name.writer())"""))
+ Assert.True(grammar.Contains("""primitives.restler_static_string(_archive_post_name.reader(), quoted=False)"""))
+
+ // Validate (hash, sig) annotation. hash - header producer (POST), sig - header consumer (PUT)
+ Assert.True(grammar.Contains("""primitives.restler_custom_payload_query("hash", writer=_archive_post_hash_query.writer())"""))
+ Assert.True(grammar.Contains("""primitives.restler_static_string(_archive_post_hash_query.reader(), quoted=False)"""))
+
+
+
/// Test that the entire body should be able to be replaced with a custom payload
/// from the dictionary
diff --git a/src/compiler/Restler.Compiler.Test/Restler.Compiler.Test.fsproj b/src/compiler/Restler.Compiler.Test/Restler.Compiler.Test.fsproj
index 7ce3ee4..2beacc1 100644
--- a/src/compiler/Restler.Compiler.Test/Restler.Compiler.Test.fsproj
+++ b/src/compiler/Restler.Compiler.Test/Restler.Compiler.Test.fsproj
@@ -76,6 +76,9 @@
PreserveNewest
+
+ PreserveNewest
+
PreserveNewest
diff --git a/src/compiler/Restler.Compiler.Test/baselines/grammarTests/required_params_grammar.json b/src/compiler/Restler.Compiler.Test/baselines/grammarTests/required_params_grammar.json
index 5b2444c..170212d 100644
--- a/src/compiler/Restler.Compiler.Test/baselines/grammarTests/required_params_grammar.json
+++ b/src/compiler/Restler.Compiler.Test/baselines/grammarTests/required_params_grammar.json
@@ -43,12 +43,10 @@
"LeafNode": {
"name": "name",
"payload": {
- "Fuzzable": [
- "String",
- "fuzzstring",
- null,
- null
- ]
+ "Fuzzable": {
+ "primitiveType": "String",
+ "defaultValue": "fuzzstring"
+ }
},
"isRequired": false,
"isReadOnly": false
@@ -58,12 +56,10 @@
"LeafNode": {
"name": "tags",
"payload": {
- "Fuzzable": [
- "Object",
- "{ \"fuzz\": false }",
- null,
- null
- ]
+ "Fuzzable": {
+ "primitiveType": "Object",
+ "defaultValue": "{ \"fuzz\": false }"
+ }
},
"isRequired": false,
"isReadOnly": false
@@ -159,12 +155,10 @@
"LeafNode": {
"name": "",
"payload": {
- "Fuzzable": [
- "String",
- "fuzzstring",
- null,
- null
- ]
+ "Fuzzable": {
+ "primitiveType": "String",
+ "defaultValue": "fuzzstring"
+ }
},
"isRequired": false,
"isReadOnly": false
@@ -198,12 +192,10 @@
"LeafNode": {
"name": "name",
"payload": {
- "Fuzzable": [
- "String",
- "fuzzstring",
- null,
- null
- ]
+ "Fuzzable": {
+ "primitiveType": "String",
+ "defaultValue": "fuzzstring"
+ }
},
"isRequired": true,
"isReadOnly": false
@@ -213,12 +205,10 @@
"LeafNode": {
"name": "tags",
"payload": {
- "Fuzzable": [
- "Object",
- "{ \"fuzz\": false }",
- null,
- null
- ]
+ "Fuzzable": {
+ "primitiveType": "Object",
+ "defaultValue": "{ \"fuzz\": false }"
+ }
},
"isRequired": false,
"isReadOnly": false
@@ -246,12 +236,10 @@
"LeafNode": {
"name": "name",
"payload": {
- "Fuzzable": [
- "String",
- "fuzzstring",
- null,
- null
- ]
+ "Fuzzable": {
+ "primitiveType": "String",
+ "defaultValue": "fuzzstring"
+ }
},
"isRequired": false,
"isReadOnly": false
@@ -261,12 +249,10 @@
"LeafNode": {
"name": "id",
"payload": {
- "Fuzzable": [
- "Number",
- "1.23",
- null,
- null
- ]
+ "Fuzzable": {
+ "primitiveType": "Number",
+ "defaultValue": "1.23"
+ }
},
"isRequired": true,
"isReadOnly": false
@@ -306,12 +292,10 @@
"LeafNode": {
"name": "",
"payload": {
- "Fuzzable": [
- "String",
- "fuzzstring",
- null,
- null
- ]
+ "Fuzzable": {
+ "primitiveType": "String",
+ "defaultValue": "fuzzstring"
+ }
},
"isRequired": false,
"isReadOnly": false
diff --git a/src/compiler/Restler.Compiler.Test/baselines/schemaTests/xMsPaths_grammar.json b/src/compiler/Restler.Compiler.Test/baselines/schemaTests/xMsPaths_grammar.json
index 27dfc33..a7b5af3 100644
--- a/src/compiler/Restler.Compiler.Test/baselines/schemaTests/xMsPaths_grammar.json
+++ b/src/compiler/Restler.Compiler.Test/baselines/schemaTests/xMsPaths_grammar.json
@@ -70,8 +70,8 @@
"LeafNode": {
"name": "",
"payload": {
- "Fuzzable": [
- {
+ "Fuzzable": {
+ "primitiveType": {
"Enum": [
"api-version",
"String",
@@ -81,10 +81,8 @@
null
]
},
- "2020-03-01",
- null,
- null
- ]
+ "defaultValue": "2020-03-01"
+ }
},
"isRequired": true,
"isReadOnly": false
@@ -215,8 +213,8 @@
"LeafNode": {
"name": "",
"payload": {
- "Fuzzable": [
- {
+ "Fuzzable": {
+ "primitiveType": {
"Enum": [
"api-version",
"String",
@@ -226,10 +224,8 @@
null
]
},
- "2020-03-01",
- null,
- null
- ]
+ "defaultValue": "2020-03-01"
+ }
},
"isRequired": true,
"isReadOnly": false
@@ -381,8 +377,8 @@
"LeafNode": {
"name": "",
"payload": {
- "Fuzzable": [
- {
+ "Fuzzable": {
+ "primitiveType": {
"Enum": [
"api-version",
"String",
@@ -392,10 +388,8 @@
null
]
},
- "2020-03-01",
- null,
- null
- ]
+ "defaultValue": "2020-03-01"
+ }
},
"isRequired": true,
"isReadOnly": false
@@ -443,12 +437,10 @@
}
},
{
- "Fuzzable": [
- "String",
- "fuzzstring",
- null,
- null
- ]
+ "Fuzzable": {
+ "primitiveType": "String",
+ "defaultValue": "fuzzstring"
+ }
}
],
"queryParameters": [
@@ -478,8 +470,8 @@
"LeafNode": {
"name": "",
"payload": {
- "Fuzzable": [
- {
+ "Fuzzable": {
+ "primitiveType": {
"Enum": [
"api-version",
"String",
@@ -489,10 +481,8 @@
null
]
},
- "2020-03-01",
- null,
- null
- ]
+ "defaultValue": "2020-03-01"
+ }
},
"isRequired": true,
"isReadOnly": false
@@ -540,12 +530,10 @@
}
},
{
- "Fuzzable": [
- "String",
- "fuzzstring",
- null,
- null
- ]
+ "Fuzzable": {
+ "primitiveType": "String",
+ "defaultValue": "fuzzstring"
+ }
}
],
"queryParameters": [
@@ -575,8 +563,8 @@
"LeafNode": {
"name": "",
"payload": {
- "Fuzzable": [
- {
+ "Fuzzable": {
+ "primitiveType": {
"Enum": [
"api-version",
"String",
@@ -586,10 +574,8 @@
null
]
},
- "2020-03-01",
- null,
- null
- ]
+ "defaultValue": "2020-03-01"
+ }
},
"isRequired": true,
"isReadOnly": false
diff --git a/src/compiler/Restler.Compiler.Test/swagger/dependencyTests/input_producer_annotations.json b/src/compiler/Restler.Compiler.Test/swagger/dependencyTests/input_producer_annotations.json
index ad78ddf..749a26c 100644
--- a/src/compiler/Restler.Compiler.Test/swagger/dependencyTests/input_producer_annotations.json
+++ b/src/compiler/Restler.Compiler.Test/swagger/dependencyTests/input_producer_annotations.json
@@ -5,6 +5,30 @@
"producer_method": "POST",
"producer_resource_name": "fileId",
"consumer_param": "fileId"
+ },
+ {
+ "producer_endpoint": "/archive",
+ "producer_method": "POST",
+ "producer_resource_name": "hash",
+ "consumer_param": "sig"
+ },
+ {
+ "producer_endpoint": "/archive",
+ "producer_method": "POST",
+ "producer_resource_name": "/tag",
+ "consumer_param": "label"
+ },
+ {
+ "producer_endpoint": "/archive",
+ "producer_method": "POST",
+ "producer_resource_name": "/tag",
+ "consumer_param": "/tag"
+ },
+ {
+ "producer_endpoint": "/archive",
+ "producer_method": "POST",
+ "producer_resource_name": "/name",
+ "consumer_param": "/name"
}
]
}
diff --git a/src/compiler/Restler.Compiler.Test/swagger/dependencyTests/input_producer_dict.json b/src/compiler/Restler.Compiler.Test/swagger/dependencyTests/input_producer_dict.json
new file mode 100644
index 0000000..7c3c021
--- /dev/null
+++ b/src/compiler/Restler.Compiler.Test/swagger/dependencyTests/input_producer_dict.json
@@ -0,0 +1,9 @@
+{
+ "restler_custom_payload": {
+ "sig": [ "12345" ],
+ "tag": ["important"]
+ },
+ "restler_custom_payload_query": {
+ "hash": [ "56789" ]
+ }
+}
\ No newline at end of file
diff --git a/src/compiler/Restler.Compiler.Test/swagger/dependencyTests/input_producer_spec.json b/src/compiler/Restler.Compiler.Test/swagger/dependencyTests/input_producer_spec.json
index 0c88aa0..84b9b2b 100644
--- a/src/compiler/Restler.Compiler.Test/swagger/dependencyTests/input_producer_spec.json
+++ b/src/compiler/Restler.Compiler.Test/swagger/dependencyTests/input_producer_spec.json
@@ -13,10 +13,98 @@
"fileId":{
"type": "String",
"description": "the file id"
-
+ },
+ "Archive": {
+ "properties": {
+ "name": {
+ "type": "object"
+ },
+ "tag": {
+ "type": "string"
+ }
+ }
}
},
"paths": {
+ "/archive/{archiveId}/{label}": {
+ "get": {
+ "parameters": [
+ {
+ "in": "path",
+ "name": "archiveId",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "in": "path",
+ "name": "label",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Success"
+ }
+ }
+ }
+ },
+ "/archive": {
+ "post": {
+ "parameters": [
+ {
+ "in": "query",
+ "name": "hash",
+ "required": true,
+ "type": "number"
+ },
+ {
+ "in": "body",
+ "name": "bodyParam",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/Archive"
+ }
+ }
+ ],
+ "responses": {
+ "201": {
+ "description": "Success"
+ }
+ }
+ }
+ },
+ "/archive/{archiveId}": {
+ "put": {
+ "parameters": [
+ {
+ "in": "path",
+ "name": "archiveId",
+ "required": true,
+ "type": "String"
+ },
+ {
+ "in": "query",
+ "name": "sig",
+ "required": true,
+ "type": "number"
+ },
+ {
+ "in": "body",
+ "name": "bodyParam",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/Archive"
+ }
+ }
+ ],
+ "responses": {
+ "201": {
+ "description": "Success"
+ }
+ }
+ }
+ },
"/file/{fileId}": {
"post": {
"parameters": [
diff --git a/src/compiler/Restler.Compiler/ApiResourceTypes.fs b/src/compiler/Restler.Compiler/ApiResourceTypes.fs
index ea22b6c..52fe80e 100644
--- a/src/compiler/Restler.Compiler/ApiResourceTypes.fs
+++ b/src/compiler/Restler.Compiler/ApiResourceTypes.fs
@@ -372,7 +372,10 @@ type Producer =
/// Currently, only assigning such values from the dictionary is supported.
/// The dictionary payload is an option type because it is only present when
/// the initial payload is being generated.
- | InputParameter of InputOnlyProducer * DictionaryPayload option
+ /// (producer, dictionary payload, isWriter)
+ /// When 'isWriter' is true, this is a writer variable that should be generated
+ /// with the original payload.
+ | InputParameter of InputOnlyProducer * DictionaryPayload option * bool
| OrderingConstraintParameter of OrderingConstraintProducer
diff --git a/src/compiler/Restler.Compiler/CodeGenerator.fs b/src/compiler/Restler.Compiler/CodeGenerator.fs
index 99db0fe..3e0cabf 100644
--- a/src/compiler/Restler.Compiler/CodeGenerator.fs
+++ b/src/compiler/Restler.Compiler/CodeGenerator.fs
@@ -24,28 +24,31 @@ module Types =
trackedParameterName: string option
}
+ type DynamicObjectWriter =
+ | DynamicObjectWriter of string
+
/// RESTler grammar built-in types
/// IMPORTANT ! All primitives must be supported in restler/engine/primitives.py
type RequestPrimitiveType =
| Restler_static_string_constant of string
| Restler_static_string_variable of string * bool
| Restler_static_string_jtoken_delim of string
- | Restler_fuzzable_string of RequestPrimitiveTypeData
- | Restler_fuzzable_datetime of RequestPrimitiveTypeData
- | Restler_fuzzable_date of RequestPrimitiveTypeData
- | Restler_fuzzable_object of RequestPrimitiveTypeData
+ | Restler_fuzzable_string of RequestPrimitiveTypeData * DynamicObjectWriter option
+ | Restler_fuzzable_datetime of RequestPrimitiveTypeData * DynamicObjectWriter option
+ | Restler_fuzzable_date of RequestPrimitiveTypeData * DynamicObjectWriter option
+ | Restler_fuzzable_object of RequestPrimitiveTypeData * DynamicObjectWriter option
| Restler_fuzzable_delim of RequestPrimitiveTypeData
- | Restler_fuzzable_uuid4 of RequestPrimitiveTypeData
- | Restler_fuzzable_group of RequestPrimitiveTypeData
- | Restler_fuzzable_bool of RequestPrimitiveTypeData
- | Restler_fuzzable_int of RequestPrimitiveTypeData
- | Restler_fuzzable_number of RequestPrimitiveTypeData
+ | Restler_fuzzable_uuid4 of RequestPrimitiveTypeData * DynamicObjectWriter option
+ | Restler_fuzzable_group of RequestPrimitiveTypeData * DynamicObjectWriter option
+ | Restler_fuzzable_bool of RequestPrimitiveTypeData * DynamicObjectWriter option
+ | Restler_fuzzable_int of RequestPrimitiveTypeData * DynamicObjectWriter option
+ | Restler_fuzzable_number of RequestPrimitiveTypeData * DynamicObjectWriter option
| Restler_multipart_formdata of string
- | Restler_custom_payload of RequestPrimitiveTypeData
- | Restler_custom_payload_header of string
- | Restler_custom_payload_query of string
+ | Restler_custom_payload of RequestPrimitiveTypeData * DynamicObjectWriter option
+ | Restler_custom_payload_header of string * DynamicObjectWriter option
+ | Restler_custom_payload_query of string * DynamicObjectWriter option
/// (Payload name, dynamic object writer name)
- | Restler_custom_payload_uuid4_suffix of string * string option
+ | Restler_custom_payload_uuid4_suffix of string * DynamicObjectWriter option
| Restler_refreshable_authentication_token of string
| Restler_basepath of string
| Shadow_values of string
@@ -72,20 +75,27 @@ let rec getRestlerPythonPayload (payload:FuzzingPayload) (isQuoted:bool) : Reque
match p with
| Constant (t,v) ->
Restler_static_string_constant v
- | Fuzzable (t,v,exv,parameterName) ->
- match t with
- | Bool -> Restler_fuzzable_bool { defaultValue = v ; isQuoted = false ; exampleValue = exv ; trackedParameterName = parameterName }
+ | Fuzzable fp ->
+ let v = fp.defaultValue
+ let exv = fp.exampleValue
+ let parameterName = fp.parameterName
+ let dynamicObject = if fp.dynamicObject.IsSome then Some (DynamicObjectWriter fp.dynamicObject.Value.variableName) else None
+ match fp.primitiveType with
+ | Bool ->
+ Restler_fuzzable_bool ({ defaultValue = v ; isQuoted = false ; exampleValue = exv ; trackedParameterName = parameterName }, dynamicObject)
| PrimitiveType.DateTime ->
- Restler_fuzzable_datetime { defaultValue = v ; isQuoted = isQuoted ; exampleValue = exv ; trackedParameterName = parameterName }
+ Restler_fuzzable_datetime ({ defaultValue = v ; isQuoted = isQuoted ; exampleValue = exv ; trackedParameterName = parameterName }, dynamicObject)
| PrimitiveType.Date ->
- Restler_fuzzable_date { defaultValue = v ; isQuoted = isQuoted ; exampleValue = exv ; trackedParameterName = parameterName }
+ Restler_fuzzable_date ({ defaultValue = v ; isQuoted = isQuoted ; exampleValue = exv ; trackedParameterName = parameterName }, dynamicObject)
+
| PrimitiveType.String ->
- Restler_fuzzable_string { defaultValue = v ; isQuoted = isQuoted ; exampleValue = exv ; trackedParameterName = parameterName }
- | PrimitiveType.Object -> Restler_fuzzable_object { defaultValue = v ; isQuoted = false ; exampleValue = exv ; trackedParameterName = parameterName }
- | Int -> Restler_fuzzable_int { defaultValue = v ; isQuoted = false; exampleValue = exv ; trackedParameterName = parameterName }
- | Number -> Restler_fuzzable_number { defaultValue = v ; isQuoted = false ; exampleValue = exv ; trackedParameterName = parameterName }
+ Restler_fuzzable_string ({ defaultValue = v ; isQuoted = isQuoted ; exampleValue = exv ; trackedParameterName = parameterName }, dynamicObject)
+ | PrimitiveType.Object -> Restler_fuzzable_object ({ defaultValue = v ; isQuoted = false ; exampleValue = exv ; trackedParameterName = parameterName }, dynamicObject)
+ | Int -> Restler_fuzzable_int ({ defaultValue = v ; isQuoted = false; exampleValue = exv ; trackedParameterName = parameterName }, dynamicObject)
+
+ | Number -> Restler_fuzzable_number ({ defaultValue = v ; isQuoted = false ; exampleValue = exv ; trackedParameterName = parameterName }, dynamicObject)
| Uuid ->
- Restler_fuzzable_uuid4 { defaultValue = v ; isQuoted = isQuoted ; exampleValue = exv ; trackedParameterName = parameterName }
+ Restler_fuzzable_uuid4 ({ defaultValue = v ; isQuoted = isQuoted ; exampleValue = exv ; trackedParameterName = parameterName }, dynamicObject)
| PrimitiveType.Enum (enumPropertyName, _, enumeration, defaultValue) ->
let defaultStr =
match defaultValue with
@@ -97,18 +107,19 @@ let rec getRestlerPythonPayload (payload:FuzzingPayload) (isQuoted:bool) : Reque
(enumeration |> List.map (fun s -> sprintf "'%s'" s) |> String.concat ",")
defaultStr
)
- Restler_fuzzable_group
- { defaultValue = groupValue ; isQuoted = isQuoted ; exampleValue = exv ; trackedParameterName = parameterName }
+ Restler_fuzzable_group (
+ { defaultValue = groupValue ; isQuoted = isQuoted ; exampleValue = exv ; trackedParameterName = parameterName }, dynamicObject)
| Custom c ->
+ let dynamicObject = if c.dynamicObject.IsSome then Some (DynamicObjectWriter c.dynamicObject.Value.variableName) else None
match c.payloadType with
| CustomPayloadType.String ->
- Restler_custom_payload { defaultValue = c.payloadValue ; isQuoted = isQuoted ; exampleValue = None ; trackedParameterName = None }
+ Restler_custom_payload ({ defaultValue = c.payloadValue ; isQuoted = isQuoted ; exampleValue = None ; trackedParameterName = None }, dynamicObject)
| CustomPayloadType.UuidSuffix ->
- Restler_custom_payload_uuid4_suffix (c.payloadValue, if c.dynamicObject.IsSome then Some c.dynamicObject.Value.variableName else None)
+ Restler_custom_payload_uuid4_suffix (c.payloadValue, dynamicObject)
| CustomPayloadType.Header ->
- Restler_custom_payload_header c.payloadValue // TODO: need test
+ Restler_custom_payload_header (c.payloadValue, dynamicObject)
| CustomPayloadType.Query ->
- Restler_custom_payload_query c.payloadValue // TODO: need test
+ Restler_custom_payload_query (c.payloadValue, dynamicObject)
| DynamicObject dv ->
Restler_static_string_variable (sprintf "%s.reader()" dv.variableName, isQuoted)
| PayloadParts p ->
@@ -350,10 +361,10 @@ let generatePythonParameter includeOptionalParameters parameterKind (requestPara
| FuzzingPayload.Constant (primitiveType, v) ->
isPrimitiveTypeQuoted primitiveType (isNull v),
false, false
- | FuzzingPayload.Fuzzable (primitiveType, _, _,_) ->
+ | FuzzingPayload.Fuzzable fp ->
// Note: this is a current RESTler limitation -
// fuzzable values may not be set to null without changing the grammar.
- isPrimitiveTypeQuoted primitiveType false,
+ isPrimitiveTypeQuoted fp.primitiveType false,
true, false
| FuzzingPayload.DynamicObject dv ->
isPrimitiveTypeQuoted dv.primitiveType false,
@@ -1065,6 +1076,12 @@ let getRequests(requests:Request list) includeOptionalParameters =
let quotedStr = sprintf "%s%s%s" exDelim exStr exDelim
sprintf ", param_name=%s" quotedStr
+ let formatDynamicObjectVariable (dynamicObject:DynamicObjectWriter option) =
+ match dynamicObject with
+ | None -> ""
+ | Some (DynamicObjectWriter v) ->
+ sprintf ", writer=%s.writer()" v
+
let str =
match p with
| Restler_static_string_jtoken_delim s ->
@@ -1088,7 +1105,7 @@ let getRequests(requests:Request list) includeOptionalParameters =
sprintf "primitives.restler_static_string(%s, quoted=%s)"
s
(if isQuoted then "True" else "False")
- | Restler_fuzzable_string s ->
+ | Restler_fuzzable_string (s, dynamicObject) ->
if String.IsNullOrEmpty s.defaultValue then
printfn "ERROR: fuzzable strings should not be empty. Skipping."
""
@@ -1103,39 +1120,44 @@ let getRequests(requests:Request list) includeOptionalParameters =
(if s.isQuoted then "True" else "False")
exampleParameter
trackedParamName
- | Restler_fuzzable_group s ->
+ | Restler_fuzzable_group (s, dynamicObject) ->
sprintf "primitives.restler_fuzzable_group(%s,quoted=%s%s)"
s.defaultValue
(if s.isQuoted then "True" else "False")
(getExamplePrimitiveParameter s.exampleValue)
- | Restler_fuzzable_int s ->
- sprintf "primitives.restler_fuzzable_int(\"%s\"%s%s)"
+ | Restler_fuzzable_int (s, dynamicObject) ->
+ sprintf "primitives.restler_fuzzable_int(\"%s\"%s%s%s)"
s.defaultValue
(getExamplePrimitiveParameter s.exampleValue)
(getTrackedParamPrimitiveParameter s.trackedParameterName)
- | Restler_fuzzable_number s ->
- sprintf "primitives.restler_fuzzable_number(\"%s\"%s%s)"
+ (formatDynamicObjectVariable dynamicObject)
+ | Restler_fuzzable_number (s, dynamicObject) ->
+ sprintf "primitives.restler_fuzzable_number(\"%s\"%s%s%s)"
s.defaultValue
(getExamplePrimitiveParameter s.exampleValue)
(getTrackedParamPrimitiveParameter s.trackedParameterName)
- | Restler_fuzzable_bool s ->
- sprintf "primitives.restler_fuzzable_bool(\"%s\"%s%s)"
+ (formatDynamicObjectVariable dynamicObject)
+ | Restler_fuzzable_bool (s, dynamicObject) ->
+ sprintf "primitives.restler_fuzzable_bool(\"%s\"%s%s%s)"
s.defaultValue
(getExamplePrimitiveParameter s.exampleValue)
(getTrackedParamPrimitiveParameter s.trackedParameterName)
- | Restler_fuzzable_datetime s ->
- sprintf "primitives.restler_fuzzable_datetime(\"%s\", quoted=%s%s%s)"
+ (formatDynamicObjectVariable dynamicObject)
+ | Restler_fuzzable_datetime (s, dynamicObject) ->
+ sprintf "primitives.restler_fuzzable_datetime(\"%s\", quoted=%s%s%s%s)"
s.defaultValue
(if s.isQuoted then "True" else "False")
(getExamplePrimitiveParameter s.exampleValue)
(getTrackedParamPrimitiveParameter s.trackedParameterName)
- | Restler_fuzzable_date s ->
- sprintf "primitives.restler_fuzzable_date(\"%s\", quoted=%s%s%s)"
+ (formatDynamicObjectVariable dynamicObject)
+ | Restler_fuzzable_date (s, dynamicObject) ->
+ sprintf "primitives.restler_fuzzable_date(\"%s\", quoted=%s%s%s%s)"
s.defaultValue
(if s.isQuoted then "True" else "False")
(getExamplePrimitiveParameter s.exampleValue)
(getTrackedParamPrimitiveParameter s.trackedParameterName)
- | Restler_fuzzable_object s ->
+ (formatDynamicObjectVariable dynamicObject)
+ | Restler_fuzzable_object (s, dynamicObject) ->
if String.IsNullOrEmpty s.defaultValue then
printfn "ERROR: fuzzable objects should not be empty. Skipping."
""
@@ -1144,31 +1166,35 @@ let getRequests(requests:Request list) includeOptionalParameters =
let quotedDefaultString =
sprintf "%s%s%s" delim str delim
let exampleParameter = getExamplePrimitiveParameter s.exampleValue
- sprintf "primitives.restler_fuzzable_object(%s%s%s)"
+ sprintf "primitives.restler_fuzzable_object(%s%s%s%s)"
quotedDefaultString
exampleParameter
(getTrackedParamPrimitiveParameter s.trackedParameterName)
- | Restler_fuzzable_uuid4 s ->
- sprintf "primitives.restler_fuzzable_uuid4(\"%s\", quoted=%s%s%s)"
+ (formatDynamicObjectVariable dynamicObject)
+ | Restler_fuzzable_uuid4 (s, dynamicObject) ->
+ sprintf "primitives.restler_fuzzable_uuid4(\"%s\", quoted=%s%s%s%s)"
s.defaultValue
(if s.isQuoted then "True" else "False")
(getExamplePrimitiveParameter s.exampleValue)
(getTrackedParamPrimitiveParameter s.trackedParameterName)
- | Restler_custom_payload p ->
- sprintf "primitives.restler_custom_payload(\"%s\", quoted=%s)"
+ (formatDynamicObjectVariable dynamicObject)
+ | Restler_custom_payload (p, dynamicObject) ->
+ sprintf "primitives.restler_custom_payload(\"%s\", quoted=%s%s)"
p.defaultValue
(if p.isQuoted then "True" else "False")
- | Restler_custom_payload_uuid4_suffix (p, variableName) ->
- let variableNamePart =
- match variableName with
- | None -> ""
- | Some vn ->
- sprintf ", writer=%s.writer()" vn
- sprintf "primitives.restler_custom_payload_uuid4_suffix(\"%s\"%s)" p variableNamePart
- | Restler_custom_payload_header p ->
- sprintf "primitives.restler_custom_payload_header(\"%s\")" p
- | Restler_custom_payload_query q ->
- sprintf "primitives.restler_custom_payload_query(\"%s\")" q
+ (formatDynamicObjectVariable dynamicObject)
+ | Restler_custom_payload_uuid4_suffix (p, dynamicObject) ->
+ sprintf "primitives.restler_custom_payload_uuid4_suffix(\"%s\"%s)"
+ p
+ (formatDynamicObjectVariable dynamicObject)
+ | Restler_custom_payload_header (p, dynamicObject) ->
+ sprintf "primitives.restler_custom_payload_header(\"%s\"%s)"
+ p
+ (formatDynamicObjectVariable dynamicObject)
+ | Restler_custom_payload_query (q, dynamicObject) ->
+ sprintf "primitives.restler_custom_payload_query(\"%s\"%s)"
+ q
+ (formatDynamicObjectVariable dynamicObject)
| Restler_refreshable_authentication_token tok ->
sprintf "primitives.restler_refreshable_authentication_token(\"%s\")" tok
| Restler_basepath bp ->
diff --git a/src/compiler/Restler.Compiler/Compiler.fs b/src/compiler/Restler.Compiler/Compiler.fs
index c9bf8dc..badd86b 100644
--- a/src/compiler/Restler.Compiler/Compiler.fs
+++ b/src/compiler/Restler.Compiler/Compiler.fs
@@ -52,7 +52,7 @@ type UserSpecifiedRequestConfig =
let getWriterVariable (producer:Producer) (kind:DynamicObjectVariableKind) =
match producer with
- | InputParameter (iop, _) ->
+ | InputParameter (iop, _, _) ->
{
requestId = iop.id.RequestId
accessPathParts = iop.getInputParameterAccessPath()
@@ -133,7 +133,7 @@ let getResponseParsers (dependencies:seq) (orderingC
match ro.id.ResourceReference with
| HeaderResource _ -> Some DynamicObjectVariableKind.Header
| _ -> Some DynamicObjectVariableKind.BodyResponseProperty
- | InputParameter (_, _) ->
+ | InputParameter (_, _,_) ->
Some DynamicObjectVariableKind.InputParameter
| _ -> None
match writerVariableKind with
@@ -496,11 +496,16 @@ module private Parameters =
match parameterPayload with
| LeafNode leafProperty ->
let leafNodePayload =
+
match leafProperty.payload with
- | Fuzzable (Enum(propertyName, propertyType, values, defaultValue), x, y, z) ->
- Fuzzable (Enum(p.Name, propertyType, values, defaultValue), x, y, z)
- | Fuzzable (a, b, c, _) ->
- Fuzzable (a, b, c, if trackParameters then Some p.Name else None)
+ | Fuzzable fp ->
+ match fp.primitiveType with
+ | Enum(propertyName, propertyType, values, defaultValue) ->
+ let primitiveType = PrimitiveType.Enum(p.Name, propertyType, values, defaultValue)
+ Fuzzable { fp with primitiveType = primitiveType }
+ | _ ->
+ Fuzzable {fp with
+ parameterName = if trackParameters then Some p.Name else None }
| _ -> leafProperty.payload
LeafNode { leafProperty with payload = leafNodePayload }
| InternalNode (internalNode, children) ->
diff --git a/src/compiler/Restler.Compiler/Dependencies.fs b/src/compiler/Restler.Compiler/Dependencies.fs
index 446fc93..a3f9097 100644
--- a/src/compiler/Restler.Compiler/Dependencies.fs
+++ b/src/compiler/Restler.Compiler/Dependencies.fs
@@ -463,27 +463,16 @@ let findProducerWithResourceName
else
Seq.empty
- // If the consumer is exactly the same as the producer, then check if
- // a custom payload is defined in the dictionary. If so, return the empty seq
- // (the custom payload will be picked up later), and if not create a custom UUID suffix
let dictionary, inputOnlyProducer =
match inputOnlyProducers |> Seq.tryHead with
| None -> dictionary, None
| Some iop ->
if annotationProducerRequestId = consumer.id.RequestId then
- // This is when the value is written
- if dictionary.restler_custom_payload.Value.ContainsKey(consumerResourceName) then
- dictionary, None // TODO: support custom payloads
- else
- let dictionary, suffixProducer = addUuidSuffixEntryForConsumer consumerResourceName dictionary consumer.id
- match suffixProducer with
- | Some (DictionaryPayload dp) ->
- dictionary, Some (InputParameter(iop, Some dp))
- | _ ->
- raise (invalidOp("a suffix entry must be a dictionary payload"))
+ // This is the writer. This will be handled later.
+ dictionary, None
else
// Read the value - no associated dictionary payload
- dictionary, Some (InputParameter(iop, None))
+ dictionary, Some (InputParameter(iop, None, false))
let annotationProducer =
[ responseProducers
@@ -523,6 +512,56 @@ let findProducerWithResourceName
]
|> Seq.concat
+
+ // Check if this consumer is a writer for an input-only producer.
+ // If yes, this information must be added to the dictionary matches (if any).
+ let dictionary, inputProducerMatches =
+ match annotationProducer with
+ | Some _ -> dictionary, Seq.empty
+ | None ->
+ // Find the input-only producer corresponding to this consumer
+ let matchingInputOnlyProducers =
+ producers.getInputOnlyProducers(consumer.id.ResourceName)
+ let inputOnlyProducers =
+ matchingInputOnlyProducers
+ |> Seq.filter (fun p ->
+ p.id.RequestId = consumer.id.RequestId &&
+ p.id.ResourceReference = consumer.id.ResourceReference)
+
+ match inputOnlyProducers |> Seq.tryHead with
+ | Some iop ->
+ match [ dictionaryMatches ; uuidSuffixDictionaryMatches ] |> Seq.concat |> Seq.tryHead with
+ | Some p ->
+ // Add the dictionary payload
+ let dictionaryPayload =
+ match p with
+ | DictionaryPayload dp -> dp
+ | _ -> raise (invalidArg "dictionaryMatches" "A dictionary payload was expected.")
+ dictionary, InputParameter(iop, Some dictionaryPayload, true) |> stn
+ | None ->
+ // By default, create a custom_payload_uuid_suffix, so a different ID will be
+ // generated each time the value is written.
+ // TODO: This logic will only work for string types (e.g. names). It does not currently work for other types that
+ // need a unique value assigned (e.g. GUIDs and integers).
+ // Once a type is introduced for unique GUIDs and integers, this logic should be modified to create an
+ // appropriate dictionary entry that will direct RESTler to automatically generate unique values.
+ //
+ // If a dictionary payload is not returned below, a fuzzable payload will be generated as usual, and the
+ // fuzzed value will be asssigned to the producer (writer variable).
+ match consumer.id.PrimitiveType with
+ | PrimitiveType.String ->
+ let dictionary, suffixProducer =
+ addUuidSuffixEntryForConsumer consumerResourceName dictionary consumer.id
+ match suffixProducer with
+ | Some (DictionaryPayload dp) ->
+ dictionary, InputParameter(iop, Some dp, true) |> stn
+ | _ ->
+ raise (invalidOp("a suffix entry must be a dictionary payload"))
+ | _ ->
+ dictionary, InputParameter(iop, None, true) |> stn
+ | None ->
+ dictionary, Seq.empty
+
// Here the producers just match on the resource name. If the container also matches,
// it will match fully.
let inferredExactMatches = matchingResourceProducersByEndpoint
@@ -618,6 +657,7 @@ let findProducerWithResourceName
else
dictionary,
[ if annotationProducer.IsSome then stn annotationProducer.Value else Seq.empty
+ inputProducerMatches
dictionaryMatches
uuidSuffixDictionaryMatches
inferredExactMatches
@@ -783,7 +823,7 @@ let findAnnotation globalAnnotations
let getPayloadPrimitiveType (payload:FuzzingPayload) =
match payload with
| Constant (t,_) -> t
- | Fuzzable (t,_,_,_) -> t
+ | Fuzzable fp -> fp.primitiveType
| Custom cp -> cp.primitiveType
| DynamicObject d -> d.primitiveType
| PayloadParts _ ->
@@ -884,7 +924,7 @@ let getParameterDependencies parameterKind globalAnnotations
let resourceAccessPath = PropertyAccessPaths.getLeafAccessPath parentAccessPath p
let primitiveType =
match p.payload with
- | FuzzingPayload.Fuzzable (pt, _, _,_) -> Some pt
+ | FuzzingPayload.Fuzzable fp -> Some fp.primitiveType
| FuzzingPayload.Constant (pt, _) -> Some pt
| FuzzingPayload.Custom c -> Some c.primitiveType
| _ -> None
@@ -958,47 +998,52 @@ let createHeaderResponseProducer (requestId:RequestId) (headerParameterName:stri
let createInputOnlyProducerFromAnnotation (a:ProducerConsumerAnnotation)
(pathConsumers:(RequestId * seq) [])
(queryConsumers:(RequestId * seq) [])
- (bodyConsumers:(RequestId * seq) []) =
+ (bodyConsumers:(RequestId * seq) [])
+ (headerConsumers:(RequestId * seq) [])=
+
+ let findConsumer (candidateConsumers:(RequestId * seq) []) resourceName =
+ let c = candidateConsumers
+ |> Array.tryFind (fun (reqId, _) -> reqId = a.producerId)
+
+ match c with
+ | None ->
+ // The consumer for the parameter was not found.
+ None
+ | Some (req, consumers) ->
+ consumers
+ |> Seq.tryFind (fun c -> c.id.ResourceName = resourceName)
+
// Find the producer in the list of consumers
match a.producerParameter.Value with
| ResourceName rn ->
- if a.producerId.endpoint.Contains(sprintf "{%s}" rn) then
- // Look for the corresponding path producer.
- // This is needed to determine its type.
- let pc = pathConsumers
- |> Array.tryFind (fun (reqId, s) -> reqId = a.producerId)
+ let parameterKind, consumer =
+ if a.producerId.endpoint.Contains(sprintf "{%s}" rn) then
+ let pathConsumer = findConsumer pathConsumers rn
+ ParameterKind.Path, pathConsumer
+ else
+ // Check if this parameter name exists in the query or header.
+ let queryConsumer = findConsumer queryConsumers rn
+ if queryConsumer.IsSome then
+ ParameterKind.Query, queryConsumer
+ else
+ ParameterKind.Header, findConsumer headerConsumers rn
- let pathConsumer =
- match pc with
- | None ->
- // The consumer for the path parameter was not found.
- None
- | Some (req, consumers) ->
- consumers
- |> Seq.tryFind (fun c -> c.id.ResourceName = rn)
+ match consumer with
+ | None -> None
+ | Some c ->
+ // 'c' is the consumer that is the input producer.
+ // Its value will be written to a writer variable.
+ let resource = ApiResource(c.id.RequestId,
+ c.id.ResourceReference,
+ c.id.NamingConvention,
+ c.id.PrimitiveType)
+ let inputOnlyProducer =
+ {
+ InputOnlyProducer.id = resource
+ parameterKind = parameterKind
+ }
+ Some (rn, inputOnlyProducer)
- match pathConsumer with
- | None ->
- // The consumer for the path parameter was not found.
- None
- | Some c ->
- let resource = ApiResource(c.id.RequestId,
- c.id.ResourceReference,
- c.id.NamingConvention,
- c.id.PrimitiveType)
- let inputOnlyProducer =
- {
- InputOnlyProducer.id = resource
- parameterKind = ParameterKind.Path
- }
- Some (rn, inputOnlyProducer)
- else
- // TODO: add query and header support
- // Note: here, it is ambiguous whether this resource refers to a
- // query or body resource without specifying the path. For a producer
- // id, it should be required to specify a path to a body parameter.
- printfn "Warning: Query parameter input producers are not currently supported (parameter %s not found in path)." rn
- None
| ResourcePath accessPath ->
let bc = bodyConsumers
|> Array.tryFind (fun (reqId, s) -> reqId = a.producerId)
@@ -1026,6 +1071,9 @@ let createInputOnlyProducerFromAnnotation (a:ProducerConsumerAnnotation)
InputOnlyProducer.id = resource
parameterKind = ParameterKind.Body
}
+
+ // The producer needs to be found both for the consumer parameter (reader)
+ // and for the producer parameter itself (writer)
Some (c.id.ResourceName, inputOnlyProducer)
@@ -1216,10 +1264,11 @@ let extractDependencies (requestData:(RequestId*RequestData)[])
rd.localAnnotations
|> Seq.iter (fun a ->
if a.producerParameter.IsSome then
- let ip = createInputOnlyProducerFromAnnotation a pathConsumers queryConsumers bodyConsumers
+ let ip = createInputOnlyProducerFromAnnotation a pathConsumers queryConsumers bodyConsumers headerConsumers
if ip.IsSome then
let resourceName, producer = ip.Value
producers.addInputOnlyProducer(resourceName, producer)
+
)
)
@@ -1229,7 +1278,7 @@ let extractDependencies (requestData:(RequestId*RequestData)[])
globalAnnotations
|> Seq.iter (fun a ->
if a.producerParameter.IsSome then
- let ip = createInputOnlyProducerFromAnnotation a pathConsumers queryConsumers bodyConsumers
+ let ip = createInputOnlyProducerFromAnnotation a pathConsumers queryConsumers bodyConsumers headerConsumers
if ip.IsSome then
let resourceName, producer = ip.Value
producers.addInputOnlyProducer(resourceName, producer)
@@ -1485,12 +1534,12 @@ module DependencyLookup =
// Mark the type of the dynamic object to be the type of the input parameter if available
let primitiveType =
match defaultPayload with
- | FuzzingPayload.Fuzzable (primitiveType, _, _, _) -> primitiveType
+ | FuzzingPayload.Fuzzable fp -> fp.primitiveType
| _ ->
printfn "Warning: primitive type not available for %A [resource: %s]" requestId consumerResourceName
responseProducer.id.PrimitiveType
DynamicObject { primitiveType = primitiveType; variableName = variableName; isWriter = false }
- | Some (InputParameter (inputParameterProducer, dictionaryPayload)) ->
+ | Some (InputParameter (inputParameterProducer, dictionaryPayload, isWriter)) ->
let accessPath = inputParameterProducer.getInputParameterAccessPath()
let variableName = DynamicObjectNaming.generateDynamicObjectVariableName
@@ -1499,14 +1548,24 @@ module DependencyLookup =
// Mark the type of the dynamic object to be the type of the input parameter if available
let primitiveType =
match defaultPayload with
- | FuzzingPayload.Fuzzable (primitiveType, _, _, _) -> primitiveType
+ | FuzzingPayload.Fuzzable fp -> fp.primitiveType
| _ ->
printfn "Warning: primitive type not available for %A [resource: %s]" requestId consumerResourceName
inputParameterProducer.id.PrimitiveType
let dynamicObject =
{ primitiveType = primitiveType; variableName = variableName; isWriter = false }
match dictionaryPayload with
- | None -> DynamicObject dynamicObject
+ | None ->
+ if isWriter then
+ match defaultPayload with
+ | Fuzzable fp ->
+ Fuzzable { fp with dynamicObject = Some dynamicObject }
+ | Custom cp ->
+ Custom { cp with dynamicObject = Some dynamicObject }
+ | _ ->
+ failwith "Input producers are not supported for this payload type."
+ else
+ DynamicObject dynamicObject
| Some dp ->
Custom { payloadType = dp.payloadType
primitiveType = dp.primitiveType
@@ -1596,7 +1655,11 @@ module DependencyLookup =
else
let defaultPayload =
match p.payload with
- | None -> Fuzzable (PrimitiveType.String, "", None, None)
+ | None -> Fuzzable { primitiveType = PrimitiveType.String
+ defaultValue = ""
+ exampleValue = None
+ parameterName = None
+ dynamicObject = None }
| Some p -> p
let propertyAccessPath =
{ path = PropertyAccessPaths.getInnerAccessPath resourceAccessPath p
@@ -1620,14 +1683,24 @@ module DependencyLookup =
| Tree.LeafNode leafProperty ->
leafProperty.payload, leafProperty.isRequired, leafProperty.isReadOnly
| Tree.InternalNode (i, _) ->
+ let defaultFuzzablePayload =
+ { primitiveType = PrimitiveType.String
+ defaultValue = ""
+ exampleValue = None
+ parameterName = None
+ dynamicObject = None }
let payload =
match i.propertyType with
| NestedType.Object ->
- (Fuzzable (PrimitiveType.Object, "{}", None, None))
+ Fuzzable {defaultFuzzablePayload with
+ primitiveType = PrimitiveType.Object
+ defaultValue = "{}" }
| NestedType.Array ->
- (Fuzzable (PrimitiveType.Object, "[]", None, None))
+ Fuzzable {defaultFuzzablePayload with
+ primitiveType = PrimitiveType.Object
+ defaultValue = "[]" }
| NestedType.Property ->
- (Fuzzable (PrimitiveType.String, "", None, None))
+ Fuzzable defaultFuzzablePayload
payload, i.isRequired, i.isReadOnly
let dependencyPayload = getConsumerPayload dependencies pathPayload requestId parameterName EmptyAccessPath defaultPayload
@@ -1728,7 +1801,7 @@ let writeDependencies dependenciesFilePath dependencies (unresolvedOnly:bool) =
rp.id.RequestId.endpoint,
getMethod rp.id,
getParameter rp.id
- | Some (InputParameter (ip, dp)) -> // TODO: what to serialize?
+ | Some (InputParameter (ip, dp, _)) -> // TODO: what to serialize?
ip.id.RequestId.endpoint,
getMethod ip.id,
getParameter ip.id
diff --git a/src/compiler/Restler.Compiler/Grammar.fs b/src/compiler/Restler.Compiler/Grammar.fs
index 962f993..6773ae0 100644
--- a/src/compiler/Restler.Compiler/Grammar.fs
+++ b/src/compiler/Restler.Compiler/Grammar.fs
@@ -166,6 +166,26 @@ type CustomPayload =
dynamicObject: DynamicObject option
}
+type FuzzablePayload =
+ {
+ /// The primitive type of the payload, as declared in the specification
+ primitiveType : PrimitiveType
+
+ /// The default value of the payload
+ defaultValue : string
+
+ /// The example value specified in the spec, if any
+ exampleValue : string option
+
+ /// The parameter name, if available.
+ parameterName : string option
+
+ /// The associated dynamic object, whose value should be
+ /// assigned to the value generated from this payload.
+ /// For example, an input value from a request body property.
+ dynamicObject: DynamicObject option
+ }
+
/// The payload for a property specified in as a request parameter
type FuzzingPayload =
/// Example: (Int "1")
@@ -173,7 +193,7 @@ type FuzzingPayload =
/// (data type, default value, example value, parameter name)
/// Example: (Int "1", "2")
- | Fuzzable of PrimitiveType * string * string option * string option
+ | Fuzzable of FuzzablePayload
/// The custom payload, as specified in the fuzzing dictionary
| Custom of CustomPayload
diff --git a/src/compiler/Restler.Compiler/SwaggerVisitors.fs b/src/compiler/Restler.Compiler/SwaggerVisitors.fs
index c93f316..4e0f495 100644
--- a/src/compiler/Restler.Compiler/SwaggerVisitors.fs
+++ b/src/compiler/Restler.Compiler/SwaggerVisitors.fs
@@ -119,7 +119,16 @@ module SchemaUtilities =
let getFuzzableValueForObjectType (objectType:NJsonSchema.JsonObjectType) (format:string) (exampleValue: string option) (propertyName: string option)
(trackParameters:bool) =
- Fuzzable (getGrammarPrimitiveTypeWithDefaultValue objectType format exampleValue propertyName trackParameters)
+ let primitiveType, defaultValue, exampleValue, propertyName =
+ getGrammarPrimitiveTypeWithDefaultValue objectType format exampleValue propertyName trackParameters
+ Fuzzable
+ {
+ primitiveType = primitiveType
+ defaultValue = defaultValue
+ exampleValue = exampleValue
+ parameterName = propertyName
+ dynamicObject = None
+ }
/// Get a boolean property from 'ExtensionData', if it exists.
let getExtensionDataBooleanPropertyValue (extensionData:System.Collections.Generic.IDictionary) (extensionDataKeyName:string) =
@@ -224,7 +233,14 @@ module SwaggerVisitors =
match enumValues with
| [] -> "null"
| h::rest -> h
- Fuzzable (PrimitiveType.Enum (propertyName, grammarPrimitiveType, enumValues, defaultValue), defaultFuzzableEnumValue, exv, None)
+ Fuzzable
+ {
+ primitiveType = PrimitiveType.Enum (propertyName, grammarPrimitiveType, enumValues, defaultValue)
+ defaultValue = defaultFuzzableEnumValue
+ exampleValue = exv
+ parameterName = None
+ dynamicObject = None
+ }
| NJsonSchema.JsonObjectType.Object
| NJsonSchema.JsonObjectType.None ->
// Example of JsonObjectType.None: "content": {} without a type specified in Swagger.
@@ -232,7 +248,14 @@ module SwaggerVisitors =
getFuzzableValueForObjectType NJsonSchema.JsonObjectType.Object propertySchema.Format exampleValue (Some propertyName) trackParameters
| NJsonSchema.JsonObjectType.File ->
// Fuzz it as a string.
- Fuzzable (PrimitiveType.String, "file object", None, if trackParameters then Some propertyName else None)
+ Fuzzable
+ {
+ primitiveType = PrimitiveType.String
+ defaultValue = "file object"
+ exampleValue = None
+ parameterName = if trackParameters then Some propertyName else None
+ dynamicObject = None
+ }
| nst ->
raise (UnsupportedType (sprintf "Unsupported type formatting: %A" nst))
{ LeafProperty.name = propertyName; payload = payload ;isRequired = isRequired ; isReadOnly = isReadOnly }
@@ -255,8 +278,8 @@ module SwaggerVisitors =
| LeafNode leafProperty ->
let payload = leafProperty.payload
match payload with
- | Fuzzable(a, b, c, _) ->
- let payload = Fuzzable (a, b, c, Some paramName)
+ | Fuzzable fp ->
+ let payload = Fuzzable { fp with parameterName = Some paramName }
LeafNode { leafProperty with LeafProperty.payload = payload }
| x -> tree
| InternalNode (_,_) -> tree
@@ -383,15 +406,15 @@ module SwaggerVisitors =
| Some v ->
let examplePropertyPayload =
match fuzzablePropertyPayload.payload with
- | Fuzzable (primitiveType, defaultValue, _, propertyName) ->
- let payloadValue = GenerateGrammarElements.formatJTokenProperty primitiveType v
+ | Fuzzable fp ->
+ let payloadValue = GenerateGrammarElements.formatJTokenProperty fp.primitiveType v
// Replace the default payload with the example payload, preserving type information.
// 'generateFuzzablePayload' is specified a schema example is found for the parent
// object (e.g. an array).
if generateFuzzablePayload then
- Fuzzable (primitiveType, defaultValue, Some payloadValue, propertyName)
+ Fuzzable { fp with exampleValue = Some payloadValue }
else
- Constant (primitiveType, payloadValue)
+ Constant (fp.primitiveType, payloadValue)
| _ -> raise (invalidOp(sprintf "invalid payload %A, expected fuzzable" fuzzablePropertyPayload))
{ fuzzablePropertyPayload with payload = examplePropertyPayload }
@@ -507,7 +530,14 @@ module SwaggerVisitors =
match s.Type with
| JsonObjectType.Object ->
// Do not insert a default fuzzable property for objects
- Fuzzable (PrimitiveType.Object, "{ }", None, None)
+ Fuzzable
+ {
+ primitiveType = PrimitiveType.Object
+ defaultValue = "{ }"
+ exampleValue = None
+ parameterName = None
+ dynamicObject = None
+ }
| _ ->
getFuzzableValueForObjectType s.Item.Type s.Format None None trackParameters
@@ -523,7 +553,15 @@ module SwaggerVisitors =
else
Some (getValueForObjectType schema.Item)
| JsonObjectType.None ->
- Some (Fuzzable (PrimitiveType.Object, "{ }", None, None))
+ let fp =
+ {
+ primitiveType = PrimitiveType.Object
+ defaultValue = "{ }"
+ exampleValue = None
+ parameterName = None
+ dynamicObject = None
+ }
+ Some (Fuzzable fp)
| _ ->
Some (getValueForObjectType schema)
@@ -733,7 +771,14 @@ module SwaggerVisitors =
let leafPayload =
let exampleValue = GenerateGrammarElements.formatJTokenProperty primitiveType v
if generateFuzzablePayloadsForExamples then
- FuzzingPayload.Fuzzable (primitiveType, defaultValue, Some exampleValue, None)
+ FuzzingPayload.Fuzzable
+ {
+ primitiveType = primitiveType
+ defaultValue = defaultValue
+ exampleValue = Some exampleValue
+ parameterName = None
+ dynamicObject = None
+ }
else
FuzzingPayload.Constant (primitiveType, exampleValue)