PageRenderTime 52ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/Raven.Database/Server/Controllers/DocumentsController.cs

https://github.com/nwendel/ravendb
C# | 262 lines | 234 code | 28 blank | 0 comment | 32 complexity | b10f857c6e9380ee5c306e56391983da MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, BSD-3-Clause, CC-BY-SA-3.0
  1. using System;
  2. using System.Diagnostics;
  3. using System.Linq;
  4. using System.Net;
  5. using System.Net.Http;
  6. using System.Net.Http.Headers;
  7. using System.Threading;
  8. using System.Threading.Tasks;
  9. using System.Web.Http;
  10. using Raven.Abstractions.Data;
  11. using Raven.Abstractions.Extensions;
  12. using Raven.Database.Extensions;
  13. using Raven.Database.Server.WebApi.Attributes;
  14. using Raven.Json.Linq;
  15. namespace Raven.Database.Server.Controllers
  16. {
  17. public class DocumentsController : RavenDbApiController
  18. {
  19. [HttpGet]
  20. [Route("docs")]
  21. [Route("databases/{databaseName}/docs")]
  22. public HttpResponseMessage DocsGet([FromUri] string id)
  23. {
  24. if (string.IsNullOrEmpty(id))
  25. return GetEmptyMessage(HttpStatusCode.BadRequest);
  26. return DocGet(id);
  27. }
  28. [HttpHead]
  29. [Route("docs")]
  30. [Route("databases/{databaseName}/docs")]
  31. public HttpResponseMessage DocsHead([FromUri] string id)
  32. {
  33. if (string.IsNullOrEmpty(id))
  34. return GetEmptyMessage(HttpStatusCode.BadRequest);
  35. return DocHead(id);
  36. }
  37. [HttpGet]
  38. [Route("docs")]
  39. [Route("databases/{databaseName}/docs")]
  40. public HttpResponseMessage DocsGet()
  41. {
  42. using (var cts = new CancellationTokenSource())
  43. using (cts.TimeoutAfter(DatabasesLandlord.SystemConfiguration.DatabaseOperationTimeout))
  44. {
  45. long documentsCount = 0;
  46. var lastDocEtag = Etag.Empty;
  47. Database.TransactionalStorage.Batch(
  48. accessor =>
  49. {
  50. lastDocEtag = accessor.Staleness.GetMostRecentDocumentEtag();
  51. documentsCount = accessor.Documents.GetDocumentsCount();
  52. });
  53. lastDocEtag = lastDocEtag.HashWith(BitConverter.GetBytes(documentsCount));
  54. if (MatchEtag(lastDocEtag)) return GetEmptyMessage(HttpStatusCode.NotModified);
  55. var startsWith = GetQueryStringValue("startsWith");
  56. HttpResponseMessage msg;
  57. int nextPageStart = GetNextPageStart();
  58. if (string.IsNullOrEmpty(startsWith))
  59. {
  60. var results = Database.Documents.GetDocuments(GetStart(), GetPageSize(Database.Configuration.MaxPageSize),
  61. GetEtagFromQueryString(), cts.Token);
  62. msg = GetMessageWithObject(results);
  63. }
  64. else
  65. {
  66. var transformer = GetQueryStringValue("transformer");
  67. var transformerParameters = this.ExtractTransformerParameters();
  68. msg =
  69. GetMessageWithObject(
  70. Database.Documents.GetDocumentsWithIdStartingWith(
  71. startsWith,
  72. GetQueryStringValue("matches"),
  73. GetQueryStringValue("exclude"),
  74. GetStart(),
  75. GetPageSize(Database.Configuration.MaxPageSize),
  76. cts.Token,
  77. ref nextPageStart, transformer, transformerParameters,
  78. skipAfter: GetQueryStringValue("skipAfter")));
  79. }
  80. WriteHeaders(new RavenJObject { { Constants.NextPageStart, nextPageStart } }, lastDocEtag, msg);
  81. return msg;
  82. }
  83. }
  84. [HttpPost]
  85. [Route("docs")]
  86. [Route("databases/{databaseName}/docs")]
  87. public async Task<HttpResponseMessage> DocsPost()
  88. {
  89. var json = await ReadJsonAsync();
  90. var id = Database.Documents.Put(null, Etag.Empty, json,
  91. InnerHeaders.FilterHeadersToObject(),
  92. GetRequestTransaction());
  93. return GetMessageWithObject(id);
  94. }
  95. [HttpHead]
  96. [Route("docs/{*docId}")]
  97. [Route("databases/{databaseName}/docs/{*docId}")]
  98. public HttpResponseMessage DocHead(string docId)
  99. {
  100. var msg = GetEmptyMessage();
  101. msg.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json") { CharSet = "utf-8" };
  102. var transactionInformation = GetRequestTransaction();
  103. var documentMetadata = Database.Documents.GetDocumentMetadata(docId, transactionInformation);
  104. if (documentMetadata == null)
  105. {
  106. msg.StatusCode = HttpStatusCode.NotFound;
  107. return msg;
  108. }
  109. Debug.Assert(documentMetadata.Etag != null);
  110. if (MatchEtag(documentMetadata.Etag) && documentMetadata.NonAuthoritativeInformation == false)
  111. {
  112. msg.StatusCode = HttpStatusCode.NotModified;
  113. return msg;
  114. }
  115. if (documentMetadata.NonAuthoritativeInformation != null && documentMetadata.NonAuthoritativeInformation.Value)
  116. msg.StatusCode = HttpStatusCode.NonAuthoritativeInformation;
  117. documentMetadata.Metadata[Constants.DocumentIdFieldName] = documentMetadata.Key;
  118. documentMetadata.Metadata[Constants.LastModified] = documentMetadata.LastModified; //HACK ? to get the document's last modified value into the response headers
  119. WriteHeaders(documentMetadata.Metadata, documentMetadata.Etag, msg);
  120. return msg;
  121. }
  122. [HttpGet]
  123. [Route("docs/{*docId}")]
  124. [Route("databases/{databaseName}/docs/{*docId}")]
  125. public HttpResponseMessage DocGet(string docId)
  126. {
  127. var msg = GetEmptyMessage();
  128. if (string.IsNullOrEmpty(GetHeader("If-None-Match")))
  129. return GetDocumentDirectly(docId, msg);
  130. Database.TransactionalStorage.Batch(
  131. _ => // we are running this here to ensure transactional safety for the two operations
  132. {
  133. var transactionInformation = GetRequestTransaction();
  134. var documentMetadata = Database.Documents.GetDocumentMetadata(docId, transactionInformation);
  135. if (documentMetadata == null)
  136. {
  137. msg = GetEmptyMessage(HttpStatusCode.NotFound);
  138. return;
  139. }
  140. Debug.Assert(documentMetadata.Etag != null);
  141. if (MatchEtag(documentMetadata.Etag) && documentMetadata.NonAuthoritativeInformation != true)
  142. {
  143. msg.StatusCode = HttpStatusCode.NotModified;
  144. return;
  145. }
  146. if (documentMetadata.NonAuthoritativeInformation != null && documentMetadata.NonAuthoritativeInformation.Value)
  147. msg.StatusCode = HttpStatusCode.NonAuthoritativeInformation;
  148. msg = GetDocumentDirectly(docId, msg);
  149. });
  150. return msg;
  151. }
  152. [HttpDelete]
  153. [Route("docs/{*docId}")]
  154. [Route("databases/{databaseName}/docs/{*docId}")]
  155. public HttpResponseMessage DocDelete(string docId)
  156. {
  157. Database.Documents.Delete(docId, GetEtag(), GetRequestTransaction());
  158. return GetEmptyMessage(HttpStatusCode.NoContent);
  159. }
  160. [HttpPut]
  161. [Route("docs/{*docId}")]
  162. [Route("databases/{databaseName}/docs/{*docId}")]
  163. public async Task<HttpResponseMessage> DocPut(string docId)
  164. {
  165. var json = await ReadJsonAsync();
  166. var putResult = Database.Documents.Put(docId, GetEtag(), json, InnerHeaders.FilterHeadersToObject(), GetRequestTransaction());
  167. return GetMessageWithObject(putResult, HttpStatusCode.Created);
  168. }
  169. [HttpPatch]
  170. [Route("docs/{*docId}")]
  171. [Route("databases/{databaseName}/docs/{*docId}")]
  172. public async Task<HttpResponseMessage> DocPatch(string docId)
  173. {
  174. var patchRequestJson = await ReadJsonArrayAsync();
  175. var patchRequests = patchRequestJson.Cast<RavenJObject>().Select(PatchRequest.FromJson).ToArray();
  176. var patchResult = Database.Patches.ApplyPatch(docId, GetEtag(), patchRequests, GetRequestTransaction());
  177. return ProcessPatchResult(docId, patchResult.PatchResult, null, null);
  178. }
  179. [HttpEval]
  180. [Route("docs/{*docId}")]
  181. [Route("databases/{databaseName}/docs/{*docId}")]
  182. public async Task<HttpResponseMessage> DocEval(string docId)
  183. {
  184. var advPatchRequestJson = await ReadJsonObjectAsync<RavenJObject>();
  185. var advPatch = ScriptedPatchRequest.FromJson(advPatchRequestJson);
  186. bool testOnly;
  187. bool.TryParse(GetQueryStringValue("test"), out testOnly);
  188. var advPatchResult = Database.Patches.ApplyPatch(docId, GetEtag(), advPatch, GetRequestTransaction(), testOnly);
  189. return ProcessPatchResult(docId, advPatchResult.Item1.PatchResult, advPatchResult.Item2, advPatchResult.Item1.Document);
  190. }
  191. private HttpResponseMessage GetDocumentDirectly(string docId, HttpResponseMessage msg)
  192. {
  193. if (Database == null)
  194. {
  195. msg.StatusCode = HttpStatusCode.NotFound;
  196. return msg;
  197. }
  198. var doc = Database.Documents.Get(docId, GetRequestTransaction());
  199. if (doc == null)
  200. {
  201. msg.StatusCode = HttpStatusCode.NotFound;
  202. return msg;
  203. }
  204. if (doc.NonAuthoritativeInformation != null && doc.NonAuthoritativeInformation.Value)
  205. msg.StatusCode = HttpStatusCode.NonAuthoritativeInformation;
  206. Debug.Assert(doc.Etag != null);
  207. doc.Metadata[Constants.LastModified] = doc.LastModified;
  208. doc.Metadata[Constants.DocumentIdFieldName] = Uri.EscapeUriString(doc.Key ?? string.Empty);
  209. msg.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json") { CharSet = "utf-8" };
  210. return WriteData(doc.DataAsJson, doc.Metadata, doc.Etag, msg: msg);
  211. }
  212. private HttpResponseMessage ProcessPatchResult(string docId, PatchResult patchResult, object debug, RavenJObject document)
  213. {
  214. switch (patchResult)
  215. {
  216. case PatchResult.DocumentDoesNotExists:
  217. return GetEmptyMessage(HttpStatusCode.NotFound);
  218. case PatchResult.Patched:
  219. var msg = GetMessageWithObject(new { Patched = true, Debug = debug });
  220. msg.Headers.Location = Database.Configuration.GetFullUrl("docs/" + docId);
  221. return msg;
  222. case PatchResult.Tested:
  223. return GetMessageWithObject(new
  224. {
  225. Patched = false,
  226. Debug = debug,
  227. Document = document
  228. });
  229. default:
  230. throw new ArgumentOutOfRangeException("Value " + patchResult + " is not understood");
  231. }
  232. }
  233. }
  234. }