/// <summary> /// Starts the bulk service. After this point every operation on API is put in a bulk buffer and not sent to Clarizen until CheckCommit or Close functions are executed. /// </summary> /// <param name="isTransactional"></param> /// <param name="batch"></param> public void Start(bool isTransactional = false, bool batch = false, bool includeRequestsInResponse = false, int timeout = 120000) { _isBulkTransactional = isTransactional; _batch = batch; _includeRequestsInResponse = includeRequestsInResponse; _timeout = timeout; APIBulkCallCount = 0; ClarizenAPI.StartBulkService(); }
/// <summary> /// Sends any items in the bulk buffer to the API and then closes the bulk service. Results are returned as objects of type T. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="hasErrors">Identifies if the API returned any errors</param> /// <param name="enableTimeAudit">Log how long each operation takes</param> /// <param name="sleepTime">Seconds to wait between each API call</param> /// <returns></returns> public async Task <Tuple <List <T>, bool> > Close <T>(bool enableTimeAudit = false, int sleepTime = 0) { Tuple <List <T>, bool> result = null; if (APIBulkCallCount > 0) { result = await Commit <T>(enableTimeAudit, sleepTime); } ClarizenAPI.CancelBulkService(); return(result); }
/// <summary> /// Sends any items in the bulk buffer to the API and then closes the bulk service. /// </summary> /// <param name="enableTimeAudit">Log how long each operation takes</param> /// <param name="sleepTime">Seconds to wait between each API call</param> /// <returns></returns> public async Task <bool> Close(bool enableTimeAudit = false, int sleepTime = 0) { bool result = true; if (APIBulkCallCount > 0) { result = await Commit(enableTimeAudit, sleepTime); } ClarizenAPI.CancelBulkService(); return(result); }
/// <summary> /// Get multiple objects of type T by running a query (IClarizenQuery) /// </summary> /// <typeparam name="T"></typeparam> /// <param name="query">Clarizen Query Language (CZQL) query</param> /// <param name="pageSize">Default size to be used for pagination</param> /// <returns></returns> public async Task <List <T> > GetAll <T>(Ekin.Clarizen.Interfaces.IClarizenQuery query, int?pageSize = null, int?sleepTime = null) { GetAllResult result = await ClarizenAPI.GetAll(query, typeof(T), pageSize, sleepTime); if (result == null || result.Errors.Any()) { var detailedMsg = "Error: " + string.Join(System.Environment.NewLine, result.Errors.Select(i => i.Message)); Logs.AddError("Ekin.Clarizen.BulkOperations", "Get All (Query)", detailedMsg); return(null); } return((List <T>)result.Data); }
/// <summary> /// Starts the bulk service. After this point every operation on API is put in a bulk buffer and not sent to Clarizen until CheckCommit or Close functions are executed. /// </summary> /// <param name="isTransactional"></param> /// <param name="batch"></param> public void Start(bool isTransactional = false, bool batch = false, bool includeRequestsInResponse = false, int?timeout = null) { _isBulkTransactional = isTransactional; _batch = batch; _includeRequestsInResponse = includeRequestsInResponse; if (timeout != null) { _timeout = timeout.Value; } APIBulkCallCount = 0; ClarizenAPI.StartBulkService(); }
/// <summary> /// Get multiple objects of type T by providing an EntityName /// </summary> /// <typeparam name="T"></typeparam> /// <param name="entityName">Name of the Clarizen entity, e.g. Timesheet, WorkItem, etc.</param> /// <param name="customCondition"></param> /// <returns></returns> public async Task <List <T> > GetAll <T>(string entityName, string customCondition = "", int sleepTime = 0) { customCondition = customCondition?.Trim(); CZQLCondition condition = null; if (!string.IsNullOrEmpty(customCondition)) { condition = new CZQLCondition(customCondition); } GetAllResult result = await ClarizenAPI.GetAll(entityName, typeof(T), condition, sleepTime); if (result == null || result.Errors.Any()) { var detailedMsg = "Error: " + string.Join(System.Environment.NewLine, result.Errors.Select(i => i.Message)); Logs.AddError("Ekin.Clarizen.BulkOperations", "Get All " + entityName, detailedMsg); return(null); } return((List <T>)result.Data); }
internal async Task <bool> Commit(bool enableTimeAudit = false, int sleepTime = 0) { bool result = true; DateTime startTime = DateTime.Now; Ekin.Clarizen.Bulk.Execute bulkService = await ClarizenAPI.CommitBulkService(_isBulkTransactional, _batch, _includeRequestsInResponse, _timeout); if (bulkService.IsCalledSuccessfully) { foreach (Response res in bulkService.Data.Responses) { if (res.StatusCode != 200) { Logs.AddError("Ekin.Clarizen.BulkOperations", "CommitBulkService", "Bulk item failed. Error: " + ((Error)res.Body).Formatted, _includeRequestsInResponse ? res.Request : null); result = false; } } } else { Logs.AddError("Ekin.Clarizen.BulkOperations", "CommitBulkService", "Bulk service failed. Error: " + bulkService.Error); result = false; } DateTime endTime = DateTime.Now; if (enableTimeAudit) { Logs.AddAudit("Ekin.Clarizen.BulkOperations", "CommitBulkService", string.Format("Bulk API call completed in {0:0.00}s", (endTime - startTime).TotalSeconds)); } if (sleepTime > 0) { await Task.Delay(sleepTime * 1000); } return(result); }
/// <summary> /// Uploads a file and links it to an entity /// </summary> /// <param name="InputData">Either a string or a byte[] to upload</param> /// <param name="DocumentName">Name of the document entity in Clarizen</param> /// <param name="FileType">File Type in Clarizen, e.g. /FileType/PDF</param> /// <param name="DocumentType">Document Type in Clarizen, e.g. /DocumentType/YourCustomType (Optional)</param> /// <param name="LinkedEntity">An entity in Clarizen to link the uploaded file to (Optional)</param> /// <returns></returns> public async Task <bool> Upload(object InputData, string DocumentName, string FileName, EntityId FileType, EntityId DocumentType = null, EntityId LinkedEntity = null) { Logs = new Ekin.Log.LogFactory(); #region Validate input if (InputData == null) { Logs.AddError("Ekin.Clarizen.FileUploadHelper", "Upload", "InputData cannot be null"); return(false); } if (!(InputData is string || InputData is byte[])) { Logs.AddError("Ekin.Clarizen.FileUploadHelper", "Upload", "InputData should be a string or a byte array"); return(false); } #endregion Validate input #region Create a Document entity Document document = new Document { Id = "/Document", Name = DocumentName, FileType = FileType, DocumentType = DocumentType }; Ekin.Clarizen.Data.Objects_put clarizenDocument = await ClarizenAPI.CreateObject(document.Id, document); if (!clarizenDocument.IsCalledSuccessfully || clarizenDocument.Data == null || string.IsNullOrWhiteSpace(clarizenDocument.Data.Id)) { Logs.AddError("Ekin.Clarizen.FileUploadHelper", "Upload", "Blank document couldn't be created in Clarizen. Error: " + clarizenDocument.Error); return(false); } else { document.Id = clarizenDocument.Data.Id; } #endregion Create a Document entity #region Get Upload URL from Clarizen Ekin.Clarizen.Files.GetUploadUrl uploadUrlCall = await ClarizenAPI.GetUploadUrl(); if (!uploadUrlCall.IsCalledSuccessfully || uploadUrlCall.Data == null || string.IsNullOrWhiteSpace(uploadUrlCall.Data.UploadUrl)) { Logs.AddError("Ekin.Clarizen.FileUploadHelper", "Upload", "Document upload url couldn't be retrieved from Clarizen. Error: " + uploadUrlCall.Error); return(false); } string uploadUrl = uploadUrlCall.Data.UploadUrl; #endregion Get Upload URL from Clarizen #region Send the file to the Upload URL using (var client = new HttpClient()) { using (var formData = new MultipartFormDataContent()) { if (InputData is string) { formData.Add(new StringContent((string)InputData), "file", FileName); } else if (InputData is byte[]) { formData.Add(new ByteArrayContent((byte[])InputData), "file", FileName); } HttpResponseMessage response = await client.PostAsync(uploadUrl, formData).ConfigureAwait(false); if (!response.IsSuccessStatusCode) { using (HttpContent content = response.Content) { string data = await content.ReadAsStringAsync().ConfigureAwait(false); Logs.AddError("Ekin.Clarizen.FileUploadHelper", "Upload", "Document couldn't be uploaded. Error: " + data); } return(false); } System.IO.Stream resultStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); } } #endregion Send the file to the Upload URL #region Complete the Upload var subType = ""; var extendedInfo = ""; var fileInformation = new FileInformation(StorageType.Server, document.Id, FileName, subType, extendedInfo); var uploadRequest = new Ekin.Clarizen.Files.Request.Upload(document.Id, fileInformation, uploadUrl); CallSettings callSettings = CallSettings.GetFromAPI(ClarizenAPI); callSettings.IsBulk = false; var uploadCall = new Ekin.Clarizen.Files.Upload(uploadRequest, callSettings); bool uploadResult = await uploadCall.Execute(); if (!uploadResult) { Logs.AddError("Ekin.Clarizen.FileUploadHelper", "Upload", "Document couldn't be uploaded to Clarizen. Error: " + uploadCall.Error); return(false); } #endregion Complete the Upload #region If a LinkedEntity object is provided link the file to it if (LinkedEntity != null) { AttachmentLink attachmentLink = new AttachmentLink { Id = "/AttachmentLink", Entity = LinkedEntity, Document = new EntityId(document.Id) }; Ekin.Clarizen.Data.Objects_put clarizenDocumentLink = await ClarizenAPI.CreateObject(attachmentLink.Id, attachmentLink); if (!clarizenDocumentLink.IsCalledSuccessfully || clarizenDocumentLink.Data == null || clarizenDocumentLink.Data.Id == null) { Logs.AddError("Ekin.Clarizen.FileUploadHelper", "Upload", "Document successfully created in Clarizen but it could not be linked to the System Admin user. Error: " + clarizenDocumentLink.Error); return(false); } } #endregion If a LinkedEntity object is provided link the file to it return(true); }
internal async Task <Tuple <List <T>, bool> > Commit <T>(bool enableTimeAudit = false, int sleepTime = 0) { List <T> result = new List <T> { }; DateTime startTime = DateTime.Now; bool hasErrors = false; Ekin.Clarizen.Bulk.Execute bulkService = await ClarizenAPI.CommitBulkService(_isBulkTransactional, _batch, _includeRequestsInResponse, _timeout); if (bulkService.IsCalledSuccessfully) { foreach (Response res in bulkService.Data.Responses) { if (res.StatusCode != 200) { Logs.AddError("Ekin.Clarizen.BulkOperations", "CommitBulkServiceAndGetData", "Bulk item failed. Error: " + ((Error)res.Body).Formatted, _includeRequestsInResponse ? res.Request : null); hasErrors = true; } else { try { if (res.Body is T) { result.Add((T)res.Body); } else { result.Add(default(T)); } } catch (Exception ex) { Logs.AddError("Ekin.Clarizen.BulkOperations", "CommitBulkServiceAndGetData", string.Format("Item returned from Clarizen API could not be parsed to type {0}. Error: {1}", typeof(T), ex.Message)); hasErrors = true; } } } } else { Logs.AddError("Ekin.Clarizen.BulkOperations", "CommitBulkServiceAndGetData", "Bulk service failed. Error: " + bulkService.Error); hasErrors = true; } DateTime endTime = DateTime.Now; if (enableTimeAudit) { Logs.AddAudit("Ekin.Clarizen.BulkOperations", "CommitBulkServiceAndGetData", string.Format("Bulk API call completed in {0:0.00}s", (endTime - startTime).TotalSeconds)); } if (sleepTime > 0) { await Task.Delay(sleepTime * 1000); } if (hasErrors) { return(new Tuple <List <T>, bool>(null, true)); } else { return(new Tuple <List <T>, bool>(result, false)); } }
/// <summary> /// Run multiple queries in bulk. This method returns the results recursively so if any of the queries result in pagination more bulk queries are executed until all data is retrieved. /// </summary> /// <param name="Queries">List of Clarizen Query Language (CZQL) queries</param> /// <param name="bulkSize">Number of items to send in a bulk call</param> /// <returns></returns> public async Task <List <Ekin.Clarizen.Data.Result.Query> > BulkQuery(List <Ekin.Clarizen.Data.Request.Query> Queries, int bulkSize = 100) { if (Queries?.Count == 0) { return(null); } int totalQueryCount = 0; List <int> itemsThatNeedPagination = new List <int> { }; List <Ekin.Clarizen.Data.Result.Query> results = new List <Ekin.Clarizen.Data.Result.Query> { }; bool bulkHasErrors = false; Start(false, false); for (int QueryCount = 0; QueryCount < Queries.Count; QueryCount++) { Ekin.Clarizen.Data.Request.Query query = Queries[QueryCount]; if (string.IsNullOrWhiteSpace(query.Q)) { continue; } await ClarizenAPI.ExecuteQuery(query); Tuple <List <Ekin.Clarizen.Data.Result.Query>, bool> bulkResult = await CheckCommit <Ekin.Clarizen.Data.Result.Query>(true, 0, bulkSize); bulkHasErrors = bulkResult.Item2; if (!bulkHasErrors) { if (bulkResult != null) { totalQueryCount += QueryCount + 1; results.AddRange(bulkResult.Item1); foreach (Ekin.Clarizen.Data.Result.Query queryResult in bulkResult.Item1) { if (queryResult.Paging.HasMore) { query.Paging = queryResult.Paging; itemsThatNeedPagination.Add(QueryCount); } else { query.Q = null; } } } } else { break; } } if (!bulkHasErrors) { Tuple <List <Ekin.Clarizen.Data.Result.Query>, bool> bulkResult = await Close <Ekin.Clarizen.Data.Result.Query>(true, 0); bulkHasErrors = bulkResult.Item2; if (!bulkHasErrors) { if (bulkResult != null) { results.AddRange(bulkResult.Item1); for (int QueryCount = 0; QueryCount < bulkResult.Item1.Count; QueryCount++) { Ekin.Clarizen.Data.Result.Query queryResult = bulkResult.Item1[QueryCount]; if (queryResult.Paging.HasMore) { Queries[totalQueryCount + QueryCount].Paging = queryResult.Paging; itemsThatNeedPagination.Add(totalQueryCount + QueryCount); } else { Queries[totalQueryCount + QueryCount].Q = null; } } } } } if (bulkHasErrors) { return(null); } if (itemsThatNeedPagination.Count > 0) { List <Ekin.Clarizen.Data.Result.Query> subResults = await BulkQuery(Queries, bulkSize); if (subResults == null) { return(null); } for (int QueryCount = 0; QueryCount < subResults.Count; QueryCount++) { int resultIndex = itemsThatNeedPagination[QueryCount]; if (subResults[QueryCount].Entities != null) { List <dynamic> mergeItems = new List <dynamic>(results[resultIndex].Entities); mergeItems.AddRange(subResults[QueryCount].Entities); results[resultIndex].Entities = mergeItems.ToArray(); } } } return(results); }