Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,16 @@ The call chains are structured around 4 main parts.

//Add a file as part of the request. Doing so will convert the body to a multipart/form request. Used for POST, PUT and DELETE
//eg: "file name", "file", "image/jpeg", File.ReadAllBytes("pathtofile")
.File("string", "string", "string", byte[])
.File("name", "string", "string", "string", byte[])

// Add the string content to form data as part of the request. It will use the default values of Content-Desposition.
.Form("string", "string content")

// Add the object content to form data as part of the request. Object will be converted into JSON and added to request.
.Form("string", object)

// Add the object content to form data as part of the request. Object will be converted into JSON and added to request. You can explicitly define the content type and desposition.
.Form(string name, object content, string contentDispositionName, string contentType)

//Set the host of the target server
//Useful when you want to reuse a test suite between multiple Uris
Expand Down Expand Up @@ -305,6 +314,23 @@ new RestAssured()
.Debug()
```

### Using Form
```C#
new RestAssured()
.Given()
.Name("Sending form data")
.Param("id", "some identifier")
//First parameter is variable name of content being sent in form-data.
//Second parameter is the content to be sent. This content will be converted into JSON before being attached to form-data.
//Third parameter describes the Content-Desposition
//Fourth parameter describes the Content-Type
.Form("name", content, "form-data", "multipart/form-data")
.When()
.Post("http://yourremote.com")
.Then()
.Debug()
```

### Retrieving an object
Value returned from http://yourremote.com
```Javascript
Expand Down
41 changes: 33 additions & 8 deletions src/RA/ExecutionContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ private void SetTimeout()

