From 988336aff5bc1bf7bca8271c47d030baf3bb17e6 Mon Sep 17 00:00:00 2001 From: Edward Fleming Date: Wed, 14 Jan 2015 11:09:31 -0500 Subject: [PATCH] adding readme with release steps, and working through running tests locally instead of on device --- Android-Libtester/Android-Libtester.csproj | 4 +- LibTester/DefaultContext.cs | 30 +++ LibTester/ExpressionVisitor.cs | 86 ++++++++ LibTester/LibTester.csproj | 65 ++++++ LibTester/MyEntity.cs | 32 +++ LibTester/Test.cs | 16 ++ LibTester/TestAppData.cs | 109 ++++++++++ LibTester/packages.config | 6 + README.md | 231 ++------------------- packages/repositories.config | 1 + 10 files changed, 362 insertions(+), 218 deletions(-) create mode 100644 LibTester/DefaultContext.cs create mode 100644 LibTester/ExpressionVisitor.cs create mode 100644 LibTester/LibTester.csproj create mode 100644 LibTester/MyEntity.cs create mode 100644 LibTester/Test.cs create mode 100644 LibTester/TestAppData.cs create mode 100644 LibTester/packages.config diff --git a/Android-Libtester/Android-Libtester.csproj b/Android-Libtester/Android-Libtester.csproj index 5e33e38d..521b005c 100644 --- a/Android-Libtester/Android-Libtester.csproj +++ b/Android-Libtester/Android-Libtester.csproj @@ -56,11 +56,11 @@ - - + + diff --git a/LibTester/DefaultContext.cs b/LibTester/DefaultContext.cs new file mode 100644 index 00000000..b9157272 --- /dev/null +++ b/LibTester/DefaultContext.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using LinqExtender; +using Ast = LinqExtender.Ast; + +namespace LibTester +{ + /// + /// Default context to be queried. + /// + /// Target type + public class DefaultContext : ExpressionVisitor, IQueryContext + { + /// + /// Invoked during execution of the query , with the + /// pre populated expression tree. + /// + /// Target expression block + /// Expected result + public IEnumerable Execute(Ast.Expression expression) + { + //TODO: Visit the extender expression to build your meta + + this.Visit(expression); + + ///TOOD: return your result. + return null; + } + } +} diff --git a/LibTester/ExpressionVisitor.cs b/LibTester/ExpressionVisitor.cs new file mode 100644 index 00000000..b44023dc --- /dev/null +++ b/LibTester/ExpressionVisitor.cs @@ -0,0 +1,86 @@ +using System; +using LinqExtender; +using Ast = LinqExtender.Ast; + + +namespace LibTester +{ + public class ExpressionVisitor + { + internal Ast.Expression Visit(Ast.Expression expression) + { + switch (expression.CodeType) + { + case CodeType.BlockExpression: + return VisitBlockExpression((Ast.BlockExpression)expression); + case CodeType.TypeExpression: + return VisitTypeExpression((Ast.TypeExpression)expression); + case CodeType.LambdaExpresion: + return VisitLambdaExpression((Ast.LambdaExpression)expression); + case CodeType.LogicalExpression: + return VisitLogicalExpression((Ast.LogicalExpression)expression); + case CodeType.BinaryExpression: + return VisitBinaryExpression((Ast.BinaryExpression)expression); + case CodeType.LiteralExpression: + return VisitLiteralExpression((Ast.LiteralExpression)expression); + case CodeType.MemberExpression: + return VisitMemberExpression((Ast.MemberExpression)expression); + case CodeType.OrderbyExpression: + return VisitOrderbyExpression((Ast.OrderbyExpression)expression); + } + + throw new ArgumentException("Expression type is not supported"); + } + + public virtual Ast.Expression VisitTypeExpression(Ast.TypeExpression typeExpression) + { + return typeExpression; + } + + public virtual Ast.Expression VisitBlockExpression(Ast.BlockExpression blockExpression) + { + foreach (var expression in blockExpression.Expressions) + this.Visit(expression); + + return blockExpression; + } + + public virtual Ast.Expression VisitLogicalExpression(Ast.LogicalExpression expression) + { + this.Visit(expression.Left); + this.Visit(expression.Right); + return expression; + } + + public virtual Ast.Expression VisitLambdaExpression(Ast.LambdaExpression expression) + { + if (expression.Body != null) + return this.Visit(expression.Body); + return expression; + } + + public virtual Ast.Expression VisitBinaryExpression(Ast.BinaryExpression expression) + { + this.Visit(expression.Left); + this.Visit(expression.Right); + + return expression; + } + + public virtual Ast.Expression VisitMemberExpression(Ast.MemberExpression expression) + { + return expression; + } + + public virtual Ast.Expression VisitLiteralExpression(Ast.LiteralExpression expression) + { + return expression; + } + + public virtual Ast.Expression VisitOrderbyExpression(Ast.OrderbyExpression expression) + { + return expression; + } + + } +} diff --git a/LibTester/LibTester.csproj b/LibTester/LibTester.csproj new file mode 100644 index 00000000..fc1c7758 --- /dev/null +++ b/LibTester/LibTester.csproj @@ -0,0 +1,65 @@ + + + + Debug + AnyCPU + {F305304B-081C-44B5-BE53-F24A07BCDC56} + Library + LibTester + LibTester + v4.5 + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + false + + + full + true + bin\Release + prompt + 4 + false + + + + + ..\packages\NUnit.2.6.3\lib\nunit.framework.dll + + + ..\packages\LinqExtender.3.0.1\lib\LinqExtender.dll + + + + + + + + + + + + + + + + + {6644D98D-026B-48E9-95A3-61C4D98D82E7} + Kinvey-Xamarin + + + {444456B1-5B55-48D6-9637-00ADCA92B7C9} + Kinvey-Utils + + + {BA2352E1-CB07-4795-A2EB-F70720B2BBC9} + RestSharp.Portable + + + \ No newline at end of file diff --git a/LibTester/MyEntity.cs b/LibTester/MyEntity.cs new file mode 100644 index 00000000..400f6ddc --- /dev/null +++ b/LibTester/MyEntity.cs @@ -0,0 +1,32 @@ +using System; +using RestSharp; + + +namespace AndroidLibtester +{ + public class MyEntity + { + + public MyEntity(string id){ + this.ID = id; + } + + public MyEntity(){} + + public string ID {get; set;} + + + public string Name{get;set;} + + + public string Email{get;set;} + + + public string lowercasetest{get;set;} + + + public bool IsAvailable{get; set;} + + } +} + diff --git a/LibTester/Test.cs b/LibTester/Test.cs new file mode 100644 index 00000000..05395096 --- /dev/null +++ b/LibTester/Test.cs @@ -0,0 +1,16 @@ +using NUnit.Framework; +using System; + +namespace LibTester +{ + [TestFixture ()] + public class Test + { + [Test ()] + public void TestCase () + { + // Assert.True (false); + } + } +} + diff --git a/LibTester/TestAppData.cs b/LibTester/TestAppData.cs new file mode 100644 index 00000000..9cd68a6b --- /dev/null +++ b/LibTester/TestAppData.cs @@ -0,0 +1,109 @@ +using System; +using NUnit.Framework; +using KinveyXamarin; +using System.Reflection; +using RestSharp; +using LinqExtender; + +namespace AndroidLibtester +{ + [TestFixture] + public class TestAppData + { + + string appkey = "123"; + string appsecret = "123"; + Client testClient; + AppData testData; + string myCollection = "myCollection"; + + [SetUp] + public void Setup () + { + testClient = new Client.Builder (appkey, appsecret).build (); + testData = testClient.AppData (myCollection, typeof(MyEntity)); + } + + + [TearDown] + public void Tear () + { + } + + + [Test()] + public void TestGetEntity() + { + + string someID = "some id"; + AppData.GetEntityRequest req = testData.GetEntityBlocking (someID); + + RestRequest restReq = req.BuildRestRequest (); + Assert.True (restReq.Method == Method.GET); + Assert.True (restReq.Parameters.FindAll (delegate(Parameter p){ + return p.Type == ParameterType.UrlSegment; + }).Count >= 2); + + Assert.True (restReq.Parameters.FindAll (delegate(Parameter p){ + return p.Type == ParameterType.HttpHeader; + }).Count >= 3); + + } + + [Test()] + public void TestGetAll() + { + AppData.GetRequest req = testData.GetBlocking (); + + RestRequest restReq = req.BuildRestRequest (); + Assert.True (restReq.Method == Method.GET); + Assert.True (restReq.Parameters.FindAll (delegate(Parameter p){ + return p.Type == ParameterType.UrlSegment; + }).Count >= 1); + + Assert.True (restReq.Parameters.FindAll (delegate(Parameter p){ + return p.Type == ParameterType.HttpHeader; + }).Count >= 3); + + } + + [Test()] + public void TestSaveEntity() + { + + AppData.SaveRequest req = testData.SaveBlocking (new MyEntity()); + + RestRequest restReq = req.BuildRestRequest (); + Assert.True (restReq.Method == Method.POST); + Assert.True (restReq.Parameters.FindAll (delegate(Parameter p){ + return p.Type == ParameterType.UrlSegment; + }).Count >= 1); + + Assert.True (restReq.Parameters.FindAll (delegate(Parameter p){ + return p.Type == ParameterType.HttpHeader; + }).Count >= 3); + + } + + [Test()] + public void TestDeleteEntity() + { + string someID = "some id"; + AppData.DeleteRequest req = testData.DeleteBlocking (someID); + + RestRequest restReq = req.BuildRestRequest (); + Assert.True (restReq.Method == Method.DELETE); + Assert.True (restReq.Parameters.FindAll (delegate(Parameter p){ + return p.Type == ParameterType.UrlSegment; + }).Count >= 1); + + Assert.True (restReq.Parameters.FindAll (delegate(Parameter p){ + return p.Type == ParameterType.HttpHeader; + }).Count >= 3); + + } + + + } +} + diff --git a/LibTester/packages.config b/LibTester/packages.config new file mode 100644 index 00000000..d5da7cce --- /dev/null +++ b/LibTester/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index 1b6cab12..a07d1921 100644 --- a/README.md +++ b/README.md @@ -4,219 +4,18 @@ Kinvey Xamarin Library This is a Public Class Library (PCL) for various supported Xamarin runtimes. -##DISCLAIMER - -This is an alpha release of the Kinvey-Xamarin Library. There are many wrappers that need to be added, and the current implementation requires much more verbosity than will be required. Also, future refactoring is planned which might break backwards compatibility. - -Please report any issues on the issues section of this project, thank you! - -Also check out the index branch for current features being developed - -## Build -Pre-requisites: - -* take a copy of this project's source code, and add it to your solution -* Add this project as a dependency to your application -* via nu-get, install the following packages: - * Newtonsoft.json - * SQLite.Net-PCL - * SQLite.Net.Platform.* (where * is the runtime for the current application) - * LinqExtender - - - -##Usage and Concepts - -### The Client -The Client acts as the point of interaction for all things Kinvey. It manages the current users credentials, and provides a handful of factory methods for accessing features. For example, to access all datastore operations, use `myKinveyClient.AppData(...)` and to access user operations use `myKinveyClient.User()`. - - -### Async vs Sync (*Blocking) -This library is implemented with a clean separation between blocking synchronous functionality and async functionality with delegates for results. Note that there are two versions of each factory, for example there is a `User` class containing `LoginBlocking`, and a `AsyncUser` class containing `Login`. The blocking variation requires a call to `Execute()` on the request object, and will block the current thread until it completes. The Async Variations will spawn a new thread which executes the blocking variation, passing through any parameters. Async methods also take an instance of a `KinveyDelegate`, a simple abstract class which provides on `Action` for `onSuccess` and `onFailure`, dependent on the results of the async execution. - - -## Usage (from android-testdrive project) - -###create a client - - Client kinveyClient = new Client.Builder(appKey, appSecret).build(); - -##### add offline to the client builder -Note the addition of two new builder methods to add the local database path location as well as a platform specific implementation of the sqlite.net pcl. - - kinveyClient = new Client.Builder(appKey, appSecret) - .setFilePath(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal)) - .setOfflinePlatform(new SQLitePlatformAndroid()).build(); - - - -###access user operations async (login) - - kinveyClient.User ().Login (new KinveyDelegate{ - onSuccess = (user) => { - RunOnUiThread (() => { - Toast.MakeText(this, "logged in as: " + user.Id, ToastLength.Short).Show(); - }); - }, - onError = (error) => { - RunOnUiThread (() => { - Toast.MakeText(this, "something went wrong: " + error.Message, ToastLength.Short).Show(); - }); - } - }); - - - -###access app data operations (save with cache support) - - - //get a reference to AsyncAppData - AsyncAppData entityCollection = kinveyClient.AppData(COLLECTION, typeof(MyEntity)); - - //Create an entity to save - MyEntity ent = new MyEntity(); - ent.ID = STABLE_ID; - ent.Email = "test@tester.com"; - ent.Name = "James Dean"; - - //----- - //This line here enables Caching, used for retrieving only - entityCollection.setCache (myCache, CachePolicy.CACHE_FIRST); - //----- - - - //call save and pass delegates - entityCollection.Save (ent, new KinveyDelegate { - onSuccess = (entity) => { - RunOnUiThread (() => { - Toast.MakeText (this, "logged in as: " + entity.Name, ToastLength.Short).Show (); - }); - }, - onError = (error) => { - RunOnUiThread (() => { - Toast.MakeText (this, "something went wrong: " + error.Message, ToastLength.Short).Show (); - }); - } - }); - -### Querying - -Querying can be performed through standard LINQ operators, where an instance of `AppData` can be used as the queryable object. The Kinvey library provides a subset of LINQ which includes most of the core functionality. For example, you cannot perform Join operations due to the nature of a nosql datastore. The library does support Lambda operations, as well as sorting, where clauses, and logical operators. - -For example, a query to get all Entites where a field called `last name` is equal to `smith` can be written as follows: - - var query = from cust in testData - where cust.lastname == "smith" - select cust; - - -More complex queries are also supported, such as: - - var query = from cust in testData - where (cust.ID == "10" && cust.Name == "Billy") || (cust.ID == "1" && cust.Name == "Charlie") - select cust; - - - -### Caching and Offline -Caching functionality is provided as in-memory implementation, meaning that cached data will only be available per use of the application. Offline functionality utilizes SQLite to maintain a local copy of entities, as well as a queue of pending requests. This data is persisted between uses of the application, and will perform any queued up requests once any request has been successfully completed. - -Both features are accessed through very similar methods on an Appdata instance, essentially only requiring an implementation of a `Store`, as well as a Policy to use. See below for details about the provided caching and offline policies. - -Both caching and offline can be used at the same time, allowing for very quick responses to commonly repeated requests, and a fail-over in case a network connection is lost. - -####Enabling Caching and Offline - -The Client.Builder will require both a reference to the local filesystem, as well as a platform specific implementation of the SQLite.Net-PCL. For Android, the following will store the database in private local storage: - - kinveyClient = new Client.Builder(appKey, appSecret) - .setFilePath(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal)) - .setOfflinePlatform(new SQLitePlatformAndroid()).build(); - - - -To enable caching: - - //resuse this cache object! - InMemoryCache myCache = new InMemoryCache(); - - //get a reference to AppData - AsyncAppData entityCollection = kinveyClient.AppData("MyCollection", typeof(MyEntity)); - - //enable caching - entityCollection.setCache (myCache, CachePolicy.CACHE_FIRST); - - -To enable offline: - - //get a reference to AppData - AsyncAppData entityCollection = kinveyClient.AppData("MyCollection", typeof(MyEntity)); - - //enable offline, no need to reuse SQLiteOfflineStore, all instances share a database - entityCollection.setOffline(new SQLiteOfflineStore(), OfflinePolicy.LOCAL_FIRST); - - -### Cache Policies - -* `CachePolicy.NOCACHE` - This policy will not use any caching, and will execute every request online. - -Use this policy if your application is dependant on data that is shared between multiple users and always needs to be up to date. - - -* `CachePolicy.CACHEONLY` - This policy will only retrieve data from the cache, and will not use any network connection. - -Use this policy in combination with another policy, to allow for quick response times without requiring a network connection for specific operations. - - -* `CachePolicy.CACHEFIRST` - This policy will first attempt to retrieve data from the cache. If the data has been cached, it will be returned. If the data does not exist in the cache, the data will be retrieved from Kinvey's Backend and the cache will be updated. - -Use this policy if your application can display data that doesn't change very often but you still want local updates. - - -* `CachePolicy.CACHEFIRST_NOREFRESH` - This policy will first attempt to retrieve data from the cache. If the data has been cached, it will be returned. If the data does not exist in the cache, the data will be retrieved from Kinvey's Backend but the cache will not be updated with the new results. - -Use this policy if you want to set default results, however if a request is made that cannot return these defaults a live request will be made (without modifying those default values) - - -* `CachePolicy.NETWORKFIRST` - This policy will execute the request on the network, and will store the result in the cache. If the online execution fails, the results will be pulled from the cache. - -Use this policy if you application wants the latest data but you still want responsiveness if a connection is lost - - - -###Offline Policies - - -* `OfflinePolicy.ONLINE_FIRST` - This policy will attempt to execute the request online first, and if that is successful will update the local store with the results. If the request fails due to connectivity issues, then the request will be executed against the local store. If it fails for any other reason such as an Authorization Error, the onFailure callback will be called. - -Use this policy if your application's data is constantly changing on the backend, but you want to support offline mode. - - -* `OfflinePolicy.LOCAL_FIRST` - This policy will attempt to execute the request against the local store first. If the request is a Get, and the data cannot be found in the local store, then an online request will be attempted. If that suceeds, the store will be updated, and onSuccess will be called. If that fails, then onFailure will be called. For save requests, the local store will be updated and the entity will be returned through the onSuccess callback. - -Use this policy if each user has their own data, and updates are not constantly required from your backend. - - -* `OfflinePolicy.ALWAYS_ONLINE` - This policy will not use any local storage or queueing, and will execute every request online. If no network connection is available, errors will be returned through the onFailure callback. - -Use this policy if your application is fully dependant on data in the backend, and data cannot be stored locally. - - - - -## License - - Copyright 2014 Kinvey, Inc. - - 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. - +##Release Process + +1. Ensure tests are passing +2. Set target to `Release`, and kick off a build. +3. Copy __Kinvey-Utils/obj/Release/Kinvey-Utils.dll__, __Kinvey-Xamarin/obj/Release/Kinvey-Xamarin.dll__, and __Restsharp.Portable/obj/Release/RestSharp.portable.dll__ into the __release/kinvey-xamarin-x.x__ directory +4. Zip up the __release/kinvey-xamarin-x.x.__ directory +5. Check everything into github, and tag a release. +6. Upload the zip to amazon +7. on windows, do a git pull +8. Ensure the project builds on windows +9. run `nuget pack Kinvey-Xamarin.csproj -IncludeReferencedProjects` +10. Edit the file `Kinvey-Xamarin.nuspec` to up the version number and changelog (this is an xml file) +11. run `nuget push Kinvey-Xamarin.nupkg` +12. pull the devcenter +13. modify the changelog and download.json, and deploy. \ No newline at end of file diff --git a/packages/repositories.config b/packages/repositories.config index f8150285..abe377fc 100644 --- a/packages/repositories.config +++ b/packages/repositories.config @@ -2,5 +2,6 @@ + \ No newline at end of file