예제 #1
0
        /// <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);
            }
        }
예제 #2
0
        /// <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);
            }
        }
예제 #3
0
        /// <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));
        }
예제 #4
0
        /// <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));
        }
예제 #5
0
        /// <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));
        }
예제 #6
0
        /// <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);
        }
예제 #7
0
        /// <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));
        }
예제 #8
0
        /// <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);
        }
예제 #9
0
        /// <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));
        }
예제 #10
0
        /// <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));
        }
예제 #11
0
        /// <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>());
        }
예제 #12
0
 public void InValid_FourByFour_Is_Not_Valid(string testInput)
 {
     Assert.IsTrue(FourByFour.IsNotValid(testInput));
 }