private HttpContent BuildContent()
{
if (_setupContext.Files().Any())
if (_setupContext.Files().Any() || _setupContext.Forms().Any())
return BuildMultipartContent();
if (_setupContext.Params().Any())
return BuildFormContent();
Expand All @@ -205,14 +205,24 @@ private HttpContent BuildMultipartContent()
_setupContext.Files().ForEach(x =>
{
var fileContent = new ByteArrayContent(x.Content);
fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue(x.ContentDispositionName)
{
Name = x.ContentDispositionName.Quote(),
Name = x.Name.Quote(),
FileName = x.FileName
};
fileContent.Headers.ContentType = new MediaTypeHeaderValue(x.ContentType);

content.Add(fileContent);
});

_setupContext.Forms().ForEach(x =>
{
var formContent = new StringContent(x.Content);
formContent.Headers.ContentDisposition = new ContentDispositionHeaderValue(x.ContentDispositionName)
{
Name = x.Name.Quote(),
};
formContent.Headers.ContentType = new MediaTypeHeaderValue(x.ContentType);
content.Add(formContent);
});

return content;
Expand Down Expand Up @@ -293,16 +303,30 @@ private async Task<HttpResponseMessageWrapper> ExecuteCall()
private ResponseContext BuildFromResponse(HttpResponseMessageWrapper result)
{
// var content = AsyncContext.Run(async () => await result.Response.Content.ReadAsStringAsync());
var content = result.Response.Content.ReadAsStringAsync().GetAwaiter().GetResult();

var content = result.Response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
var responseHeaders = GetResponseHeaders(result);
return new ResponseContext(
result.Response.StatusCode,
content,
result.Response.Content.Headers.ToDictionary(x => x.Key.Trim(), x => x.Value),
responseHeaders,
result.ElaspedExecution,
_loadReponses.ToList()
);
}

/// <summary>
/// Gets the response headers from HTTP response from API.
/// </summary>
/// <returns>The response headers.</returns>
/// <param name="result">Dictionary containing API response headers</param>
private Dictionary<string, IEnumerable<string>> GetResponseHeaders(HttpResponseMessageWrapper result)
{
var responseHeaders = result.Response.Headers.ToDictionary(x => x.Key.Trim(), x => x.Value);
foreach (var contentHeader in result.Response.Content.Headers.ToDictionary(x => x.Key.Trim(), x => x.Value))
{
responseHeaders.Add(contentHeader.Key, contentHeader.Value);
}
return responseHeaders;
}

/// <summary>
Expand All @@ -313,6 +337,8 @@ public ExecutionContext Debug()
{
var uri = BuildUri();

"method".WriteHeader();
_httpActionContext.HttpAction().ToString().WriteLine();
"host".WriteHeader();
uri.Host.WriteLine();
"absolute path".WriteHeader();
Expand All @@ -327,7 +353,6 @@ public ExecutionContext Debug()
uri.OriginalString.WriteLine();
"scheme".WriteHeader();
uri.Scheme.WriteLine();

return this;
}
}
Expand Down
4 changes: 3 additions & 1 deletion src/RA/FileContent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@ namespace RA
{
public class FileContent
{
public FileContent(string fileName, string contentDispositionName, string contentType, byte[] content)
public FileContent(string name, string fileName, string contentDispositionName, string contentType, byte[] content)
{
Name = name;
FileName = fileName;
ContentType = contentType;
ContentDispositionName = contentDispositionName;
Content = content;
}

public string Name { get; private set; }
public string FileName { get; private set; }
public string ContentType { get; private set; }
public string ContentDispositionName { get; private set; }
Expand Down
18 changes: 18 additions & 0 deletions src/RA/FormContent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
namespace RA
{
public class FormContent
{
public FormContent(string name, string content, string contentDispositionName = "form-data", string contentType = "multipart/form-data")
{
Name = name;
Content = content;
ContentType = contentType;
ContentDispositionName = contentDispositionName;
}

public string Name { get; private set; }
public string Content { get; private set; }
public string ContentType { get; private set; }
public string ContentDispositionName { get; private set; }
}
}
15 changes: 13 additions & 2 deletions src/RA/ResponseContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
using RA.Exceptions;
using RA.Extensions;
using System.Xml.Linq;

using System.Net.Http;

namespace RA
{
public class ResponseContext
Expand All @@ -34,6 +35,16 @@ public ResponseContext(HttpStatusCode statusCode, string content, Dictionary<str
_loadResponses = loadResponses ?? new List<LoadResponse>();

Initialize();
}

/// <summary>
/// Retrieve the specified headerName.
/// </summary>
/// <returns>Header.</returns>
/// <param name="headerName">Header name.</param>
public string RetrieveHeader(string headerName)
{
return HeaderValue(headerName);
}

/// <summary>
Expand Down Expand Up @@ -267,7 +278,7 @@ private void Parse()
}

if (!string.IsNullOrEmpty(_content))
throw new Exception(string.Format("({0}) not supported", contentType));
throw new Exception(string.Format("Content type ({0}) not supported", contentType));
}

private void ParseLoad()
Expand Down
81 changes: 66 additions & 15 deletions src/RA/SetupContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ public class SetupContext
private readonly Dictionary<string, string> _queryStrings = new Dictionary<string, string>();
private readonly Dictionary<string, string> _cookies = new Dictionary<string, string>();
private readonly List<FileContent> _files = new List<FileContent>();
private TimeSpan? _timeout = null;
private readonly List<FormContent> _forms = new List<FormContent>();
private TimeSpan? _timeout = null;

private Func<string, IDictionary<string, string>, List<string>> GetHeaderFor = (filter, headers) =>
{
Expand Down Expand Up @@ -177,14 +178,55 @@ public string Body()
/// Set a file to be used with a POST/PUT/DELETE action. Adding a file will convert the body
/// to a multipart/form.
/// </summary>
/// <param name="name">Name of multipart data. Generally this name is same as the name of the variable in the bean clas to find the file content to.</param>
/// <param name="fileName">Name of file</param>
/// <param name="contentDispositionName"></param>
/// <param name="contentType">eg: image/jpeg or application/octet-stream</param>
/// <param name="content">Byte array of the data. File.ReadAllBytes()</param>
/// <returns></returns>
public SetupContext File(string fileName, string contentDispositionName, string contentType, byte[] content)
public SetupContext File(string name, string fileName, string contentDispositionName, string contentType, byte[] content)
{
_files.Add(new FileContent(fileName, contentDispositionName, contentType, content));
_files.Add(new FileContent(name, fileName, contentDispositionName, contentType, content));
return this;
}

/// <summary>
/// Set form content to be used with a POST/PUT/DELETE action.
/// </summary>
/// <param name="name">Name of form content</param>
/// <param name="content">Form content</param>
/// <returns></returns>
public SetupContext Form(string name, string content)
{
_forms.Add(new FormContent(name, content));
return this;
}

/// <summary>
/// Set form content to be used with a POST/PUT/DELETE action.
/// </summary>
/// <param name="name">Name of form content</param>
/// <param name="content">Form content</param>
/// <returns></returns>
public SetupContext Form(string name, object content)
{
var contentAsString = JsonConvert.SerializeObject(content);
_forms.Add(new FormContent(name, contentAsString));
return this;
}

/// <summary>
/// Set form content to be used with a POST/PUT/DELETE action.
/// </summary>
/// <param name="name">Name of form content</param>
/// <param name="content">Form content</param>
/// <param name="contentDispositionName"></param>
/// <param name="contentType">eg: application/json - this may only be needed if you are sending json and files in mutipart-form content</param>
/// <returns></returns>
public SetupContext Form(string name, object content, string contentDispositionName, string contentType)
{
var contentAsString = JsonConvert.SerializeObject(content);
_forms.Add(new FormContent(name, contentAsString, contentDispositionName, contentType));
return this;
}

Expand All @@ -194,17 +236,26 @@ public SetupContext File(string fileName, string contentDispositionName, string
/// <returns></returns>
public List<FileContent> Files()
{
return _files.Select(x => new FileContent(x.FileName, x.ContentDispositionName, x.ContentType, x.Content)).ToList();
}

/// <summary>
/// Set a cookie value pair.
/// eg: name : X-XSRF-TOKEN and value : 123456789
/// </summary>
/// <param name="name"></param>
/// <param name="value"></param>
/// <returns></returns>
public SetupContext Cookie(string name, string value)
return _files.Select(x => new FileContent(x.Name, x.FileName, x.ContentDispositionName, x.ContentType, x.Content)).ToList();
}

/// <summary>
/// Returns all forms.
/// </summary>
/// <returns></returns>
public List<FormContent> Forms()
{
return _forms.Select(x => new FormContent(x.Name, x.Content, x.ContentDispositionName, x.ContentType)).ToList();
}

/// <summary>
/// Set a cookie value pair.
/// eg: name : X-XSRF-TOKEN and value : 123456789
/// </summary>
/// <param name="name"></param>
/// <param name="value"></param>
/// <returns></returns>
public SetupContext Cookie(string name, string value)
{
if(!_cookies.ContainsKey(name))
_cookies.Add(name, value);
Expand Down Expand Up @@ -465,7 +516,7 @@ public SetupContext Clone()

foreach (var file in _files)
{
setupContext.File(file.FileName, file.ContentDispositionName, file.ContentType, file.Content);
setupContext.File(file.Name, file.FileName, file.ContentDispositionName, file.ContentType, file.Content);
}

return setupContext;
Expand Down