/// <summary> /// Query using the specified <see cref="SoqlQuery"/> against the specified resource identifier. /// </summary> /// <typeparam name="TRow">The .NET class that represents the type of the underlying rows in the result set of this query.</typeparam> /// <param name="soqlQuery">A <see cref="SoqlQuery"/> to execute against the Resource.</param> /// <param name="resourceId">The identifier (4x4) for a resource on the Socrata host to target.</param> /// <returns>A collection of entities of type <typeparamref name="TRow"/>.</returns> /// <exception cref="System.ArgumentOutOfRangeException">Thrown if the specified <paramref name="resourceId"/> does not match the Socrata 4x4 pattern.</exception> /// <remarks> /// By default, Socrata will only return the first 1000 rows unless otherwise specified in SoQL using the Limit and Offset parameters. /// This method checks the specified SoqlQuery object for either the Limit or Offset parameter, and honors those parameters if present. /// If both Limit and Offset are not part of the SoqlQuery, this method attempts to retrieve all rows in the dataset across all pages. /// In other words, this method hides the fact that Socrata will only return 1000 rows at a time, unless explicity told not to via the SoqlQuery argument. /// </remarks> public IEnumerable <TRow> Query <TRow>(SoqlQuery soqlQuery, string resourceId) where TRow : class { if (FourByFour.IsNotValid(resourceId)) { throw new ArgumentOutOfRangeException("resourceId", "The provided resourceId is not a valid Socrata (4x4) resource identifier."); } //if the query explicitly asks for a limit/offset, honor the ask if (soqlQuery.LimitValue > 0 || soqlQuery.OffsetValue > 0) { var queryUri = SodaUri.ForQuery(Host, resourceId, soqlQuery); return(read <IEnumerable <TRow> >(queryUri)); } //otherwise, go nuts and get EVERYTHING else { List <TRow> allResults = new List <TRow>(); int offset = 0; soqlQuery = soqlQuery.Limit(SoqlQuery.MaximumLimit).Offset(offset); IEnumerable <TRow> offsetResults = read <IEnumerable <TRow> >(SodaUri.ForQuery(Host, resourceId, soqlQuery)); while (offsetResults.Any()) { allResults.AddRange(offsetResults); soqlQuery = soqlQuery.Offset(++offset * SoqlQuery.MaximumLimit); offsetResults = read <IEnumerable <TRow> >(SodaUri.ForQuery(Host, resourceId, soqlQuery)); } return(allResults); } }
/// <summary> /// Update/Insert the specified collection of entities in batches of the specified size, using the specified resource identifier. /// </summary> /// <param name="payload">A collection of entities, where each represents a single row in the target resource.</param> /// <param name="batchSize">The maximum number of entities to process in a single batch.</param> /// <param name="breakFunction">A function which, when evaluated true, causes a batch to be sent (possibly before it reaches <paramref name="batchSize"/>).</param> /// <param name="resourceId">The identifier (4x4) for a resource on the Socrata host to target.</param> /// <returns>A collection of <see cref="SodaResult"/>, one for each batched Upsert.</returns> /// <exception cref="System.ArgumentOutOfRangeException">Thrown if the specified <paramref name="resourceId"/> does not match the Socrata 4x4 pattern.</exception> /// <exception cref="System.InvalidOperationException">Thrown if this SodaClient was initialized without authentication credentials.</exception> public IEnumerable <SodaResult> BatchUpsert <TRow>(IEnumerable <TRow> payload, int batchSize, Func <IEnumerable <TRow>, TRow, bool> breakFunction, string resourceId) where TRow : class { if (FourByFour.IsNotValid(resourceId)) { throw new ArgumentOutOfRangeException("resourceId", "The provided resourceId is not a valid Socrata (4x4) resource identifier."); } if (String.IsNullOrEmpty(Username) || String.IsNullOrEmpty(password)) { throw new InvalidOperationException("Write operations require an authenticated client."); } Queue <TRow> queue = new Queue <TRow>(payload); while (queue.Any()) { //make the next batch to send var batch = new List <TRow>(); for (var index = 0; index < batchSize && queue.Count > 0; index++) { //if we have a break function that returns true => bail out early if (breakFunction != null && breakFunction(batch, queue.Peek())) { break; } //otherwise add the next item in queue to this batch batch.Add(queue.Dequeue()); } //now send this batch SodaResult result; try { result = Upsert <TRow>(batch, resourceId); } catch (WebException webException) { string message = webException.UnwrapExceptionMessage(); result = new SodaResult() { Message = webException.Message, IsError = true, ErrorCode = message, Data = payload }; } catch (Exception ex) { result = new SodaResult() { Message = ex.Message, IsError = true, ErrorCode = ex.Message, Data = payload }; } //yield return here creates an iterator - results aren't returned until explicitly requested via foreach //or similar interation on the result of the call to BatchUpsert. yield return(result); } }
/// <summary> /// Delete multiple rows using the provided list of id's and resource identifier. /// </summary> /// <param name="rowIds">A List of identifiers of the rows to be deleted.</param> /// <param name="resourceId">The identifier (4x4) for a resource on the Socrata host to target.</param> /// <returns>A <see cref="SodaResult"/> indicating success or failure.</returns> /// <exception cref="System.ArgumentOutOfRangeException">Thrown if the specified <paramref name="resourceId"/> does not match the Socrata 4x4 pattern.</exception> /// <exception cref="System.InvalidOperationException">Thrown if this SodaClient was initialized without authentication credentials.</exception> public SodaResult DeleteRow(List <string> rowIds, string resourceId) { if (rowIds.Count == 0) { return new SodaResult() { Message = "No row identifiers provided to delete" } } ; if (FourByFour.IsNotValid(resourceId)) { throw new ArgumentOutOfRangeException("resourceId", "The provided resourceId is not a valid Socrata (4x4) resource identifier."); } if (String.IsNullOrEmpty(Username) || String.IsNullOrEmpty(password)) { throw new InvalidOperationException("Write operations require an authenticated client."); } //Get the unique identifier field var metaData = GetMetadata(resourceId); var idFieldName = metaData.RowIdentifierField; //construct the json payload string jsonPayload = PayloadBuilder.GetDeletePayload(idFieldName, rowIds); return(Upsert(jsonPayload, SodaDataFormat.JSON, resourceId)); }
/// <summary> /// Delete a single row using the specified row identifier and the specified resource identifier. /// </summary> /// <param name="rowId">The identifier of the row to be deleted.</param> /// <param name="resourceId">The identifier (4x4) for a resource on the Socrata host to target.</param> /// <returns>A <see cref="SodaResult"/> indicating success or failure.</returns> /// <exception cref="System.ArgumentException">Thrown if the specified <paramref name="rowId"/> is null or empty.</exception> /// <exception cref="System.ArgumentOutOfRangeException">Thrown if the specified <paramref name="resourceId"/> does not match the Socrata 4x4 pattern.</exception> /// <exception cref="System.InvalidOperationException">Thrown if this SodaClient was initialized without authentication credentials.</exception> public SodaResult DeleteRow(string rowId, string resourceId) { if (String.IsNullOrEmpty(rowId)) { throw new ArgumentException("Must specify the row to be deleted using its row identifier.", "rowId"); } if (FourByFour.IsNotValid(resourceId)) { throw new ArgumentOutOfRangeException("resourceId", "The provided resourceId is not a valid Socrata (4x4) resource identifier."); } if (String.IsNullOrEmpty(Username) || String.IsNullOrEmpty(password)) { throw new InvalidOperationException("Write operations require an authenticated client."); } //Get the unique identifier field var metaData = GetMetadata(resourceId); var idFieldName = metaData.RowIdentifierField; //construct the json payload string jsonPayload = PayloadBuilder.GetDeletePayload(idFieldName, rowId); return(Upsert(jsonPayload, SodaDataFormat.JSON, resourceId)); }
/// <summary> /// Get a Resource object using the specified resource identifier. /// </summary> /// <typeparam name="TRow">The .NET class that represents the type of the underlying row in the Resource.</typeparam> /// <param name="resourceId">The identifier (4x4) for a resource on the Socrata host to target.</param> /// <returns>A Resource object with an underlying row set of type <typeparamref name="TRow"/>.</returns> /// <exception cref="System.ArgumentOutOfRangeException">Thrown if the specified <paramref name="resourceId"/> does not match the Socrata 4x4 pattern.</exception> public Resource <TRow> GetResource <TRow>(string resourceId) where TRow : class { if (FourByFour.IsNotValid(resourceId)) { throw new ArgumentOutOfRangeException("resourceId", "The provided resourceId is not a valid Socrata (4x4) resource identifier."); } return(new Resource <TRow>(resourceId, this)); }
/// <summary> /// Replace any existing rows with the payload data, using the specified resource identifier. /// </summary> /// <param name="payload">A string of serialized data.</param> /// <param name="dataFormat">One of the data-interchange formats that Socrata supports, into which the payload has been serialized.</param> /// <param name="resourceId">The identifier (4x4) for a resource on the Socrata host to target.</param> /// <returns>A <see cref="SodaResult"/> indicating success or failure.</returns> /// <exception cref="System.ArgumentOutOfRangeException">Thrown if the specified <paramref name="dataFormat"/> is equal to <see cref="SodaDataFormat.XML"/>.</exception> /// <exception cref="System.ArgumentOutOfRangeException">Thrown if the specified <paramref name="resourceId"/> does not match the Socrata 4x4 pattern.</exception> /// <exception cref="System.InvalidOperationException">Thrown if this SodaClient was initialized without authentication credentials.</exception> public SodaResult Replace(string payload, SodaDataFormat dataFormat, string resourceId) { if (dataFormat == SodaDataFormat.XML) { throw new ArgumentOutOfRangeException("dataFormat", "XML is not a valid format for write operations."); } if (FourByFour.IsNotValid(resourceId)) { throw new ArgumentOutOfRangeException("resourceId", "The provided resourceId is not a valid Socrata (4x4) resource identifier."); } if (String.IsNullOrEmpty(Username) || String.IsNullOrEmpty(password)) { throw new InvalidOperationException("Write operations require an authenticated client."); } var uri = SodaUri.ForResourceAPI(Host, resourceId); var request = new SodaRequest(uri, "PUT", AppToken, Username, password, dataFormat, payload); SodaResult result = null; try { if (dataFormat == SodaDataFormat.JSON) { result = request.ParseResponse <SodaResult>(); } else if (dataFormat == SodaDataFormat.CSV) { string resultJson = request.ParseResponse <string>(); result = Newtonsoft.Json.JsonConvert.DeserializeObject <SodaResult>(resultJson); } } catch (WebException webException) { string message = webException.UnwrapExceptionMessage(); result = new SodaResult() { Message = webException.Message, IsError = true, ErrorCode = message, Data = payload }; } catch (Exception ex) { result = new SodaResult() { Message = ex.Message, IsError = true, ErrorCode = ex.Message, Data = payload }; } return(result); }
/// <summary> /// Replace any existing rows with the payload data, using the specified resource identifier. /// </summary> /// <param name="method">One of update, replace, or delete</param> /// <param name="resourceId">The identifier (4x4) for a resource on the Socrata host to target.</param> /// <param name="permission">The permission level of the dataset, can be one of either "public" or "private".</param> /// <returns>A <see cref="Revision"/> newly created Revision.</returns> /// <exception cref="System.ArgumentOutOfRangeException">Thrown if the specified <paramref name="resourceId"/> does not match the Socrata 4x4 pattern.</exception> public Revision CreateRevision(string method, string resourceId, string permission = "private") { if (String.IsNullOrEmpty(method)) { throw new ArgumentException("Method must be specified either update, replace, or delete.", "method"); } if (FourByFour.IsNotValid(resourceId)) { throw new ArgumentOutOfRangeException("resourceId", "The provided resourceId is not a valid Socrata (4x4) resource identifier."); } if (String.IsNullOrEmpty(username) || String.IsNullOrEmpty(password)) { throw new InvalidOperationException("Write operations require an authenticated client."); } var revisionUri = SodaUri.ForRevision(Host, resourceId); // Construct Revision Request body Newtonsoft.Json.Linq.JObject payload = new Newtonsoft.Json.Linq.JObject(); Newtonsoft.Json.Linq.JObject action = new Newtonsoft.Json.Linq.JObject(); action["type"] = method; action["permission"] = permission; payload["action"] = action; var request = new SodaRequest(revisionUri, "POST", null, username, password, SodaDataFormat.JSON, payload.ToString()); Result result = null; try { result = request.ParseResponse <Result>(); } catch (WebException webException) { string message = webException.UnwrapExceptionMessage(); result = new Result() { Message = webException.Message, IsError = true, ErrorCode = message, Data = payload }; } catch (Exception ex) { result = new Result() { Message = ex.Message, IsError = true, ErrorCode = ex.Message, Data = payload }; } return(new Revision(result)); }
/// <summary> /// Get a ResourceMetadata object using the specified resource identifier. /// </summary> /// <param name="resourceId">The identifier (4x4) for a resource on the Socrata host to target.</param> /// <returns> /// A ResourceMetadata object for the specified resource identifier. /// </returns> /// <exception cref="System.ArgumentOutOfRangeException">Thrown if the specified <paramref name="resourceId"/> does not match the Socrata 4x4 pattern.</exception> public ResourceMetadata GetMetadata(string resourceId) { if (FourByFour.IsNotValid(resourceId)) { throw new ArgumentOutOfRangeException("resourceId", "The provided resourceId is not a valid Socrata (4x4) resource identifier."); } var uri = SodaUri.ForMetadata(Host, resourceId); var metadata = read <ResourceMetadata>(uri); metadata.Client = this; return(metadata); }
/// <summary> /// Replace any existing rows with a collection of entities, using the specified resource identifier. /// </summary> /// <param name="payload">A collection of entities, where each represents a single row in the target resource.</param> /// <param name="resourceId">The identifier (4x4) for a resource on the Socrata host to target.</param> /// <returns>A <see cref="SodaResult"/> indicating success or failure.</returns> /// <exception cref="System.ArgumentOutOfRangeException">Thrown if the specified <paramref name="resourceId"/> does not match the Socrata 4x4 pattern.</exception> /// <exception cref="System.InvalidOperationException">Thrown if this SodaClient was initialized without authentication credentials.</exception> public SodaResult Replace <TRow>(IEnumerable <TRow> payload, string resourceId) where TRow : class { if (FourByFour.IsNotValid(resourceId)) { throw new ArgumentOutOfRangeException("resourceId", "The provided resourceId is not a valid Socrata (4x4) resource identifier."); } if (String.IsNullOrEmpty(Username) || String.IsNullOrEmpty(password)) { throw new InvalidOperationException("Write operations require an authenticated client."); } string json = Newtonsoft.Json.JsonConvert.SerializeObject(payload); return(Replace(json, SodaDataFormat.JSON, resourceId)); }
/// <summary> /// Update/Insert the specified collection of entities in batches of the specified size, using the specified resource identifier. /// </summary> /// <param name="payload">A collection of entities, where each represents a single row in the target resource.</param> /// <param name="batchSize">The maximum number of entities to process in a single batch.</param> /// <param name="resourceId">The identifier (4x4) for a resource on the Socrata host to target.</param> /// <returns>A collection of <see cref="SodaResult"/>, one for each batch processed.</returns> /// <exception cref="System.ArgumentOutOfRangeException">Thrown if the specified <paramref name="resourceId"/> does not match the Socrata 4x4 pattern.</exception> /// <exception cref="System.InvalidOperationException">Thrown if this SodaClient was initialized without authentication credentials.</exception> public IEnumerable <SodaResult> BatchUpsert <TRow>(IEnumerable <TRow> payload, int batchSize, string resourceId) where TRow : class { if (FourByFour.IsNotValid(resourceId)) { throw new ArgumentOutOfRangeException("resourceId", "The provided resourceId is not a valid Socrata (4x4) resource identifier."); } if (String.IsNullOrEmpty(Username) || String.IsNullOrEmpty(password)) { throw new InvalidOperationException("Write operations require an authenticated client."); } //we create a no-op function that returns false for all inputs //in other words, the size of a batch will never be affected by this break function //and will always be the minimum of (batchSize, remaining items in total collection) Func <IEnumerable <TRow>, TRow, bool> neverBreak = (a, b) => false; return(BatchUpsert <TRow>(payload, batchSize, neverBreak, resourceId)); }
/// <summary> /// Delete a single row using the specified row identifier and the specified resource identifier. /// </summary> /// <param name="rowId">The identifier of the row to be deleted.</param> /// <param name="resourceId">The identifier (4x4) for a resource on the Socrata host to target.</param> /// <returns>A <see cref="SodaResult"/> indicating success or failure.</returns> /// <exception cref="System.ArgumentException">Thrown if the specified <paramref name="rowId"/> is null or empty.</exception> /// <exception cref="System.ArgumentOutOfRangeException">Thrown if the specified <paramref name="resourceId"/> does not match the Socrata 4x4 pattern.</exception> /// <exception cref="System.InvalidOperationException">Thrown if this SodaClient was initialized without authentication credentials.</exception> public SodaResult DeleteRow(string rowId, string resourceId) { if (String.IsNullOrEmpty(rowId)) { throw new ArgumentException("Must specify the row to be deleted using its row identifier.", "rowId"); } if (FourByFour.IsNotValid(resourceId)) { throw new ArgumentOutOfRangeException("resourceId", "The provided resourceId is not a valid Socrata (4x4) resource identifier."); } if (String.IsNullOrEmpty(Username) || String.IsNullOrEmpty(password)) { throw new InvalidOperationException("Write operations require an authenticated client."); } var uri = SodaUri.ForResourceAPI(Host, resourceId, rowId); var request = new SodaRequest(uri, "DELETE", AppToken, Username, password); return(request.ParseResponse <SodaResult>()); }
public void InValid_FourByFour_Is_Not_Valid(string testInput) { Assert.IsTrue(FourByFour.IsNotValid(testInput)); }