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