public BatchSubRequest Update(string objectName, string recordId, object record) { if (string.IsNullOrEmpty(objectName)) { throw new ArgumentNullException(nameof(objectName)); } if (string.IsNullOrEmpty(recordId)) { throw new ArgumentNullException(nameof(recordId)); } if (record == null) { throw new ArgumentNullException(nameof(record)); } var richInput = Dnf.UnFlatten(JObject.FromObject(record)); var request = new BatchSubRequest { RichInput = Dnf.Omit(richInput, "Id"), Method = "PATCH", Url = $"sobjects/{objectName}/{recordId}" }; BatchRequests.Add(request); return(request); }
public BatchSubRequest UpsertExternal(string objectName, string externalFieldName, string externalId, object record) { if (string.IsNullOrEmpty(objectName)) { throw new ArgumentNullException(nameof(objectName)); } if (string.IsNullOrEmpty(externalId)) { throw new ArgumentNullException(nameof(externalId)); } if (record == null) { throw new ArgumentNullException(nameof(record)); } var richInput = Dnf.UnFlatten(JObject.FromObject(record)); var request = new BatchSubRequest { RichInput = Dnf.Omit(richInput, externalFieldName), Method = "PATCH", Url = $"sobjects/{objectName}/{externalFieldName}/{Uri.EscapeDataString(externalId)}" }; BatchRequests.Add(request); return(request); }
public async Task <SaveResponse> CreateTreeAsync <T>(string objectName, IList <T> objectTree) { if (string.IsNullOrEmpty(objectName)) { throw new ArgumentNullException(nameof(objectName)); } if (objectTree == null || !objectTree.Any()) { throw new ArgumentNullException(nameof(objectTree)); } if (typeof(IAttributedObject).IsAssignableFrom(typeof(T))) { return(await Dnf.TryDeserializeObjectAsync( JsonHttp.HttpPostAsync <SaveResponse>( new CreateRequest { Records = objectTree.Cast <IAttributedObject>().ToList() }, $"composite/tree/{objectName}")) .ConfigureAwait(false) ?? new SaveResponse()); } return(await Dnf.TryDeserializeObjectAsync( JsonHttp.HttpPostAsync <SaveResponse>( new JObject { ["records"] = JToken.FromObject(objectTree) }, $"composite/tree/{objectName}")) .ConfigureAwait(false) ?? new SaveResponse()); }
public CompositeSubRequest Update(string referenceId, string objectName, string recordId, object record) { if (string.IsNullOrEmpty(referenceId)) { throw new ArgumentNullException(nameof(referenceId)); } if (string.IsNullOrEmpty(objectName)) { throw new ArgumentNullException(nameof(objectName)); } if (string.IsNullOrEmpty(recordId)) { throw new ArgumentNullException(nameof(recordId)); } if (record == null) { throw new ArgumentNullException(nameof(record)); } var body = Dnf.UnFlatten(JObject.FromObject(record)); var request = new CompositeSubRequest { Body = Dnf.Omit(body, "Id"), Method = "PATCH", ReferenceId = referenceId, Url = $"sobjects/{objectName}{recordId}" }; CompositeRequests.Add(request); return(request); }
public BatchSubRequest Search(string query) { if (string.IsNullOrEmpty(query)) { throw new ArgumentNullException(nameof(query)); } if (!query.Contains("FIND")) { throw new ArgumentException("query does not contain FIND"); } if (!query.Contains("{") || !query.Contains("}")) { throw new ArgumentException("search term must be wrapped in braces"); } var request = new BatchSubRequest { ResponseType = "query", Method = "GET", Url = $"search?q={Dnf.EscapeDataString(query)}" }; BatchRequests.Add(request); return(request); }
public CompositeSubRequest UpsertExternal(string referenceId, string objectName, string externalFieldName, string externalId, object record) { if (string.IsNullOrEmpty(referenceId)) { throw new ArgumentNullException(nameof(referenceId)); } if (string.IsNullOrEmpty(objectName)) { throw new ArgumentNullException(nameof(objectName)); } if (string.IsNullOrEmpty(externalId)) { throw new ArgumentNullException(nameof(externalId)); } if (record == null) { throw new ArgumentNullException(nameof(record)); } var body = Dnf.UnFlatten(JObject.FromObject(record)); var request = new CompositeSubRequest { Body = Dnf.Omit(body, externalFieldName), Method = "PATCH", ReferenceId = referenceId, Url = $"sobjects/{objectName}/{externalFieldName}/{Uri.EscapeDataString(externalId)}" }; CompositeRequests.Add(request); return(request); }
public BatchSubRequest Update(string objectName, object record) { if (string.IsNullOrEmpty(objectName)) { throw new ArgumentNullException(nameof(objectName)); } if (record == null) { throw new ArgumentNullException(nameof(record)); } var richInput = Dnf.UnFlatten(JObject.FromObject(record)); return(Update(objectName, richInput["Id"]?.ToString() ?? string.Empty, Dnf.Omit(richInput, "Id"))); }
public BatchSubRequest UpsertExternal(string objectName, string externalFieldName, object record) { if (string.IsNullOrEmpty(objectName)) { throw new ArgumentNullException(nameof(objectName)); } if (record == null) { throw new ArgumentNullException(nameof(record)); } var richInput = Dnf.UnFlatten(JObject.FromObject(record)); return(UpsertExternal(objectName, externalFieldName, richInput[externalFieldName]?.ToString() ?? string.Empty, Dnf.Omit(richInput, externalFieldName))); }
public BatchSubRequest ExplainAll(string query) { if (string.IsNullOrEmpty(query)) { throw new ArgumentNullException(nameof(query)); } var request = new BatchSubRequest { Method = "GET", Url = $"queryAll?explain={Dnf.EscapeDataString(query)}" }; BatchRequests.Add(request); return(request); }
public BatchSubRequest Query(string query) { if (string.IsNullOrEmpty(query)) { throw new ArgumentNullException(nameof(query)); } var request = new BatchSubRequest { ResponseType = "query", Method = "GET", Url = $"query?q={Dnf.EscapeDataString(query)}" }; BatchRequests.Add(request); return(request); }
public async IAsyncEnumerable <QueryResult <T> > SearchAsync <T>(string q) { if (string.IsNullOrEmpty(q)) { throw new ArgumentNullException(nameof(q)); } var resourceName = $"tooling/search?q={Dnf.EscapeDataString(q)}"; var result = await JsonHttp.HttpGetAsync <QueryResult <T> >(resourceName) .ConfigureAwait(false); await foreach (var nextResult in QueryByLocatorAsync(result) .ConfigureAwait(false)) { yield return(nextResult); } }
public CompositeSubRequest UpsertExternal(string referenceId, string objectName, string externalFieldName, object record) { if (string.IsNullOrEmpty(referenceId)) { throw new ArgumentNullException(nameof(referenceId)); } if (string.IsNullOrEmpty(objectName)) { throw new ArgumentNullException(nameof(objectName)); } if (record == null) { throw new ArgumentNullException(nameof(record)); } var body = Dnf.UnFlatten(JObject.FromObject(record)); return(UpsertExternal(referenceId, objectName, externalFieldName, body[externalFieldName]?.ToString() ?? string.Empty, Dnf.Omit(body, externalFieldName))); }
public BatchSubRequest Create(string objectName, object record) { if (string.IsNullOrEmpty(objectName)) { throw new ArgumentNullException(nameof(objectName)); } if (record == null) { throw new ArgumentNullException(nameof(record)); } var request = new BatchSubRequest { RichInput = Dnf.UnFlatten(JObject.FromObject(record)), Url = $"sobjects/{objectName}" }; BatchRequests.Add(request); return(request); }
public CompositeSubRequest Explain(string referenceId, string query) { if (string.IsNullOrEmpty(referenceId)) { throw new ArgumentNullException(nameof(referenceId)); } if (string.IsNullOrEmpty(query)) { throw new ArgumentNullException(nameof(query)); } var request = new CompositeSubRequest { Method = "GET", ReferenceId = referenceId, Url = $@"query?explain={Dnf.EscapeDataString(query)}" }; CompositeRequests.Add(request); return(request); }
public async Task <BatchResult> BatchAsync(IBatchRequest request) { if (request == null || request.BatchRequests.Count <= 0) { throw new ArgumentNullException(nameof(request)); } try { var resourceName = $"{request.Prefix}composite/batch"; if (request.HaltOnError) { if (request.BatchRequests.Count > Dnf.BatchLimit) { throw new ArgumentOutOfRangeException(nameof(request)); } var inputObject = new JObject { ["batchRequests"] = JToken.FromObject(request.BatchRequests.Select(req => Dnf.Assign(JObject.FromObject(req), new JObject { ["url"] = DecodeReference($"/services/data/{ApiVersion}/{request.Prefix}{req.Url?.TrimStart('/')}") }))), ["haltOnError"] = true }; var result = await JsonHttp.HttpPostAsync <BatchResultBody>(inputObject, resourceName) .ConfigureAwait(false); var results = new BatchResult(request.BatchRequests, result?.Results ?? new List <BatchSubRequestResult>()); return(results); } else { var throttler = new SemaphoreSlim(Dnf.DefaultConcurrentLimit, Dnf.DefaultConcurrentLimit); var results = new BatchResult(); var chunks = new List <IList <BatchSubRequest> >(); IList <BatchSubRequest>?chunk = null; foreach (var req in request.BatchRequests) { var added = false; if (chunk?.Count < Dnf.BatchLimit) { chunk.Add(req); added = true; } if (added) { continue; } chunk = new List <BatchSubRequest> { req }; chunks.Add(chunk); } var tasks = new List <Task>(); foreach (var requests in chunks) { await throttler.WaitAsync() .ConfigureAwait(false); tasks.Add(Task.Run(async() => { try { var inputObject = new JObject { ["batchRequests"] = JToken.FromObject(requests.Select(req => Dnf.Assign(JObject.FromObject(req), new JObject { ["url"] = DecodeReference($"/services/data/{ApiVersion}/{request.Prefix}{req.Url?.TrimStart('/')}") }))) }; var result = await JsonHttp.HttpPostAsync <BatchResultBody>(inputObject, resourceName) .ConfigureAwait(false); results.Add(requests, result?.Results ?? new List <BatchSubRequestResult>()); } catch (Exception ex) { var body = new JArray { ex.Message }; var responses = requests.Select(req => new BatchSubRequestResult { Result = body, StatusCode = 500 }).ToList(); results.Add(requests, responses); } finally { throttler.Release(); } })); } await Task.WhenAll(tasks) .ConfigureAwait(false); return(results); } } catch (Exception ex) { var body = new JArray { ex.Message }; var responses = request.BatchRequests.Select(req => new BatchSubRequestResult { Result = body, StatusCode = 500 }).ToList(); var results = new BatchResult(request.BatchRequests, responses); return(results); } }
public async Task <CompositeResult> PostAsync(ICompositeRequest request) { if (request == null || request.CompositeRequests.Count <= 0) { throw new ArgumentNullException(nameof(request)); } try { var resourceName = $"{request.Prefix}composite"; if (request.AllOrNone) { var requests = request.CompositeRequests; if (requests.Count > Dnf.CompositeLimit) { throw new ArgumentOutOfRangeException(nameof(request)); } if (requests.Count(c => IsQuery(c.ResponseType)) > Dnf.CompositeQueryLimit) { throw new ArgumentOutOfRangeException(nameof(request)); } var inputObject = new JObject { ["allOrNone"] = true, ["compositeRequest"] = JToken.FromObject(requests.Select(req => Dnf.Assign(JObject.FromObject(req), new JObject { ["url"] = DecodeReference($"/services/data/{ApiVersion}/{request.Prefix}{req.Url?.TrimStart('/')}") }))) }; var result = await JsonHttp.HttpPostAsync <CompositeResultBody>(inputObject, resourceName) .ConfigureAwait(false); var results = new CompositeResult(request.CompositeRequests, result?.CompositeResponse ?? new List <CompositeSubRequestResult>()); return(results); } else { var throttler = new SemaphoreSlim(Dnf.DefaultConcurrentLimit, Dnf.DefaultConcurrentLimit); var results = new CompositeResult(); var chunks = new List <IList <CompositeSubRequest> >(); IList <CompositeSubRequest>?chunk = null; foreach (var req in request.CompositeRequests) { var added = false; if (IsQuery(req.ResponseType)) { if (chunk != null && chunk.Count(c => IsQuery(c.ResponseType)) < Dnf.CompositeQueryLimit) { chunk.Add(req); added = true; } } else if (chunk?.Count < Dnf.CompositeLimit) { chunk.Add(req); added = true; } if (added) { continue; } chunk = new List <CompositeSubRequest> { req }; chunks.Add(chunk); } var tasks = new List <Task>(); foreach (var requests in chunks) { await throttler.WaitAsync() .ConfigureAwait(false); tasks.Add(Task.Run(async() => { try { var inputObject = new JObject { ["compositeRequest"] = JToken.FromObject(requests.Select(req => Dnf.Assign(JObject.FromObject(req), new JObject { ["url"] = DecodeReference($"/services/data/{ApiVersion}/{request.Prefix}{req.Url?.TrimStart('/')}") }))) }; var result = await JsonHttp.HttpPostAsync <CompositeResultBody>(inputObject, resourceName) .ConfigureAwait(false); results.Add(requests, result?.CompositeResponse ?? new List <CompositeSubRequestResult>()); } catch (Exception ex) { var body = new JArray { ex.Message }; var responses = requests.Select(req => new CompositeSubRequestResult { Body = body, ReferenceId = req.ReferenceId, HttpStatusCode = 500 }).ToList(); results.Add(requests, responses); } finally { throttler.Release(); } })); } await Task.WhenAll(tasks) .ConfigureAwait(false); return(results); } } catch (Exception ex) { var body = new JArray { ex.Message }; var responses = request.CompositeRequests.Select(req => new CompositeSubRequestResult { Body = body, ReferenceId = req.ReferenceId, HttpStatusCode = 500 }).ToList(); var results = new CompositeResult(request.CompositeRequests, responses); return(results); } bool IsQuery(string responseType) { return(responseType == "query" || responseType == "collections"); } }
public IList <CompositeSubRequest> Update <T>(string referenceId, bool allOrNone, IList <T> records) { if (string.IsNullOrEmpty(referenceId)) { throw new ArgumentNullException(nameof(referenceId)); } if (records == null || !records.Any()) { throw new ArgumentNullException(nameof(records)); } if (allOrNone && records.Count > 200) { throw new ArgumentOutOfRangeException(nameof(records)); } var result = new List <CompositeSubRequest>(); foreach (var(chunk, chunkIdx) in EnumerableChunk.Create(records, 200).Select((chunk, chunkIdx) => (chunk, chunkIdx))) { var bodyRecords = JToken.FromObject(chunk.Select(record => record == null ? new JObject() : Dnf.UnFlatten(JObject.FromObject(record)))); var request = new CompositeSubRequest { ResponseType = "collections", Body = new JObject { ["allOrNone"] = allOrNone, ["records"] = bodyRecords }, Method = "PATCH", ReferenceId = $"{referenceId}_{chunkIdx}", Url = "composite/sobjects" }; CompositeRequests.Add(request); result.Add(request); } return(result); }