dangl - LightQuery 1.9.0-beta0003

ASP.NET Core ActionFilter for sorting and pagination

PM> Install-Package LightQuery -Version 1.9.0-beta0003 -Source https://www.myget.org/F/dangl/api/v3/index.json

Copy to clipboard

> nuget.exe install LightQuery -Version 1.9.0-beta0003 -Source https://www.myget.org/F/dangl/api/v3/index.json

Copy to clipboard

> dotnet add package LightQuery --version 1.9.0-beta0003 --source https://www.myget.org/F/dangl/api/v3/index.json

Copy to clipboard
<PackageReference Include="LightQuery" Version="1.9.0-beta0003" />
Copy to clipboard
source https://www.myget.org/F/dangl/api/v3/index.json

nuget LightQuery  ~> 1.9.0-beta0003
Copy to clipboard

> choco install LightQuery --version 1.9.0-beta0003 --source https://www.myget.org/F/dangl/api/v2

Copy to clipboard
Import-Module PowerShellGet
Register-PSRepository -Name "dangl" -SourceLocation "https://www.myget.org/F/dangl/api/v2"
Install-Module -Name "LightQuery" -RequiredVersion "1.9.0-beta0003" -Repository "dangl" -AllowPreRelease
Copy to clipboard

LightQuery

Build Status
Online Documentation

This project aims to provide a lightweight ActionFilterAttribute that takes care of sorting and paginating Asp.Net Core API results.

This project is for you if you're still waiting for OData support in Asp.Net Core, even though you only need the most basic of operations. It's also for everyone tired of writing like the 17th string sort = "Username" parameter and lines over lines of switch statements in their controller actions.

It supports EntityFrameworkCores async query materialization with the optional LightQuery.EntityFrameworkCore package.

npm

In addition to the C# client, there's also a client for Angular 5+ on npm: ng-lightquery
Version 1.2.0 and above are compatible with Angular 6+ and rxjs >= 6.0.0.

Installation

NuGet MyGet

The package is available on nuget. Daily builds are on myget.

MyGet feed: https://www.myget.org/F/dangl/api/v3/index.json

PM> Install-Package LightQuery

Includes the core functionality to sort and paginate Asp.Net Core controller results

PM> Install-Package LightQuery.EntityFrameworkCore

Includes support for EntityFramework.Core async query materialization

PM> Install-Package LightQuery.Client

Includes LightQuery models and the QueryBuilder utility

NETStandard 2.0 and .Net 4.6.1 are supported.

Testing

Tests are run via powershell ./build.ps1 Coverage (or build.sh Coverage) in the root directory.

Documentation - Server

See below how to apply sorting & filtering to your API controllers. At a glance:

  • Return an ObjectResult from your controller with an IQueryable value
  • Use sort to sort, page & pageSize for pagination in your requests

You can find a demo in the integration test projects for an example of using this in an Asp.Net Core MVC application for sorting and filtering.

Sorting

using LightQuery;

public class ApiController : Controller
{
    [LightQuery]
    [ProducesResponseType(typeof(IEnumerable<User>), 200)]
    public IActionResult GetValues()
    {
        var values = _repository.GetAllValuesAsQueryable();
        return Ok(values);  
    }
}

Annotate your controller actions with the LightQueryAttribute and it takes care of applying url queries to the result. All ObjectResults (docs) that have an IQueryable value will be transformed. You're free to return any other results, too, from the annotated action and it will simply be ignored.

Example: http://your.api.com/values?sort=email desc

This will sort the result by its Email property (it is title-cased if no email property is found) in descending order.

Default Sort Order

You can specifiy a default sort order via the defaultSort parameter of the [LightQuery] attribute. It expects a string that is in the same format as the query string, e.g. defaultSort: "email desc". If relational sorting is active, null checks are introduced.

Relational Sorting

It is possible to sort by nested properties. This means that properties may be specified in a dotted way to access nested elements, e.g. sorting can be done by using bankAccount.balance. Take this example:

[
    {
        "name": "George",
        "bankAccount": { "balance": 500 }
    },
    {
        "name": "Alice",
        "bankAccount": { "balance": 800 }
    },
    {
        "name": "Bob",
        "bankAccount": null
    }
]

