http://git-wip-us.apache.org/repos/asf/usergrid/blob/867060fa/sdks/other/perl/Makefile.PL ---------------------------------------------------------------------- diff --git a/sdks/other/perl/Makefile.PL b/sdks/other/perl/Makefile.PL new file mode 100644 index 0000000..6ab9874 --- /dev/null +++ b/sdks/other/perl/Makefile.PL @@ -0,0 +1,41 @@ + +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +use strict; +use warnings; + +use 5.008; + +use ExtUtils::MakeMaker; + +WriteMakefile( + LICENSE => 'apache', + AUTHOR => 'Anuradha Weeraman <[email protected]', + ABSTRACT => 'Client API for Apache Usergrid', + NAME => 'Usergrid::Client', + VERSION_FROM => 'lib/Usergrid/Client.pm', + PREREQ_PM => { + 'Moose' => 0, + 'JSON' => 0, + 'REST::Client' => 0, + 'URI::Template' => 0, + 'Log::Log4perl' => 0, + 'namespace::autoclean' => 0 + }, + BUILD_REQUIRES => { + 'Test::More' => '0.98' + } +);
http://git-wip-us.apache.org/repos/asf/usergrid/blob/867060fa/sdks/other/perl/README.md ---------------------------------------------------------------------- diff --git a/sdks/other/perl/README.md b/sdks/other/perl/README.md new file mode 100644 index 0000000..61069cc --- /dev/null +++ b/sdks/other/perl/README.md @@ -0,0 +1,341 @@ +# Usergrid Perl Client  + +Usergrid::Client provides an easy to use Perl API for Apache Usergrid. + +## Quickstart + +Install Usergrid::Client from CPAN: + + $ sudo cpan Usergrid::Client + +Write a perl script that uses the Perl API to talk to Usergrid. Here's an example: + +```perl +#!/usr/bin/perl +use Usergrid::Client; + +# Create a client object for Usergrid that's used for subsequent activity +my $client = Usergrid::Client->new( + organization => 'test-organization', + application => 'test-app', + api_url => 'http://localhost:8080', + trace => 0 +); + +# Logs the user in. The security token is maintained by the library in memory +$client->login('johndoe', 'Johndoe123$'); + +# Add two entities to the "books" collection +$client->add_entity("books", { name => "Ulysses", author => "James Joyce" }); +$client->add_entity("books", { name => "Neuromancer", author => "William Gibson" }); + +# Retrieve a handle to the collection +my $books = $client->get_collection("books"); + +# Add a new attribute for quantity in stock +while ($books->has_next_entity()) { + my $book = $books->get_next_entity(); + + print "Name: " . $book->get('name') . ", "; + print "Author: " . $book->get('author') . "\n"; + + # Create a new attribute and update the entity + $book->set("in-stock", 0); + $client->update_entity($book); +} +``` + +## What is Apache Usergrid + +Usergrid is an open-source Backend-as-a-Service (âBaaSâ or âmBaaSâ) composed of +an integrated distributed NoSQL database, application layer and client tier with +SDKs for developers looking to rapidly build web and/or mobile applications. +It provides elementary services (user registration & management, data storage, +file storage, queues) and retrieval features (full text search, geolocation +search, joins) to power common app features. + +It is a multi-tenant system designed for deployment to public cloud environments +(such as Amazon Web Services, Rackspace, etc.) or to run on traditional server +infrastructures so that anyone can run their own private BaaS deployment. + +For architects and back-end teams, it aims to provide a distributed, easily +extendable, operationally predictable and highly scalable solution. For +front-end developers, it aims to simplify the development process by enabling +them to rapidly build and operate mobile and web applications without requiring +backend expertise. + +Source: [Usergrid Documentation](https://usergrid.apache.org/docs/) + +For more information, visit [http://www.usergrid.org](http://www.usergrid.org) + +## Installation + +### Prerequisites +Usergrid::Client depends on the following modules which can be installed +from CPAN as shown below: + + $ sudo cpan install Moose + $ sudo cpan install JSON + $ sudo cpan install REST::Client + $ sudo cpan install URI::Template + $ sudo cpan install Log::Log4perl + $ sudo cpan install namespace::autoclean + +### Build and install + + $ perl Build.PL + $ ./Build + $ ./Build test + $ sudo ./Build install + +### For legacy users on older versions of Perl + + $ perl Makefile.PL + $ make + $ make test + $ sudo make install + +## Usage + +### Getting started + +In order to login to Usergrid using the API, create a Usergrid::Client object +as shown below and invoke the login function. + +```perl +# Create the client object that will be used for all subsequent requests +my $client = Usergrid::Client->new( + organization => $organization, + application => $application, + api_url => $api_url +); + +$client->login($username, $password); +``` + +For troubleshooting the requests and responses, tracing can be enabled, which +will log all requests and responses to standard output. + +```perl +# Create the client object that will be used for all subsequent requests +my $client = Usergrid::Client->new( + organization => $organization, + application => $application, + api_url => $api_url, + trace => 1 +); +``` + +To get more details on the API, read the following perldocs: + + Usergrid::Client + Usergrid::Collection + Usergrid::Entity + +### Entities + +Creating and updating an entity is easy. Here's how: + +```perl +$book = $client->add_entity("books", { name => "Neuromancer", author => "William Gibson" }); +$book->set('genre', 'Cyberpunk'); + +$client->update_entity($book); +``` + +Querying an entity can be done by UUID: + +```perl +$book = $client->get_entity("books", $uuid); +``` + +or by name: + +```perl +$book = $client->get_entity("books", "Neuromancer"); +``` + +Similarly, an entity can be deleted by UUID or by name: + +```perl +$client->delete_entity_by_id("books", $uuid_or_name); + +# An entity can be also deleted by passing an entity object +$client->delete_entity($entity); +``` + +### Collections + +A collection can be retrieved as shown below: + +```perl +$collection = $client->get_collection("books"); + +# Outputs the number of records in the collection +print "$collection->count()\n"; +``` + +To iterate over the collection: + +```perl +while ($collection->has_next_entity()) { + $book = $collection->get_next_entity(); + print "$book->get('name')\n"; +} +``` + +Note that by default a collection returns a maximum of 10 records per page. This +can be overridden when retrieving the collection as shown below: + +```perl +$collection = $client->get_collection("books", 30); + +# Retrieve the first book in the collection's current page +$first_book = $collection->get_first_entity(); + +# Retrieve the last book in the collection's current page +$last_book = $collection->get_last_entity(); +``` + +To navigate the pages in the collection: + +```perl +$collection->get_next_page(); + +$collection->get_prev_page(); +``` + +Both of the above return FALSE if the end or the beginning of the collection +is reached. + +When iterating through a collection, the auto_page attribute can be set +to allow the collection to transparently fetch the next page when iterating. + +```perl +$collection = $client->get_collection("books"); + +$collection->auto_page(1); + +while ($collection->has_next_entity()) { + my $entity = $collection->get_next_entity(); + # do something +} +``` + +### Querying & Batch Updates + +Collections can be queried using a SQL-like query language for greater control +over the data set that is returned. + +```perl +$collection = $client->query_collection("books", "select * where genre = 'Cyberpunk'", $limit ); +``` + +Queries can also be used when deleting collections: + +```perl +$collection = $client->delete_collection("books", "select * where genre = 'Cyberpunk'", $limit); +``` + +If the $limit is omitted in the above method calls, a default of 10 is assumed. + +A collection can be batch updated as shown below: + +```perl +$client->update_collection("books", { in_stock => 1 }); +``` + +A query can be used to fine-tune the update: + +```perl +$client->update_collection("books", { in_stock => 0 }, "select * where genre = 'Cyberpunk'", $limit); +``` + +Similarly, entities can be deleted in batch: + +```perl +$client->delete_collection("books", "select * where genre = 'Novel'", $limit); +``` + +### Entity Connections + +Connections can be created between entities through relationships as shown below: + +```perl +$book1 = $client->add_entity("books", { name => "Neuromancer", author => "William Gibson" }); +$book2 = $client->add_entity("books", { name => "Count Zero", author => "William Gibson" }); +$book3 = $client->add_entity("books", { name => "Mona Lisa Overdrive", author => "William Gibson" }); + +$client->connect_entities($book1, "similar_to", $book2); +$client->connect_entities($book1, "similar_to", $book3); +``` + +They can also be queried just like any other collection: + +```perl +$collection = $client->query_connections($book1, "similar_to"); + +# Queries and limits can also be passed in +$collection = $client->query_connections($book1, "similar_to", $query, $limit); +``` + +To delete a connection: + +```perl +$client->disconnect_entities($book1, "similar_to", $book2); +``` + +### Code Coverage + +Code coverage reporting requires Devel::Cover module which can be +installed from CPAN as shown: + + $ sudo cpan install Devel::Cover + +For generating reports on code coverage: + + $ ./Build testcover + +The generated report artifacts are located in cover_db/. + +## Release notes + +### 0.22 + +* Auto paging for collections + +### 0.21 + +* Documentation updates + +### 0.2 + +* Creating, querying and deleting entity connections +* Bi-directional collection pagination + +### 0.11 + +* Added namespace::autoclean.pm as a dependency to fix build break on some + platforms + +### 0.1 + +* Initial release +* Application and admin authentication +* Support for collections and entities (CRUD & queries) + +## License +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +the ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. http://git-wip-us.apache.org/repos/asf/usergrid/blob/867060fa/sdks/other/perl/examples/books.pl ---------------------------------------------------------------------- diff --git a/sdks/other/perl/examples/books.pl b/sdks/other/perl/examples/books.pl new file mode 100644 index 0000000..842d378 --- /dev/null +++ b/sdks/other/perl/examples/books.pl @@ -0,0 +1,48 @@ +#!/usr/bin/perl + +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +use Usergrid::Client; + +# Create a client object for Usergrid that's used for subsequent activity +my $client = Usergrid::Client->new( + organization => 'test-organization', + application => 'test-app', + api_url => 'http://localhost:8080', + trace => 0 +); + +# Logs the user in. The security token is maintained by the library in memory +$client->login('johndoe', 'Johndoe123$'); + +# Add two entities to the "books" collection +$client->add_entity("books", { name => "Ulysses", author => "James Joyce" }); +$client->add_entity("books", { name => "Neuromancer", author => "William Gibson" }); + +# Retrieve a handle to the collection +my $books = $client->get_collection("books"); + +# Add a new attribute for quantity in stock +while ($books->has_next_entity()) { + my $book = $books->get_next_entity(); + + print "Name: " . $book->get('name') . ", "; + print "Author: " . $book->get('author') . "\n"; + + # Create a new attribute and update the entity + $book->set("in-stock", 0); + $client->update_entity($book); +} http://git-wip-us.apache.org/repos/asf/usergrid/blob/867060fa/sdks/other/perl/lib/Usergrid/Client.pm ---------------------------------------------------------------------- diff --git a/sdks/other/perl/lib/Usergrid/Client.pm b/sdks/other/perl/lib/Usergrid/Client.pm new file mode 100644 index 0000000..ccb276b --- /dev/null +++ b/sdks/other/perl/lib/Usergrid/Client.pm @@ -0,0 +1,492 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +package Usergrid::Client; + +use Moose; +use namespace::autoclean; +use Usergrid::Entity; +use Usergrid::Collection; + +with ( + 'Usergrid::Request', +); + +our $VERSION = '0.22'; + +=head1 NAME + +Usergrid::Client - Usergrid Perl Client + +=head1 SYNOPSIS + + use Usergrid::Client; + + my $client = Usergrid::Client->new( + organization => 'test-organization', + application => 'test-app', + api_url => 'http://localhost:8080' + ); + + $client->login('johndoe', 'Johndoe123$'); + + $client->add_entity("books", { name => "Ulysses" }); + $client->add_entity("books", { name => "Neuromancer" }); + + my $books = $client->get_collection("books"); + + while ($books->has_next_entity()) { + my $book = $books->get_next_entity(); + + print "Name: " . $book->get('name') . ", "; + print "Author: " . $book->get('author') . "\n"; + + $book->set("in-stock", 0); + $client->update_entity($book); + } + +=head1 DESCRIPTION + +Usergrid::Client provides an easy to use Perl API for Apache Usergrid. + +=head1 WHAT IS APACHE USERGRID + +Usergrid is an open-source Backend-as-a-Service ("BaaS" or "mBaaS") composed of +an integrated distributed NoSQL database, application layer and client tier with +SDKs for developers looking to rapidly build web and/or mobile applications. +It provides elementary services (user registration & management, data storage, +file storage, queues) and retrieval features (full text search, geolocation +search, joins) to power common app features. + +It is a multi-tenant system designed for deployment to public cloud environments +(such as Amazon Web Services, Rackspace, etc.) or to run on traditional server +infrastructures so that anyone can run their own private BaaS deployment. + +For architects and back-end teams, it aims to provide a distributed, easily +extendable, operationally predictable and highly scalable solution. For +front-end developers, it aims to simplify the development process by enabling +them to rapidly build and operate mobile and web applications without requiring +backend expertise. + +Source: L<https://usergrid.apache.org/docs/> + +For more information, visit L<http://www.usergrid.org> + +=head1 ATTRIBUTES + +The following attributes are made available via the L<Usergrid::Request> role: + +=over 4 + +=item organization (String) + +Organization name + +=item application (String) + +Application name + +=item api_url (String) + +URL of the Usergrid instance + +=item trace (Boolean) + +Enable/disable request and response tracing for debugging and troubleshooting +(Optional) + +=back + +=head1 METHODS + +The following methods are provided in this API for interacting with the Apache +Usergrid backend. + +=head2 Authentication + +=over 4 + +=item login ( $username, $password ) + +Performs application user authentication. Returns the user token for the +logged in user. The token is also kept in memory and used for subsequent +authentication of requests. + +=cut +sub login { + my ($self, $username, $password) = @_; + + my %request = ( + grant_type => "password", + username => $username, + password => $password + ); + + my $uri = URI::Template + ->new('/{organization}/{application}/token') + ->process( + organization => $self->organization, + application => $self->application + ); + + my $token = $self->POST($uri, \%request); + + $self->user_token($token); + + return $self->user_token; +} + +=item admin_login ( $username, $password ) + +Performs admin user authentication. Returns the user token for the +logged in user. The token is also kept in memory and used for subsequent +authentication of requests. + +=cut +sub management_login { + my ($self, $username, $password) = @_; + + my %request = ( + grant_type => "password", + username => $username, + password => $password + ); + + my $token = $self->POST('/management/token', \%request); + + $self->user_token($token); + + return $self->user_token; +} + +=back + +=head2 Entities + +This section covers some of the entity level methods available in the API. +Entities form one of the basic building blocks of Usergrid and is analogous to +a row in an RDBMS table. + +=over 4 + +=item add_entity ( $collection, \%entity ) + +Creates a new entity within the specified collection. Returns a L<Usergrid::Entity> +for the newly added entity. + +=cut +sub add_entity { + my ($self, $collection, $entity) = @_; + + my $uri = URI::Template + ->new('/{organization}/{application}/{collection}') + ->process( + organization => $self->organization, + application => $self->application, + collection => $collection + ); + + return Usergrid::Entity->new( object => $self->POST($uri, $entity)); +} + +=item update_entity ( L<Usergrid::Entity> ) + +Saves changes to the given entity. Returns the updated L<Usergrid::Entity>. + +=cut +sub update_entity { + my ($self, $entity) = @_; + + my $uri = URI::Template + ->new('/{organization}/{application}/{collection}/{uuid}') + ->process( + organization => $self->organization, + application => $self->application, + collection => $entity->get('type'), + uuid => $entity->get('uuid') + ); + + return Usergrid::Entity->new( object => $self->PUT($uri, + $entity->object->{'entities'}[0]) ); +} + +=item get_entity ( $collection, $id ) + +Returns a L<Usergrid::Entity> identified by either UUID or name. +If the entity does not exist, the method returns FALSE. + +=cut +sub get_entity { + my ($self, $collection, $id_or_name) = @_; + + my $uri = URI::Template + ->new('/{organization}/{application}/{collection}/{id_or_name}') + ->process( + organization => $self->organization, + application => $self->application, + collection => $collection, + id_or_name => $id_or_name + ); + + my $response = $self->GET($uri); + + return undef if (! $response); + + return Usergrid::Entity->new( object => $response ); +} + +=item delete_entity_by_id ( $collection, $id ) + +Deletes an entity from the collection identified by either UUID or name. Returns +a L<Usergrid::Entity> of the deleted entity. + +=cut +sub delete_entity_by_id { + my ($self, $collection, $id_or_name) = @_; + + my $uri = URI::Template + ->new('/{organization}/{application}/{collection}/{id}') + ->process( + organization => $self->organization, + application => $self->application, + collection => $collection, + id => $id_or_name + ); + + return Usergrid::Entity->new( object => $self->DELETE($uri) ); +} + +=item delete_entity ( L<Usergrid::Entity> ) + +Deletes the specified L<Usergrid::Entity>. Returns an instance of the deleted +L<Usergrid::Entity> if successful. + +=cut +sub delete_entity { + my ($self, $entity) = @_; + + my $uri = URI::Template + ->new('/{organization}/{application}/{collection}/{uuid}') + ->process( + organization => $self->organization, + application => $self->application, + collection => $entity->get('type'), + uuid => $entity->get('uuid') + ); + + return Usergrid::Entity->new( object => $self->DELETE($uri) ); +} + +=item connect_entities ( L<Usergrid::Entity>, $relationship, L<Usergrid::Entity> ) + +Creates a connection from Entity #1 to Entity #2 signified by the relationship. +The first entity in this relationship is identified as the connecting entity and +the second as the connected entity. The relationship is a string that signifies +the type of connection. This returns a L<Usergrid::Entity> of the connecting +entity. + +=cut +sub connect_entities { + my ($self, $connecting, $relationship, $connected) = @_; + + my $uri = URI::Template + ->new ('/{organization}/{application}/{connecting_collection}/{connecting}/{relationship}/{connected_collection}/{connected}') + ->process( + organization => $self->organization, + application => $self->application, + connecting_collection => $connecting->get('type'), + connecting => $connecting->get('uuid'), + relationship => $relationship, + connected_collection => $connected->get('type'), + connected => $connected->get('uuid') + ); + + return Usergrid::Entity->new ( object => $self->POST($uri)); +} + +=item disconnect_entities ( L<Usergrid::Entity>, $relationship, L<Usergrid::Entity> ) + +Removes the connection between the two entities signified by the relationship. +This does not affect the entities in any other way apart from the removal of the +connection that is depicted by the relationship. This returns a L<Usergrid::Entity> +of the connecting entity with the given relationship removed. + +=cut +sub disconnect_entities { +my ($self, $connecting, $relationship, $connected) = @_; + +my $uri = URI::Template + ->new ('/{organization}/{application}/{connecting_collection}/{connecting}/{relationship}/{connected_collection}/{connected}') + ->process( + organization => $self->organization, + application => $self->application, + connecting_collection => $connecting->get('type'), + connecting => $connecting->get('uuid'), + relationship => $relationship, + connected_collection => $connected->get('type'), + connected => $connected->get('uuid') + ); + + return Usergrid::Entity->new ( object => $self->DELETE($uri)); +} + +=back + +=head2 Collections + +This section covers the methods related to retrieving and working with +collections in the Usergrid API. Collections contains groups of entities and is +analogous to a table in an RDBMS. + +=over 4 + +=item get_collection ( $collection, [ $limit ] ) + +Returns a L<Usergrid::Collection> with the list of entities up to the maximum +specified limit, which is 10 if not provided. + +=cut +sub get_collection { + my ($self, $collection, $limit) = @_; + + my $uri = URI::Template + ->new('/{organization}/{application}/{collection}?limit={limit}') + ->process( + organization => $self->organization, + application => $self->application, + collection => $collection, + limit => ( defined $limit ) ? $limit: 10 + ); + + return $self->_collection($self->GET($uri), $uri); +} + +=item update_collection ( $collection, \%attributes, [ $query ], [ $limit ] ) + +Updates all the entities in the specified collection with the provided attributes. +Optionally pass in the SQL-like query to narrow the scope of the objects that +are affected. This also supports specifying a limit to restrict the maximum number of +records that are updated. If not specified, the limit defaults to 10 entities. + +=cut +sub update_collection { + my ($self, $collection, $properties, $query, $limit) = @_; + + my $uri = URI::Template + ->new('/{organization}/{application}/{collection}/?limit={limit}&ql={query}') + ->process( + organization => $self->organization, + application => $self->application, + collection => $collection, + limit => ( defined $limit ) ? $limit : 10, + query => ( defined $query ) ? $query : undef + ); + + return $self->_collection($self->PUT($uri, $properties), $uri); +} + +=item delete_collection ( $collection, [ $query ], [ $limit ] ) + +Batch delete entities in the specified collection. Optionally pass in a SQL-like +query to narrow the scope of the objects that are affected and a limit to restrict +the maximum number of records that are deleted. If not specified, the limit +defaults to 10 entities. + +=cut +sub delete_collection { + my ($self, $collection, $query, $limit) = @_; + + my $uri = URI::Template + ->new('/{organization}/{application}/{collection}/?limit={limit}&ql={query}') + ->process( + organization => $self->organization, + application => $self->application, + collection => $collection, + limit => ( defined $limit ) ? $limit : 10, + query => ( defined $query ) ? $query : undef + ); + + return $self->_collection($self->DELETE($uri), $uri); +} + +=item query_collection ( $collection, $query, [ $limit ] ) + +Queries all the entities in the specified collection using a SQL-like query string. +This also supports specifying a limit to restrict the maximum number of +records that are returned. If not specified, the limit defaults to 10 entities. + +=cut +sub query_collection { + my ($self, $collection, $query, $limit) = @_; + + my $uri = URI::Template + ->new('/{organization}/{application}/{collection}?limit={limit}&ql={ql}') + ->process( + organization => $self->organization, + application => $self->application, + collection => $collection, + limit => ( defined $limit ) ? $limit : 10, + ql => $query + ); + + return $self->_collection($self->GET($uri), $uri); +} + +=item query_connections ( $entity, $relationship, [ $query ], [ $limit ] ) + +Returns a collection of entities for the given relationship, optionally filtered +by a SQL-like query and limited to the maximum number of records specified. If no limit +is provided, a default of 10 entities is assumed. + +=cut +sub query_connections { + my ($self, $entity, $relationship, $query, $limit) = @_; + + my $uri = URI::Template + ->new('/{organization}/{application}/{collection}/{id}/{relationship}?limit={limit}&ql={ql}') + ->process( + organization => $self->organization, + application => $self->application, + collection => $entity->get('type'), + id => $entity->get('uuid'), + relationship => $relationship, + limit => ( defined $limit ) ? $limit : 10, + ql => $query + ); + + return $self->_collection($self->GET($uri), $uri); +} + +__PACKAGE__->meta->make_immutable; + +1; + +__END__ + +=back + +=head1 SEE ALSO + +L<Usergrid::Collection>, L<Usergrid::Entity>, L<Usergrid::Request> + +=head1 LICENSE + +This software is distributed under the Apache 2 license. + +=head1 AUTHOR + +Anuradha Weeraman <[email protected]> + +=cut http://git-wip-us.apache.org/repos/asf/usergrid/blob/867060fa/sdks/other/perl/lib/Usergrid/Collection.pm ---------------------------------------------------------------------- diff --git a/sdks/other/perl/lib/Usergrid/Collection.pm b/sdks/other/perl/lib/Usergrid/Collection.pm new file mode 100644 index 0000000..8d64ce6 --- /dev/null +++ b/sdks/other/perl/lib/Usergrid/Collection.pm @@ -0,0 +1,212 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +package Usergrid::Collection; + +use Moose; +use namespace::autoclean; + +with ( + 'Usergrid::Request', +); + +my @stack; + +=head1 NAME + +Usergrid::Collection - a Usergrid collection + +=head1 DESCRIPTION + +Encapsulates Usergrid collections and provides methods for iterating and paging +through them. + +=head1 ATTRIBUTES + +=over 4 + +=item object + +A hash reference with the collection data + +=item uri + +The URI from which this collection was retrieved + +=item auto_page + +When set, the collection will automatically move to the next page when iterating +through the collection + +=back + +=cut +has 'object' => ( is => 'rw', required => 1 ); +has 'uri' => ( is => 'rw', required => 1 ); +has 'auto_page' => ( is => 'rw', isa => 'Bool' ); +has 'iterator' => ( is => 'rw', isa => 'Int', default => sub { -1 } ); + +=head1 METHODS + +=over 4 + +=item has_next_entity + +Returns true if there's another entity available during iteration. + +=cut +sub has_next_entity { + my $self = shift; + my $next = $self->iterator + 1; + if ( $self->auto_page && $next >= $self->count()) { + return $self->get_next_page(); + } + return ($next >= 0 && $next < $self->count()); +} + +=item get_next_entity + +Returns the next available L<Usergrid::Entity>. If there's no entity available +to return, it returns a FALSE. If the auto_page attribute it set, the next page +is automatically fetched and the next entity is returned. + +=cut +sub get_next_entity { + my $self = shift; + if ($self->has_next_entity()) { + $self->iterator ($self->iterator + 1); + return Usergrid::Entity->new ( object => $self->object->{'entities'}[$self->iterator] ); + } + return undef; +} + +=item count + +Returns the count of the items in the collection. + +=cut +sub count { + my $self = shift; + return scalar @{$self->object->{'entities'}}; +} + +=item reset_iterator + +Rewinds the iterator back to the beginning. + +=cut +sub reset_iterator { + my $self = shift; + $self->iterator (-1); +} + +=item get_first_entity + +Returns the first entity in the collection. This is only applicable for the +current page of the collection. + +=cut +sub get_first_entity { + my $self = shift; + return ($self->count() > 0) ? Usergrid::Entity->new ( + object => $self->object->{'entities'}[0] ) : undef; +} + +=item get_last_entity + +Returns the last entity in the collection. This is only applicable for the +current page of the collection. + +=cut +sub get_last_entity { + my $self = shift; + return ($self->count() > 0) ? Usergrid::Entity->new ( + object => $self->object->{'entities'}[$self->count() - 1] ) : undef; + +} + +=item get_next_page + +Fetches the next page in the collection. Returns false when there are no more reults. + +=cut +sub get_next_page { + my $self = shift; + + my $csr = $self->object->{'cursor'}; + + my $object = $self->GET($self->uri . "&cursor=". $csr); + + if ($object->{'count'} > 0) { + push @stack, "1" if (scalar @stack == 0); + push @stack, $csr; + + $self->object( $object ); + $self->reset_iterator(); + + return $self; + } + + 0; +} + +=item get_prev_page + +Fetches the previous page in the collection. Returns false when there are no more reults. + +=cut +sub get_prev_page { + my $self = shift; + my $object; + + if (scalar @stack > 0) { + my $csr = pop @stack; + + if ($csr eq "1") { + $object = $self->GET($self->uri); + } else { + $object = $self->GET($self->uri . "&cursor=" . $csr); + } + + $self->object( $object ); + $self->reset_iterator(); + + return $self; + } + + 0; +} + +__PACKAGE__->meta->make_immutable; + +1; + +__END__ + +=back + +=head1 SEE ALSO + +L<Usergrid::Client>, L<Usergrid::Entity>, L<Usergrid::Request> + +=head1 LICENSE + +This software is distributed under the Apache 2 license. + +=head1 AUTHOR + +Anuradha Weeraman <[email protected]> + +=cut http://git-wip-us.apache.org/repos/asf/usergrid/blob/867060fa/sdks/other/perl/lib/Usergrid/Entity.pm ---------------------------------------------------------------------- diff --git a/sdks/other/perl/lib/Usergrid/Entity.pm b/sdks/other/perl/lib/Usergrid/Entity.pm new file mode 100644 index 0000000..051e9b6 --- /dev/null +++ b/sdks/other/perl/lib/Usergrid/Entity.pm @@ -0,0 +1,92 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +package Usergrid::Entity; + +use Moose; +use namespace::autoclean; + +=head1 NAME + +Usergrid::Entity - a Usergrid entity + +=head1 DESCRIPTION + +Encapsulates Usergrid entities and provides methods for accessing the underlying +data. + +=head1 ATTRIBUTES + +=over 4 + +=item object + +A hash reference with the entity data + +=back + +=cut +has 'object' => ( is => 'rw', required => 1); + +=head1 METHODS + +=over 4 + +=item get ( $attribute_name ) + +Returns the value of the specified attribute. + +=cut +sub get { + my ($self, $key) = @_; + return $self->object->{$key} if (defined $self->object->{$key}); + return $self->object->{'entities'}[0]->{$key}; +} + +=item set ( $attribute_name, $value ) + +Sets the value of the specified attribute. + +=cut +sub set { + my ($self, $key, $value) = @_; + if (defined $self->object->{$key}) { + $self->object->{$key} = $value; + return; + } + $self->object->{'entities'}[0]->{$key} = $value; +} + +__PACKAGE__->meta->make_immutable; + +1; + +__END__ + +=back + +=head1 SEE ALSO + +L<Usergrid::Client>, L<Usergrid::Collection>, L<Usergrid::Request> + +=head1 LICENSE + +This software is distributed under the Apache 2 license. + +=head1 AUTHOR + +Anuradha Weeraman <[email protected]> + +=cut http://git-wip-us.apache.org/repos/asf/usergrid/blob/867060fa/sdks/other/perl/lib/Usergrid/Request.pm ---------------------------------------------------------------------- diff --git a/sdks/other/perl/lib/Usergrid/Request.pm b/sdks/other/perl/lib/Usergrid/Request.pm new file mode 100644 index 0000000..3768039 --- /dev/null +++ b/sdks/other/perl/lib/Usergrid/Request.pm @@ -0,0 +1,249 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +package Usergrid::Request; + +use Moose::Role; +use namespace::autoclean; +use Carp qw(confess); +use Log::Log4perl qw(:easy); +use REST::Client; +use URI::Template; +use JSON; + +my $json = JSON->new->allow_nonref; + +=head1 NAME + +Usergrid::Request - Role that provides HTTP invocation and utility methods + +=head1 DESCRIPTION + +This is a Role that is applied to L<Usergrid::Client> and L<Usergrid::Collection> +which provides methods that relate to HTTP invocation as well as some utility +functions. + +=head1 ATTRIBUTES + +=over 4 + +=item organization (String) + +Organization name + +=item application (String) + +Application name + +=item api_url (String) + +URL of the Usergrid instance + +=item trace (Boolean) + +Enable/disable request and response tracing for debugging and troubleshooting +(Optional) + +=cut +has 'organization' => ( is => 'rw', isa => 'Str', required => 1); +has 'application' => ( is => 'rw', isa => 'Str', required => 1); +has 'api_url' => ( is => 'rw', isa => 'Str', required => 1); + +has 'trace' => ( is => 'rw', isa => 'Bool', trigger => \&_enable_tracing); + +has 'user_token' => ( is => 'rw'); + +# internal method +sub _is_token_required { + my ($self, $resource) = @_; + return 0 if $resource =~ m/\/management\/token/; + 1; +} + +# internal method +sub _api_request { + my ($self, $method, $resource, $request) = @_; + + $self->trace_message("$method $resource"); + $self->trace_message("REQUEST: " . $self->prettify($request)) if ($request); + + my $client = REST::Client->new(); + $client->setHost($self->api_url); + + if ($self->_is_token_required($resource) == 1 && defined $self->user_token) { + $client->addHeader('Authorization', + 'Bearer ' . $self->user_token->{'access_token'}); + } + + my $json_req = $self->json_encode($request) if ($request); + + $client->DELETE($resource) if ($method eq 'DELETE'); + $client->GET($resource) if ($method eq 'GET'); + $client->POST($resource, $json_req) if ($method eq 'POST'); + $client->PUT($resource, $json_req) if ($method eq 'PUT'); + + my $response = $client->responseContent(); + + $self->trace_message("RESPONSE: " . $self->prettify($response)) if ($response); + + return undef if ($client->responseCode() eq "404"); + + confess "Bad request" if ($client->responseCode() eq "400"); + confess "Unauthorized" if ($client->responseCode() eq "401"); + confess "Forbidden" if ($client->responseCode() eq "403"); + confess "Server error" if ($client->responseCode() eq "500"); + + return $self->json_decode($response); +} + +# internal method +sub _enable_tracing { + my ($self, $state, $old_state) = @_; + if ($state) { + Log::Log4perl::easy_init($DEBUG); + our $logger = Log::Log4perl->get_logger(); + } +} + +# internal method +sub _collection { + my ($self, $object, $uri) = @_; + + return Usergrid::Collection->new ( + object => $object, + uri => $uri, + organization => $self->organization, + application => $self->application, + api_url => $self->api_url, + trace => $self->trace, + user_token => $self->user_token + ); +} + +=back + +=head1 METHODS + +=head2 HTTP Invocation Methods + +=over 4 + +=item DELETE ( $resource ) + +Invokes HTTP DELETE on the specified resource. + +=cut +sub DELETE { + my ($self, $resource) = @_; + $self->_api_request('DELETE', $resource); +} + +=item GET ( $resource ) + +Invokes HTTP GET on the specified resource. + +=cut +sub GET { + my ($self, $resource) = @_; + $self->_api_request('GET', $resource); +} + +=item POST ( $resource, \%request ) + +Invokes HTTP POST on the specified resource and passes in the payload +for the request. + +=cut +sub POST { + my ($self, $resource, $request) = @_; + $self->_api_request('POST', $resource, $request); +} + +=item PUT ( $resource, \%request ) + +Invokes HTTP PUT on the specified resource and passes in the payload +for the request. + +=cut +sub PUT { + my ($self, $resource, $request) = @_; + $self->_api_request('PUT', $resource, $request); +} + +=back + +=head2 Utility Methods + +=over 4 + +=item trace_message ( $message ) + +Utility method to log a message to console if tracing is enabled. + +=cut +sub trace_message { + my ($self, $message) = @_; + $Usergrid::Request::logger->debug($message) if (defined $Usergrid::Request::logger); +} + +=item prettify ( $message, \%object ) + +Returns a prettified string representation hash reference. + +=cut +sub prettify { + my ($self, $json_obj) = @_; + return $json->pretty->encode($json_obj); +} + +=item json_encode ( \%hashref ) + +Returns a JSON object from a hash reference. + +=cut +sub json_encode { + my ($self, $json_obj) = @_; + $json->encode($json_obj); +} + +=item json_decode ( $json_object ) + +Returns a hash reference from a JSON object. + +=cut +sub json_decode { + my ($self, $json_obj) = @_; + $json->decode($json_obj); +} + +1; + +__END__ + +=back + +=head1 SEE ALSO + +L<Usergrid::Client>, L<Usergrid::Collection>, L<Usergrid::Entity> + +=head1 LICENSE + +This software is distributed under the Apache 2 license. + +=head1 AUTHOR + +Anuradha Weeraman <[email protected]> + +=cut http://git-wip-us.apache.org/repos/asf/usergrid/blob/867060fa/sdks/other/perl/t/01_init.t ---------------------------------------------------------------------- diff --git a/sdks/other/perl/t/01_init.t b/sdks/other/perl/t/01_init.t new file mode 100644 index 0000000..c410155 --- /dev/null +++ b/sdks/other/perl/t/01_init.t @@ -0,0 +1,27 @@ +#!/usr/bin/perl + +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +use strict; +use warnings; + +use Test::More tests => 3; + +BEGIN { + use_ok 'Usergrid::Client' || print "Bail out!\n"; + use_ok 'Usergrid::Entity' || print "Bail out!\n"; + use_ok 'Usergrid::Collection' || print "Bail out!\n"; +} http://git-wip-us.apache.org/repos/asf/usergrid/blob/867060fa/sdks/other/perl/t/02_login.t ---------------------------------------------------------------------- diff --git a/sdks/other/perl/t/02_login.t b/sdks/other/perl/t/02_login.t new file mode 100644 index 0000000..8397b92 --- /dev/null +++ b/sdks/other/perl/t/02_login.t @@ -0,0 +1,64 @@ +#!/usr/bin/perl + +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +use strict; +use warnings; +use Usergrid::Client; +use IO::Socket::INET; +use Test::More; + +# TEST DATA +our $hostname = 'localhost'; +our $port = '8080'; +our $api_url = "http://$hostname:$port"; +our $organization = 'test-organization'; +our $application = 'test-app'; +our $username = 'testuser'; +our $password = 'Testuser123$'; +########### + +if (_check_port($hostname, $port)) { + plan tests => 1; +} else { + plan skip_all => "server $api_url not reachable" +} + +sub _check_port { + my ($hostname, $port) = @_; + new IO::Socket::INET ( PeerAddr => $hostname, PeerPort => $port, + Proto => 'tcp' ) || return 0; + return 1; +} + +my ($user, $token); + +# Create the client object that will be used for all subsequent requests +my $client = Usergrid::Client->new( + organization => $organization, + application => $application, + api_url => $api_url, + trace => 0 +); + +# Create a test user +$user = $client->add_entity("users", { username=>$username, password=>$password }); + +$token = $client->login($username, $password); + +ok ( $token->{user}->{username} eq $username, "user logged in" ); + +$client->delete_entity($user); http://git-wip-us.apache.org/repos/asf/usergrid/blob/867060fa/sdks/other/perl/t/03_update.t ---------------------------------------------------------------------- diff --git a/sdks/other/perl/t/03_update.t b/sdks/other/perl/t/03_update.t new file mode 100644 index 0000000..b05a260 --- /dev/null +++ b/sdks/other/perl/t/03_update.t @@ -0,0 +1,102 @@ +#!/usr/bin/perl + +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +use strict; +use warnings; +use Usergrid::Client; +use IO::Socket::INET; +use Test::More; + +# TEST DATA +my $hostname = 'localhost'; +my $port = '8080'; +my $api_url = "http://$hostname:$port"; +my $organization = 'test-organization'; +my $application = 'test-app'; +my $username = 'testuser'; +my $password = 'Testuser123$'; +########### + +if (_check_port($hostname, $port)) { + plan tests => 7; +} else { + plan skip_all => "server $api_url not reachable" +} + +sub _check_port { + my ($hostname, $port) = @_; + new IO::Socket::INET ( PeerAddr => $hostname, PeerPort => $port, + Proto => 'tcp' ) || return 0; + return 1; +} + +my ($user, $token, $book, $deleted_book); + +# Create the client object that will be used for all subsequent requests +my $client = Usergrid::Client->new( + organization => $organization, + application => $application, + api_url => $api_url, + trace => 0 +); + +# Create a test user +$user = $client->add_entity("users", { username=>$username, password=>$password }); + +$token = $client->login($username, $password); + +eval { + + ok ( $token->{user}->{username} eq $username, "user logged in" ); + + $book = $client->add_entity("books", { name => "Ulysses", author => "James Joyce" }); + + ok ( $book->get('author') eq "James Joyce", "check entity creation" ); + + $book->set('genre', 'Modernist'); + + $book = $client->update_entity($book); + + ok ( $book->get('genre') eq "Modernist", "check for new attribute" ); + + $book->set('genre', 'Novel'); + + $book = $client->update_entity($book); + + ok ( $book->get('genre') eq "Novel", "check for updated attribute" ); + + $book = $client->get_entity("books", $book->get('uuid')); + + ok ( $book->get('genre') eq "Novel", "check again for updated attribute by uuid" ); + + $book = $client->get_entity("books", "Ulysses"); + + ok ( $book->get('name') eq 'Ulysses', "get object by name") ; + + $book = $client->delete_entity_by_id("books", $book->get('uuid')); + + $deleted_book = $client->get_entity("books", $book->get('uuid')); + + ok ( (! defined $deleted_book), "deleted book cannot be found" ); + +}; + +diag($@) if $@; + +# Cleanup +$client->delete_entity($book); +$client->delete_entity($user); http://git-wip-us.apache.org/repos/asf/usergrid/blob/867060fa/sdks/other/perl/t/04_collection.t ---------------------------------------------------------------------- diff --git a/sdks/other/perl/t/04_collection.t b/sdks/other/perl/t/04_collection.t new file mode 100644 index 0000000..d823ced --- /dev/null +++ b/sdks/other/perl/t/04_collection.t @@ -0,0 +1,104 @@ +#!/usr/bin/perl + +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +use strict; +use warnings; +use Usergrid::Client; +use IO::Socket::INET; +use Test::More; + +# TEST DATA +my $hostname = 'localhost'; +my $port = '8080'; +my $api_url = "http://$hostname:$port"; +my $organization = 'test-organization'; +my $application = 'test-app'; +my $username = 'testuser'; +my $password = 'Testuser123$'; +########### + +if (_check_port($hostname, $port)) { + plan tests => 10; +} else { + plan skip_all => "server $api_url not reachable" +} + +sub _check_port { + my ($hostname, $port) = @_; + new IO::Socket::INET ( PeerAddr => $hostname, PeerPort => $port, + Proto => 'tcp' ) || return 0; + return 1; +} + +my ($user, $token, $book, $collection); + +# Create the client object that will be used for all subsequent requests +my $client = Usergrid::Client->new( + organization => $organization, + application => $application, + api_url => $api_url, + trace => 0 +); + +# Create a test user +$user = $client->add_entity("users", { username=>$username, password=>$password }); + +$token = $client->login($username, $password); + +eval { + + $collection = $client->get_collection("books"); + + ok ( $collection->count() == 0, "count must be initially zero" ); + + $client->add_entity("books", { name => "Ulysses", author => "James Joyce" }); + $client->add_entity("books", { name => "Neuromancer", author => "William Gibson" }); + $client->add_entity("books", { name => "On the Road", author => "Jack Kerouac" }); + $client->add_entity("books", { name => "Ubik", author => "Philip K. Dick" }); + $client->add_entity("books", { name => "Reef", author => "Romesh Gunasekera" }); + + $collection = $client->get_collection("books"); + + ok ( $collection->count() == 5, "count must now be five" ); + + while ($collection->has_next_entity()) { + $book = $collection->get_next_entity(); + ok ( length($book->get('name')) > 3, "check the book titles" ); + } + + $collection->reset_iterator(); + + ok ( $collection->iterator == -1, "iterator must be reset" ); + + ok ( $collection->count() == 5, "count must be five" ); + + while ($collection->has_next_entity()) { + $book = $collection->get_next_entity(); + $client->delete_entity($book); + } + + $collection = $client->get_collection("books"); + + ok ( $collection->count() == 0, "count must now be again zero" ); + +}; + +diag($@) if $@; + +# Cleanup +$collection = $client->delete_collection("books", undef, 30); +$client->delete_entity($user); http://git-wip-us.apache.org/repos/asf/usergrid/blob/867060fa/sdks/other/perl/t/05_query.t ---------------------------------------------------------------------- diff --git a/sdks/other/perl/t/05_query.t b/sdks/other/perl/t/05_query.t new file mode 100644 index 0000000..40e3f1b --- /dev/null +++ b/sdks/other/perl/t/05_query.t @@ -0,0 +1,108 @@ +#!/usr/bin/perl + +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +use strict; +use warnings; +use Usergrid::Client; +use IO::Socket::INET; +use Test::More; + +# TEST DATA +my $hostname = 'localhost'; +my $port = '8080'; +my $api_url = "http://$hostname:$port"; +my $organization = 'test-organization'; +my $application = 'test-app'; +my $username = 'testuser'; +my $password = 'Testuser123$'; +########### + +if (_check_port($hostname, $port)) { + plan tests => 8; +} else { + plan skip_all => "server $api_url not reachable" +} + +sub _check_port { + my ($hostname, $port) = @_; + new IO::Socket::INET ( PeerAddr => $hostname, PeerPort => $port, + Proto => 'tcp' ) || return 0; + return 1; +} + +my ($user, $token, $book, $collection, $subset); + +# Create the client object that will be used for all subsequent requests +my $client = Usergrid::Client->new( + organization => $organization, + application => $application, + api_url => $api_url, + trace => 0 +); + +# Create a test user +$user = $client->add_entity("users", { username=>$username, password=>$password }); + +$token = $client->login($username, $password); + +eval { + + $collection = $client->get_collection("books"); + + ok ( $collection->count() == 0, "count must be initially zero" ); + + for (my $i = 0; $i < 30; $i++) { + $client->add_entity("books", { name => "book $i", index => $i }); + } + + $collection = $client->get_collection("books", 30); + + ok ( $collection->count() == 30, "count must now be 30" ); + + $book = $collection->get_first_entity(); + + ok ( $book->get('index') eq '0', "first index should be 0"); + + $book = $collection->get_last_entity(); + + ok ( $book->get('index') eq '29', "last index should be 29"); + + $subset = $client->query_collection("books", "select * where index = '5'", 15 ); + + ok ( $subset->count() == 1, "subset is 1" ); + ok ( $subset->object->{'params'}->{'limit'}[0] eq '15', "check limit override" ); + + $book = $subset->get_next_entity(); + + ok ( $book->get('index') eq '5', "query returned the fifth book" ); + + while ($collection->has_next_entity()) { + $book = $collection->get_next_entity(); + $client->delete_entity($book); + } + + $collection = $client->get_collection("books"); + + ok ( $collection->count() == 0, "count must now be again zero" ); + +}; + +diag($@) if $@; + +# Cleanup +$collection = $client->delete_collection("books", undef, 30); +$client->delete_entity($user); http://git-wip-us.apache.org/repos/asf/usergrid/blob/867060fa/sdks/other/perl/t/06_batch_update.t ---------------------------------------------------------------------- diff --git a/sdks/other/perl/t/06_batch_update.t b/sdks/other/perl/t/06_batch_update.t new file mode 100644 index 0000000..04ff11d --- /dev/null +++ b/sdks/other/perl/t/06_batch_update.t @@ -0,0 +1,107 @@ +#!/usr/bin/perl + +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +use strict; +use warnings; +use Usergrid::Client; +use IO::Socket::INET; +use Test::More; + +# TEST DATA +my $hostname = 'localhost'; +my $port = '8080'; +my $api_url = "http://$hostname:$port"; +my $organization = 'test-organization'; +my $application = 'test-app'; +my $username = 'testuser'; +my $password = 'Testuser123$'; +########### + +if (_check_port($hostname, $port)) { + plan tests => 34; +} else { + plan skip_all => "server $api_url not reachable" +} + +sub _check_port { + my ($hostname, $port) = @_; + new IO::Socket::INET ( PeerAddr => $hostname, PeerPort => $port, + Proto => 'tcp' ) || return 0; + return 1; +} + +my ($user, $token, $book, $collection, $count); + +# Create the client object that will be used for all subsequent requests +my $client = Usergrid::Client->new( + organization => $organization, + application => $application, + api_url => $api_url, + trace => 0 +); + +# Create a test user +$user = $client->add_entity("users", { username=>$username, password=>$password }); + +$token = $client->login($username, $password); + +eval { + + $collection = $client->get_collection("books"); + + ok ( $collection->count() == 0, "count must be initially zero" ); + + for (my $i = 0; $i < 30; $i++) { + $client->add_entity("books", { name => "book $i", index => $i }); + } + + $collection = $client->get_collection("books", 30); + + ok ( $collection->count() == 30, "count must now be 30" ); + + $client->update_collection("books", { in_stock => 1 }); + + $collection = $client->get_collection("books", 30); + + while ($collection->has_next_entity()) { + $book = $collection->get_next_entity(); + ok ( $book->get('in_stock') == 1 ); + } + + $client->update_collection("books", { in_stock => 0 }, "select * where index = '1' or index = '2' or index = '3' or index = '4' or index = '5'"); + + $collection = $client->get_collection("books", 30); + + while ($collection->has_next_entity()) { + $book = $collection->get_next_entity(); + $count++ if ($book->get('index') =~ /[12345]/ && $book->get('in_stock') == 0); + $client->delete_entity($book); + } + + ok ( $count == 5, "batch update only 5 entities" ); + + $collection = $client->get_collection("books"); + + ok ( $collection->count() == 0, "count must now be again zero" ); + +}; + +diag($@) if $@; + +# Cleanup +$collection = $client->delete_collection("books", undef, 30); +$client->delete_entity($user); http://git-wip-us.apache.org/repos/asf/usergrid/blob/867060fa/sdks/other/perl/t/07_batch_delete.t ---------------------------------------------------------------------- diff --git a/sdks/other/perl/t/07_batch_delete.t b/sdks/other/perl/t/07_batch_delete.t new file mode 100644 index 0000000..2ed6d72 --- /dev/null +++ b/sdks/other/perl/t/07_batch_delete.t @@ -0,0 +1,87 @@ +#!/usr/bin/perl + +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +use strict; +use warnings; +use Usergrid::Client; +use IO::Socket::INET; +use Test::More; + +# TEST DATA +my $hostname = 'localhost'; +my $port = '8080'; +my $api_url = "http://$hostname:$port"; +my $organization = 'test-organization'; +my $application = 'test-app'; +my $username = 'testuser'; +my $password = 'Testuser123$'; +########### + +if (_check_port($hostname, $port)) { + plan tests => 3; +} else { + plan skip_all => "server $api_url not reachable" +} + +sub _check_port { + my ($hostname, $port) = @_; + new IO::Socket::INET ( PeerAddr => $hostname, PeerPort => $port, + Proto => 'tcp' ) || return 0; + return 1; +} + +my ($user, $token, $book, $collection, $count); + +# Create the client object that will be used for all subsequent requests +my $client = Usergrid::Client->new( + organization => $organization, + application => $application, + api_url => $api_url, + trace => 0 +); + +# Create a test user +$user = $client->add_entity("users", { username=>$username, password=>$password }); + +$token = $client->login($username, $password); + +eval { + + $collection = $client->get_collection("books"); + + ok ( $collection->count() == 0, "count must be initially zero" ); + + for (my $i = 0; $i < 30; $i++) { + $client->add_entity("books", { name => "book $i", index => $i }); + } + + $collection = $client->get_collection("books", 30); + + ok ( $collection->count() == 30, "count must now be 30" ); + + $client->delete_collection("books", "select * where index = '1' or index = '2' or index = '3' or index = '4' or index = '5'"); + + $collection = $client->get_collection("books", 30); + + ok ( $collection->count() == 25, "deleted 5 entities" ); +}; + +diag($@) if $@; + +# Cleanup +$collection = $client->delete_collection("books", undef, 30); +$client->delete_entity($user); http://git-wip-us.apache.org/repos/asf/usergrid/blob/867060fa/sdks/other/perl/t/08_connections.t ---------------------------------------------------------------------- diff --git a/sdks/other/perl/t/08_connections.t b/sdks/other/perl/t/08_connections.t new file mode 100644 index 0000000..35550e0 --- /dev/null +++ b/sdks/other/perl/t/08_connections.t @@ -0,0 +1,94 @@ +#!/usr/bin/perl + +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +use strict; +use warnings; +use Usergrid::Client; +use IO::Socket::INET; +use Test::More; + +# TEST DATA +my $hostname = 'localhost'; +my $port = '8080'; +my $api_url = "http://$hostname:$port"; +my $organization = 'test-organization'; +my $application = 'test-app'; +my $username = 'testuser'; +my $password = 'Testuser123$'; +########### + +if (_check_port($hostname, $port)) { + plan tests => 5; +} else { + plan skip_all => "server $api_url not reachable" +} + +sub _check_port { + my ($hostname, $port) = @_; + new IO::Socket::INET ( PeerAddr => $hostname, PeerPort => $port, + Proto => 'tcp' ) || return 0; + return 1; +} + +my ($user, $token, $book, $book1, $book2, $book3, $collection); + +# Create the client object that will be used for all subsequent requests +my $client = Usergrid::Client->new( + organization => $organization, + application => $application, + api_url => $api_url, + trace => 0 +); + +# Create a test user +$user = $client->add_entity("users", { username=>$username, password=>$password }); + +$token = $client->login($username, $password); + +eval { + $book1 = $client->add_entity("books", { name => "Neuromancer", author => "William Gibson" }); + $book2 = $client->add_entity("books", { name => "Count Zero", author => "William Gibson" }); + $book3 = $client->add_entity("books", { name => "Mona Lisa Overdrive", author => "William Gibson" }); + + $client->connect_entities($book1, "similar_to", $book2); + $client->connect_entities($book1, "similar_to", $book3); + + $collection = $client->query_connections($book1, "similar_to"); + + ok ( $collection->count == 2, "two connections must exist" ); + + while ($collection->has_next_entity()) { + $book = $collection->get_next_entity(); + ok ( $book->get('name') eq 'Count Zero' || $book->get('name') eq ('Mona Lisa Overdrive'), "check connections"); + } + + $client->disconnect_entities($book1, "similar_to", $book2); + + $collection = $client->query_connections($book1, "similar_to"); + + ok ( $collection->count() == 1 ); + + $book = $collection->get_next_entity(); + + ok ( $book->get('name') eq 'Mona Lisa Overdrive', "check remaining connection"); +}; + +diag($@) if $@; + +# Cleanup +$client->delete_collection("books", undef, 10); +$client->delete_entity($user); http://git-wip-us.apache.org/repos/asf/usergrid/blob/867060fa/sdks/other/perl/t/09_paging.t ---------------------------------------------------------------------- diff --git a/sdks/other/perl/t/09_paging.t b/sdks/other/perl/t/09_paging.t new file mode 100644 index 0000000..9a38a7f --- /dev/null +++ b/sdks/other/perl/t/09_paging.t @@ -0,0 +1,140 @@ +#!/usr/bin/perl + +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +use strict; +use warnings; +use Usergrid::Client; +use IO::Socket::INET; +use Test::More; + +# TEST DATA +my $hostname = 'localhost'; +my $port = '8080'; +my $api_url = "http://$hostname:$port"; +my $organization = 'test-organization'; +my $application = 'test-app'; +my $username = 'testuser'; +my $password = 'Testuser123$'; +########### + +if (_check_port($hostname, $port)) { + plan tests => 24; +} else { + plan skip_all => "server $api_url not reachable" +} + +sub _check_port { + my ($hostname, $port) = @_; + new IO::Socket::INET ( PeerAddr => $hostname, PeerPort => $port, + Proto => 'tcp' ) || return 0; + return 1; +} + +my ($user, $token, $book, $collection, $subset, $count); + +# Create the client object that will be used for all subsequent requests +my $client = Usergrid::Client->new( + organization => $organization, + application => $application, + api_url => $api_url, + trace => 0 +); + +# Create a test user +$user = $client->add_entity("users", { username=>$username, password=>$password }); + +$token = $client->login($username, $password); + +eval { + + $collection = $client->get_collection("books"); + + ok ( $collection->count() == 0, "count must be initially zero" ); + + for (my $i = 0; $i < 30; $i++) { + $client->add_entity("books", { name => "book $i", index => $i }); + } + + $collection = $client->get_collection("books", 30); + + ok ( $collection->count() == 30, "count must now be 30" ); + + $collection = $client->get_collection("books", 10); + + ok ( $collection->count == 10, "checking first page count" ); + ok ( $collection->get_first_entity()->get('name') eq 'book 0', "Check first entity of first page"); + ok ( $collection->get_last_entity()->get('name') eq 'book 9', "Check last entity of first page"); + + $collection->get_next_page(); + + ok ( $collection->count == 10, "checking second page count" ); + ok ( $collection->get_first_entity()->get('name') eq 'book 10', "Check first entity of second page"); + ok ( $collection->get_last_entity()->get('name') eq 'book 19', "Check last entity of second page"); + + $collection->get_next_page(); + + ok ( $collection->count == 10, "checking third page count" ); + ok ( $collection->get_first_entity()->get('name') eq 'book 20', "Check first entity of third page"); + ok ( $collection->get_last_entity()->get('name') eq 'book 29', "Check last entity of third page"); + + if (! $collection->get_next_page()) { + pass ( "no more results" ); + } + + $collection->get_prev_page(); + + ok ( $collection->count == 10, "checking third page count" ); + ok ( $collection->get_first_entity()->get('name') eq 'book 20', "Check first entity of third page in reverse"); + ok ( $collection->get_last_entity()->get('name') eq 'book 29', "Check last entity of third page in reverse"); + + $collection->get_prev_page(); + + ok ( $collection->count == 10, "checking second page count" ); + ok ( $collection->get_first_entity()->get('name') eq 'book 10', "Check first entity of second page in reverse"); + ok ( $collection->get_last_entity()->get('name') eq 'book 19', "Check last entity of second page in reverse"); + + $collection->get_prev_page(); + + ok ( $collection->count == 10, "checking first page count" ); + ok ( $collection->get_first_entity()->get('name') eq 'book 0', "Check first entity of first page in reverse"); + ok ( $collection->get_last_entity()->get('name') eq 'book 9', "Check last entity of first page in reverse"); + + if (! $collection->get_prev_page()) { + pass ( "no more results in reverse" ); + } + + $count = 0; + $collection = $client->get_collection("books", 10); + do { + $count += $collection->count(); + } while ($collection->get_next_page()); + + ok ( $count == 30, "should return 30 entities in forward" ); + + $count = 0; + while ($collection->get_prev_page()) { + $count += $collection->count(); + } + + ok ( $count == 30, "should return 30 entities in reverse" ); +}; + +diag($@) if $@; + +# Cleanup +$client->delete_collection("books", undef, 30); +$client->delete_entity($user); http://git-wip-us.apache.org/repos/asf/usergrid/blob/867060fa/sdks/other/perl/t/10_auto_paging.t ---------------------------------------------------------------------- diff --git a/sdks/other/perl/t/10_auto_paging.t b/sdks/other/perl/t/10_auto_paging.t new file mode 100644 index 0000000..5ed98f7 --- /dev/null +++ b/sdks/other/perl/t/10_auto_paging.t @@ -0,0 +1,94 @@ +#!/usr/bin/perl + +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +use strict; +use warnings; +use Usergrid::Client; +use IO::Socket::INET; +use Test::More; + +# TEST DATA +my $hostname = 'localhost'; +my $port = '8080'; +my $api_url = "http://$hostname:$port"; +my $organization = 'test-organization'; +my $application = 'test-app'; +my $username = 'testuser'; +my $password = 'Testuser123$'; +########### + +if (_check_port($hostname, $port)) { + plan tests => 3; +} else { + plan skip_all => "server $api_url not reachable" +} + +sub _check_port { + my ($hostname, $port) = @_; + new IO::Socket::INET ( PeerAddr => $hostname, PeerPort => $port, + Proto => 'tcp' ) || return 0; + return 1; +} + +my ($user, $token, $book, $collection, $subset, $count); + +# Create the client object that will be used for all subsequent requests +my $client = Usergrid::Client->new( + organization => $organization, + application => $application, + api_url => $api_url, + trace => 0 +); + +# Create a test user +$user = $client->add_entity("users", { username=>$username, password=>$password }); + +$token = $client->login($username, $password); + +eval { + + $collection = $client->get_collection("books"); + + ok ( $collection->count() == 0, "count must be initially zero" ); + + for (my $i = 0; $i < 30; $i++) { + $client->add_entity("books", { name => "book $i", index => $i }); + } + + $collection = $client->get_collection("books"); + + ok ( $collection->get_last_entity()->get('name') eq 'book 9', "only return 10 entites per page by default"); + + $collection = $client->get_collection("books"); + + $collection->auto_page(1); + + $count = 0; + + while ($collection->has_next_entity()) { + $count += 1; + my $entity = $collection->get_next_entity(); + } + + ok ( $count == 30, "should return 30 entities in when auto paging" ); +}; + +diag($@) if $@; + +# Cleanup +$client->delete_collection("books", undef, 30); +$client->delete_entity($user); http://git-wip-us.apache.org/repos/asf/usergrid/blob/867060fa/sdks/other/php/LICENSE ---------------------------------------------------------------------- diff --git a/sdks/other/php/LICENSE b/sdks/other/php/LICENSE new file mode 100644 index 0000000..e06d208 --- /dev/null +++ b/sdks/other/php/LICENSE @@ -0,0 +1,202 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +
