Skip to content

Commit

Permalink
Merge pull request #45 from Cratis:feature/observable-sorting-paging
Browse files Browse the repository at this point in the history
Feature/observable-sorting-paging
  • Loading branch information
einari authored Jul 22, 2024
2 parents d5f5c1d + df7fd99 commit 78ce617
Show file tree
Hide file tree
Showing 76 changed files with 859 additions and 308 deletions.
1 change: 0 additions & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@
<PackageVersion Include="Cratis.Specifications.XUnit" Version="3.0.3" />
<PackageVersion Include="xunit" Version="2.8.1" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.1" />
<PackageVersion Include="moq" Version="4.20.70" />
<PackageVersion Include="Microsoft.NET.Test.SDK" Version="17.10.0" />
<PackageVersion Include="NSubstitute" Version="5.1.0" />
</ItemGroup>
Expand Down
4 changes: 2 additions & 2 deletions Samples/eCommerce/Basic/API/Carts/Cart.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ public async Task<Guid> AddItem([FromBody] AddItemToCart addItemToCart)
/// </summary>
/// <returns><see cref="ClientObservable{T}"/> for <see cref="Read.Carts.Cart"/>. </returns>
[HttpGet("observe")]
public async Task<ISubject<Read.Carts.Cart>> ObserveCartForCurrentUser()
public ISubject<Read.Carts.Cart> ObserveCartForCurrentUser()
{
var cartId = (CartId)(User.Identity?.GetUserIdAsGuid() ?? Guid.Empty);
return await cartQueries.Observe(cartId);
return cartQueries.Observe(cartId);
}
}
12 changes: 12 additions & 0 deletions Samples/eCommerce/Basic/API/Products/Catalog.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Cratis. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Reactive.Subjects;
using Cratis.Applications;
using Domain.Products;
using MongoDB.Driver;
Expand Down Expand Up @@ -36,6 +37,17 @@ public async Task AddProduct([FromBody] AddProduct addProduct)
[HttpGet]
public IQueryable<Product> AllProducts() => catalogQueries.All();

/// <summary>
/// Gets all the products in the catalog.
/// </summary>
/// <returns>Collection of products.</returns>
[HttpGet("observe")]
public ISubject<IEnumerable<Product>> ObserveAllProducts() => catalogQueries.ObserveAll();

/// <summary>
/// Generate some products.
/// </summary>
/// <returns>Awaitable task.</returns>
[HttpGet("generate"), AspNetResult]
public async Task Generate()
{
Expand Down
3 changes: 2 additions & 1 deletion Samples/eCommerce/Basic/Domain.Interfaces/Carts/ICart.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using Concepts;
using Concepts.Carts;

namespace Domain.Carts;

Expand All @@ -18,4 +19,4 @@ public interface ICart : IGrainWithGuidKey
/// <param name="quantity">Number of items to add.</param>
/// <returns>Awaitable task.</returns>
Task AddItem(SKU sku, Price price, Quantity quantity);
}
}
1 change: 1 addition & 0 deletions Samples/eCommerce/Basic/Domain/Carts/Cart.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using Concepts;
using Cratis.Applications.Commands;
using Read.Carts;

namespace Domain.Carts;
Expand Down
2 changes: 1 addition & 1 deletion Samples/eCommerce/Basic/Read/Carts/CartQueries.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ public class CartQueries(IMongoCollection<Cart> collection) : ICartQueries
public async Task<Cart> Get(CartId cartId) => await collection.FindByIdAsync(cartId) ?? new() { Id = cartId };

/// <inheritdoc/>
public Task<ISubject<Cart>> Observe(CartId cartId) => collection.ObserveById(cartId);
public ISubject<Cart> Observe(CartId cartId) => collection.ObserveById(cartId);
}
2 changes: 1 addition & 1 deletion Samples/eCommerce/Basic/Read/Carts/ICartQueries.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@ public interface ICartQueries
/// </summary>
/// <param name="cartId">The <see cref="CartId"/> for the cart.</param>
/// <returns>The subject for <see cref="Cart"/>.</returns>
Task<ISubject<Cart>> Observe(CartId cartId);
ISubject<Cart> Observe(CartId cartId);
}
5 changes: 5 additions & 0 deletions Samples/eCommerce/Basic/Read/Products/CatalogQueries.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (c) Cratis. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Reactive.Subjects;

namespace Read.Products;

/// <summary>
Expand All @@ -11,4 +13,7 @@ public class CatalogQueries(IMongoCollection<Product> collection) : ICatalogQuer
{
/// <inheritdoc/>
public IQueryable<Product> All() => collection.AsQueryable();

/// <inheritdoc/>
public ISubject<IEnumerable<Product>> ObserveAll() => collection.Observe(_ => _.IsRegistered);
}
8 changes: 8 additions & 0 deletions Samples/eCommerce/Basic/Read/Products/ICatalogQueries.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (c) Cratis. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Reactive.Subjects;

namespace Read.Products;

/// <summary>
Expand All @@ -13,4 +15,10 @@ public interface ICatalogQueries
/// </summary>
/// <returns>Queryable of products.</returns>
IQueryable<Product> All();

/// <summary>
/// Observe all products.
/// </summary>
/// <returns>Subject of a collection of products.</returns>
ISubject<IEnumerable<Product>> ObserveAll();
}
4 changes: 2 additions & 2 deletions Samples/eCommerce/Basic/Web/API/Carts/CartForCurrentUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export class CartForCurrentUser extends QueryFor<Cart> {
}


static use(): [QueryResultWithState<Cart>, PerformQuery, SetSorting] {
return useQuery<Cart, CartForCurrentUser>(CartForCurrentUser);
static use(sorting?: Sorting): [QueryResultWithState<Cart[]>, PerformQuery, SetSorting] {
return useQuery<Cart[], CartForCurrentUser>(CartForCurrentUser, undefined, sorting);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import Handlebars from 'handlebars';

const routeTemplate = Handlebars.compile('/api/carts/observe');


export class ObserveCartForCurrentUser extends ObservableQueryFor<Cart> {
readonly route: string = '/api/carts/observe';
readonly routeTemplate: Handlebars.TemplateDelegate = routeTemplate;
Expand All @@ -24,7 +25,8 @@ export class ObserveCartForCurrentUser extends ObservableQueryFor<Cart> {
];
}

static use(): [QueryResultWithState<Cart>] {
return useObservableQuery<Cart, ObserveCartForCurrentUser>(ObserveCartForCurrentUser);

static use(sorting?: Sorting): [QueryResultWithState<Cart[]>, SetSorting] {
return useObservableQuery<Cart[], ObserveCartForCurrentUser>(ObserveCartForCurrentUser, undefined, sorting);
}
}
11 changes: 5 additions & 6 deletions Samples/eCommerce/Basic/Web/API/Products/AllProducts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*--------------------------------------------------------------------------------------------*/

// eslint-disable-next-line header/header
import { QueryFor, QueryResultWithState, SortingActions, SortingActionsForQuery, Paging } from '@cratis/applications/queries';
import { QueryFor, QueryResultWithState, Sorting, SortingActions, SortingActionsForQuery, Paging } from '@cratis/applications/queries';
import { useQuery, useQueryWithPaging, PerformQuery, SetSorting, SetPage } from '@cratis/applications.react/queries';
import { Product } from './Product';
import Handlebars from 'handlebars';
Expand Down Expand Up @@ -48,7 +48,6 @@ class AllProductsSortByWithoutQuery {
}
}


export class AllProducts extends QueryFor<Product[]> {
readonly route: string = '/api/products/catalog';
readonly routeTemplate: Handlebars.TemplateDelegate = routeTemplate;
Expand All @@ -74,11 +73,11 @@ export class AllProducts extends QueryFor<Product[]> {
return this._sortBy;
}

static use(): [QueryResultWithState<Product[]>, PerformQuery, SetSorting] {
return useQuery<Product[], AllProducts>(AllProducts);
static use(sorting?: Sorting): [QueryResultWithState<Product[]>, PerformQuery, SetSorting] {
return useQuery<Product[], AllProducts>(AllProducts, undefined, sorting);
}

static useWithPaging(pageSize: number): [QueryResultWithState<Product[]>, number, PerformQuery, SetSorting, SetPage] {
return useQueryWithPaging<Product[], AllProducts>(AllProducts, new Paging(0, pageSize));
static useWithPaging(pageSize: number, sorting?: Sorting): [QueryResultWithState<Product[]>, number, PerformQuery, SetSorting, SetPage] {
return useQueryWithPaging<Product[], AllProducts>(AllProducts, new Paging(0, pageSize), undefined, sorting);
}
}
83 changes: 83 additions & 0 deletions Samples/eCommerce/Basic/Web/API/Products/ObserveAllProducts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*---------------------------------------------------------------------------------------------
* **DO NOT EDIT** - This file is an automatically generated file.
*--------------------------------------------------------------------------------------------*/

// eslint-disable-next-line header/header
import { ObservableQueryFor, QueryResultWithState, Sorting, SortingActions, SortingActionsForObservableQuery, Paging } from '@cratis/applications/queries';
import { useObservableQuery, useObservableQueryWithPaging, SetSorting, SetPage } from '@cratis/applications.react/queries';
import { Product } from './Product';
import Handlebars from 'handlebars';

const routeTemplate = Handlebars.compile('/api/products/catalog/observe');

class ObserveAllProductsSortBy {
private _id: SortingActionsForObservableQuery<Product[]>;
private _name: SortingActionsForObservableQuery<Product[]>;
private _isRegistered: SortingActionsForObservableQuery<Product[]>;

constructor(readonly query: ObserveAllProducts) {
this._id = new SortingActionsForObservableQuery<Product[]>('id', query);
this._name = new SortingActionsForObservableQuery<Product[]>('name', query);
this._isRegistered = new SortingActionsForObservableQuery<Product[]>('isRegistered', query);
}

get id(): SortingActionsForObservableQuery<Product[]> {
return this._id;
}
get name(): SortingActionsForObservableQuery<Product[]> {
return this._name;
}
get isRegistered(): SortingActionsForObservableQuery<Product[]> {
return this._isRegistered;
}
}

class ObserveAllProductsSortByWithoutQuery {
private _id: SortingActions = new SortingActions('id');
private _name: SortingActions = new SortingActions('name');
private _isRegistered: SortingActions = new SortingActions('isRegistered');

get id(): SortingActions {
return this._id;
}
get name(): SortingActions {
return this._name;
}
get isRegistered(): SortingActions {
return this._isRegistered;
}
}

export class ObserveAllProducts extends ObservableQueryFor<Product[]> {
readonly route: string = '/api/products/catalog/observe';
readonly routeTemplate: Handlebars.TemplateDelegate = routeTemplate;
readonly defaultValue: Product[] = [];
private readonly _sortBy: ObserveAllProductsSortBy;
private static readonly _sortBy: ObserveAllProductsSortByWithoutQuery = new ObserveAllProductsSortByWithoutQuery();

constructor() {
super(Product, true);
this._sortBy = new ObserveAllProductsSortBy(this);
}

get requestArguments(): string[] {
return [
];
}

get sortBy(): ObserveAllProductsSortBy {
return this._sortBy;
}

static get sortBy(): ObserveAllProductsSortByWithoutQuery {
return this._sortBy;
}

static use(sorting?: Sorting): [QueryResultWithState<Product[]>, SetSorting] {
return useObservableQuery<Product[], ObserveAllProducts>(ObserveAllProducts, undefined, sorting);
}

static useWithPaging(pageSize: number, sorting?: Sorting): [QueryResultWithState<Product[]>, SetSorting, SetPage] {
return useObservableQueryWithPaging<Product[], ObserveAllProducts>(ObserveAllProducts, new Paging(0, pageSize), undefined, sorting);
}
}
1 change: 1 addition & 0 deletions Samples/eCommerce/Basic/Web/API/Products/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ export * from './Product';
export * from './SetStockForProduct';
export * from './SetPrice';
export * from './AddProduct';
export * from './ObserveAllProducts';
export * from './AllProducts';
4 changes: 2 additions & 2 deletions Samples/eCommerce/Basic/Web/App.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// Copyright (c) Cratis. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

import React from 'react';
import { IdentityProvider } from '@cratis/applications.react/identity';
import React, { useEffect } from 'react';
import { IdentityProvider, useIdentity } from '@cratis/applications.react/identity';
import { MVVM } from '@cratis/applications.react.mvvm';
import { BrowserRouter } from "react-router-dom";
import { Feature } from './Feature';
Expand Down
27 changes: 20 additions & 7 deletions Samples/eCommerce/Basic/Web/Catalog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,41 @@

import { withViewModel } from '@cratis/applications.react.mvvm';
import { CatalogViewModel } from './CatalogViewModel';
import { AllProducts } from './API/Products';
import { AllProducts, ObserveAllProducts } from './API/Products';
import { DataTable } from 'primereact/datatable';
import { Column } from 'primereact/column';
import { useState } from 'react';
import { useEffect, useState } from 'react';
import { SortDirection, Sorting } from '@cratis/applications/queries';

export const Catalog = withViewModel(CatalogViewModel, ({ viewModel }) => {
const [products, currentPage, perform, setSorting, setPage] = AllProducts.useWithPaging(10);
const [pageSize, setPageSize] = useState(10);
// // const [products, currentPage, perform, setSorting, setPage] = AllProducts.useWithPaging(pageSize);
const [observableProducts, setSorting, setPage] = ObserveAllProducts.useWithPaging(10);
const [descending, setDescending] = useState(false);
const [currentPage, setCurrentPage] = useState(0);

return (
<div>
<div>Page {currentPage + 1}</div>
<DataTable value={products.data}>
<DataTable value={observableProducts.data}>
<Column field="id" header="SKU" />
<Column field="name" header="Name" />
</DataTable>

<button onClick={() => {
const page = currentPage + 1;
setPage(page);
setCurrentPage(currentPage - 1);
setPage(currentPage - 1);
}}>Previous page</button>

<button onClick={() => {
setCurrentPage(currentPage + 1);
setPage(currentPage + 1);
}}>Next page</button>
<br/>
<br />

<button onClick={() => {
setPage(20);
}}>More stuff</button>

<button onClick={() => {
if (descending) {
Expand Down
8 changes: 7 additions & 1 deletion Samples/eCommerce/Basic/Web/Feature.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ import { DataTable } from 'primereact/datatable';
import { Column } from 'primereact/column';
import { useIdentity } from '@cratis/applications.react/identity';

export const Feature = withViewModel(FeatureViewModel, ({ viewModel }) => {

export interface FeatureProps {
blah: string;
}

export const Feature = withViewModel<FeatureViewModel, FeatureProps>(FeatureViewModel, ({ viewModel, props }) => {
console.log(props.blah);

const identity = useIdentity();
return (
Expand Down
3 changes: 2 additions & 1 deletion Samples/eCommerce/Basic/Web/FeatureViewModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@

import { injectable } from 'tsyringe';
import { Cart, CartForCurrentUser, ObserveCartForCurrentUser } from './API/Carts';
import { FeatureProps } from './Feature';

@injectable()
export class FeatureViewModel {
constructor(readonly query: ObserveCartForCurrentUser) {
constructor(readonly query: ObserveCartForCurrentUser, readonly props: FeatureProps) {
query.subscribe(result => {
this.cart = result.data;
});
Expand Down
4 changes: 4 additions & 0 deletions Samples/eCommerce/Basic/Web/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import './Styles/theme.css';
import React from 'react';
import { App } from './App';

const Blah = () => {
console.log('Blah');
return (<></>);
};

ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ void Establish()
headers[MicrosoftIdentityPlatformHeaders.PrincipalHeader] =
Convert.ToBase64String(JsonSerializer.SerializeToUtf8Bytes(client_principal));

identity_provider.Setup(_ => _.Provide(IsAny<IdentityProviderContext>())).Returns((IdentityProviderContext context) =>
identity_provider.Provide(Arg.Any<IdentityProviderContext>()).Returns((CallInfo x) =>
{
identity_provider_context = context;
identity_provider_context = x.Arg<IdentityProviderContext>();
return Task.FromResult(details_result);
});
}
Expand Down
Loading

0 comments on commit 78ce617

Please sign in to comment.