If you apply the sorting expression bankAccount.balance, the user Bob will not be present in the result set because the bankAccount property is null. The query will only be applied to George and Alice.

Handling Null Values in Relational Sorting

With v2.0.0, LightQuery introduced a new property wrapNestedSortInNullChecks to the ASP.NET Core controller attributes.

This defaults to false for regular [LightQuery] and to true for [AsyncLightQuery]. It controls whether nested sorting / relational sorting will introduce null checks, e.g. sorting by x.SubProperty.SubId is either translated as .Where(x => x.SubProperty != null).OrderBy(x => x.SubProperty.SubId) or directly as .OrderBy(x => x.SubProperty.SubId). For Entity Framework (using the [AsyncLightQuery] attribute), the database provider usually handles null checking via appropriate join conditions and versions before .NET 5 might produce errors otherwise.

ThenSort

LightQuery supports an additional sort level via the thenSort parameter. For example, take the following url:

`http://your.api.com/values?sort=country&thenSort=email desc`

This would return your values first sorted by the country property and then by the email (descending) property. There is currently no support for multiple thenSort parameters and relational sorting is ignored in thenSort.

Pagination & Sorting

Paging is active when the request includes pagination query parameters or via explicitly setting the forcePagination parameter to true in the attributes' constructor. Sorting works in combination with paging.

using LightQuery;

public class ApiController : Controller
{
    [LightQuery(forcePagination: true, defaultPageSize: 3, defaultSort: "columnName desc")]
    [ProducesResponseType(typeof(PaginationResult<User>), 200)]
    public IActionResult GetValues()
    {
        var values = _repository.GetAllValuesAsQueryable();
        return Ok(values);  
    }
}

Example: http://your.api.com/values?sort=email&page=2&pageSize=3

Response

{
    "page": 2,
    "pageSize": 3,
    "totalCount": 20,
    "data": [
        { "userName": "Dave", "email": "dave@example.com" },
        { "userName": "Emilia", "email": "emilia@example.com" },
        { "userName": "Fred", "email": "fred@example.com" }
    ]
}

Async Materialization

The LightQuery.EntityFrameworkCore package provides an AsyncLightQueryAttribute. This can be used for data sources that support async materialization of queries, e.g. ToListAsync(). To use it, you also need to return just an IQueryable because LightQuery will itself call the async methods when materializing the result.

So, to return a paginatable list of users that is asynchronously materialized, just return something like OK(context.Users).

Documentation - C# Client

The LightQuery.Client package contains the PaginationResult<T> base class as well as a QueryBuilder utlity class to construct queries.

Example

using LightQuery.Client;

var url = QueryBuilder.Build(page: 3, pageSize: 25, sortParam: "email");
var response = await _client.GetAsync(url);
var responseContent = await response.Content.ReadAsStringAsync();
var deserializedResponse = JsonConvert.DeserializeObject<PaginationResult<User>>(responseContent);

PaginationBaseService

The LightQuery.Client package contains an abstract class PaginationBaseService<T> that can be used in reactive clients. It is similar in functionality to the TypeScript client.

Documentation - TypeScript & Angular

The npm package ng-lightquery contains client libraries for LightQuery that can be used in Angular 5+ projects. It has a generic PaginationBaseService<T> that your own services can inherit from. As of now, you have to provide a concrete implementation for each generic type argument that you want to use, since the dependency injection in Angular does not currently resolve generics. So if you want two LightQuery services - one to retrieve users and one to retrieve values - you need to create two services yourself.

Example with Angular Material 2 DataTable

You'll have three files in this example:

users.component.html

The Angular template which contains an Anguler Material table view.

<md-table [dataSource]="dataSource"
          mdSort
          [mdSortActive]="usersService.sort?.propertyName"
          [mdSortDirection]="usersService.sort?.isDescending ? 'desc' : 'asc'"
          (mdSortChange)="onSort($event)">
    <ng-container cdkColumnDef="email">
        <md-header-cell md-sort-header *cdkHeaderCellDef> Email </md-header-cell>
        <md-cell *cdkCellDef="let row">
            {{row.email}}
        </md-cell>
    </ng-container>
    <md-header-row *cdkHeaderRowDef="['email']"></md-header-row>
    <md-row *cdkRowDef="let row; columns: ['email'];"></md-row>
</md-table>
<md-paginator [length]="usersPaginated.totalCount"
              [pageSize]="usersPaginated.pageSize"
              [pageIndex]="usersPaginated.page - 1"
              (page)="onPage($event)">
</md-paginator>

users.component.ts

The component which is backing the view.

export class UsersComponent implements OnInit, OnDestroy {

    constructor(public userService: UserService) { }

    private usersPaginatedSubscription: Subscription;
    usersPaginated: PaginationResult<User>;
    dataSource: DataSource<User>;

    onPage(pageEvent: PageEvent) {
        this.userService.page = pageEvent.pageIndex + 1;
        this.userService.pageSize = pageEvent.pageSize;
    }

    onSort(event: { active: string, direction: string }) {
        if (!event.direction) {
            this.userService.sort = null;
        } else {
            this.userService.sort = { propertyName: event.active, isDescending: event.direction === 'desc' };
        }
    }

    ngOnInit() {
        this.dataSource = this.userService;
        this.usersPaginatedSubscription = this.userService.paginationResult.subscribe(r => this.usersPaginated = r);
    }

    ngOnDestroy() {
        this.usersPaginatedSubscription.unsubscribe();
    }
}

users.service.ts

To use the pagination service, simple let your own service inherit from the one provided by the ng-lightquery package via extends PaginationBaseService<T>. You can omit the implementation of the DataSource<User> interface and the connect() and disconnect() methods if you're not working with Angular Material.

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { PaginationBaseService } from 'ng-lightquery';
import { User } from '../models/user';
import { DataSource } from '@angular/cdk/collections';

@Injectable()
export class UsersDetailsService extends PaginationBaseService<User> implements DataSource<User> {

    constructor(protected http: HttpClient) {
      super(http);
      this.baseUrl = '/api/users';
      // You can optionally initialize with some default values,
      // e.g. for sorting, page size or custom url query attributes
      this.sort = {
        isDescending: false,
        propertyName: 'email'
      };
    }

  connect(): Observable<User[]> {
    return this.paginationResult
      .map((r: PaginationResult<User>) => r.data);
  }

  disconnect() { }

}

Swagger & OpenAPI Support

The packages LightQuery.NSwag and LightQuery.Swashbuckle support the automatic generation of correct Swagger & OpenAPI parameter descriptions for the sort and pagination parameters.

Example with NSwag

Just add the LightQuery.NSwag.LightQueryOperationsProcessor to your document generation:

services.AddSwaggerDocument(nSwagConfig =>
{
    nSwagConfig.DocumentName = "swagger20";
    nSwagConfig.OperationProcessors.Add(new LightQueryOperationsProcessor());
});
services.AddOpenApiDocument(nSwagConfig =>
{
    nSwagConfig.DocumentName = "openapi30";
    nSwagConfig.OperationProcessors.Add(new LightQueryOperationsProcessor());
});

Example with Swashbuckle

Just add the LightQuery.Swashbuckle.LightQueryOperationFilter to your document generation:

services.AddSwaggerGen(options =>
{
    options.SwaggerDoc("swagger20", new OpenApiInfo()
    {
        Description = "swagger20"
    });
    options.OperationFilter<LightQueryOperationFilter>();
});

services.AddSwaggerGen(options =>
{
    options.SwaggerDoc("openapi30", new OpenApiInfo()
    {
        Description = "openapi30"
    });
    options.OperationFilter<LightQueryOperationFilter>();
});


Assembly Strong Naming & Usage in Signed Applications

This module produces strong named assemblies when compiled. When consumers of this package require strongly named assemblies, for example when they themselves are signed, the outputs should work as-is. The key file to create the strong name is adjacent to the csproj file in the root of the source project. Please note that this does not increase security or provide tamper-proof binaries, as the key is available in the source code per Microsoft guidelines


MIT Licence

v1.9.0:

  • Addition of the LightQuery.Swashbuckle (thanks to GitHub user @berkayakcay) and LightQuery.NSwag packages to support Swagger & OpenAPI generation

v1.8.1:

  • The Angular library was updated to be compatible with Angular v9.1

v1.8.0:

  • Add a thenSort parameter to specify a second sort option. This translates to something like queryable.OrderBy(sort).ThenBy(thenSort)
  • Fix C# client not cancelling previous requests when query parameters in the PaginationBaseService were changed. If a new request is started due to parameter changes while another request is still en-route, the previous request is discarded and no event is emitted for when the previous request completes
  • The C# client has now an additional constructor overload for the PaginationBaseService to be able to pass a CancellationToken

v1.7.2:

  • Fix possible NullReferenceException in case of relational sorting where an invalid property name is passed via the query. Thanks to GitHub user @smitpatel for discovering it!

v1.7.1:

  • Fixed a bug that caused Entity Framework Core to throw an InvalidOperationException when sorting was applied to projections to a class that inherited from the base query type. The error was an incorrectly used reflection invocation to determine the type the query applies to

v1.7.0:

  • Add support for ASP.NET Core 3.0

v1.6.2:

  • The .NET PaginationBaseService no longer makes requests when the url is null

v1.6.1:

  • Fix issue where BadRequest results in AsyncLightQuery decorated controllers with forcePagination:true were returning an empty OkResult with status code 200 instead of the original 404 - Bad Request

v1.6.0:

  • The generated assemblies now have a strong name. This is a breaking change of the binary API and will require recompilation on all systems that consume this package. The strong name of the generated assembly allows compatibility with other, signed tools. Please note that this does not increase security or provide tamper-proof binaries, as the key is available in the source code per Microsoft guidelines

v1.5.2:

  • Bugfix: Empty results now report the page as 1 instead of 0. Thanks to GitHub user @erdembas for the pull request!

v1.5.1:

  • When a page is requested that is higher than the last available one, the last available one will be returned instead. Thanks to GitHub user @erdembas for the pull request!

v1.5.0:

  • It's now possible to do relational sorting, meaning that nested properties can be used for sorting. For example, it is now possible to sort by user.bankAccount.balance. Thanks to GitHub user @erdembas for the pull request!

v1.4.0:

  • The defaultSort parameter was introduced for the server side controller attributes. Thanks to GitHub user @erdembas for the pull request!

v1.3.0:

  • Raise minimum supported .NET Standard version to netstandard2.0
  • Bump version to align .NET and npm package versions

v1.1.0:

  • Publish ng-lightquery npm package for angular and include PaginationBaseService in the client libraries

v1.0.2:

  • Update version to align with new releases of LightQuery.EntityFrameworkCore and LightQuery.Client. Added support for EntityFrameworkCore async materialization of queries and a client side package for easier consuming of APIs

v1.0.1:

  • Forced pagination was not applied when no query string at all was present in the Http request

v1.0.0:

  • Initial release
  • .NETFramework 4.6.1
    • LightQuery.Client (>= 1.9.0-beta0003)
    • LightQuery.Shared (>= 1.9.0-beta0003)
  • .NETCoreApp 3.0
    • LightQuery.Client (>= 1.9.0-beta0003)
    • LightQuery.Shared (>= 1.9.0-beta0003)
  • .NETStandard 2.0
    • LightQuery.Client (>= 1.9.0-beta0003)
    • LightQuery.Shared (>= 1.9.0-beta0003)
  • .NETCoreApp 3.0: 3.0.0.0
  • .NETFramework 4.6.1: 4.6.1.0
  • .NETStandard 2.0: 2.0.0.0

Owners

Georg Dangl

Authors

Georg Dangl

Project URL

https://github.com/GeorgDangl/LightQuery

License

MIT

Tags

Asp-Net-Core Querying Sorting Filtering

Info

1458 total downloads
14 downloads for version 1.9.0-beta0003
Download (27.41 KB)
Found on the current feed only

Package history

Version Size Last updated Downloads Mirrored?
2.4.1 28.54 KB Thu, 04 Jul 2024 22:18:38 GMT 4
2.4.0 28.5 KB Wed, 21 Feb 2024 23:19:17 GMT 10
2.3.0 36.73 KB Fri, 25 Nov 2022 16:04:15 GMT 12
2.3.0-beta0014 36.76 KB Fri, 25 Nov 2022 16:00:14 GMT 11
2.2.3-beta0002 32.39 KB Wed, 21 Sep 2022 09:12:47 GMT 11
2.2.3-beta0001 32.38 KB Mon, 22 Aug 2022 11:47:32 GMT 12
2.2.2 32.35 KB Mon, 22 Aug 2022 11:44:01 GMT 11
2.2.2-beta0004 32.38 KB Mon, 22 Aug 2022 11:38:44 GMT 10
2.2.1 32.32 KB Wed, 03 Aug 2022 14:34:52 GMT 13
2.2.1-beta0007 32.33 KB Wed, 03 Aug 2022 14:30:54 GMT 12
2.2.1-beta0006 32.3 KB Wed, 03 Aug 2022 13:47:20 GMT 11
2.2.1-beta0004 32.28 KB Wed, 03 Aug 2022 13:43:17 GMT 11
2.2.1-beta0001 32.3 KB Fri, 01 Jul 2022 15:48:29 GMT 12
2.2.0 32.25 KB Fri, 01 Jul 2022 15:42:15 GMT 14
2.2.0-beta0043 32.29 KB Fri, 01 Jul 2022 15:38:01 GMT 10
2.2.0-beta0039 32.3 KB Wed, 22 Jun 2022 20:31:36 GMT 9
2.2.0-beta0037 32.27 KB Fri, 29 Apr 2022 14:30:00 GMT 11
2.2.0-beta0036 32.29 KB Wed, 20 Apr 2022 20:45:04 GMT 12
2.2.0-beta0034 32.3 KB Wed, 20 Apr 2022 20:39:28 GMT 10
2.1.1-beta0022 32.26 KB Tue, 29 Mar 2022 12:27:13 GMT 11
2.1.1-beta0020 32.27 KB Mon, 21 Feb 2022 22:05:13 GMT 11
2.1.1-beta0013 32.26 KB Sat, 12 Feb 2022 09:42:26 GMT 11
2.1.1-beta0012 32.25 KB Sat, 12 Feb 2022 09:30:41 GMT 11
2.1.1-beta0009 32.27 KB Sat, 12 Feb 2022 09:01:39 GMT 11
2.1.1-beta0005 32.25 KB Fri, 11 Feb 2022 18:42:06 GMT 10
2.1.1-beta0003 32.24 KB Fri, 11 Feb 2022 14:47:00 GMT 11
2.1.1-beta0001 32.24 KB Thu, 02 Dec 2021 21:00:02 GMT 11
2.1.0 32.24 KB Wed, 25 Aug 2021 19:34:30 GMT 11
2.1.0-beta0012 32.28 KB Wed, 25 Aug 2021 19:16:04 GMT 12
2.0.1-beta0004 32.26 KB Wed, 25 Aug 2021 18:35:41 GMT 13
2.0.0 32.21 KB Thu, 15 Jul 2021 20:49:46 GMT 10
2.0.0-beta0028 32.25 KB Sat, 26 Jun 2021 11:41:53 GMT 9
2.0.0-beta0027 32.25 KB Sat, 26 Jun 2021 11:36:18 GMT 10
2.0.0-beta0025 32.24 KB Sat, 19 Jun 2021 20:30:49 GMT 9
2.0.0-beta0024 32.25 KB Sat, 19 Jun 2021 20:20:29 GMT 10
2.0.0-beta0023 32.13 KB Mon, 14 Jun 2021 20:13:29 GMT 10
2.0.0-beta0022 31.76 KB Wed, 26 May 2021 12:46:44 GMT 11
2.0.0-beta0020 31.76 KB Tue, 18 May 2021 20:49:59 GMT 8
1.9.2-pullrequest0010-0001 27.53 KB Sat, 05 Sep 2020 00:27:58 GMT 10
1.9.2-beta0018 27.63 KB Sun, 09 May 2021 10:33:33 GMT 10
1.9.2-beta0016 27.61 KB Thu, 06 May 2021 22:29:35 GMT 11
1.9.2-beta0014 27.63 KB Thu, 22 Apr 2021 19:19:09 GMT 10
1.9.2-beta0012 27.62 KB Mon, 08 Mar 2021 19:33:25 GMT 9
1.9.2-beta0010 27.62 KB Wed, 13 Jan 2021 17:01:38 GMT 11
1.9.2-beta0009 27.61 KB Tue, 12 Jan 2021 22:29:59 GMT 13
1.9.2-beta0006 27.42 KB Mon, 19 Oct 2020 14:03:55 GMT 11
1.9.2-beta0002 27.47 KB Mon, 19 Oct 2020 13:55:55 GMT 14
1.9.1 27.56 KB Mon, 01 Mar 2021 14:30:45 GMT 12
1.9.1-beta0005 27.44 KB Thu, 16 Jul 2020 08:59:17 GMT 12
1.9.0 27.36 KB Mon, 01 Jun 2020 06:10:38 GMT 10
1.9.0-beta0012 27.42 KB Sun, 31 May 2020 20:19:41 GMT 11
1.9.0-beta0003 27.41 KB Wed, 27 May 2020 22:26:59 GMT 14
1.9.0-beta0002 27.41 KB Wed, 27 May 2020 21:58:27 GMT 11
1.8.2-beta0001 27.35 KB Tue, 19 May 2020 20:23:09 GMT 11
1.8.1 27.29 KB Thu, 14 May 2020 17:58:12 GMT 12
1.8.1-beta0004 27.35 KB Thu, 14 May 2020 17:31:41 GMT 14
1.8.1-beta0003 27.32 KB Thu, 14 May 2020 16:58:55 GMT 11
1.8.1-beta0001 27.29 KB Thu, 30 Jan 2020 21:50:48 GMT 13
1.8.0 27.25 KB Mon, 06 Jan 2020 13:54:26 GMT 12
1.7.3-beta0006 27.29 KB Mon, 06 Jan 2020 12:44:28 GMT 13
1.7.3-beta0005 27.3 KB Mon, 06 Jan 2020 12:37:59 GMT 11
1.7.3-beta0004 27.25 KB Sun, 05 Jan 2020 22:41:36 GMT 12
1.7.2 27.02 KB Wed, 16 Oct 2019 19:34:06 GMT 13
1.7.2-beta0002 27.09 KB Wed, 16 Oct 2019 19:11:15 GMT 11
1.7.1 26.95 KB Wed, 16 Oct 2019 14:53:38 GMT 13
1.7.1-beta0005 26.98 KB Wed, 16 Oct 2019 12:30:55 GMT 12
1.7.1-beta0004 26.99 KB Wed, 16 Oct 2019 09:24:28 GMT 13
1.7.1-beta0001 26.85 KB Mon, 14 Oct 2019 17:32:49 GMT 12
1.7.0 26.8 KB Mon, 14 Oct 2019 10:05:59 GMT 13
1.7.0-beta0002 26.87 KB Sat, 12 Oct 2019 21:27:25 GMT 11
1.6.3-beta0001 21.27 KB Sun, 29 Sep 2019 18:16:36 GMT 12
1.6.2 9.64 KB Sun, 29 Sep 2019 16:20:27 GMT 13
1.6.2-beta0004 9.68 KB Sun, 29 Sep 2019 16:04:47 GMT 12
1.6.2-beta0001 9.64 KB Fri, 06 Sep 2019 10:32:04 GMT 12
1.6.1 9.59 KB Fri, 09 Aug 2019 17:07:34 GMT 14
1.6.1-beta0003 9.63 KB Fri, 09 Aug 2019 14:25:51 GMT 11
1.6.0 9.34 KB Tue, 06 Aug 2019 15:49:06 GMT 13
1.6.0-beta0010 9.37 KB Tue, 06 Aug 2019 15:35:40 GMT 12
1.5.3-beta0002 8.4 KB Mon, 20 May 2019 10:40:28 GMT 12
1.5.3-beta0001 8.41 KB Sat, 02 Mar 2019 13:31:43 GMT 12
1.5.2 8.39 KB Thu, 31 Jan 2019 12:04:42 GMT 13
1.5.2-dependabot-npm--0001 27.77 KB Mon, 19 Oct 2020 13:55:55 GMT 9
1.5.2-beta0004 8.43 KB Thu, 31 Jan 2019 11:51:51 GMT 12
1.5.2-beta0002 8.38 KB Thu, 31 Jan 2019 11:41:11 GMT 12
1.5.1 8.31 KB Thu, 31 Jan 2019 07:39:52 GMT 13
1.5.1-beta0009 8.35 KB Thu, 31 Jan 2019 07:12:56 GMT 14
1.5.1-beta0007 8.35 KB Thu, 31 Jan 2019 07:07:58 GMT 12
1.5.1-beta0001 8.06 KB Thu, 03 Jan 2019 14:55:09 GMT 12
1.5.0 8.04 KB Thu, 29 Nov 2018 21:05:03 GMT 12
1.4.1-beta0013 8.07 KB Thu, 29 Nov 2018 20:50:59 GMT 12
1.4.1-beta0007 7.97 KB Thu, 29 Nov 2018 16:59:34 GMT 12
1.4.0 7.94 KB Tue, 27 Nov 2018 21:16:06 GMT 13
1.3.1-beta0008 7.97 KB Tue, 27 Nov 2018 21:05:29 GMT 12
1.3.1-beta0002 7.49 KB Tue, 27 Nov 2018 20:10:29 GMT 14
1.3.0 7.4 KB Sat, 15 Sep 2018 21:45:20 GMT 15
1.1.1-build-28 7.15 KB Sat, 05 May 2018 21:56:06 GMT 12
1.1.1-build-27 7.15 KB Sat, 05 May 2018 21:53:46 GMT 13
1.1.1-build-26 7.15 KB Mon, 16 Apr 2018 12:52:00 GMT 12
1.1.1-build-25 7.07 KB Thu, 01 Feb 2018 18:11:56 GMT 13
1.1.1-build-24 7.06 KB Thu, 04 Jan 2018 20:09:45 GMT 13
1.1.1-build-23 7.07 KB Thu, 21 Dec 2017 18:44:51 GMT 12
1.1.1-build-22 7.07 KB Thu, 21 Dec 2017 18:41:19 GMT 13
1.1.1-build-21 7.06 KB Fri, 01 Dec 2017 16:47:04 GMT 13
1.1.1-build-19 7.07 KB Thu, 23 Nov 2017 18:01:40 GMT 13
1.1.1-build-18 7.07 KB Sat, 18 Nov 2017 17:27:20 GMT 13
1.1.1-build-17 7.07 KB Fri, 17 Nov 2017 12:03:12 GMT 12
1.1.1-build-16 7.07 KB Thu, 16 Nov 2017 22:12:09 GMT 15
1.1.1-beta0056 7.42 KB Sat, 15 Sep 2018 21:29:29 GMT 11
1.1.1-beta0054 7.43 KB Sat, 15 Sep 2018 21:19:14 GMT 13
1.1.1-beta0051 7.42 KB Sat, 15 Sep 2018 19:50:15 GMT 12
1.1.0 7.01 KB Thu, 16 Nov 2017 21:22:38 GMT 13
1.0.3-build-9 7.03 KB Sun, 12 Nov 2017 21:09:07 GMT 12
1.0.3-build-8 7.03 KB Sun, 12 Nov 2017 20:03:35 GMT 14
1.0.3-build-7 7.03 KB Sun, 12 Nov 2017 18:15:40 GMT 13
1.0.3-build-6 7.03 KB Sun, 12 Nov 2017 15:47:09 GMT 13
1.0.3-build-5 7.03 KB Sun, 12 Nov 2017 15:33:47 GMT 12
1.0.3-build-4 7.03 KB Sun, 12 Nov 2017 15:21:49 GMT 12
1.0.3-build-3 7.03 KB Sun, 12 Nov 2017 12:23:25 GMT 13
1.0.3-build-2 7.03 KB Fri, 01 Sep 2017 21:00:19 GMT 12
1.0.3-build-14 7.04 KB Thu, 16 Nov 2017 18:54:47 GMT 12
1.0.3-build-12 7.04 KB Wed, 15 Nov 2017 23:00:34 GMT 12
1.0.3-build-11 7.04 KB Wed, 15 Nov 2017 21:17:42 GMT 10
1.0.3-build-10 7.03 KB Mon, 13 Nov 2017 21:51:57 GMT 13
1.0.3-build-1 7.03 KB Fri, 01 Sep 2017 20:57:00 GMT 15