Support input dependencies based on annotations for any variable type. (#450)
Before this change, only 'restler_custom_payload_uuid4_suffix' could have an associated input-only producer. Now, any parameter that can be annotated may be associated with an input-only producer. Testing: - Manual testing: modified demo_server to have a writer. - Added unit test
This commit is contained in:
Родитель
662a9e86d8
Коммит
14d4c7ad50
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -76,6 +76,9 @@
|
|||
<Content Include="swagger\dependencyTests\input_producer_spec.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="swagger\dependencyTests\input_producer_dict.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="swagger\dependencyTests\input_producer_annotations.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"restler_custom_payload": {
|
||||
"sig": [ "12345" ],
|
||||
"tag": ["important"]
|
||||
},
|
||||
"restler_custom_payload_query": {
|
||||
"hash": [ "56789" ]
|
||||
}
|
||||
}
|
|
@ -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": [
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 ->
|
||||
|
|
|
@ -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<ProducerConsumerDependency>) (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) ->
|
||||
|
|
|
@ -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<Consumer>) [])
|
||||
(queryConsumers:(RequestId * seq<Consumer>) [])
|
||||
(bodyConsumers:(RequestId * seq<Consumer>) []) =
|
||||
(bodyConsumers:(RequestId * seq<Consumer>) [])
|
||||
(headerConsumers:(RequestId * seq<Consumer>) [])=
|
||||
|
||||
let findConsumer (candidateConsumers:(RequestId * seq<Consumer>) []) 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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<string, obj>) (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)
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче