From 4ca43cb36bcd6c39d1993cdebcaad36bff1b5ed9 Mon Sep 17 00:00:00 2001 From: Thang Chung Date: Sat, 25 Apr 2020 17:20:20 +0700 Subject: [PATCH] Finished product management (#17) --- .github/workflows/graph-api.yml | 51 +- .github/workflows/identity-api.yml | 4 +- .github/workflows/inventory-api.yml | 52 +- .github/workflows/product-catalog-api.yml | 52 +- .helm/inventory-api/Chart.yaml | 5 - .helm/inventory-api/templates/NOTES.txt | 19 - .helm/inventory-api/templates/_helpers.tpl | 23 - .helm/inventory-api/templates/deployment.yaml | 56 -- .helm/inventory-api/templates/service.yaml | 27 - .helm/inventory-api/values.yaml | 33 -- .helm/product-catalog-api/.helmignore | 21 - .helm/product-catalog-api/Chart.yaml | 5 - .helm/product-catalog-api/templates/NOTES.txt | 19 - .../templates/_helpers.tpl | 23 - .../templates/deployment.yaml | 53 -- .../templates/service.yaml | 23 - .helm/product-catalog-api/values.yaml | 35 -- README.md | 4 +- components/appconfig.yaml | 9 + .../messagebus.yaml => components/pubsub.yaml | 0 .../components => components}/statestore.yaml | 4 +- components/zipkin.yaml | 11 + .helm/readme.md => docs/deployment_guide.md | 18 +- docs/developer_guide.md | 12 +- helm/seq/values.dev.yaml | 7 + {.helm => helm}/sqlserver/values.dev.yaml | 0 .../inventory-api => helm/zipkin}/.helmignore | 1 + helm/zipkin/Chart.yaml | 21 + helm/zipkin/templates/deployment.yaml | 50 ++ helm/zipkin/templates/service.yaml | 15 + helm/zipkin/values.yaml | 31 ++ k8s/redis.yaml | 42 -- k8s/sqlserver.yaml | 47 -- practical-dapr.sln | 1 - .../N8T.Infrastructure/Dapr/Extensions.cs | 2 +- .../Grpc/ExceptionHandleInterceptor.cs | 8 +- .../N8T.Infrastructure/Grpc/Extensions.cs | 2 - .../N8T.Infrastructure/Kestrel/Extensions.cs | 26 + .../N8T.Infrastructure.csproj | 1 + .../N8T.Infrastructure/Status/Extensions.cs | 56 ++ .../N8T.Infrastructure/Status/StatusModel.cs | 19 + .../N8T.Infrastructure/Tye/Extensions.cs | 18 + src/GraphApi/CoolStore.GraphApi/Program.cs | 22 +- .../Apis/GraphQL/InventoryType.cs | 15 + .../Apis/GraphQL/Query.cs | 23 + .../Apis/GraphQL/QueryType.cs | 14 + .../Grpc/DaprService.cs | 4 +- .../Apis/Rest/HomeController.cs | 17 + .../Domain/Inventory.cs | 4 +- .../CoolStore.InventoryApi/Program.cs | 45 +- .../Console/readme.txt | 0 .../Gateways/InventoryGateway.cs | 2 +- .../GraphQL/CatalogProductType.cs | 2 +- .../GraphQL/CategoryType.cs | 2 +- .../GraphQL/CreateProductInputType.cs | 8 +- .../Apis/GraphQL/DeleteProductInputType.cs | 13 + .../GraphQL/Filters/ProductFilterType.cs | 2 +- .../GraphQL/InventoryType.cs | 2 +- .../Apis/GraphQL/Mutation.cs | 34 ++ .../Apis/GraphQL/MutationType.cs | 25 + .../{UserInterface => Apis}/GraphQL/Query.cs | 10 +- .../GraphQL/QueryType.cs | 10 +- .../GraphQL/Sorts/ProductSortType.cs | 2 +- .../Apis/GraphQL/UpdateProductInputType.cs | 13 + .../Apis/Rest/HomeController.cs | 17 + .../ProductCreatedHandler.cs | 2 +- .../CreateProduct/CreateProductRequest.cs | 16 - .../CreateProduct/CreateProductCommand.cs | 16 + .../CreateProduct/CreateProductHandler.cs | 14 +- .../CreateProduct/CreateProductValidator.cs | 4 +- .../DeleteProduct/DeleteProductCommand.cs | 10 + .../DeleteProduct/DeleteProductHandler.cs | 35 ++ .../DeleteProduct/DeleteProductValidator.cs | 15 + .../GetCategories/GetCategoriesHandler.cs | 25 + .../GetCategories/GetCategoriesQuery.cs | 10 + .../GetCategories/GetCategoriesValidator.cs | 8 + .../GetProducts/GetProductsHandler.cs | 2 +- .../GetProducts/GetProductsQuery.cs | 2 +- .../GetProducts/GetProductsValidator.cs | 2 +- .../UpdateProduct/UpdateProductCommand.cs | 17 + .../UpdateProduct/UpdateProductHandler.cs | 62 +++ .../UpdateProduct/UpdateProductValidator.cs | 41 ++ .../Domain/Category.cs | 4 +- .../Domain/Product.cs | 2 +- .../CoolStore.ProductCatalogApi/Program.cs | 37 +- .../UserInterface/GraphQL/Mutation.cs | 22 - .../UserInterface/GraphQL/MutationType.cs | 15 - .../UserInterface/Rest/readme.txt | 1 - .../Controllers/InventoryController.cs | 17 +- .../Controllers/ProductController.cs | 43 +- .../GraphQL/Generated/.hash.md5 | 2 +- .../GraphQL/Generated/CatalogProductDto2.cs | 24 + .../GraphQL/Generated/CategoryDto1.cs | 24 + .../GraphQL/Generated/DeleteProductInput.cs | 13 + .../Generated/DeleteProductInputSerializer.cs | 68 +++ .../Generated/DeleteProductMutation.cs | 20 + .../DeleteProductMutationOperation.cs | 34 ++ .../DeleteProductMutationResultParser.cs | 43 ++ .../GraphQL/Generated/GetCategories.cs | 20 + .../Generated/GetCategoriesOperation.cs | 25 + .../Generated/GetCategoriesResultParser.cs | 73 +++ .../GraphQL/Generated/GetInventories.cs | 20 + .../Generated/GetInventoriesOperation.cs | 25 + .../Generated/GetInventoriesResultParser.cs | 73 +++ .../GraphQL/Generated/GraphQLClient.cs | 94 ++++ ...raphQLClientServiceCollectionExtensions.cs | 6 + .../GraphQL/Generated/ICatalogProductDto2.cs | 15 + .../GraphQL/Generated/ICategoryDto1.cs | 15 + .../Generated/IDeleteProductMutation.cs | 13 + .../GraphQL/Generated/IGetCategories.cs | 13 + .../GraphQL/Generated/IGetInventories.cs | 13 + .../GraphQL/Generated/IGraphQLClient.cs | 30 ++ .../GraphQL/Generated/IInventoryDto1.cs | 15 + .../Generated/IUpdateProductMutation.cs | 13 + .../GraphQL/Generated/InventoryDto1.cs | 24 + .../GraphQL/Generated/Queries.cs | 480 +++++++++++++++++- .../GraphQL/Generated/UpdateProductInput.cs | 25 + .../Generated/UpdateProductInputSerializer.cs | 116 +++++ .../Generated/UpdateProductMutation.cs | 20 + .../UpdateProductMutationOperation.cs | 34 ++ .../UpdateProductMutationResultParser.cs | 64 +++ .../GraphQL/GraphQL.graphql | 18 + .../GraphQL/Queries.graphql | 25 + .../CoolStore.WebUI.Host/GraphQL/berry.json | 2 +- .../CoolStore.WebUI.Host/appsettings.json | 4 +- .../CoolStore.WebUI/Components/Models.cs | 8 + .../Pages/Products/CreateProduct.razor | 2 + .../Pages/Products/CreateProductModel.cs | 11 +- .../Pages/Products/Products.razor | 17 +- src/WebUI/CoolStore.WebUI/_Imports.razor | 1 + test.cs | 28 - tye.yaml | 27 +- 132 files changed, 2444 insertions(+), 767 deletions(-) delete mode 100644 .helm/inventory-api/Chart.yaml delete mode 100644 .helm/inventory-api/templates/NOTES.txt delete mode 100644 .helm/inventory-api/templates/_helpers.tpl delete mode 100644 .helm/inventory-api/templates/deployment.yaml delete mode 100644 .helm/inventory-api/templates/service.yaml delete mode 100644 .helm/inventory-api/values.yaml delete mode 100644 .helm/product-catalog-api/.helmignore delete mode 100644 .helm/product-catalog-api/Chart.yaml delete mode 100644 .helm/product-catalog-api/templates/NOTES.txt delete mode 100644 .helm/product-catalog-api/templates/_helpers.tpl delete mode 100644 .helm/product-catalog-api/templates/deployment.yaml delete mode 100644 .helm/product-catalog-api/templates/service.yaml delete mode 100644 .helm/product-catalog-api/values.yaml create mode 100644 components/appconfig.yaml rename src/GraphApi/CoolStore.GraphApi/components/messagebus.yaml => components/pubsub.yaml (100%) rename {src/GraphApi/CoolStore.GraphApi/components => components}/statestore.yaml (84%) create mode 100644 components/zipkin.yaml rename .helm/readme.md => docs/deployment_guide.md (58%) create mode 100644 helm/seq/values.dev.yaml rename {.helm => helm}/sqlserver/values.dev.yaml (100%) rename {.helm/inventory-api => helm/zipkin}/.helmignore (97%) create mode 100644 helm/zipkin/Chart.yaml create mode 100644 helm/zipkin/templates/deployment.yaml create mode 100644 helm/zipkin/templates/service.yaml create mode 100644 helm/zipkin/values.yaml delete mode 100644 k8s/redis.yaml delete mode 100644 k8s/sqlserver.yaml create mode 100644 src/BuildingBlocks/N8T.Infrastructure/Kestrel/Extensions.cs create mode 100644 src/BuildingBlocks/N8T.Infrastructure/Status/Extensions.cs create mode 100644 src/BuildingBlocks/N8T.Infrastructure/Status/StatusModel.cs create mode 100644 src/Inventory/CoolStore.InventoryApi/Apis/GraphQL/InventoryType.cs create mode 100644 src/Inventory/CoolStore.InventoryApi/Apis/GraphQL/Query.cs create mode 100644 src/Inventory/CoolStore.InventoryApi/Apis/GraphQL/QueryType.cs rename src/Inventory/CoolStore.InventoryApi/{UserInterface => Apis}/Grpc/DaprService.cs (92%) create mode 100644 src/Inventory/CoolStore.InventoryApi/Apis/Rest/HomeController.cs rename src/ProductCatalog/CoolStore.ProductCatalogApi/{UserInterface => Apis}/Console/readme.txt (100%) rename src/ProductCatalog/CoolStore.ProductCatalogApi/{UserInterface => Apis}/Gateways/InventoryGateway.cs (95%) rename src/ProductCatalog/CoolStore.ProductCatalogApi/{UserInterface => Apis}/GraphQL/CatalogProductType.cs (95%) rename src/ProductCatalog/CoolStore.ProductCatalogApi/{UserInterface => Apis}/GraphQL/CategoryType.cs (85%) rename src/ProductCatalog/CoolStore.ProductCatalogApi/{UserInterface => Apis}/GraphQL/CreateProductInputType.cs (56%) create mode 100644 src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/GraphQL/DeleteProductInputType.cs rename src/ProductCatalog/CoolStore.ProductCatalogApi/{UserInterface => Apis}/GraphQL/Filters/ProductFilterType.cs (91%) rename src/ProductCatalog/CoolStore.ProductCatalogApi/{UserInterface => Apis}/GraphQL/InventoryType.cs (85%) create mode 100644 src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/GraphQL/Mutation.cs create mode 100644 src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/GraphQL/MutationType.cs rename src/ProductCatalog/CoolStore.ProductCatalogApi/{UserInterface => Apis}/GraphQL/Query.cs (57%) rename src/ProductCatalog/CoolStore.ProductCatalogApi/{UserInterface => Apis}/GraphQL/QueryType.cs (67%) rename src/ProductCatalog/CoolStore.ProductCatalogApi/{UserInterface => Apis}/GraphQL/Sorts/ProductSortType.cs (88%) create mode 100644 src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/GraphQL/UpdateProductInputType.cs create mode 100644 src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/Rest/HomeController.cs rename src/ProductCatalog/CoolStore.ProductCatalogApi/Application/{Process => Publishers}/PublishProductCreated/ProductCreatedHandler.cs (90%) delete mode 100644 src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCase/CreateProduct/CreateProductRequest.cs create mode 100644 src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/CreateProduct/CreateProductCommand.cs rename src/ProductCatalog/CoolStore.ProductCatalogApi/Application/{UseCase => UseCases}/CreateProduct/CreateProductHandler.cs (80%) rename src/ProductCatalog/CoolStore.ProductCatalogApi/Application/{UseCase => UseCases}/CreateProduct/CreateProductValidator.cs (91%) create mode 100644 src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/DeleteProduct/DeleteProductCommand.cs create mode 100644 src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/DeleteProduct/DeleteProductHandler.cs create mode 100644 src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/DeleteProduct/DeleteProductValidator.cs create mode 100644 src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/GetCategories/GetCategoriesHandler.cs create mode 100644 src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/GetCategories/GetCategoriesQuery.cs create mode 100644 src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/GetCategories/GetCategoriesValidator.cs rename src/ProductCatalog/CoolStore.ProductCatalogApi/Application/{UseCase => UseCases}/GetProducts/GetProductsHandler.cs (94%) rename src/ProductCatalog/CoolStore.ProductCatalogApi/Application/{UseCase => UseCases}/GetProducts/GetProductsQuery.cs (70%) rename src/ProductCatalog/CoolStore.ProductCatalogApi/Application/{UseCase => UseCases}/GetProducts/GetProductsValidator.cs (62%) create mode 100644 src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/UpdateProduct/UpdateProductCommand.cs create mode 100644 src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/UpdateProduct/UpdateProductHandler.cs create mode 100644 src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/UpdateProduct/UpdateProductValidator.cs delete mode 100644 src/ProductCatalog/CoolStore.ProductCatalogApi/UserInterface/GraphQL/Mutation.cs delete mode 100644 src/ProductCatalog/CoolStore.ProductCatalogApi/UserInterface/GraphQL/MutationType.cs delete mode 100644 src/ProductCatalog/CoolStore.ProductCatalogApi/UserInterface/Rest/readme.txt create mode 100644 src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/CatalogProductDto2.cs create mode 100644 src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/CategoryDto1.cs create mode 100644 src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/DeleteProductInput.cs create mode 100644 src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/DeleteProductInputSerializer.cs create mode 100644 src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/DeleteProductMutation.cs create mode 100644 src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/DeleteProductMutationOperation.cs create mode 100644 src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/DeleteProductMutationResultParser.cs create mode 100644 src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/GetCategories.cs create mode 100644 src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/GetCategoriesOperation.cs create mode 100644 src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/GetCategoriesResultParser.cs create mode 100644 src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/GetInventories.cs create mode 100644 src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/GetInventoriesOperation.cs create mode 100644 src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/GetInventoriesResultParser.cs create mode 100644 src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/ICatalogProductDto2.cs create mode 100644 src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/ICategoryDto1.cs create mode 100644 src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/IDeleteProductMutation.cs create mode 100644 src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/IGetCategories.cs create mode 100644 src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/IGetInventories.cs create mode 100644 src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/IInventoryDto1.cs create mode 100644 src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/IUpdateProductMutation.cs create mode 100644 src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/InventoryDto1.cs create mode 100644 src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/UpdateProductInput.cs create mode 100644 src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/UpdateProductInputSerializer.cs create mode 100644 src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/UpdateProductMutation.cs create mode 100644 src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/UpdateProductMutationOperation.cs create mode 100644 src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/UpdateProductMutationResultParser.cs create mode 100644 src/WebUI/CoolStore.WebUI/Components/Models.cs delete mode 100644 test.cs diff --git a/.github/workflows/graph-api.yml b/.github/workflows/graph-api.yml index 0d66f15..9c9720e 100644 --- a/.github/workflows/graph-api.yml +++ b/.github/workflows/graph-api.yml @@ -13,31 +13,34 @@ on: branches: [ master ] jobs: - # build-dotnet: - # runs-on: ubuntu-latest - # steps: - # - uses: actions/checkout@v2 - # - name: Setup .NET Core - # uses: actions/setup-dotnet@v1 - # with: - # dotnet-version: 3.1.101 - # - name: Build with dotnet - # run: dotnet build src/GraphApi/CoolStore.GraphApi/CoolStore.GraphApi.csproj --configuration Release - - build-docker: + build-dotnet: runs-on: ubuntu-latest steps: + - uses: actions/checkout@v2 - - uses: aarnott/nbgv@v0.3 + + - name: Setup .NET Core + uses: actions/setup-dotnet@v1 with: - setAllVars: true - - name: Build docker image - run: docker build . --file ./src/GraphApi/CoolStore.GraphApi/Dockerfile --tag image - - name: Log into registry - run: echo "${{ secrets.GH_TOKEN }}" | docker login docker.pkg.github.com -u ${{ github.actor }} --password-stdin - - name: Push image - run: | - VERSION=$NBGV_SemVer2 - IMAGE_ID=docker.pkg.github.com/${{ github.repository }}/dapr-graph-api - docker tag image $IMAGE_ID:$VERSION - docker push $IMAGE_ID:$VERSION + dotnet-version: 3.1.201 + + - name: Build with dotnet + run: dotnet build src/GraphApi/CoolStore.GraphApi/CoolStore.GraphApi.csproj --configuration Release + + # build-docker: + # runs-on: ubuntu-latest + # steps: + # - uses: actions/checkout@v2 + # - uses: aarnott/nbgv@v0.3 + # with: + # setAllVars: true + # - name: Build docker image + # run: docker build . --file ./src/GraphApi/CoolStore.GraphApi/Dockerfile --tag image + # - name: Log into registry + # run: echo "${{ secrets.GH_TOKEN }}" | docker login docker.pkg.github.com -u ${{ github.actor }} --password-stdin + # - name: Push image + # run: | + # VERSION=$NBGV_SemVer2 + # IMAGE_ID=docker.pkg.github.com/${{ github.repository }}/dapr-graph-api + # docker tag image $IMAGE_ID:$VERSION + # docker push $IMAGE_ID:$VERSION diff --git a/.github/workflows/identity-api.yml b/.github/workflows/identity-api.yml index 4febc8d..fcfebd1 100644 --- a/.github/workflows/identity-api.yml +++ b/.github/workflows/identity-api.yml @@ -20,7 +20,7 @@ jobs: - run: echo ${{ github.event.path }} - run: | git fetch --unshallow - + - uses: aarnott/nbgv@v0.3 with: setAllVars: true @@ -28,7 +28,7 @@ jobs: - name: Setup .NET Core uses: actions/setup-dotnet@v1 with: - dotnet-version: 3.1.101 + dotnet-version: 3.1.201 - name: Build with dotnet run: dotnet build src/Identity/CoolStore.IdentityServer/CoolStore.IdentityServer.csproj --configuration Release diff --git a/.github/workflows/inventory-api.yml b/.github/workflows/inventory-api.yml index 89274df..c85038e 100644 --- a/.github/workflows/inventory-api.yml +++ b/.github/workflows/inventory-api.yml @@ -8,40 +8,42 @@ on: paths: - "src/Inventory/**" - "src/BuildingBlocks/**" - - ".helm/inventory-api/**" - ".github/workflows/inventory-api.yml" pull_request: branches: [ master ] jobs: - # build-dotnet: - # runs-on: ubuntu-latest - # steps: - # - uses: actions/checkout@v2 - # - name: Setup .NET Core - # uses: actions/setup-dotnet@v1 - # with: - # dotnet-version: 3.1.101 - # - name: Build with dotnet - # run: dotnet build src/Inventory/CoolStore.InventoryApi/CoolStore.InventoryApi.csproj --configuration Release - - build-docker: + build-dotnet: runs-on: ubuntu-latest steps: + - uses: actions/checkout@v2 - - uses: aarnott/nbgv@v0.3 + + - name: Setup .NET Core + uses: actions/setup-dotnet@v1 with: - setAllVars: true - - name: Build docker image - run: docker build . --file ./src/Inventory/CoolStore.InventoryApi/Dockerfile --tag image - - name: Log into registry - run: echo "${{ secrets.GH_TOKEN }}" | docker login docker.pkg.github.com -u ${{ github.actor }} --password-stdin - - name: Push image - run: | - VERSION=$NBGV_GitCommitIdShort - IMAGE_ID=docker.pkg.github.com/${{ github.repository }}/dapr-inventory-api - docker tag image $IMAGE_ID:$VERSION - docker push $IMAGE_ID:$VERSION + dotnet-version: 3.1.201 + + - name: Build with dotnet + run: dotnet build src/Inventory/CoolStore.InventoryApi/CoolStore.InventoryApi.csproj --configuration Release + + # build-docker: + # runs-on: ubuntu-latest + # steps: + # - uses: actions/checkout@v2 + # - uses: aarnott/nbgv@v0.3 + # with: + # setAllVars: true + # - name: Build docker image + # run: docker build . --file ./src/Inventory/CoolStore.InventoryApi/Dockerfile --tag image + # - name: Log into registry + # run: echo "${{ secrets.GH_TOKEN }}" | docker login docker.pkg.github.com -u ${{ github.actor }} --password-stdin + # - name: Push image + # run: | + # VERSION=$NBGV_GitCommitIdShort + # IMAGE_ID=docker.pkg.github.com/${{ github.repository }}/dapr-inventory-api + # docker tag image $IMAGE_ID:$VERSION + # docker push $IMAGE_ID:$VERSION # build-publish-aks: # runs-on: ubuntu-latest diff --git a/.github/workflows/product-catalog-api.yml b/.github/workflows/product-catalog-api.yml index ac8214d..d2d3ac9 100644 --- a/.github/workflows/product-catalog-api.yml +++ b/.github/workflows/product-catalog-api.yml @@ -8,40 +8,42 @@ on: paths: - "src/ProductCatalog/**" - "src/BuildingBlocks/**" - - ".helm/product-catalog-api/**" - ".github/workflows/product-catalog-api.yml" pull_request: branches: [ master ] jobs: - # build-dotnet: - # runs-on: ubuntu-latest - # steps: - # - uses: actions/checkout@v2 - # - name: Setup .NET Core - # uses: actions/setup-dotnet@v1 - # with: - # dotnet-version: 3.1.101 - # - name: Build with dotnet - # run: dotnet build src/ProductCatalog/CoolStore.ProductCatalogApi/CoolStore.ProductCatalogApi.csproj --configuration Release - - build-docker: + build-dotnet: runs-on: ubuntu-latest steps: + - uses: actions/checkout@v2 - - uses: aarnott/nbgv@v0.3 + + - name: Setup .NET Core + uses: actions/setup-dotnet@v1 with: - setAllVars: true - - name: Build docker image - run: docker build . --file ./src/ProductCatalog/CoolStore.ProductCatalogApi/Dockerfile --tag image - - name: Log into registry - run: echo "${{ secrets.GH_TOKEN }}" | docker login docker.pkg.github.com -u ${{ github.actor }} --password-stdin - - name: Push image - run: | - VERSION=$NBGV_GitCommitIdShort - IMAGE_ID=docker.pkg.github.com/${{ github.repository }}/dapr-product-catalog-api - docker tag image $IMAGE_ID:$VERSION - docker push $IMAGE_ID:$VERSION + dotnet-version: 3.1.201 + + - name: Build with dotnet + run: dotnet build src/ProductCatalog/CoolStore.ProductCatalogApi/CoolStore.ProductCatalogApi.csproj --configuration Release + + # build-docker: + # runs-on: ubuntu-latest + # steps: + # - uses: actions/checkout@v2 + # - uses: aarnott/nbgv@v0.3 + # with: + # setAllVars: true + # - name: Build docker image + # run: docker build . --file ./src/ProductCatalog/CoolStore.ProductCatalogApi/Dockerfile --tag image + # - name: Log into registry + # run: echo "${{ secrets.GH_TOKEN }}" | docker login docker.pkg.github.com -u ${{ github.actor }} --password-stdin + # - name: Push image + # run: | + # VERSION=$NBGV_GitCommitIdShort + # IMAGE_ID=docker.pkg.github.com/${{ github.repository }}/dapr-product-catalog-api + # docker tag image $IMAGE_ID:$VERSION + # docker push $IMAGE_ID:$VERSION # build-publish-aks: # runs-on: ubuntu-latest diff --git a/.helm/inventory-api/Chart.yaml b/.helm/inventory-api/Chart.yaml deleted file mode 100644 index 83102e4..0000000 --- a/.helm/inventory-api/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -appVersion: "1.0" -description: A Helm chart for Kubernetes -name: dapr-inventory-api -version: 0.1.0 diff --git a/.helm/inventory-api/templates/NOTES.txt b/.helm/inventory-api/templates/NOTES.txt deleted file mode 100644 index e04e519..0000000 --- a/.helm/inventory-api/templates/NOTES.txt +++ /dev/null @@ -1,19 +0,0 @@ -1. Get the application URL by running these commands: -{{- if .Values.ingress.enabled }} -{{- range .Values.ingress.hosts }} - http{{ if $.Values.ingress.tls }}s{{ end }}://{{ . }}{{ $.Values.ingress.path }} -{{- end }} -{{- else if contains "NodePort" .Values.service.type }} - export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "inventory.fullname" . }}) - export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") - echo http://$NODE_IP:$NODE_PORT -{{- else if contains "LoadBalancer" .Values.service.type }} - NOTE: It may take a few minutes for the LoadBalancer IP to be available. - You can watch the status of by running 'kubectl get svc -w {{ template "inventory.fullname" . }}' - export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "inventory.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') - echo http://$SERVICE_IP:{{ .Values.service.port }} -{{- else if contains "ClusterIP" .Values.service.type }} - export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "inventory.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") - echo "Visit http://127.0.0.1:8080 to use your application" - kubectl port-forward $POD_NAME 8080:80 -{{- end }} diff --git a/.helm/inventory-api/templates/_helpers.tpl b/.helm/inventory-api/templates/_helpers.tpl deleted file mode 100644 index 17bade3..0000000 --- a/.helm/inventory-api/templates/_helpers.tpl +++ /dev/null @@ -1,23 +0,0 @@ -{{/* vim: set filetype=mustache: */}} -{{/* -Expand the name of the chart. -*/}} -{{- define "inventory.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -*/}} -{{- define "inventory.fullname" -}} -{{- default .Release.Name .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "inventory.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} -{{- end -}} diff --git a/.helm/inventory-api/templates/deployment.yaml b/.helm/inventory-api/templates/deployment.yaml deleted file mode 100644 index 179bb80..0000000 --- a/.helm/inventory-api/templates/deployment.yaml +++ /dev/null @@ -1,56 +0,0 @@ -{{- $name := include "inventory.fullname" . -}} -{{- $cfgname := printf "%s-%s" "cfg" $name -}} -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ template "inventory.fullname" . }} - labels: - app: {{ template "inventory.name" . }} - chart: {{ template "inventory.chart" . }} - release: {{ .Release.Name }} - heritage: {{ .Release.Service }} -spec: - replicas: {{ .Values.replicaCount }} - selector: - matchLabels: - app: {{ template "inventory.name" . }} - release: {{ .Release.Name }} - template: - metadata: - labels: - app: {{ template "inventory.name" . }} - release: {{ .Release.Name }} - annotations: - dapr.io/enabled: "true" - dapr.io/id: inventory-api - dapr.io/port: "{{ .Values.service.appPort }}" - dapr.io/config: "tracing" - dapr.io/log-level: "debug" - spec: - {{ if .Values.imagePullSecrets -}} - imagePullSecrets: - {{ range .Values.imagePullSecrets -}} - - name: {{ .name }} - {{- end -}} - {{- end }} - containers: - - name: {{ .Chart.Name }} - image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" - imagePullPolicy: {{ .Values.image.pullPolicy }} - ports: - - name: app-http - containerPort: {{ .Values.service.appPort }} - protocol: TCP - - name: http-port - containerPort: {{ .Values.service.httpPort }} - protocol: TCP - - name: grpc-port - containerPort: {{ .Values.service.grpcPort }} - protocol: TCP - env: - {{- if .Values.env.values -}} - {{- range .Values.env.values }} - - name: {{ .name }} - value: {{ .value | quote }} - {{- end -}} - {{- end -}} \ No newline at end of file diff --git a/.helm/inventory-api/templates/service.yaml b/.helm/inventory-api/templates/service.yaml deleted file mode 100644 index ab96143..0000000 --- a/.helm/inventory-api/templates/service.yaml +++ /dev/null @@ -1,27 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: inventory-api - labels: - app: {{ template "inventory.name" . }} - chart: {{ template "inventory.chart" . }} - release: {{ .Release.Name }} - heritage: {{ .Release.Service }} -spec: - type: {{ .Values.service.type }} - ports: - - port: {{ .Values.service.appPort }} - targetPort: app-http - protocol: TCP - name: app-http - - port: {{ .Values.service.httpPort }} - targetPort: http-port - protocol: TCP - name: http-port - - port: {{ .Values.service.grpcPort }} - targetPort: grpc-port - protocol: TCP - name: grpc-port - selector: - app: {{ template "inventory.name" . }} - release: {{ .Release.Name }} \ No newline at end of file diff --git a/.helm/inventory-api/values.yaml b/.helm/inventory-api/values.yaml deleted file mode 100644 index 6373ebf..0000000 --- a/.helm/inventory-api/values.yaml +++ /dev/null @@ -1,33 +0,0 @@ -replicaCount: 1 -applicationName: practical-dapr - -image: - repository: dapracr.azurecr.io/dapr-inventory-api - tag: latest - pullPolicy: Always - -imagePullSecrets: [] - -service: - type: ClusterIP - appPort: 5301 - httpPort: 5302 - grpcPort: 5303 - -ingress: - enabled: false - -# resources: -# limits: -# cpu: "500m" -# requests: -# cpu: "100m" - -env: - values: - - name: DAPR_HTTP_PORT - value: 5302 - - name: DAPR_GRPC_PORT - value: 5303 - - name: ConnectionStrings__MainDb - value: "Server=tcp:sqlserver,1433;Database=CoolStoreDb;User Id=sa;Password=P@ssw0rd;MultipleActiveResultSets=True;" \ No newline at end of file diff --git a/.helm/product-catalog-api/.helmignore b/.helm/product-catalog-api/.helmignore deleted file mode 100644 index f0c1319..0000000 --- a/.helm/product-catalog-api/.helmignore +++ /dev/null @@ -1,21 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*~ -# Various IDEs -.project -.idea/ -*.tmproj diff --git a/.helm/product-catalog-api/Chart.yaml b/.helm/product-catalog-api/Chart.yaml deleted file mode 100644 index 644213a..0000000 --- a/.helm/product-catalog-api/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -appVersion: "1.0" -description: A Helm chart for Kubernetes -name: dapr-product-catalog-api -version: 0.1.0 diff --git a/.helm/product-catalog-api/templates/NOTES.txt b/.helm/product-catalog-api/templates/NOTES.txt deleted file mode 100644 index 1555f4d..0000000 --- a/.helm/product-catalog-api/templates/NOTES.txt +++ /dev/null @@ -1,19 +0,0 @@ -1. Get the application URL by running these commands: -{{- if .Values.ingress.enabled }} -{{- range .Values.ingress.hosts }} - http{{ if $.Values.ingress.tls }}s{{ end }}://{{ . }}{{ $.Values.ingress.path }} -{{- end }} -{{- else if contains "NodePort" .Values.service.type }} - export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "product-catalog.fullname" . }}) - export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") - echo http://$NODE_IP:$NODE_PORT -{{- else if contains "LoadBalancer" .Values.service.type }} - NOTE: It may take a few minutes for the LoadBalancer IP to be available. - You can watch the status of by running 'kubectl get svc -w {{ template "product-catalog.fullname" . }}' - export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "product-catalog.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') - echo http://$SERVICE_IP:{{ .Values.service.port }} -{{- else if contains "ClusterIP" .Values.service.type }} - export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "product-catalog.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") - echo "Visit http://127.0.0.1:8080 to use your application" - kubectl port-forward $POD_NAME 8080:80 -{{- end }} diff --git a/.helm/product-catalog-api/templates/_helpers.tpl b/.helm/product-catalog-api/templates/_helpers.tpl deleted file mode 100644 index d83abb7..0000000 --- a/.helm/product-catalog-api/templates/_helpers.tpl +++ /dev/null @@ -1,23 +0,0 @@ -{{/* vim: set filetype=mustache: */}} -{{/* -Expand the name of the chart. -*/}} -{{- define "product-catalog.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -*/}} -{{- define "product-catalog.fullname" -}} -{{- default .Release.Name .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "product-catalog.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} -{{- end -}} diff --git a/.helm/product-catalog-api/templates/deployment.yaml b/.helm/product-catalog-api/templates/deployment.yaml deleted file mode 100644 index 5fe3694..0000000 --- a/.helm/product-catalog-api/templates/deployment.yaml +++ /dev/null @@ -1,53 +0,0 @@ -{{- $name := include "product-catalog.fullname" . -}} -{{- $cfgname := printf "%s-%s" "cfg" $name -}} -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ template "product-catalog.fullname" . }} - labels: - app: {{ template "product-catalog.name" . }} - chart: {{ template "product-catalog.chart" . }} - release: {{ .Release.Name }} - heritage: {{ .Release.Service }} -spec: - replicas: {{ .Values.replicaCount }} - selector: - matchLabels: - app: {{ template "product-catalog.name" . }} - release: {{ .Release.Name }} - template: - metadata: - labels: - app: {{ template "product-catalog.name" . }} - release: {{ .Release.Name }} - annotations: - dapr.io/enabled: "true" - dapr.io/id: product-catalog-api - dapr.io/port: "{{ .Values.service.appPort }}" - dapr.io/config: "tracing" - dapr.io/log-level: "debug" - spec: - {{ if .Values.imagePullSecrets -}} - imagePullSecrets: - {{ range .Values.imagePullSecrets -}} - - name: {{ .name }} - {{- end -}} - {{- end }} - containers: - - name: {{ .Chart.Name }} - image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" - imagePullPolicy: {{ .Values.image.pullPolicy }} - ports: - - name: app-http - containerPort: {{ .Values.service.appPort }} - protocol: TCP - - name: http - containerPort: {{ .Values.service.httpPort }} - protocol: TCP - env: - {{- if .Values.env.values -}} - {{- range .Values.env.values }} - - name: {{ .name }} - value: {{ .value | quote }} - {{- end -}} - {{- end -}} \ No newline at end of file diff --git a/.helm/product-catalog-api/templates/service.yaml b/.helm/product-catalog-api/templates/service.yaml deleted file mode 100644 index a241518..0000000 --- a/.helm/product-catalog-api/templates/service.yaml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: product-catalog-api - labels: - app: {{ template "product-catalog.name" . }} - chart: {{ template "product-catalog.chart" . }} - release: {{ .Release.Name }} - heritage: {{ .Release.Service }} -spec: - type: {{ .Values.service.type }} - ports: - - port: {{ .Values.service.appPort }} - targetPort: app-http - protocol: TCP - name: app-http - - port: {{ .Values.service.httpPort }} - targetPort: http - protocol: TCP - name: http - selector: - app: {{ template "product-catalog.name" . }} - release: {{ .Release.Name }} diff --git a/.helm/product-catalog-api/values.yaml b/.helm/product-catalog-api/values.yaml deleted file mode 100644 index 95c065c..0000000 --- a/.helm/product-catalog-api/values.yaml +++ /dev/null @@ -1,35 +0,0 @@ -replicaCount: 1 -applicationName: practical-dapr - -image: - repository: dapracr.azurecr.io/dapr-product-catalog-api - tag: latest - pullPolicy: Always - -imagePullSecrets: [] - -service: - type: ClusterIP - appPort: 5201 - httpPort: 5202 - grpcPort: 5203 - -ingress: - enabled: false - -# resources: -# limits: -# cpu: "500m" -# requests: -# cpu: "100m" - -env: - values: - - name: DAPR_HTTP_PORT - value: 5202 - - name: Services__ProductCatalogService__RestUri - value: http://localhost:5202 - - name: Services__InventoryService__GrpcUri - value: http://inventory-api:5303 - - name: ConnectionStrings__MainDb - value: "Server=tcp:sqlserver,1433;Database=CoolStoreDb;User Id=sa;Password=P@ssw0rd;MultipleActiveResultSets=True;" \ No newline at end of file diff --git a/README.md b/README.md index 1d1033d..7d3cdc9 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,9 @@ If you liked `practical-dapr` project or if it helped you, please give a star :s ![](docs/assets/high_level_architecture.png) -# **Don't believe it, check yourself at [Developer Guidance](/docs/developer_guide.md)**. +# [Developer Guidance](/docs/developer_guide.md) + +# [Deployment Guidance](/docs/deployment_guide.md) ## Contributing diff --git a/components/appconfig.yaml b/components/appconfig.yaml new file mode 100644 index 0000000..11963dd --- /dev/null +++ b/components/appconfig.yaml @@ -0,0 +1,9 @@ +apiVersion: dapr.io/v1alpha1 +kind: Configuration +metadata: + name: appconfig +spec: + tracing: + enabled: true + expandParams: true + includeBody: true diff --git a/src/GraphApi/CoolStore.GraphApi/components/messagebus.yaml b/components/pubsub.yaml similarity index 100% rename from src/GraphApi/CoolStore.GraphApi/components/messagebus.yaml rename to components/pubsub.yaml diff --git a/src/GraphApi/CoolStore.GraphApi/components/statestore.yaml b/components/statestore.yaml similarity index 84% rename from src/GraphApi/CoolStore.GraphApi/components/statestore.yaml rename to components/statestore.yaml index 0649229..d53529a 100644 --- a/src/GraphApi/CoolStore.GraphApi/components/statestore.yaml +++ b/components/statestore.yaml @@ -1,7 +1,7 @@ apiVersion: dapr.io/v1alpha1 kind: Component metadata: - name: statestore + name: default spec: type: state.redis metadata: @@ -10,4 +10,4 @@ spec: - name: redisPassword value: "" - name: actorStateStore - value: "true" + value: "false" diff --git a/components/zipkin.yaml b/components/zipkin.yaml new file mode 100644 index 0000000..fe709cc --- /dev/null +++ b/components/zipkin.yaml @@ -0,0 +1,11 @@ +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: zipkin +spec: + type: exporters.zipkin + metadata: + - name: enabled + value: "true" + - name: exporterAddress + value: "http://zipkin.dev.svc.cluster.local:9411/api/v2/spans" diff --git a/.helm/readme.md b/docs/deployment_guide.md similarity index 58% rename from .helm/readme.md rename to docs/deployment_guide.md index 90d64c2..9fc21f0 100644 --- a/.helm/readme.md +++ b/docs/deployment_guide.md @@ -1,4 +1,8 @@ -- Install Sql Server + +# Install helm charts + +## Install Sql Server + ```bash $ helm install sqlserver stable/mssql-linux --debug --namespace dev --values sqlserver/values.dev.yaml ``` @@ -16,8 +20,14 @@ SELECT name FROM sys.databases GO ``` -- Test your charts +## Install Zipkin + +```bash +$ helm upgrade --namespace dev --install --wait zipkin zipkin +``` + +## Install Seq + ```bash -$ helm upgrade --debug --namespace dev --install --wait product-catalog-api product-catalog-api --dry-run -$ helm upgrade --debug --namespace dev --install --wait inventory-api inventory-api --dry-run +$ helm install seq stable/seq --debug --namespace dev --values sqlserver/values.dev.yaml ``` diff --git a/docs/developer_guide.md b/docs/developer_guide.md index 2dc9f5b..825bb6d 100644 --- a/docs/developer_guide.md +++ b/docs/developer_guide.md @@ -100,10 +100,20 @@ mutation createProductMutation($createProductInput: CreateProductInput!) { ![](assets/commucation_style.png) -## Debugging the application +## Debugging Follow steps at [Debugging Dapr application using Tye tool](https://dev.to/thangchung/debugging-dapr-application-using-tye-tool-1djb) +## Distributed logs and tracing + +```bash +$ tye run --dtrace zipkin=http://localhost:9411 --logs seq=http://localhost:5340 +``` + +Now, you can access Seq at http://localhost:5340, and Zipkin at http://localhost:9411. + +Run serveral queries on `graph-api`, then come back to `Seq` and `Zipkin` UIs, you should see logs and tracing. + ## Setup azure cloud services - [Publish docker image to ACR and AKS](https://docs.microsoft.com/en-us/azure/dev-spaces/how-to/github-actions) and [example](https://github.com/Azure/dev-spaces/blob/master/.github/workflows/bikes.yml) \ No newline at end of file diff --git a/helm/seq/values.dev.yaml b/helm/seq/values.dev.yaml new file mode 100644 index 0000000..de322b9 --- /dev/null +++ b/helm/seq/values.dev.yaml @@ -0,0 +1,7 @@ +fullnameOverride: seq +image: + tag: 5.1.3200 +persistence: + enabled: false +auth: + enabled: false diff --git a/.helm/sqlserver/values.dev.yaml b/helm/sqlserver/values.dev.yaml similarity index 100% rename from .helm/sqlserver/values.dev.yaml rename to helm/sqlserver/values.dev.yaml diff --git a/.helm/inventory-api/.helmignore b/helm/zipkin/.helmignore similarity index 97% rename from .helm/inventory-api/.helmignore rename to helm/zipkin/.helmignore index f0c1319..50af031 100644 --- a/.helm/inventory-api/.helmignore +++ b/helm/zipkin/.helmignore @@ -19,3 +19,4 @@ .project .idea/ *.tmproj +.vscode/ diff --git a/helm/zipkin/Chart.yaml b/helm/zipkin/Chart.yaml new file mode 100644 index 0000000..986b78a --- /dev/null +++ b/helm/zipkin/Chart.yaml @@ -0,0 +1,21 @@ +apiVersion: v2 +name: zipkin +description: Zipkin Helm chart + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. +appVersion: "1.0" diff --git a/helm/zipkin/templates/deployment.yaml b/helm/zipkin/templates/deployment.yaml new file mode 100644 index 0000000..19e073a --- /dev/null +++ b/helm/zipkin/templates/deployment.yaml @@ -0,0 +1,50 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Release.Name }} + labels: + app: {{ .Release.Name }} + chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + release: {{ .Release.Name }} + heritage: {{ .Release.Name }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ .Release.Name }} + release: {{ .Release.Name }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: 9411 + protocol: TCP + resources: + {{- toYaml .Values.resources | nindent 12 }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/helm/zipkin/templates/service.yaml b/helm/zipkin/templates/service.yaml new file mode 100644 index 0000000..250fcd6 --- /dev/null +++ b/helm/zipkin/templates/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Release.Name }} + labels: + app: {{ .Release.Name }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + app: {{ .Release.Name }} diff --git a/helm/zipkin/values.yaml b/helm/zipkin/values.yaml new file mode 100644 index 0000000..05bd575 --- /dev/null +++ b/helm/zipkin/values.yaml @@ -0,0 +1,31 @@ +# Default values for zipkin. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 1 + +image: + repository: openzipkin/zipkin + pullPolicy: IfNotPresent + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +service: + type: ClusterIP + port: 9411 + +resources: + requests: + cpu: 200m + memory: 100Mi + limits: + cpu: 300m + memory: 150Mi + +nodeSelector: {} + +tolerations: [] + +affinity: {} diff --git a/k8s/redis.yaml b/k8s/redis.yaml deleted file mode 100644 index 44aa749..0000000 --- a/k8s/redis.yaml +++ /dev/null @@ -1,42 +0,0 @@ -# kind: Deployment -# apiVersion: apps/v1 -# metadata: -# name: redis -# labels: -# app.kubernetes.io/name: redis -# app.kubernetes.io/part-of: practical-dapr -# spec: -# selector: -# matchLabels: -# app.kubernetes.io/name: redis -# replicas: 1 -# template: -# metadata: -# labels: -# app.kubernetes.io/name: redis -# app.kubernetes.io/part-of: practical-dapr -# spec: -# containers: -# - name: redis -# image: redis -# resources: -# requests: -# cpu: 100m -# memory: 100Mi -# ports: -# - containerPort: 6379 - -# --- -# apiVersion: v1 -# kind: Service -# metadata: -# name: redis -# labels: -# app.kubernetes.io/name: redis -# app.kubernetes.io/part-of: practical-dapr -# spec: -# ports: -# - port: 6379 -# targetPort: 6379 -# selector: -# app.kubernetes.io/name: redis diff --git a/k8s/sqlserver.yaml b/k8s/sqlserver.yaml deleted file mode 100644 index 6f22922..0000000 --- a/k8s/sqlserver.yaml +++ /dev/null @@ -1,47 +0,0 @@ -kind: Deployment -apiVersion: apps/v1 -metadata: - name: sqlserver - labels: - app.kubernetes.io/name: sqlserver - app.kubernetes.io/part-of: practical-dapr -spec: - selector: - matchLabels: - app.kubernetes.io/name: sqlserver - replicas: 1 - template: - metadata: - labels: - app.kubernetes.io/name: sqlserver - app.kubernetes.io/part-of: practical-dapr - spec: - containers: - - name: sqlserver - image: mcr.microsoft.com/mssql/server:2017-latest - env: - - name: SA_PASSWORD - value: "P@ssw0rd" - - name: ACCEPT_EULA - value: "Y" - resources: - requests: - cpu: 200m - memory: 200Mi - ports: - - containerPort: 1433 - ---- -apiVersion: v1 -kind: Service -metadata: - name: sqlserver - labels: - app.kubernetes.io/name: sqlserver - app.kubernetes.io/part-of: practical-dapr -spec: - ports: - - port: 1433 - targetPort: 1433 - selector: - app.kubernetes.io/name: sqlserver diff --git a/practical-dapr.sln b/practical-dapr.sln index 71b6eaf..7ed89e4 100644 --- a/practical-dapr.sln +++ b/practical-dapr.sln @@ -18,7 +18,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "__", "__", "{9DCADFAA-8ECD- LICENSE = LICENSE nuget.config = nuget.config README.md = README.md - services.json = services.json tye.yaml = tye.yaml EndProjectSection EndProject diff --git a/src/BuildingBlocks/N8T.Infrastructure/Dapr/Extensions.cs b/src/BuildingBlocks/N8T.Infrastructure/Dapr/Extensions.cs index 67df428..0bf2e24 100644 --- a/src/BuildingBlocks/N8T.Infrastructure/Dapr/Extensions.cs +++ b/src/BuildingBlocks/N8T.Infrastructure/Dapr/Extensions.cs @@ -15,7 +15,7 @@ public static DaprClient GetDaprClient( string appId, ILogger logger = null) { - var url = config.GetTyeAppUrl(appId); + var url = config.GetTyeGrpcAppUrl(appId); logger?.LogInformation($"Dapr Client Url: {url}"); var client = new DaprClientBuilder() diff --git a/src/BuildingBlocks/N8T.Infrastructure/Grpc/ExceptionHandleInterceptor.cs b/src/BuildingBlocks/N8T.Infrastructure/Grpc/ExceptionHandleInterceptor.cs index 2aeb4ce..2a3e242 100644 --- a/src/BuildingBlocks/N8T.Infrastructure/Grpc/ExceptionHandleInterceptor.cs +++ b/src/BuildingBlocks/N8T.Infrastructure/Grpc/ExceptionHandleInterceptor.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Diagnostics; using System.Threading.Tasks; using Grpc.Core; @@ -29,7 +29,7 @@ public override async Task UnaryServerHandler(TR context.Status.StatusCode, ex.ValidationResultModel.ToString()); - throw new RpcException(new Status(StatusCode.Internal, ex.ValidationResultModel.ToString())); + throw new RpcException(new global::Grpc.Core.Status(StatusCode.Internal, ex.ValidationResultModel.ToString())); } catch (Exception ex) { @@ -38,8 +38,8 @@ public override async Task UnaryServerHandler(TR context.Status.StatusCode, ex.Message); - throw new RpcException(new Status(StatusCode.Internal, ex.Message)); + throw new RpcException(new global::Grpc.Core.Status(StatusCode.Internal, ex.Message)); } } } -} \ No newline at end of file +} diff --git a/src/BuildingBlocks/N8T.Infrastructure/Grpc/Extensions.cs b/src/BuildingBlocks/N8T.Infrastructure/Grpc/Extensions.cs index 077e029..2382c83 100644 --- a/src/BuildingBlocks/N8T.Infrastructure/Grpc/Extensions.cs +++ b/src/BuildingBlocks/N8T.Infrastructure/Grpc/Extensions.cs @@ -54,8 +54,6 @@ public static IServiceCollection AddCustomGrpc(this IServiceCollection services, public static IServiceCollection AddCustomGrpcClient(this IServiceCollection services, Action doMoreActions = null) { - //AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true); - services.AddSingleton(); doMoreActions?.Invoke(services); diff --git a/src/BuildingBlocks/N8T.Infrastructure/Kestrel/Extensions.cs b/src/BuildingBlocks/N8T.Infrastructure/Kestrel/Extensions.cs new file mode 100644 index 0000000..d51ad80 --- /dev/null +++ b/src/BuildingBlocks/N8T.Infrastructure/Kestrel/Extensions.cs @@ -0,0 +1,26 @@ +using System.Net; +using Microsoft.AspNetCore.Server.Kestrel.Core; +using Microsoft.Extensions.Configuration; +using N8T.Infrastructure.Tye; + +namespace N8T.Infrastructure.Kestrel +{ + public static class Extensions + { + public static KestrelServerOptions ListenHttpAndGrpcProtocols( + this KestrelServerOptions options, + IConfiguration config, + string appId) + { + options.Limits.MinRequestBodyDataRate = null; + + options.Listen(IPAddress.Any, config.GetHttpPort(appId)); + + options.Listen(IPAddress.Any, + config.GetGrpcPort(appId), + listenOptions => { listenOptions.Protocols = HttpProtocols.Http2; }); + + return options; + } + } +} diff --git a/src/BuildingBlocks/N8T.Infrastructure/N8T.Infrastructure.csproj b/src/BuildingBlocks/N8T.Infrastructure/N8T.Infrastructure.csproj index 24bb078..1d7cf4d 100644 --- a/src/BuildingBlocks/N8T.Infrastructure/N8T.Infrastructure.csproj +++ b/src/BuildingBlocks/N8T.Infrastructure/N8T.Infrastructure.csproj @@ -38,6 +38,7 @@ + diff --git a/src/BuildingBlocks/N8T.Infrastructure/Status/Extensions.cs b/src/BuildingBlocks/N8T.Infrastructure/Status/Extensions.cs new file mode 100644 index 0000000..dc7c884 --- /dev/null +++ b/src/BuildingBlocks/N8T.Infrastructure/Status/Extensions.cs @@ -0,0 +1,56 @@ +using System.Linq; +using System.Net; +using System.Runtime.InteropServices; +using System.Text.Json; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.PlatformAbstractions; + +namespace N8T.Infrastructure.Status +{ + public static class Extensions + { + public static string BuildAppStatus(this IConfiguration config) + { + return JsonSerializer.Serialize(config.BuildAppStatusModel()); + } + + public static StatusModel BuildAppStatusModel(this IConfiguration config) + { + var model = new StatusModel(); + + model.AppName = PlatformServices.Default.Application.ApplicationName; + model.AppVersion = PlatformServices.Default.Application.ApplicationVersion; + model.BasePath = PlatformServices.Default.Application.ApplicationBasePath; + + foreach (var env in config.GetChildren()) model.Envs.Add(env.Key, env.Value); + + model.OSArchitecture = !RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + ? RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX) + ? "Linux or OSX" + : "Others" + : "Windows"; + + model.OSDescription = RuntimeInformation.OSDescription; + + model.ProcessArchitecture = RuntimeInformation.ProcessArchitecture == Architecture.Arm + ? "Arm" + : RuntimeInformation.ProcessArchitecture == Architecture.Arm64 + ? "Arm64" + : RuntimeInformation.ProcessArchitecture == Architecture.X64 + ? "x64" + : RuntimeInformation.ProcessArchitecture == Architecture.X86 + ? "x86" + : "Others"; + + model.RuntimeFramework = PlatformServices.Default.Application.RuntimeFramework.ToString(); + model.FrameworkDescription = RuntimeInformation.FrameworkDescription; + + model.HostName = Dns.GetHostName(); + model.IPAddress = Dns.GetHostAddresses(Dns.GetHostName()) + .Where(ip => ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) + .Aggregate(" ", (a, b) => $"{a} {b}"); + + return model; + } + } +} diff --git a/src/BuildingBlocks/N8T.Infrastructure/Status/StatusModel.cs b/src/BuildingBlocks/N8T.Infrastructure/Status/StatusModel.cs new file mode 100644 index 0000000..5e63219 --- /dev/null +++ b/src/BuildingBlocks/N8T.Infrastructure/Status/StatusModel.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; + +namespace N8T.Infrastructure.Status +{ + public class StatusModel + { + public string AppName { get; set; } + public string AppVersion { get; set; } + public string OSArchitecture { get; set; } + public string OSDescription { get; set; } + public string ProcessArchitecture { get; set; } + public string BasePath { get; set; } + public string RuntimeFramework { get; set; } + public string FrameworkDescription { get; set; } + public string HostName { get; set; } + public string IPAddress { get; set; } + public IDictionary Envs { get; set; } = new Dictionary(); + } +} diff --git a/src/BuildingBlocks/N8T.Infrastructure/Tye/Extensions.cs b/src/BuildingBlocks/N8T.Infrastructure/Tye/Extensions.cs index 4736846..bef8f1d 100644 --- a/src/BuildingBlocks/N8T.Infrastructure/Tye/Extensions.cs +++ b/src/BuildingBlocks/N8T.Infrastructure/Tye/Extensions.cs @@ -25,6 +25,24 @@ public static string GetTyeAppUrl(this IConfiguration config, string appId) return url; } + public static string GetTyeGrpcAppUrl(this IConfiguration config, string appId) + { + var host = config[$"service:{appId}:https:host"]; + var port = config[$"service:{appId}:https:port"]; + var url = $"http://{host}:{port}"; // insecure mode - https termination + return url; + } + + public static int GetHttpPort(this IConfiguration config, string appId) + { + return config[$"service:{appId}:port"].ConvertTo(); + } + + public static int GetGrpcPort(this IConfiguration config, string appId) + { + return config[$"service:{appId}:https:port"].ConvertTo(); + } + public static string GetTyeSqlServerConnString(this IConfiguration config, string appId, string dbName, string dbPassword = "P@ssw0rd") { var connString = config[$"connectionstring:{appId}"] ?? diff --git a/src/GraphApi/CoolStore.GraphApi/Program.cs b/src/GraphApi/CoolStore.GraphApi/Program.cs index 493af93..5ebb038 100644 --- a/src/GraphApi/CoolStore.GraphApi/Program.cs +++ b/src/GraphApi/CoolStore.GraphApi/Program.cs @@ -11,7 +11,6 @@ using Microsoft.Extensions.DependencyInjection; using N8T.Infrastructure; using N8T.Infrastructure.Tye; -using Serilog; namespace CoolStore.GraphApi { @@ -26,14 +25,6 @@ private static async Task Main(string[] args) var config = configBuilder.Build(); - Log.Logger = new LoggerConfiguration() - .Enrich.FromLogContext() - .WriteTo.Console() - .CreateLogger(); - - builder.Host - .UseSerilog(); - builder.Services.AddHttpContextAccessor(); builder.Services.AddHttpClient("productCatalog", @@ -42,11 +33,12 @@ private static async Task Main(string[] args) client.BaseAddress = new Uri($"{config.GetTyeAppUrl("product-catalog-api")}/graphql"); }); - // builder.Services.AddHttpClient("inventory", - // (sp, client) => - // { - // client.BaseAddress = new Uri($"{serviceOptions.InventoryService.RestUri}/graphql"); - // }); + builder.Services.AddHttpClient("inventory", + (sp, client) => + { + client.BaseAddress = new Uri($"{config.GetTyeAppUrl("inventory-api")}/graphql"); + }); + // builder.Services.AddHttpClient("shopping_cart", // (sp, client) => // { @@ -58,7 +50,7 @@ private static async Task Main(string[] args) .AddGraphQLSubscriptions() .AddStitchedSchema(stitchingBuilder => stitchingBuilder .AddSchemaFromHttp("productCatalog") - //.AddSchemaFromHttp("inventory") + .AddSchemaFromHttp("inventory") //.AddSchemaFromHttp("shopping_cart") ); diff --git a/src/Inventory/CoolStore.InventoryApi/Apis/GraphQL/InventoryType.cs b/src/Inventory/CoolStore.InventoryApi/Apis/GraphQL/InventoryType.cs new file mode 100644 index 0000000..d2bef82 --- /dev/null +++ b/src/Inventory/CoolStore.InventoryApi/Apis/GraphQL/InventoryType.cs @@ -0,0 +1,15 @@ +using CoolStore.InventoryApi.Dtos; +using HotChocolate.Types; + +namespace CoolStore.ProductCatalogApi.Apis.GraphQL +{ + public class InventoryType : ObjectType + { + protected override void Configure(IObjectTypeDescriptor descriptor) + { + descriptor.Field(t => t.Id).Type>(); + + base.Configure(descriptor); + } + } +} diff --git a/src/Inventory/CoolStore.InventoryApi/Apis/GraphQL/Query.cs b/src/Inventory/CoolStore.InventoryApi/Apis/GraphQL/Query.cs new file mode 100644 index 0000000..6c0fcbd --- /dev/null +++ b/src/Inventory/CoolStore.InventoryApi/Apis/GraphQL/Query.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using CoolStore.InventoryApi.Application.UseCases.GetAvailabilityInventories; +using CoolStore.InventoryApi.Dtos; +using MediatR; + +namespace CoolStore.ProductCatalogApi.Apis.GraphQL +{ + public class Query + { + private readonly IMediator _mediator; + + public Query(IMediator mediator) + { + _mediator = mediator; + } + + public async Task> GetInventories() + { + return await _mediator.Send(new GetInventoriesQuery()); + } + } +} diff --git a/src/Inventory/CoolStore.InventoryApi/Apis/GraphQL/QueryType.cs b/src/Inventory/CoolStore.InventoryApi/Apis/GraphQL/QueryType.cs new file mode 100644 index 0000000..96dfece --- /dev/null +++ b/src/Inventory/CoolStore.InventoryApi/Apis/GraphQL/QueryType.cs @@ -0,0 +1,14 @@ +using HotChocolate.Types; + +namespace CoolStore.ProductCatalogApi.Apis.GraphQL +{ + public sealed class QueryType : ObjectType + { + protected override void Configure(IObjectTypeDescriptor descriptor) + { + descriptor + .Field(x => x.GetInventories()) + .Name("inventories"); + } + } +} diff --git a/src/Inventory/CoolStore.InventoryApi/UserInterface/Grpc/DaprService.cs b/src/Inventory/CoolStore.InventoryApi/Apis/Grpc/DaprService.cs similarity index 92% rename from src/Inventory/CoolStore.InventoryApi/UserInterface/Grpc/DaprService.cs rename to src/Inventory/CoolStore.InventoryApi/Apis/Grpc/DaprService.cs index 1023612..e74c51e 100644 --- a/src/Inventory/CoolStore.InventoryApi/UserInterface/Grpc/DaprService.cs +++ b/src/Inventory/CoolStore.InventoryApi/Apis/Grpc/DaprService.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; using CoolStore.InventoryApi.Application.UseCases.GetAvailabilityInventories; using CoolStore.InventoryApi.Application.UseCases.GetInventory; @@ -10,7 +9,7 @@ using N8T.Infrastructure.Grpc; using CoolStoreDapr = CoolStore.Dapr.Client.Autogen.Grpc; -namespace CoolStore.InventoryApi.UserInterface.Grpc +namespace CoolStore.InventoryApi.Apis.Grpc { public class DaprService : CoolStoreDapr.Dapr.DaprBase { @@ -38,7 +37,6 @@ public DaprService( case "GetInventories": { var result = await _mediator.Send(new GetInventoriesQuery()); - _logger.LogInformation($"Got {result.ToList().Count} items."); var inventories = new List(); inventories.AddRange(result); responseEnvelope.Data = inventories.ConvertToAnyTypeAsync(); diff --git a/src/Inventory/CoolStore.InventoryApi/Apis/Rest/HomeController.cs b/src/Inventory/CoolStore.InventoryApi/Apis/Rest/HomeController.cs new file mode 100644 index 0000000..8f79fc5 --- /dev/null +++ b/src/Inventory/CoolStore.InventoryApi/Apis/Rest/HomeController.cs @@ -0,0 +1,17 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using N8T.Infrastructure.Status; + +namespace CoolStore.InventoryApi.Apis.Rest +{ + [ApiController] + [Route("")] + public class HomeController : ControllerBase + { + [HttpGet("/status")] + public IActionResult Status([FromServices] IConfiguration config) + { + return Content(config.BuildAppStatus()); + } + } +} diff --git a/src/Inventory/CoolStore.InventoryApi/Domain/Inventory.cs b/src/Inventory/CoolStore.InventoryApi/Domain/Inventory.cs index 13ca611..d928afb 100644 --- a/src/Inventory/CoolStore.InventoryApi/Domain/Inventory.cs +++ b/src/Inventory/CoolStore.InventoryApi/Domain/Inventory.cs @@ -6,9 +6,9 @@ namespace CoolStore.InventoryApi.Domain public class Inventory : EntityBase, IAggregateRoot { public Guid Id { get; private set; } - public string Location { get; private set; } = string.Empty; + public string Location { get; private set; } = default!; public string? Description { get; private set; } - public string Website { get; private set; } = string.Empty; + public string Website { get; private set; } = default!; private Inventory() { diff --git a/src/Inventory/CoolStore.InventoryApi/Program.cs b/src/Inventory/CoolStore.InventoryApi/Program.cs index 354bbc0..4a84c18 100644 --- a/src/Inventory/CoolStore.InventoryApi/Program.cs +++ b/src/Inventory/CoolStore.InventoryApi/Program.cs @@ -1,23 +1,29 @@ using System.Diagnostics; using System.Threading.Tasks; +using CoolStore.InventoryApi.Apis.Grpc; using CoolStore.InventoryApi.Infrastructure.Persistence; -using CoolStore.InventoryApi.UserInterface.Grpc; +using CoolStore.ProductCatalogApi.Apis.GraphQL; +using HotChocolate; +using HotChocolate.AspNetCore; +using HotChocolate.AspNetCore.Playground; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using N8T.Infrastructure; using N8T.Infrastructure.Data; +using N8T.Infrastructure.GraphQL; using N8T.Infrastructure.Grpc; +using N8T.Infrastructure.Kestrel; using N8T.Infrastructure.Tye; using N8T.Infrastructure.ValidationModel; -using Serilog; namespace CoolStore.InventoryApi { internal class Program { + public const string INVENTORY_API_ID = "inventory-api"; + private static async Task Main(string[] args) { Activity.DefaultIdFormat = ActivityIdFormat.W3C; @@ -29,39 +35,46 @@ private static async Task Main(string[] args) var config = configBuilder.Build(); - Log.Logger = new LoggerConfiguration() - .Enrich.FromLogContext() - .WriteTo.Console() - .CreateLogger(); - builder.Host .ConfigureWebHostDefaults(webBuilder => { webBuilder.ConfigureKestrel(options => - { - options.ConfigureEndpointDefaults(o => o.Protocols = HttpProtocols.Http2); - }); - }) - .UseSerilog(); + options.ListenHttpAndGrpcProtocols(config, INVENTORY_API_ID)); + }); var connString = config.GetTyeSqlServerConnString("sqlserver", "inventorydb"); builder.Services - .AddLogging() + .AddHttpContextAccessor() .AddCustomMediatR(typeof(Program)) .AddCustomValidators(typeof(Program).Assembly) .AddCustomDbContext(typeof(Program).Assembly, connString) - .AddCustomGrpc(); + .AddCustomGraphQL(c => + { + c.RegisterQueryType(); + c.RegisterObjectTypes(typeof(Program).Assembly); + c.RegisterExtendedScalarTypes(); + }) + .AddCustomGrpc() + .AddControllers(); var app = builder.Build(); - app + app.UseStaticFiles() + .UseGraphQL("/graphql") + .UsePlayground(new PlaygroundOptions {QueryPath = "/graphql", Path = "/playground"}) .UseRouting() .UseCloudEvents() .UseEndpoints(endpoints => { + endpoints.MapControllers(); endpoints.MapSubscribeHandler(); endpoints.MapGrpcService(); + endpoints.MapGet("/", context => + { + context.Response.Redirect("/playground"); + return Task.CompletedTask; + }); }); await app.RunAsync(); diff --git a/src/ProductCatalog/CoolStore.ProductCatalogApi/UserInterface/Console/readme.txt b/src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/Console/readme.txt similarity index 100% rename from src/ProductCatalog/CoolStore.ProductCatalogApi/UserInterface/Console/readme.txt rename to src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/Console/readme.txt diff --git a/src/ProductCatalog/CoolStore.ProductCatalogApi/UserInterface/Gateways/InventoryGateway.cs b/src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/Gateways/InventoryGateway.cs similarity index 95% rename from src/ProductCatalog/CoolStore.ProductCatalogApi/UserInterface/Gateways/InventoryGateway.cs rename to src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/Gateways/InventoryGateway.cs index fc5c525..84c631e 100644 --- a/src/ProductCatalog/CoolStore.ProductCatalogApi/UserInterface/Gateways/InventoryGateway.cs +++ b/src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/Gateways/InventoryGateway.cs @@ -9,7 +9,7 @@ using N8T.Infrastructure; using N8T.Infrastructure.Dapr; -namespace CoolStore.ProductCatalogApi.UserInterface.Gateways +namespace CoolStore.ProductCatalogApi.Apis.Gateways { public class InventoryGateway : IInventoryGateway { diff --git a/src/ProductCatalog/CoolStore.ProductCatalogApi/UserInterface/GraphQL/CatalogProductType.cs b/src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/GraphQL/CatalogProductType.cs similarity index 95% rename from src/ProductCatalog/CoolStore.ProductCatalogApi/UserInterface/GraphQL/CatalogProductType.cs rename to src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/GraphQL/CatalogProductType.cs index 14d6c61..04c82d7 100644 --- a/src/ProductCatalog/CoolStore.ProductCatalogApi/UserInterface/GraphQL/CatalogProductType.cs +++ b/src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/GraphQL/CatalogProductType.cs @@ -6,7 +6,7 @@ using HotChocolate.Types; using N8T.Infrastructure; -namespace CoolStore.ProductCatalogApi.UserInterface.GraphQL +namespace CoolStore.ProductCatalogApi.Apis.GraphQL { public class CatalogProductType : ObjectType { diff --git a/src/ProductCatalog/CoolStore.ProductCatalogApi/UserInterface/GraphQL/CategoryType.cs b/src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/GraphQL/CategoryType.cs similarity index 85% rename from src/ProductCatalog/CoolStore.ProductCatalogApi/UserInterface/GraphQL/CategoryType.cs rename to src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/GraphQL/CategoryType.cs index 97e8709..ac77752 100644 --- a/src/ProductCatalog/CoolStore.ProductCatalogApi/UserInterface/GraphQL/CategoryType.cs +++ b/src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/GraphQL/CategoryType.cs @@ -1,7 +1,7 @@ using CoolStore.ProductCatalogApi.Dtos; using HotChocolate.Types; -namespace CoolStore.ProductCatalogApi.UserInterface.GraphQL +namespace CoolStore.ProductCatalogApi.Apis.GraphQL { public class CategoryType : ObjectType { diff --git a/src/ProductCatalog/CoolStore.ProductCatalogApi/UserInterface/GraphQL/CreateProductInputType.cs b/src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/GraphQL/CreateProductInputType.cs similarity index 56% rename from src/ProductCatalog/CoolStore.ProductCatalogApi/UserInterface/GraphQL/CreateProductInputType.cs rename to src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/GraphQL/CreateProductInputType.cs index 3b87033..1a68433 100644 --- a/src/ProductCatalog/CoolStore.ProductCatalogApi/UserInterface/GraphQL/CreateProductInputType.cs +++ b/src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/GraphQL/CreateProductInputType.cs @@ -1,11 +1,11 @@ -using CoolStore.ProductCatalogApi.Application.UseCase.CreateProduct; +using CoolStore.ProductCatalogApi.Application.UseCases.CreateProduct; using HotChocolate.Types; -namespace CoolStore.ProductCatalogApi.UserInterface.GraphQL +namespace CoolStore.ProductCatalogApi.Apis.GraphQL { - public class CreateProductInputType : InputObjectType + public class CreateProductInputType : InputObjectType { - protected override void Configure(IInputObjectTypeDescriptor descriptor) + protected override void Configure(IInputObjectTypeDescriptor descriptor) { descriptor.Name("CreateProductInput"); } diff --git a/src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/GraphQL/DeleteProductInputType.cs b/src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/GraphQL/DeleteProductInputType.cs new file mode 100644 index 0000000..ddc09ae --- /dev/null +++ b/src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/GraphQL/DeleteProductInputType.cs @@ -0,0 +1,13 @@ +using CoolStore.ProductCatalogApi.Application.UseCases.DeleteProduct; +using HotChocolate.Types; + +namespace CoolStore.ProductCatalogApi.Apis.GraphQL +{ + public class DeleteProductInputType : InputObjectType + { + protected override void Configure(IInputObjectTypeDescriptor descriptor) + { + descriptor.Name("DeleteProductInput"); + } + } +} diff --git a/src/ProductCatalog/CoolStore.ProductCatalogApi/UserInterface/GraphQL/Filters/ProductFilterType.cs b/src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/GraphQL/Filters/ProductFilterType.cs similarity index 91% rename from src/ProductCatalog/CoolStore.ProductCatalogApi/UserInterface/GraphQL/Filters/ProductFilterType.cs rename to src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/GraphQL/Filters/ProductFilterType.cs index 32181a6..19322dc 100644 --- a/src/ProductCatalog/CoolStore.ProductCatalogApi/UserInterface/GraphQL/Filters/ProductFilterType.cs +++ b/src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/GraphQL/Filters/ProductFilterType.cs @@ -1,7 +1,7 @@ using CoolStore.ProductCatalogApi.Dtos; using HotChocolate.Types.Filters; -namespace CoolStore.ProductCatalogApi.UserInterface.GraphQL.Filters +namespace CoolStore.ProductCatalogApi.Apis.GraphQL.Filters { public class ProductFilterType : FilterInputType { diff --git a/src/ProductCatalog/CoolStore.ProductCatalogApi/UserInterface/GraphQL/InventoryType.cs b/src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/GraphQL/InventoryType.cs similarity index 85% rename from src/ProductCatalog/CoolStore.ProductCatalogApi/UserInterface/GraphQL/InventoryType.cs rename to src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/GraphQL/InventoryType.cs index 74aae66..e8e9ead 100644 --- a/src/ProductCatalog/CoolStore.ProductCatalogApi/UserInterface/GraphQL/InventoryType.cs +++ b/src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/GraphQL/InventoryType.cs @@ -1,7 +1,7 @@ using CoolStore.ProductCatalogApi.Dtos; using HotChocolate.Types; -namespace CoolStore.ProductCatalogApi.UserInterface.GraphQL +namespace CoolStore.ProductCatalogApi.Apis.GraphQL { public class InventoryType : ObjectType { diff --git a/src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/GraphQL/Mutation.cs b/src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/GraphQL/Mutation.cs new file mode 100644 index 0000000..7c4a2f9 --- /dev/null +++ b/src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/GraphQL/Mutation.cs @@ -0,0 +1,34 @@ +using System.Threading.Tasks; +using CoolStore.ProductCatalogApi.Application.UseCases.CreateProduct; +using CoolStore.ProductCatalogApi.Application.UseCases.DeleteProduct; +using CoolStore.ProductCatalogApi.Application.UseCases.UpdateProduct; +using CoolStore.ProductCatalogApi.Dtos; +using MediatR; + +namespace CoolStore.ProductCatalogApi.Apis.GraphQL +{ + public class Mutation + { + private readonly IMediator _mediator; + + public Mutation(IMediator mediator) + { + _mediator = mediator; + } + + public async Task CreateProduct(CreateProductCommand createProductInput) + { + return await _mediator.Send(createProductInput); + } + + public async Task UpdateProduct(UpdateProductCommand updateProductInput) + { + return await _mediator.Send(updateProductInput); + } + + public async Task DeleteProduct(DeleteProductCommand deleteProductInput) + { + return await _mediator.Send(deleteProductInput); + } + } +} diff --git a/src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/GraphQL/MutationType.cs b/src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/GraphQL/MutationType.cs new file mode 100644 index 0000000..a2b39a7 --- /dev/null +++ b/src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/GraphQL/MutationType.cs @@ -0,0 +1,25 @@ +using HotChocolate.Types; + +namespace CoolStore.ProductCatalogApi.Apis.GraphQL +{ + public sealed class MutationType : ObjectType + { + protected override void Configure(IObjectTypeDescriptor descriptor) + { + descriptor.Field(t => t.CreateProduct(default!)) + .Type>() + .Argument("createProductInput", a => a + .Type>()); + + descriptor.Field(t => t.UpdateProduct(default!)) + .Type>() + .Argument("updateProductInput", a => a + .Type>()); + + descriptor.Field(t => t.DeleteProduct(default!)) + .Type>() + .Argument("deleteProductInput", a => a + .Type>()); + } + } +} diff --git a/src/ProductCatalog/CoolStore.ProductCatalogApi/UserInterface/GraphQL/Query.cs b/src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/GraphQL/Query.cs similarity index 57% rename from src/ProductCatalog/CoolStore.ProductCatalogApi/UserInterface/GraphQL/Query.cs rename to src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/GraphQL/Query.cs index 29e0d44..c8e6df2 100644 --- a/src/ProductCatalog/CoolStore.ProductCatalogApi/UserInterface/GraphQL/Query.cs +++ b/src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/GraphQL/Query.cs @@ -1,10 +1,11 @@ using System.Collections.Generic; using System.Threading.Tasks; -using CoolStore.ProductCatalogApi.Application.UseCase.GetProducts; +using CoolStore.ProductCatalogApi.Application.UseCases.GetCategories; +using CoolStore.ProductCatalogApi.Application.UseCases.GetProducts; using CoolStore.ProductCatalogApi.Dtos; using MediatR; -namespace CoolStore.ProductCatalogApi.UserInterface.GraphQL +namespace CoolStore.ProductCatalogApi.Apis.GraphQL { public class Query { @@ -19,5 +20,10 @@ public async Task> GetProducts() { return await _mediator.Send(new GetProductsQuery()); } + + public async Task> GetCategories() + { + return await _mediator.Send(new GetCategoriesQuery()); + } } } diff --git a/src/ProductCatalog/CoolStore.ProductCatalogApi/UserInterface/GraphQL/QueryType.cs b/src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/GraphQL/QueryType.cs similarity index 67% rename from src/ProductCatalog/CoolStore.ProductCatalogApi/UserInterface/GraphQL/QueryType.cs rename to src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/GraphQL/QueryType.cs index b40b8e3..c5ce0e8 100644 --- a/src/ProductCatalog/CoolStore.ProductCatalogApi/UserInterface/GraphQL/QueryType.cs +++ b/src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/GraphQL/QueryType.cs @@ -1,10 +1,10 @@ +using CoolStore.ProductCatalogApi.Apis.GraphQL.Filters; +using CoolStore.ProductCatalogApi.Apis.GraphQL.Sorts; using CoolStore.ProductCatalogApi.Dtos; -using CoolStore.ProductCatalogApi.UserInterface.GraphQL.Filters; -using CoolStore.ProductCatalogApi.UserInterface.GraphQL.Sorts; using HotChocolate.Types; using N8T.Infrastructure.GraphQL.OffsetPaging; -namespace CoolStore.ProductCatalogApi.UserInterface.GraphQL +namespace CoolStore.ProductCatalogApi.Apis.GraphQL { public sealed class QueryType : ObjectType { @@ -16,6 +16,10 @@ protected override void Configure(IObjectTypeDescriptor descriptor) .UseOffsetPaging() .UseFiltering() .UseSorting(); + + descriptor + .Field(x => x.GetCategories()) + .Name("categories"); } } } diff --git a/src/ProductCatalog/CoolStore.ProductCatalogApi/UserInterface/GraphQL/Sorts/ProductSortType.cs b/src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/GraphQL/Sorts/ProductSortType.cs similarity index 88% rename from src/ProductCatalog/CoolStore.ProductCatalogApi/UserInterface/GraphQL/Sorts/ProductSortType.cs rename to src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/GraphQL/Sorts/ProductSortType.cs index e28ea00..3fb9e09 100644 --- a/src/ProductCatalog/CoolStore.ProductCatalogApi/UserInterface/GraphQL/Sorts/ProductSortType.cs +++ b/src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/GraphQL/Sorts/ProductSortType.cs @@ -1,7 +1,7 @@ using CoolStore.ProductCatalogApi.Dtos; using HotChocolate.Types.Sorting; -namespace CoolStore.ProductCatalogApi.UserInterface.GraphQL.Sorts +namespace CoolStore.ProductCatalogApi.Apis.GraphQL.Sorts { public class ProductSortType : SortInputType { diff --git a/src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/GraphQL/UpdateProductInputType.cs b/src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/GraphQL/UpdateProductInputType.cs new file mode 100644 index 0000000..2113cb9 --- /dev/null +++ b/src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/GraphQL/UpdateProductInputType.cs @@ -0,0 +1,13 @@ +using CoolStore.ProductCatalogApi.Application.UseCases.UpdateProduct; +using HotChocolate.Types; + +namespace CoolStore.ProductCatalogApi.Apis.GraphQL +{ + public class UpdateProductInputType : InputObjectType + { + protected override void Configure(IInputObjectTypeDescriptor descriptor) + { + descriptor.Name("UpdateProductInput"); + } + } +} diff --git a/src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/Rest/HomeController.cs b/src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/Rest/HomeController.cs new file mode 100644 index 0000000..064a88b --- /dev/null +++ b/src/ProductCatalog/CoolStore.ProductCatalogApi/Apis/Rest/HomeController.cs @@ -0,0 +1,17 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using N8T.Infrastructure.Status; + +namespace CoolStore.ProductCatalogApi.Apis.Rest +{ + [ApiController] + [Route("")] + public class HomeController : ControllerBase + { + [HttpGet("/status")] + public IActionResult Status([FromServices] IConfiguration config) + { + return Content(config.BuildAppStatus()); + } + } +} diff --git a/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/Process/PublishProductCreated/ProductCreatedHandler.cs b/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/Publishers/PublishProductCreated/ProductCreatedHandler.cs similarity index 90% rename from src/ProductCatalog/CoolStore.ProductCatalogApi/Application/Process/PublishProductCreated/ProductCreatedHandler.cs rename to src/ProductCatalog/CoolStore.ProductCatalogApi/Application/Publishers/PublishProductCreated/ProductCreatedHandler.cs index 0f6c381..8655df5 100644 --- a/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/Process/PublishProductCreated/ProductCreatedHandler.cs +++ b/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/Publishers/PublishProductCreated/ProductCreatedHandler.cs @@ -6,7 +6,7 @@ using MediatR; using Microsoft.Extensions.Logging; -namespace CoolStore.ProductCatalogApi.Application.Process.PublishProductCreated +namespace CoolStore.ProductCatalogApi.Application.Publishers.PublishProductCreated { public class PublishProductCreatedHandler : INotificationHandler { diff --git a/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCase/CreateProduct/CreateProductRequest.cs b/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCase/CreateProduct/CreateProductRequest.cs deleted file mode 100644 index f155e2b..0000000 --- a/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCase/CreateProduct/CreateProductRequest.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using CoolStore.ProductCatalogApi.Dtos; -using MediatR; - -namespace CoolStore.ProductCatalogApi.Application.UseCase.CreateProduct -{ - public class CreateProductRequest : IRequest - { - public string Name { get; set; } = string.Empty; - public string? Description { get; set; } - public double Price { get; set; } - public string ImageUrl { get; set; } = "https://picsum.photos/1200/900?image=1"; - public Guid InventoryId { get; set; } - public Guid CategoryId { get; set; } - } -} diff --git a/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/CreateProduct/CreateProductCommand.cs b/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/CreateProduct/CreateProductCommand.cs new file mode 100644 index 0000000..8d2db65 --- /dev/null +++ b/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/CreateProduct/CreateProductCommand.cs @@ -0,0 +1,16 @@ +using System; +using CoolStore.ProductCatalogApi.Dtos; +using MediatR; + +namespace CoolStore.ProductCatalogApi.Application.UseCases.CreateProduct +{ + public class CreateProductCommand : IRequest + { + public string Name { get; set; } = default!; + public string? Description { get; set; } + public double Price { get; set; } = default!; + public string ImageUrl { get; set; } = "https://picsum.photos/1200/900?image=1"; + public Guid InventoryId { get; set; } = default!; + public Guid CategoryId { get; set; } = default!; + } +} diff --git a/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCase/CreateProduct/CreateProductHandler.cs b/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/CreateProduct/CreateProductHandler.cs similarity index 80% rename from src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCase/CreateProduct/CreateProductHandler.cs rename to src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/CreateProduct/CreateProductHandler.cs index 8447a8c..849dcc2 100644 --- a/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCase/CreateProduct/CreateProductHandler.cs +++ b/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/CreateProduct/CreateProductHandler.cs @@ -6,32 +6,30 @@ using CoolStore.ProductCatalogApi.Infrastructure.Persistence; using MediatR; using Microsoft.EntityFrameworkCore; -using N8T.Infrastructure; using N8T.Infrastructure.Data; -namespace CoolStore.ProductCatalogApi.Application.UseCase.CreateProduct +namespace CoolStore.ProductCatalogApi.Application.UseCases.CreateProduct { - public class ProductCreatedHandler : IRequestHandler + public class ProductCreatedHandler : IRequestHandler { private readonly ProductCatalogDbContext _dbContext; public ProductCreatedHandler(ProductCatalogDbContext dbContext) { - _dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext)); + _dbContext = dbContext; } [TransactionScope] - public async Task Handle(CreateProductRequest request, CancellationToken cancellationToken) + public async Task Handle(CreateProductCommand request, CancellationToken cancellationToken) { var product = Product.Of(Guid.NewGuid(), request.Name, request!.Description, request.Price, request.ImageUrl, request.InventoryId, request.CategoryId); var cats = await _dbContext.Categories.ToListAsync(cancellationToken: cancellationToken); var category = await _dbContext.Categories - .FirstOrDefaultAsync(x => x.Id == request.CategoryId.ConvertTo(), - cancellationToken: cancellationToken); + .FirstOrDefaultAsync(x => x.Id == request.CategoryId, cancellationToken: cancellationToken); - if (category == null) throw new NullReferenceException("Couldn't find out {Category}"); + if (category == null) throw new NullReferenceException($"Couldn't find out any Category # {request.CategoryId}"); product.AssignCategory(category); var entityCreated = await _dbContext.Products.AddAsync(product, cancellationToken); diff --git a/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCase/CreateProduct/CreateProductValidator.cs b/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/CreateProduct/CreateProductValidator.cs similarity index 91% rename from src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCase/CreateProduct/CreateProductValidator.cs rename to src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/CreateProduct/CreateProductValidator.cs index cad047e..e982ec3 100644 --- a/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCase/CreateProduct/CreateProductValidator.cs +++ b/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/CreateProduct/CreateProductValidator.cs @@ -1,8 +1,8 @@ using FluentValidation; -namespace CoolStore.ProductCatalogApi.Application.UseCase.CreateProduct +namespace CoolStore.ProductCatalogApi.Application.UseCases.CreateProduct { - public class CreateProductValidator : AbstractValidator + public class CreateProductValidator : AbstractValidator { public CreateProductValidator() { diff --git a/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/DeleteProduct/DeleteProductCommand.cs b/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/DeleteProduct/DeleteProductCommand.cs new file mode 100644 index 0000000..b82c5c3 --- /dev/null +++ b/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/DeleteProduct/DeleteProductCommand.cs @@ -0,0 +1,10 @@ +using System; +using MediatR; + +namespace CoolStore.ProductCatalogApi.Application.UseCases.DeleteProduct +{ + public class DeleteProductCommand: IRequest + { + public Guid Id { get; set; } + } +} diff --git a/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/DeleteProduct/DeleteProductHandler.cs b/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/DeleteProduct/DeleteProductHandler.cs new file mode 100644 index 0000000..6cf950c --- /dev/null +++ b/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/DeleteProduct/DeleteProductHandler.cs @@ -0,0 +1,35 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using CoolStore.ProductCatalogApi.Infrastructure.Persistence; +using MediatR; +using Microsoft.EntityFrameworkCore; +using N8T.Infrastructure.Data; + +namespace CoolStore.ProductCatalogApi.Application.UseCases.DeleteProduct +{ + public class DeleteProductHandler : IRequestHandler + { + private readonly ProductCatalogDbContext _dbContext; + + public DeleteProductHandler(ProductCatalogDbContext dbContext) + { + _dbContext = dbContext; + } + + [TransactionScope] + public async Task Handle(DeleteProductCommand request, CancellationToken cancellationToken) + { + var product = await _dbContext.Products.FirstOrDefaultAsync(x => x.Id == request.Id); + if (product == null) + { + throw new Exception($"Couldn't find product #{request.Id}"); + } + + product.MarkAsDeleted(); + + var effected = await _dbContext.SaveChangesAsync(cancellationToken); + return effected > 0; + } + } +} diff --git a/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/DeleteProduct/DeleteProductValidator.cs b/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/DeleteProduct/DeleteProductValidator.cs new file mode 100644 index 0000000..36a1c47 --- /dev/null +++ b/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/DeleteProduct/DeleteProductValidator.cs @@ -0,0 +1,15 @@ +using FluentValidation; + +namespace CoolStore.ProductCatalogApi.Application.UseCases.DeleteProduct +{ + public class DeleteProductValidator : AbstractValidator + { + public DeleteProductValidator() + { + RuleFor(x => x.Id) + .NotNull() + .NotEmpty() + .WithMessage("${Id} couldn't be null or empty."); + } + } +} diff --git a/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/GetCategories/GetCategoriesHandler.cs b/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/GetCategories/GetCategoriesHandler.cs new file mode 100644 index 0000000..112c001 --- /dev/null +++ b/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/GetCategories/GetCategoriesHandler.cs @@ -0,0 +1,25 @@ +using System.Linq; +using CoolStore.ProductCatalogApi.Dtos; +using CoolStore.ProductCatalogApi.Infrastructure.Persistence; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace CoolStore.ProductCatalogApi.Application.UseCases.GetCategories +{ + public class GetCategoriesHandler : RequestHandler> + { + private readonly ProductCatalogDbContext _dbContext; + + public GetCategoriesHandler(ProductCatalogDbContext dbContext) + { + _dbContext = dbContext; + } + + protected override IQueryable Handle(GetCategoriesQuery request) + { + return _dbContext.Categories + .AsNoTracking() + .Select(x => new CategoryDto {Id = x.Id, Name = x.Name}); + } + } +} diff --git a/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/GetCategories/GetCategoriesQuery.cs b/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/GetCategories/GetCategoriesQuery.cs new file mode 100644 index 0000000..f0128ed --- /dev/null +++ b/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/GetCategories/GetCategoriesQuery.cs @@ -0,0 +1,10 @@ +using System.Linq; +using CoolStore.ProductCatalogApi.Dtos; +using MediatR; + +namespace CoolStore.ProductCatalogApi.Application.UseCases.GetCategories +{ + public class GetCategoriesQuery: IRequest> + { + } +} diff --git a/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/GetCategories/GetCategoriesValidator.cs b/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/GetCategories/GetCategoriesValidator.cs new file mode 100644 index 0000000..85d4e22 --- /dev/null +++ b/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/GetCategories/GetCategoriesValidator.cs @@ -0,0 +1,8 @@ +using FluentValidation; + +namespace CoolStore.ProductCatalogApi.Application.UseCases.GetCategories +{ + public class GetCategoriesValidator : AbstractValidator + { + } +} diff --git a/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCase/GetProducts/GetProductsHandler.cs b/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/GetProducts/GetProductsHandler.cs similarity index 94% rename from src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCase/GetProducts/GetProductsHandler.cs rename to src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/GetProducts/GetProductsHandler.cs index f69e5c2..a2ce2db 100644 --- a/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCase/GetProducts/GetProductsHandler.cs +++ b/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/GetProducts/GetProductsHandler.cs @@ -5,7 +5,7 @@ using MediatR; using Microsoft.EntityFrameworkCore; -namespace CoolStore.ProductCatalogApi.Application.UseCase.GetProducts +namespace CoolStore.ProductCatalogApi.Application.UseCases.GetProducts { public class GetProductsHandler : RequestHandler> { diff --git a/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCase/GetProducts/GetProductsQuery.cs b/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/GetProducts/GetProductsQuery.cs similarity index 70% rename from src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCase/GetProducts/GetProductsQuery.cs rename to src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/GetProducts/GetProductsQuery.cs index 8d1e8fb..bcb0943 100644 --- a/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCase/GetProducts/GetProductsQuery.cs +++ b/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/GetProducts/GetProductsQuery.cs @@ -2,7 +2,7 @@ using CoolStore.ProductCatalogApi.Dtos; using MediatR; -namespace CoolStore.ProductCatalogApi.Application.UseCase.GetProducts +namespace CoolStore.ProductCatalogApi.Application.UseCases.GetProducts { public class GetProductsQuery : IRequest> { diff --git a/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCase/GetProducts/GetProductsValidator.cs b/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/GetProducts/GetProductsValidator.cs similarity index 62% rename from src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCase/GetProducts/GetProductsValidator.cs rename to src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/GetProducts/GetProductsValidator.cs index b4cc893..8e0db52 100644 --- a/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCase/GetProducts/GetProductsValidator.cs +++ b/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/GetProducts/GetProductsValidator.cs @@ -1,6 +1,6 @@ using FluentValidation; -namespace CoolStore.ProductCatalogApi.Application.UseCase.GetProducts +namespace CoolStore.ProductCatalogApi.Application.UseCases.GetProducts { public class GetProductsValidator : AbstractValidator { diff --git a/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/UpdateProduct/UpdateProductCommand.cs b/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/UpdateProduct/UpdateProductCommand.cs new file mode 100644 index 0000000..23685bb --- /dev/null +++ b/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/UpdateProduct/UpdateProductCommand.cs @@ -0,0 +1,17 @@ +using System; +using CoolStore.ProductCatalogApi.Dtos; +using MediatR; + +namespace CoolStore.ProductCatalogApi.Application.UseCases.UpdateProduct +{ + public class UpdateProductCommand : IRequest + { + public Guid Id { get; set; } = default!; + public string Name { get; set; } = default!; + public string? Description { get; set; } + public double Price { get; set; } = default!; + public string ImageUrl { get; set; } = "https://picsum.photos/1200/900?image=1"; + public Guid InventoryId { get; set; } = default!; + public Guid CategoryId { get; set; } = default!; + } +} diff --git a/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/UpdateProduct/UpdateProductHandler.cs b/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/UpdateProduct/UpdateProductHandler.cs new file mode 100644 index 0000000..97d2e67 --- /dev/null +++ b/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/UpdateProduct/UpdateProductHandler.cs @@ -0,0 +1,62 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using CoolStore.ProductCatalogApi.Dtos; +using CoolStore.ProductCatalogApi.Infrastructure.Persistence; +using MediatR; +using Microsoft.EntityFrameworkCore; +using N8T.Infrastructure.Data; + +namespace CoolStore.ProductCatalogApi.Application.UseCases.UpdateProduct +{ + public class UpdateCreatedHandler : IRequestHandler + { + private readonly ProductCatalogDbContext _dbContext; + + public UpdateCreatedHandler(ProductCatalogDbContext dbContext) + { + _dbContext = dbContext; + } + + [TransactionScope] + public async Task Handle(UpdateProductCommand request, CancellationToken cancellationToken) + { + var product = await _dbContext.Products.FirstOrDefaultAsync(x => x.Id == request.Id); + if (product == null) + { + throw new Exception($"Couldn't find product # {request.Id}"); + } + + product.UpdateProduct( + request.Name, + request.Description, + request.Price, + request.ImageUrl, + request.InventoryId); + + var cats = await _dbContext.Categories.ToListAsync(cancellationToken: cancellationToken); + var category = await _dbContext.Categories + .FirstOrDefaultAsync(x => x.Id == request.CategoryId, cancellationToken: cancellationToken); + + if (category == null) + { + throw new NullReferenceException($"Couldn't find out any Category # {request.CategoryId}"); + } + + product.AssignCategory(category); + + var entityCreated = _dbContext.Products.Update(product); + await _dbContext.SaveChangesAsync(cancellationToken); + var productUpdated = entityCreated.Entity; + + return new CatalogProductDto + { + Id = productUpdated.Id, + Name = productUpdated.Name, + Description = productUpdated.Description, + ImageUrl = productUpdated.ImageUrl, + Price = productUpdated.Price + }; + } + } +} diff --git a/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/UpdateProduct/UpdateProductValidator.cs b/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/UpdateProduct/UpdateProductValidator.cs new file mode 100644 index 0000000..d0bb3a7 --- /dev/null +++ b/src/ProductCatalog/CoolStore.ProductCatalogApi/Application/UseCases/UpdateProduct/UpdateProductValidator.cs @@ -0,0 +1,41 @@ +using FluentValidation; + +namespace CoolStore.ProductCatalogApi.Application.UseCases.UpdateProduct +{ + public class UpdateProductValidator : AbstractValidator + { + public UpdateProductValidator() + { + RuleFor(x => x.Id) + .NotNull() + .NotEmpty() + .WithMessage("${Id} couldn't be null or empty."); + + RuleFor(x => x.Name) + .NotNull() + .NotEmpty() + .WithMessage("${Name} couldn't be null or empty."); + + RuleFor(x => x.ImageUrl) + .NotNull() + .NotEmpty() + .WithMessage("${ImageUrl} couldn't be null or empty."); + + RuleFor(x => x.Price) + .NotNull() + .NotEmpty() + .GreaterThan(0) + .WithMessage("${HighPrice} couldn't be null, empty and less than zero."); + + RuleFor(x => x.CategoryId) + .NotNull() + .NotEmpty() + .WithMessage("${CategoryId} couldn't be null or empty."); + + RuleFor(x => x.InventoryId) + .NotNull() + .NotEmpty() + .WithMessage("${InventoryId} couldn't be null or empty."); + } + } +} diff --git a/src/ProductCatalog/CoolStore.ProductCatalogApi/Domain/Category.cs b/src/ProductCatalog/CoolStore.ProductCatalogApi/Domain/Category.cs index 942490d..a09292d 100644 --- a/src/ProductCatalog/CoolStore.ProductCatalogApi/Domain/Category.cs +++ b/src/ProductCatalog/CoolStore.ProductCatalogApi/Domain/Category.cs @@ -6,7 +6,7 @@ namespace CoolStore.ProductCatalogApi.Domain public class Category : EntityBase, IAggregateRoot { public Guid Id { get; private set; } - public string Name { get; private set; } = string.Empty; + public string Name { get; private set; } = default!; private Category() { @@ -19,7 +19,7 @@ public static Category Of(Guid id, string name) public static Category Empty() { - return new Category {Id = Guid.Empty, Name = string.Empty}; + return new Category {Id = default!, Name = default!}; } } } diff --git a/src/ProductCatalog/CoolStore.ProductCatalogApi/Domain/Product.cs b/src/ProductCatalog/CoolStore.ProductCatalogApi/Domain/Product.cs index 63790bc..8f72bd0 100644 --- a/src/ProductCatalog/CoolStore.ProductCatalogApi/Domain/Product.cs +++ b/src/ProductCatalog/CoolStore.ProductCatalogApi/Domain/Product.cs @@ -7,7 +7,7 @@ namespace CoolStore.ProductCatalogApi.Domain public class Product : EntityBase, IAggregateRoot { public Guid Id { get; private set; } - public string Name { get; private set; } = string.Empty; + public string Name { get; private set; } = default!; public string? Description { get; private set; } public double Price { get; private set; } public string ImageUrl { get; private set; } = "https://picsum.photos/1200/900?image=1"; diff --git a/src/ProductCatalog/CoolStore.ProductCatalogApi/Program.cs b/src/ProductCatalog/CoolStore.ProductCatalogApi/Program.cs index a370f36..7248d4b 100644 --- a/src/ProductCatalog/CoolStore.ProductCatalogApi/Program.cs +++ b/src/ProductCatalog/CoolStore.ProductCatalogApi/Program.cs @@ -1,10 +1,10 @@ using System; using System.Diagnostics; using System.Threading.Tasks; +using CoolStore.ProductCatalogApi.Apis.Gateways; +using CoolStore.ProductCatalogApi.Apis.GraphQL; using CoolStore.ProductCatalogApi.Domain; using CoolStore.ProductCatalogApi.Infrastructure.Persistence; -using CoolStore.ProductCatalogApi.UserInterface.Gateways; -using CoolStore.ProductCatalogApi.UserInterface.GraphQL; using HotChocolate; using HotChocolate.AspNetCore; using HotChocolate.AspNetCore.Playground; @@ -15,7 +15,6 @@ using N8T.Infrastructure.GraphQL; using N8T.Infrastructure.Tye; using N8T.Infrastructure.ValidationModel; -using Serilog; namespace CoolStore.ProductCatalogApi { @@ -33,20 +32,13 @@ private static async Task Main(string[] args) var config = configBuilder.Build(); - Log.Logger = new LoggerConfiguration() - .Enrich.FromLogContext() - .WriteTo.Console() - .CreateLogger(); - - builder.Host - .UseSerilog(); - var connString = config.GetTyeSqlServerConnString("sqlserver", "productcatalogdb"); builder.Services - .AddLogging() .AddHttpContextAccessor() .AddCustomMediatR(typeof(Program)) + .AddCustomValidators(typeof(Program).Assembly) + .AddCustomDbContext(typeof(Program).Assembly, connString) .AddCustomGraphQL(c => { c.RegisterQueryType(); @@ -54,17 +46,13 @@ private static async Task Main(string[] args) c.RegisterObjectTypes(typeof(Program).Assembly); c.RegisterExtendedScalarTypes(); }) - .AddCustomValidators(typeof(Program).Assembly) - .AddCustomDbContext(typeof(Program).Assembly, connString) - .AddCustomServices(); + .AddScoped(); var app = builder.Build(); - app.UseStaticFiles(); - app.UseGraphQL("/graphql"); - app.UsePlayground(new PlaygroundOptions {QueryPath = "/graphql", Path = "/playground",}); - - app + app.UseStaticFiles() + .UseGraphQL("/graphql") + .UsePlayground(new PlaygroundOptions {QueryPath = "/graphql", Path = "/playground"}) .UseRouting() .UseCloudEvents() .UseEndpoints(endpoints => @@ -80,13 +68,4 @@ private static async Task Main(string[] args) await app.RunAsync(); } } - - internal static class Extensions - { - internal static IServiceCollection AddCustomServices(this IServiceCollection services) - { - services.AddScoped(); - return services; - } - } } diff --git a/src/ProductCatalog/CoolStore.ProductCatalogApi/UserInterface/GraphQL/Mutation.cs b/src/ProductCatalog/CoolStore.ProductCatalogApi/UserInterface/GraphQL/Mutation.cs deleted file mode 100644 index 2083b87..0000000 --- a/src/ProductCatalog/CoolStore.ProductCatalogApi/UserInterface/GraphQL/Mutation.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Threading.Tasks; -using CoolStore.ProductCatalogApi.Application.UseCase.CreateProduct; -using CoolStore.ProductCatalogApi.Dtos; -using MediatR; - -namespace CoolStore.ProductCatalogApi.UserInterface.GraphQL -{ - public class Mutation - { - private readonly IMediator _mediator; - - public Mutation(IMediator mediator) - { - _mediator = mediator; - } - - public async Task CreateProduct(CreateProductRequest createProductInput) - { - return await _mediator.Send(createProductInput); - } - } -} diff --git a/src/ProductCatalog/CoolStore.ProductCatalogApi/UserInterface/GraphQL/MutationType.cs b/src/ProductCatalog/CoolStore.ProductCatalogApi/UserInterface/GraphQL/MutationType.cs deleted file mode 100644 index 691051d..0000000 --- a/src/ProductCatalog/CoolStore.ProductCatalogApi/UserInterface/GraphQL/MutationType.cs +++ /dev/null @@ -1,15 +0,0 @@ -using HotChocolate.Types; - -namespace CoolStore.ProductCatalogApi.UserInterface.GraphQL -{ - public sealed class MutationType : ObjectType - { - protected override void Configure(IObjectTypeDescriptor descriptor) - { - descriptor.Field(t => t.CreateProduct(default)) - .Type>() - .Argument("createProductInput", a => a - .Type>()); - } - } -} diff --git a/src/ProductCatalog/CoolStore.ProductCatalogApi/UserInterface/Rest/readme.txt b/src/ProductCatalog/CoolStore.ProductCatalogApi/UserInterface/Rest/readme.txt deleted file mode 100644 index 46ce39a..0000000 --- a/src/ProductCatalog/CoolStore.ProductCatalogApi/UserInterface/Rest/readme.txt +++ /dev/null @@ -1 +0,0 @@ -One-off tasks \ No newline at end of file diff --git a/src/WebUI/CoolStore.WebUI.Host/Controllers/InventoryController.cs b/src/WebUI/CoolStore.WebUI.Host/Controllers/InventoryController.cs index 0e9c0b7..61b76d4 100644 --- a/src/WebUI/CoolStore.WebUI.Host/Controllers/InventoryController.cs +++ b/src/WebUI/CoolStore.WebUI.Host/Controllers/InventoryController.cs @@ -1,8 +1,7 @@ -using System; -using System.Collections.Generic; using System.Collections.Immutable; +using System.Linq; using System.Threading.Tasks; -using CoolStore.WebUI.Models; +using CoolStore.WebUI.Components; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -21,14 +20,12 @@ public InventoryController(IGraphQLClient client) [Authorize] [HttpGet] - public Task> GetInventories() + public async Task> GetInventories() { - var result = new List - { - new KeyValueModel {Key = new Guid("90C9479E-A11C-4D6D-AAAA-0405B6C0EFCD"), Value = "Vietnam"} - }; - - return Task.FromResult(result.ToImmutableList()); + var response = await _client.GetInventoriesAsync(); + return response.Data.Inventories + .Select(x => new KeyValueModel {Key = x.Id.ToString(), Value = x.Location}) + .ToImmutableList(); } } } diff --git a/src/WebUI/CoolStore.WebUI.Host/Controllers/ProductController.cs b/src/WebUI/CoolStore.WebUI.Host/Controllers/ProductController.cs index a3621a1..9031d3c 100644 --- a/src/WebUI/CoolStore.WebUI.Host/Controllers/ProductController.cs +++ b/src/WebUI/CoolStore.WebUI.Host/Controllers/ProductController.cs @@ -1,11 +1,12 @@ using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading.Tasks; +using CoolStore.WebUI.Components; using CoolStore.WebUI.Models; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using N8T.Infrastructure; using StrawberryShake; namespace CoolStore.WebUI.Host.Controllers @@ -61,7 +62,7 @@ public async Task GetProducts(int page = 1, int pageSize = 20, str var result = await _client.GetProductsAsync(page, pageSize, filterObjectOptional); var model = new ProductModel {TotalCount = result.Data.Products.TotalCount}; - model.Items.AddRange(result.Data.Products.Edges.Select(x=>new ProductItemModel + model.Items.AddRange(result.Data.Products.Edges.Select(x => new ProductItemModel { Id = x.Id, Name = x.Name, @@ -80,7 +81,7 @@ public async Task GetProducts(int page = 1, int pageSize = 20, str var result = await _client.GetProductsAsync(page, pageSize); var model = new ProductModel {TotalCount = result.Data.Products.TotalCount}; - model.Items.AddRange(result.Data.Products.Edges.Select(x=>new ProductItemModel + model.Items.AddRange(result.Data.Products.Edges.Select(x => new ProductItemModel { Id = x.Id, Name = x.Name, @@ -98,14 +99,12 @@ public async Task GetProducts(int page = 1, int pageSize = 20, str [Authorize] [HttpGet("categories")] - public Task> GetCategories() + public async Task> GetCategories() { - var result = new List - { - new KeyValueModel {Key = new Guid("77666AA8-682C-4047-B075-04839281630A"), Value = "Beverage products"} - }; - - return Task.FromResult(result.ToImmutableList()); + var response = await _client.GetCategoriesAsync(); + return response.Data.Categories + .Select(x => new KeyValueModel {Key = x.Id.ToString(), Value = x.Name}) + .ToImmutableList(); } [Authorize] @@ -116,8 +115,8 @@ await _client.CreateProductMutationAsync(new Optional(new Cr { Name = model.Name, Price = model.Price, - InventoryId = model.InventoryId, - CategoryId = model.CategoryId, + InventoryId = model.InventoryId.ConvertTo(), + CategoryId = model.CategoryId.ConvertTo(), ImageUrl = model.ImageUrl, Description = model.Description })); @@ -127,7 +126,25 @@ await _client.CreateProductMutationAsync(new Optional(new Cr [HttpPut] public async Task EditProduct([FromBody] EditProductModel model) { - throw new NotImplementedException(); + await _client.UpdateProductMutationAsync(new Optional(new UpdateProductInput + { + Name = model.Name, + Price = model.Price, + InventoryId = model.InventoryId.ConvertTo(), + CategoryId = model.CategoryId.ConvertTo(), + ImageUrl = model.ImageUrl, + Description = model.Description + })); + } + + [Authorize] + [HttpDelete("{id}")] + public async Task DeleteProduct(string id) + { + await _client.DeleteProductMutationAsync(new Optional(new DeleteProductInput + { + Id = id.ConvertTo() + })); } } } diff --git a/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/.hash.md5 b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/.hash.md5 index 0fb3e33..3387817 100644 --- a/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/.hash.md5 +++ b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/.hash.md5 @@ -1 +1 @@ -11.0.0.0__+TVjTK12Fq1ZTtNtZwqrSg==_H2a0soBy+K8p9it+9QmcIQ== \ No newline at end of file +11.0.0.0__ahPzJdFu8pJLHCYn56O1Jg==_svcN9nDB2/RP14wPZOsNyA== \ No newline at end of file diff --git a/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/CatalogProductDto2.cs b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/CatalogProductDto2.cs new file mode 100644 index 0000000..d77fadc --- /dev/null +++ b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/CatalogProductDto2.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using StrawberryShake; + +namespace CoolStore.WebUI.Host +{ + [System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial class CatalogProductDto2 + : ICatalogProductDto2 + { + public CatalogProductDto2( + System.Guid id, + string name) + { + Id = id; + Name = name; + } + + public System.Guid Id { get; } + + public string Name { get; } + } +} diff --git a/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/CategoryDto1.cs b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/CategoryDto1.cs new file mode 100644 index 0000000..1feab96 --- /dev/null +++ b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/CategoryDto1.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using StrawberryShake; + +namespace CoolStore.WebUI.Host +{ + [System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial class CategoryDto1 + : ICategoryDto1 + { + public CategoryDto1( + System.Guid id, + string name) + { + Id = id; + Name = name; + } + + public System.Guid Id { get; } + + public string Name { get; } + } +} diff --git a/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/DeleteProductInput.cs b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/DeleteProductInput.cs new file mode 100644 index 0000000..6275865 --- /dev/null +++ b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/DeleteProductInput.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using StrawberryShake; + +namespace CoolStore.WebUI.Host +{ + [System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial class DeleteProductInput + { + public Optional Id { get; set; } + } +} diff --git a/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/DeleteProductInputSerializer.cs b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/DeleteProductInputSerializer.cs new file mode 100644 index 0000000..4992825 --- /dev/null +++ b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/DeleteProductInputSerializer.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using StrawberryShake; + +namespace CoolStore.WebUI.Host +{ + [System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial class DeleteProductInputSerializer + : IInputSerializer + { + private bool _needsInitialization = true; + private IValueSerializer _uuidSerializer; + + public string Name { get; } = "DeleteProductInput"; + + public ValueKind Kind { get; } = ValueKind.InputObject; + + public Type ClrType => typeof(DeleteProductInput); + + public Type SerializationType => typeof(IReadOnlyDictionary); + + public void Initialize(IValueSerializerCollection serializerResolver) + { + if (serializerResolver is null) + { + throw new ArgumentNullException(nameof(serializerResolver)); + } + _uuidSerializer = serializerResolver.Get("Uuid"); + _needsInitialization = false; + } + + public object Serialize(object value) + { + if (_needsInitialization) + { + throw new InvalidOperationException( + $"The serializer for type `{Name}` has not been initialized."); + } + + if (value is null) + { + return null; + } + + var input = (DeleteProductInput)value; + var map = new Dictionary(); + + if (input.Id.HasValue) + { + map.Add("id", SerializeNullableUuid(input.Id.Value)); + } + + return map; + } + + private object SerializeNullableUuid(object value) + { + return _uuidSerializer.Serialize(value); + } + + public object Deserialize(object value) + { + throw new NotSupportedException( + "Deserializing input values is not supported."); + } + } +} diff --git a/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/DeleteProductMutation.cs b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/DeleteProductMutation.cs new file mode 100644 index 0000000..e256151 --- /dev/null +++ b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/DeleteProductMutation.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using StrawberryShake; + +namespace CoolStore.WebUI.Host +{ + [System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial class DeleteProductMutation + : IDeleteProductMutation + { + public DeleteProductMutation( + bool deleteProduct) + { + DeleteProduct = deleteProduct; + } + + public bool DeleteProduct { get; } + } +} diff --git a/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/DeleteProductMutationOperation.cs b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/DeleteProductMutationOperation.cs new file mode 100644 index 0000000..f29f948 --- /dev/null +++ b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/DeleteProductMutationOperation.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using StrawberryShake; + +namespace CoolStore.WebUI.Host +{ + [System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial class DeleteProductMutationOperation + : IOperation + { + public string Name => "deleteProductMutation"; + + public IDocument Document => Queries.Default; + + public OperationKind Kind => OperationKind.Mutation; + + public Type ResultType => typeof(IDeleteProductMutation); + + public Optional DeleteProductInput { get; set; } + + public IReadOnlyList GetVariableValues() + { + var variables = new List(); + + if (DeleteProductInput.HasValue) + { + variables.Add(new VariableValue("deleteProductInput", "DeleteProductInput", DeleteProductInput.Value)); + } + + return variables; + } + } +} diff --git a/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/DeleteProductMutationResultParser.cs b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/DeleteProductMutationResultParser.cs new file mode 100644 index 0000000..cd50f9b --- /dev/null +++ b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/DeleteProductMutationResultParser.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text.Json; +using StrawberryShake; +using StrawberryShake.Configuration; +using StrawberryShake.Http; +using StrawberryShake.Http.Subscriptions; +using StrawberryShake.Transport; + +namespace CoolStore.WebUI.Host +{ + [System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial class DeleteProductMutationResultParser + : JsonResultParserBase + { + private readonly IValueSerializer _booleanSerializer; + + public DeleteProductMutationResultParser(IValueSerializerCollection serializerResolver) + { + if (serializerResolver is null) + { + throw new ArgumentNullException(nameof(serializerResolver)); + } + _booleanSerializer = serializerResolver.Get("Boolean"); + } + + protected override IDeleteProductMutation ParserData(JsonElement data) + { + return new DeleteProductMutation + ( + DeserializeBoolean(data, "deleteProduct") + ); + + } + + private bool DeserializeBoolean(JsonElement obj, string fieldName) + { + JsonElement value = obj.GetProperty(fieldName); + return (bool)_booleanSerializer.Deserialize(value.GetBoolean()); + } + } +} diff --git a/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/GetCategories.cs b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/GetCategories.cs new file mode 100644 index 0000000..6597aee --- /dev/null +++ b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/GetCategories.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using StrawberryShake; + +namespace CoolStore.WebUI.Host +{ + [System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial class GetCategories + : IGetCategories + { + public GetCategories( + global::System.Collections.Generic.IReadOnlyList categories) + { + Categories = categories; + } + + public global::System.Collections.Generic.IReadOnlyList Categories { get; } + } +} diff --git a/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/GetCategoriesOperation.cs b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/GetCategoriesOperation.cs new file mode 100644 index 0000000..2646e81 --- /dev/null +++ b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/GetCategoriesOperation.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using StrawberryShake; + +namespace CoolStore.WebUI.Host +{ + [System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial class GetCategoriesOperation + : IOperation + { + public string Name => "getCategories"; + + public IDocument Document => Queries.Default; + + public OperationKind Kind => OperationKind.Query; + + public Type ResultType => typeof(IGetCategories); + + public IReadOnlyList GetVariableValues() + { + return Array.Empty(); + } + } +} diff --git a/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/GetCategoriesResultParser.cs b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/GetCategoriesResultParser.cs new file mode 100644 index 0000000..82f32cb --- /dev/null +++ b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/GetCategoriesResultParser.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text.Json; +using StrawberryShake; +using StrawberryShake.Configuration; +using StrawberryShake.Http; +using StrawberryShake.Http.Subscriptions; +using StrawberryShake.Transport; + +namespace CoolStore.WebUI.Host +{ + [System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial class GetCategoriesResultParser + : JsonResultParserBase + { + private readonly IValueSerializer _uuidSerializer; + private readonly IValueSerializer _stringSerializer; + + public GetCategoriesResultParser(IValueSerializerCollection serializerResolver) + { + if (serializerResolver is null) + { + throw new ArgumentNullException(nameof(serializerResolver)); + } + _uuidSerializer = serializerResolver.Get("Uuid"); + _stringSerializer = serializerResolver.Get("String"); + } + + protected override IGetCategories ParserData(JsonElement data) + { + return new GetCategories + ( + ParseGetCategoriesCategories(data, "categories") + ); + + } + + private global::System.Collections.Generic.IReadOnlyList ParseGetCategoriesCategories( + JsonElement parent, + string field) + { + JsonElement obj = parent.GetProperty(field); + + int objLength = obj.GetArrayLength(); + var list = new global::CoolStore.WebUI.Host.ICategoryDto1[objLength]; + for (int objIndex = 0; objIndex < objLength; objIndex++) + { + JsonElement element = obj[objIndex]; + list[objIndex] = new CategoryDto1 + ( + DeserializeUuid(element, "id"), + DeserializeString(element, "name") + ); + + } + + return list; + } + + private System.Guid DeserializeUuid(JsonElement obj, string fieldName) + { + JsonElement value = obj.GetProperty(fieldName); + return (System.Guid)_uuidSerializer.Deserialize(value.GetString()); + } + + private string DeserializeString(JsonElement obj, string fieldName) + { + JsonElement value = obj.GetProperty(fieldName); + return (string)_stringSerializer.Deserialize(value.GetString()); + } + } +} diff --git a/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/GetInventories.cs b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/GetInventories.cs new file mode 100644 index 0000000..c275f43 --- /dev/null +++ b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/GetInventories.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using StrawberryShake; + +namespace CoolStore.WebUI.Host +{ + [System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial class GetInventories + : IGetInventories + { + public GetInventories( + global::System.Collections.Generic.IReadOnlyList inventories) + { + Inventories = inventories; + } + + public global::System.Collections.Generic.IReadOnlyList Inventories { get; } + } +} diff --git a/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/GetInventoriesOperation.cs b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/GetInventoriesOperation.cs new file mode 100644 index 0000000..9ff1be8 --- /dev/null +++ b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/GetInventoriesOperation.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using StrawberryShake; + +namespace CoolStore.WebUI.Host +{ + [System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial class GetInventoriesOperation + : IOperation + { + public string Name => "getInventories"; + + public IDocument Document => Queries.Default; + + public OperationKind Kind => OperationKind.Query; + + public Type ResultType => typeof(IGetInventories); + + public IReadOnlyList GetVariableValues() + { + return Array.Empty(); + } + } +} diff --git a/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/GetInventoriesResultParser.cs b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/GetInventoriesResultParser.cs new file mode 100644 index 0000000..2badb37 --- /dev/null +++ b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/GetInventoriesResultParser.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text.Json; +using StrawberryShake; +using StrawberryShake.Configuration; +using StrawberryShake.Http; +using StrawberryShake.Http.Subscriptions; +using StrawberryShake.Transport; + +namespace CoolStore.WebUI.Host +{ + [System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial class GetInventoriesResultParser + : JsonResultParserBase + { + private readonly IValueSerializer _uuidSerializer; + private readonly IValueSerializer _stringSerializer; + + public GetInventoriesResultParser(IValueSerializerCollection serializerResolver) + { + if (serializerResolver is null) + { + throw new ArgumentNullException(nameof(serializerResolver)); + } + _uuidSerializer = serializerResolver.Get("Uuid"); + _stringSerializer = serializerResolver.Get("String"); + } + + protected override IGetInventories ParserData(JsonElement data) + { + return new GetInventories + ( + ParseGetInventoriesInventories(data, "inventories") + ); + + } + + private global::System.Collections.Generic.IReadOnlyList ParseGetInventoriesInventories( + JsonElement parent, + string field) + { + JsonElement obj = parent.GetProperty(field); + + int objLength = obj.GetArrayLength(); + var list = new global::CoolStore.WebUI.Host.IInventoryDto1[objLength]; + for (int objIndex = 0; objIndex < objLength; objIndex++) + { + JsonElement element = obj[objIndex]; + list[objIndex] = new InventoryDto1 + ( + DeserializeUuid(element, "id"), + DeserializeString(element, "location") + ); + + } + + return list; + } + + private System.Guid DeserializeUuid(JsonElement obj, string fieldName) + { + JsonElement value = obj.GetProperty(fieldName); + return (System.Guid)_uuidSerializer.Deserialize(value.GetString()); + } + + private string DeserializeString(JsonElement obj, string fieldName) + { + JsonElement value = obj.GetProperty(fieldName); + return (string)_stringSerializer.Deserialize(value.GetString()); + } + } +} diff --git a/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/GraphQLClient.cs b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/GraphQLClient.cs index 537e44d..1f00b28 100644 --- a/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/GraphQLClient.cs +++ b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/GraphQLClient.cs @@ -49,6 +49,48 @@ public GraphQLClient(global::StrawberryShake.IOperationExecutorPool executorPool return _executor.ExecuteAsync(operation, cancellationToken); } + public global::System.Threading.Tasks.Task> GetCategoriesAsync( + global::System.Threading.CancellationToken cancellationToken = default) + { + + return _executor.ExecuteAsync( + new GetCategoriesOperation(), + cancellationToken); + } + + public global::System.Threading.Tasks.Task> GetCategoriesAsync( + GetCategoriesOperation operation, + global::System.Threading.CancellationToken cancellationToken = default) + { + if (operation is null) + { + throw new ArgumentNullException(nameof(operation)); + } + + return _executor.ExecuteAsync(operation, cancellationToken); + } + + public global::System.Threading.Tasks.Task> GetInventoriesAsync( + global::System.Threading.CancellationToken cancellationToken = default) + { + + return _executor.ExecuteAsync( + new GetInventoriesOperation(), + cancellationToken); + } + + public global::System.Threading.Tasks.Task> GetInventoriesAsync( + GetInventoriesOperation operation, + global::System.Threading.CancellationToken cancellationToken = default) + { + if (operation is null) + { + throw new ArgumentNullException(nameof(operation)); + } + + return _executor.ExecuteAsync(operation, cancellationToken); + } + public global::System.Threading.Tasks.Task> CreateProductMutationAsync( global::StrawberryShake.Optional createProductInput = default, global::System.Threading.CancellationToken cancellationToken = default) @@ -74,5 +116,57 @@ public GraphQLClient(global::StrawberryShake.IOperationExecutorPool executorPool return _executor.ExecuteAsync(operation, cancellationToken); } + + public global::System.Threading.Tasks.Task> UpdateProductMutationAsync( + global::StrawberryShake.Optional updateProductInput = default, + global::System.Threading.CancellationToken cancellationToken = default) + { + if (updateProductInput.HasValue && updateProductInput.Value is null) + { + throw new ArgumentNullException(nameof(updateProductInput)); + } + + return _executor.ExecuteAsync( + new UpdateProductMutationOperation { UpdateProductInput = updateProductInput }, + cancellationToken); + } + + public global::System.Threading.Tasks.Task> UpdateProductMutationAsync( + UpdateProductMutationOperation operation, + global::System.Threading.CancellationToken cancellationToken = default) + { + if (operation is null) + { + throw new ArgumentNullException(nameof(operation)); + } + + return _executor.ExecuteAsync(operation, cancellationToken); + } + + public global::System.Threading.Tasks.Task> DeleteProductMutationAsync( + global::StrawberryShake.Optional deleteProductInput = default, + global::System.Threading.CancellationToken cancellationToken = default) + { + if (deleteProductInput.HasValue && deleteProductInput.Value is null) + { + throw new ArgumentNullException(nameof(deleteProductInput)); + } + + return _executor.ExecuteAsync( + new DeleteProductMutationOperation { DeleteProductInput = deleteProductInput }, + cancellationToken); + } + + public global::System.Threading.Tasks.Task> DeleteProductMutationAsync( + DeleteProductMutationOperation operation, + global::System.Threading.CancellationToken cancellationToken = default) + { + if (operation is null) + { + throw new ArgumentNullException(nameof(operation)); + } + + return _executor.ExecuteAsync(operation, cancellationToken); + } } } diff --git a/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/GraphQLClientServiceCollectionExtensions.cs b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/GraphQLClientServiceCollectionExtensions.cs index e0c3921..e36bed4 100644 --- a/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/GraphQLClientServiceCollectionExtensions.cs +++ b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/GraphQLClientServiceCollectionExtensions.cs @@ -40,8 +40,14 @@ public static IOperationClientBuilder AddGraphQLClient( IOperationClientBuilder builder = serviceCollection.AddOperationClientOptions(_clientName) .AddValueSerializer(() => new CatalogProductDtoFilterSerializer()) .AddValueSerializer(() => new CreateProductInputSerializer()) + .AddValueSerializer(() => new UpdateProductInputSerializer()) + .AddValueSerializer(() => new DeleteProductInputSerializer()) .AddResultParser(serializers => new GetProductsResultParser(serializers)) + .AddResultParser(serializers => new GetCategoriesResultParser(serializers)) + .AddResultParser(serializers => new GetInventoriesResultParser(serializers)) .AddResultParser(serializers => new CreateProductMutationResultParser(serializers)) + .AddResultParser(serializers => new UpdateProductMutationResultParser(serializers)) + .AddResultParser(serializers => new DeleteProductMutationResultParser(serializers)) .AddOperationFormatter(serializers => new JsonOperationFormatter(serializers)) .AddHttpOperationPipeline(b => b.UseHttpDefaultPipeline()); diff --git a/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/ICatalogProductDto2.cs b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/ICatalogProductDto2.cs new file mode 100644 index 0000000..c65cc0d --- /dev/null +++ b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/ICatalogProductDto2.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using StrawberryShake; + +namespace CoolStore.WebUI.Host +{ + [System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial interface ICatalogProductDto2 + { + System.Guid Id { get; } + + string Name { get; } + } +} diff --git a/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/ICategoryDto1.cs b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/ICategoryDto1.cs new file mode 100644 index 0000000..0b66f12 --- /dev/null +++ b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/ICategoryDto1.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using StrawberryShake; + +namespace CoolStore.WebUI.Host +{ + [System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial interface ICategoryDto1 + { + System.Guid Id { get; } + + string Name { get; } + } +} diff --git a/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/IDeleteProductMutation.cs b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/IDeleteProductMutation.cs new file mode 100644 index 0000000..55c3498 --- /dev/null +++ b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/IDeleteProductMutation.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using StrawberryShake; + +namespace CoolStore.WebUI.Host +{ + [System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial interface IDeleteProductMutation + { + bool DeleteProduct { get; } + } +} diff --git a/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/IGetCategories.cs b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/IGetCategories.cs new file mode 100644 index 0000000..96dff15 --- /dev/null +++ b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/IGetCategories.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using StrawberryShake; + +namespace CoolStore.WebUI.Host +{ + [System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial interface IGetCategories + { + global::System.Collections.Generic.IReadOnlyList Categories { get; } + } +} diff --git a/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/IGetInventories.cs b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/IGetInventories.cs new file mode 100644 index 0000000..e5b4423 --- /dev/null +++ b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/IGetInventories.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using StrawberryShake; + +namespace CoolStore.WebUI.Host +{ + [System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial interface IGetInventories + { + global::System.Collections.Generic.IReadOnlyList Inventories { get; } + } +} diff --git a/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/IGraphQLClient.cs b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/IGraphQLClient.cs index e71eee5..d36c8bc 100644 --- a/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/IGraphQLClient.cs +++ b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/IGraphQLClient.cs @@ -20,6 +20,20 @@ public partial interface IGraphQLClient GetProductsOperation operation, CancellationToken cancellationToken = default); + Task> GetCategoriesAsync( + CancellationToken cancellationToken = default); + + Task> GetCategoriesAsync( + GetCategoriesOperation operation, + CancellationToken cancellationToken = default); + + Task> GetInventoriesAsync( + CancellationToken cancellationToken = default); + + Task> GetInventoriesAsync( + GetInventoriesOperation operation, + CancellationToken cancellationToken = default); + Task> CreateProductMutationAsync( Optional createProductInput = default, CancellationToken cancellationToken = default); @@ -27,5 +41,21 @@ public partial interface IGraphQLClient Task> CreateProductMutationAsync( CreateProductMutationOperation operation, CancellationToken cancellationToken = default); + + Task> UpdateProductMutationAsync( + Optional updateProductInput = default, + CancellationToken cancellationToken = default); + + Task> UpdateProductMutationAsync( + UpdateProductMutationOperation operation, + CancellationToken cancellationToken = default); + + Task> DeleteProductMutationAsync( + Optional deleteProductInput = default, + CancellationToken cancellationToken = default); + + Task> DeleteProductMutationAsync( + DeleteProductMutationOperation operation, + CancellationToken cancellationToken = default); } } diff --git a/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/IInventoryDto1.cs b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/IInventoryDto1.cs new file mode 100644 index 0000000..38cbc33 --- /dev/null +++ b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/IInventoryDto1.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using StrawberryShake; + +namespace CoolStore.WebUI.Host +{ + [System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial interface IInventoryDto1 + { + System.Guid Id { get; } + + string Location { get; } + } +} diff --git a/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/IUpdateProductMutation.cs b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/IUpdateProductMutation.cs new file mode 100644 index 0000000..efec109 --- /dev/null +++ b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/IUpdateProductMutation.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using StrawberryShake; + +namespace CoolStore.WebUI.Host +{ + [System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial interface IUpdateProductMutation + { + global::CoolStore.WebUI.Host.ICatalogProductDto2 UpdateProduct { get; } + } +} diff --git a/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/InventoryDto1.cs b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/InventoryDto1.cs new file mode 100644 index 0000000..500bbbf --- /dev/null +++ b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/InventoryDto1.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using StrawberryShake; + +namespace CoolStore.WebUI.Host +{ + [System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial class InventoryDto1 + : IInventoryDto1 + { + public InventoryDto1( + System.Guid id, + string location) + { + Id = id; + Location = location; + } + + public System.Guid Id { get; } + + public string Location { get; } + } +} diff --git a/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/Queries.cs b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/Queries.cs index 186c9e0..bb41676 100644 --- a/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/Queries.cs +++ b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/Queries.cs @@ -21,38 +21,38 @@ public partial class Queries }; private readonly byte[] _hash = new byte[] { - 51, - 56, - 100, - 52, - 98, - 101, - 56, - 57, - 100, - 52, - 102, - 53, - 101, 102, + 48, 98, 56, - 48, + 54, 56, - 102, - 53, - 50, - 53, + 57, 54, + 97, + 56, 53, + 102, 50, - 101, 52, + 54, + 54, + 56, + 97, + 56, + 100, + 100, + 49, + 101, + 56, + 99, 57, - 53, - 102, - 52, - 54 + 55, + 98, + 56, + 57, + 100, + 49 }; private readonly byte[] _content = new byte[] { @@ -395,6 +395,128 @@ public partial class Queries 32, 125, 32, + 113, + 117, + 101, + 114, + 121, + 32, + 103, + 101, + 116, + 67, + 97, + 116, + 101, + 103, + 111, + 114, + 105, + 101, + 115, + 32, + 123, + 32, + 99, + 97, + 116, + 101, + 103, + 111, + 114, + 105, + 101, + 115, + 32, + 123, + 32, + 95, + 95, + 116, + 121, + 112, + 101, + 110, + 97, + 109, + 101, + 32, + 105, + 100, + 32, + 110, + 97, + 109, + 101, + 32, + 125, + 32, + 125, + 32, + 113, + 117, + 101, + 114, + 121, + 32, + 103, + 101, + 116, + 73, + 110, + 118, + 101, + 110, + 116, + 111, + 114, + 105, + 101, + 115, + 32, + 123, + 32, + 105, + 110, + 118, + 101, + 110, + 116, + 111, + 114, + 105, + 101, + 115, + 32, + 123, + 32, + 95, + 95, + 116, + 121, + 112, + 101, + 110, + 97, + 109, + 101, + 32, + 105, + 100, + 32, + 108, + 111, + 99, + 97, + 116, + 105, + 111, + 110, + 32, + 125, + 32, + 125, + 32, 109, 117, 116, @@ -548,6 +670,293 @@ public partial class Queries 32, 125, 32, + 125, + 32, + 109, + 117, + 116, + 97, + 116, + 105, + 111, + 110, + 32, + 117, + 112, + 100, + 97, + 116, + 101, + 80, + 114, + 111, + 100, + 117, + 99, + 116, + 77, + 117, + 116, + 97, + 116, + 105, + 111, + 110, + 40, + 36, + 117, + 112, + 100, + 97, + 116, + 101, + 80, + 114, + 111, + 100, + 117, + 99, + 116, + 73, + 110, + 112, + 117, + 116, + 58, + 32, + 85, + 112, + 100, + 97, + 116, + 101, + 80, + 114, + 111, + 100, + 117, + 99, + 116, + 73, + 110, + 112, + 117, + 116, + 33, + 41, + 32, + 123, + 32, + 117, + 112, + 100, + 97, + 116, + 101, + 80, + 114, + 111, + 100, + 117, + 99, + 116, + 40, + 117, + 112, + 100, + 97, + 116, + 101, + 80, + 114, + 111, + 100, + 117, + 99, + 116, + 73, + 110, + 112, + 117, + 116, + 58, + 32, + 36, + 117, + 112, + 100, + 97, + 116, + 101, + 80, + 114, + 111, + 100, + 117, + 99, + 116, + 73, + 110, + 112, + 117, + 116, + 41, + 32, + 123, + 32, + 95, + 95, + 116, + 121, + 112, + 101, + 110, + 97, + 109, + 101, + 32, + 105, + 100, + 32, + 110, + 97, + 109, + 101, + 32, + 125, + 32, + 125, + 32, + 109, + 117, + 116, + 97, + 116, + 105, + 111, + 110, + 32, + 100, + 101, + 108, + 101, + 116, + 101, + 80, + 114, + 111, + 100, + 117, + 99, + 116, + 77, + 117, + 116, + 97, + 116, + 105, + 111, + 110, + 40, + 36, + 100, + 101, + 108, + 101, + 116, + 101, + 80, + 114, + 111, + 100, + 117, + 99, + 116, + 73, + 110, + 112, + 117, + 116, + 58, + 32, + 68, + 101, + 108, + 101, + 116, + 101, + 80, + 114, + 111, + 100, + 117, + 99, + 116, + 73, + 110, + 112, + 117, + 116, + 33, + 41, + 32, + 123, + 32, + 100, + 101, + 108, + 101, + 116, + 101, + 80, + 114, + 111, + 100, + 117, + 99, + 116, + 40, + 100, + 101, + 108, + 101, + 116, + 101, + 80, + 114, + 111, + 100, + 117, + 99, + 116, + 73, + 110, + 112, + 117, + 116, + 58, + 32, + 36, + 100, + 101, + 108, + 101, + 116, + 101, + 80, + 114, + 111, + 100, + 117, + 99, + 116, + 73, + 110, + 112, + 117, + 116, + 41, + 32, 125 }; @@ -583,11 +992,36 @@ public override string ToString() => } } + query getCategories { + categories { + id + name + } + } + + query getInventories { + inventories { + id + location + } + } + mutation createProductMutation($createProductInput: CreateProductInput!) { createProduct(createProductInput: $createProductInput) { id name } + } + + mutation updateProductMutation($updateProductInput: UpdateProductInput!) { + updateProduct(updateProductInput: $updateProductInput) { + id + name + } + } + + mutation deleteProductMutation($deleteProductInput: DeleteProductInput!) { + deleteProduct(deleteProductInput: $deleteProductInput) }"; } } diff --git a/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/UpdateProductInput.cs b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/UpdateProductInput.cs new file mode 100644 index 0000000..278719c --- /dev/null +++ b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/UpdateProductInput.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using StrawberryShake; + +namespace CoolStore.WebUI.Host +{ + [System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial class UpdateProductInput + { + public Optional CategoryId { get; set; } + + public Optional Description { get; set; } + + public Optional Id { get; set; } + + public Optional ImageUrl { get; set; } + + public Optional InventoryId { get; set; } + + public Optional Name { get; set; } + + public Optional Price { get; set; } + } +} diff --git a/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/UpdateProductInputSerializer.cs b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/UpdateProductInputSerializer.cs new file mode 100644 index 0000000..5667807 --- /dev/null +++ b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/UpdateProductInputSerializer.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using StrawberryShake; + +namespace CoolStore.WebUI.Host +{ + [System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial class UpdateProductInputSerializer + : IInputSerializer + { + private bool _needsInitialization = true; + private IValueSerializer _uuidSerializer; + private IValueSerializer _stringSerializer; + private IValueSerializer _floatSerializer; + + public string Name { get; } = "UpdateProductInput"; + + public ValueKind Kind { get; } = ValueKind.InputObject; + + public Type ClrType => typeof(UpdateProductInput); + + public Type SerializationType => typeof(IReadOnlyDictionary); + + public void Initialize(IValueSerializerCollection serializerResolver) + { + if (serializerResolver is null) + { + throw new ArgumentNullException(nameof(serializerResolver)); + } + _uuidSerializer = serializerResolver.Get("Uuid"); + _stringSerializer = serializerResolver.Get("String"); + _floatSerializer = serializerResolver.Get("Float"); + _needsInitialization = false; + } + + public object Serialize(object value) + { + if (_needsInitialization) + { + throw new InvalidOperationException( + $"The serializer for type `{Name}` has not been initialized."); + } + + if (value is null) + { + return null; + } + + var input = (UpdateProductInput)value; + var map = new Dictionary(); + + if (input.CategoryId.HasValue) + { + map.Add("categoryId", SerializeNullableUuid(input.CategoryId.Value)); + } + + if (input.Description.HasValue) + { + map.Add("description", SerializeNullableString(input.Description.Value)); + } + + if (input.Id.HasValue) + { + map.Add("id", SerializeNullableUuid(input.Id.Value)); + } + + if (input.ImageUrl.HasValue) + { + map.Add("imageUrl", SerializeNullableString(input.ImageUrl.Value)); + } + + if (input.InventoryId.HasValue) + { + map.Add("inventoryId", SerializeNullableUuid(input.InventoryId.Value)); + } + + if (input.Name.HasValue) + { + map.Add("name", SerializeNullableString(input.Name.Value)); + } + + if (input.Price.HasValue) + { + map.Add("price", SerializeNullableFloat(input.Price.Value)); + } + + return map; + } + + private object SerializeNullableUuid(object value) + { + return _uuidSerializer.Serialize(value); + } + private object SerializeNullableString(object value) + { + if (value is null) + { + return null; + } + + + return _stringSerializer.Serialize(value); + } + private object SerializeNullableFloat(object value) + { + return _floatSerializer.Serialize(value); + } + + public object Deserialize(object value) + { + throw new NotSupportedException( + "Deserializing input values is not supported."); + } + } +} diff --git a/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/UpdateProductMutation.cs b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/UpdateProductMutation.cs new file mode 100644 index 0000000..a6a2ed2 --- /dev/null +++ b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/UpdateProductMutation.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using StrawberryShake; + +namespace CoolStore.WebUI.Host +{ + [System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial class UpdateProductMutation + : IUpdateProductMutation + { + public UpdateProductMutation( + global::CoolStore.WebUI.Host.ICatalogProductDto2 updateProduct) + { + UpdateProduct = updateProduct; + } + + public global::CoolStore.WebUI.Host.ICatalogProductDto2 UpdateProduct { get; } + } +} diff --git a/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/UpdateProductMutationOperation.cs b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/UpdateProductMutationOperation.cs new file mode 100644 index 0000000..6d5782d --- /dev/null +++ b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/UpdateProductMutationOperation.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using StrawberryShake; + +namespace CoolStore.WebUI.Host +{ + [System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial class UpdateProductMutationOperation + : IOperation + { + public string Name => "updateProductMutation"; + + public IDocument Document => Queries.Default; + + public OperationKind Kind => OperationKind.Mutation; + + public Type ResultType => typeof(IUpdateProductMutation); + + public Optional UpdateProductInput { get; set; } + + public IReadOnlyList GetVariableValues() + { + var variables = new List(); + + if (UpdateProductInput.HasValue) + { + variables.Add(new VariableValue("updateProductInput", "UpdateProductInput", UpdateProductInput.Value)); + } + + return variables; + } + } +} diff --git a/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/UpdateProductMutationResultParser.cs b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/UpdateProductMutationResultParser.cs new file mode 100644 index 0000000..e7f5b18 --- /dev/null +++ b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Generated/UpdateProductMutationResultParser.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text.Json; +using StrawberryShake; +using StrawberryShake.Configuration; +using StrawberryShake.Http; +using StrawberryShake.Http.Subscriptions; +using StrawberryShake.Transport; + +namespace CoolStore.WebUI.Host +{ + [System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial class UpdateProductMutationResultParser + : JsonResultParserBase + { + private readonly IValueSerializer _uuidSerializer; + private readonly IValueSerializer _stringSerializer; + + public UpdateProductMutationResultParser(IValueSerializerCollection serializerResolver) + { + if (serializerResolver is null) + { + throw new ArgumentNullException(nameof(serializerResolver)); + } + _uuidSerializer = serializerResolver.Get("Uuid"); + _stringSerializer = serializerResolver.Get("String"); + } + + protected override IUpdateProductMutation ParserData(JsonElement data) + { + return new UpdateProductMutation + ( + ParseUpdateProductMutationUpdateProduct(data, "updateProduct") + ); + + } + + private global::CoolStore.WebUI.Host.ICatalogProductDto2 ParseUpdateProductMutationUpdateProduct( + JsonElement parent, + string field) + { + JsonElement obj = parent.GetProperty(field); + + return new CatalogProductDto2 + ( + DeserializeUuid(obj, "id"), + DeserializeString(obj, "name") + ); + } + + private System.Guid DeserializeUuid(JsonElement obj, string fieldName) + { + JsonElement value = obj.GetProperty(fieldName); + return (System.Guid)_uuidSerializer.Deserialize(value.GetString()); + } + + private string DeserializeString(JsonElement obj, string fieldName) + { + JsonElement value = obj.GetProperty(fieldName); + return (string)_stringSerializer.Deserialize(value.GetString()); + } + } +} diff --git a/src/WebUI/CoolStore.WebUI.Host/GraphQL/GraphQL.graphql b/src/WebUI/CoolStore.WebUI.Host/GraphQL/GraphQL.graphql index 6780635..f232ed2 100644 --- a/src/WebUI/CoolStore.WebUI.Host/GraphQL/GraphQL.graphql +++ b/src/WebUI/CoolStore.WebUI.Host/GraphQL/GraphQL.graphql @@ -37,6 +37,10 @@ input CreateProductInput { price: Float! } +input DeleteProductInput { + id: Uuid! +} + type InventoryDto { description: String id: Uuid! @@ -46,6 +50,8 @@ type InventoryDto { type Mutation { createProduct(createProductInput: CreateProductInput!): CatalogProductDto! + deleteProduct(deleteProductInput: DeleteProductInput!): Boolean! + updateProduct(updateProductInput: UpdateProductInput!): CatalogProductDto! } type OffsetPagingOfCatalogProductDto { @@ -55,6 +61,8 @@ type OffsetPagingOfCatalogProductDto { } type Query { + categories: [CategoryDto!]! + inventories: [InventoryDto!]! products( order_by: CatalogProductDtoSort page: Int @@ -67,3 +75,13 @@ enum SortOperationKind { ASC DESC } + +input UpdateProductInput { + categoryId: Uuid! + description: String + id: Uuid! + imageUrl: String! + inventoryId: Uuid! + name: String! + price: Float! +} diff --git a/src/WebUI/CoolStore.WebUI.Host/GraphQL/Queries.graphql b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Queries.graphql index bbda458..a8d36ee 100644 --- a/src/WebUI/CoolStore.WebUI.Host/GraphQL/Queries.graphql +++ b/src/WebUI/CoolStore.WebUI.Host/GraphQL/Queries.graphql @@ -21,9 +21,34 @@ query getProducts($page: Int, $pageSize: Int, $where: CatalogProductDtoFilter) { } } +query getCategories { + categories { + id + name + } +} + +query getInventories { + inventories { + id + location + } +} + mutation createProductMutation($createProductInput: CreateProductInput!) { createProduct(createProductInput: $createProductInput) { id name } } + +mutation updateProductMutation($updateProductInput: UpdateProductInput!) { + updateProduct(updateProductInput: $updateProductInput) { + id + name + } +} + +mutation deleteProductMutation($deleteProductInput: DeleteProductInput!) { + deleteProduct(deleteProductInput: $deleteProductInput) +} diff --git a/src/WebUI/CoolStore.WebUI.Host/GraphQL/berry.json b/src/WebUI/CoolStore.WebUI.Host/GraphQL/berry.json index d6f8a75..c00f8dd 100644 --- a/src/WebUI/CoolStore.WebUI.Host/GraphQL/berry.json +++ b/src/WebUI/CoolStore.WebUI.Host/GraphQL/berry.json @@ -4,7 +4,7 @@ "Name": "GraphQL", "Type": "http", "File": "GraphQL.graphql", - "Url": "http://localhost:60917/graphql" + "Url": "http://localhost:64237/graphql" } ], "ClientName": "GraphQLClient" diff --git a/src/WebUI/CoolStore.WebUI.Host/appsettings.json b/src/WebUI/CoolStore.WebUI.Host/appsettings.json index 2eb43fe..be58798 100644 --- a/src/WebUI/CoolStore.WebUI.Host/appsettings.json +++ b/src/WebUI/CoolStore.WebUI.Host/appsettings.json @@ -1,7 +1,7 @@ { "IsDev": false, - "IdentityUrl": "http://localhost:60919", - "GraphQLUrl": "http://localhost:60917", + "IdentityUrl": "http://localhost:59562", + "GraphQLUrl": "http://localhost:59557", "Logging": { "LogLevel": { "Default": "Information", diff --git a/src/WebUI/CoolStore.WebUI/Components/Models.cs b/src/WebUI/CoolStore.WebUI/Components/Models.cs new file mode 100644 index 0000000..631a0dd --- /dev/null +++ b/src/WebUI/CoolStore.WebUI/Components/Models.cs @@ -0,0 +1,8 @@ +namespace CoolStore.WebUI.Components +{ + public class KeyValueModel + { + public string Key { get; set; } + public string Value { get; set; } + } +} diff --git a/src/WebUI/CoolStore.WebUI/Pages/Products/CreateProduct.razor b/src/WebUI/CoolStore.WebUI/Pages/Products/CreateProduct.razor index 93eecaf..114c2ee 100644 --- a/src/WebUI/CoolStore.WebUI/Pages/Products/CreateProduct.razor +++ b/src/WebUI/CoolStore.WebUI/Pages/Products/CreateProduct.razor @@ -7,10 +7,12 @@
Create a New Product
+
+

No product has found!

+
+
+ Loading... +
+
} else { @@ -35,7 +39,7 @@ else
Edit - +
@@ -99,6 +103,15 @@ else }); } + async Task DeleteProduct(Guid id) + { + await Task.Run(async () => + { + await HttpClient.DeleteAsync($"api/products/{id}"); + _productModel = await HttpClient.GetJsonAsync(ProcessUrl(_filter)); + }); + } + private string ProcessUrl(string filter) { var filtered = $"api/products/{Page}/{_pageSize}/&"; diff --git a/src/WebUI/CoolStore.WebUI/_Imports.razor b/src/WebUI/CoolStore.WebUI/_Imports.razor index e0fee25..7a85b3b 100644 --- a/src/WebUI/CoolStore.WebUI/_Imports.razor +++ b/src/WebUI/CoolStore.WebUI/_Imports.razor @@ -9,3 +9,4 @@ @using CoolStore.WebUI.Shared @using CoolStore.WebUI.Models @using CoolStore.WebUI +@using CoolStore.WebUI.Components diff --git a/test.cs b/test.cs deleted file mode 100644 index 88903fa..0000000 --- a/test.cs +++ /dev/null @@ -1,28 +0,0 @@ -public class InventoryGateway : IInventoryGateway -{ - private readonly IConfiguration _config; - - public InventoryGateway(IConfiguration config) - { - _config = config; - } - - public async Task> GetInventoriesAsync( - IReadOnlyCollection invIds, - CancellationToken cancellationToken) - { - var daprClient = _config.GetDaprClient("inventory-api"); - - var request = new InventoriesByIdsDto(); - request.Ids.AddRange(invIds.Select(x => x.ToString())); - - var inventories = await daprClient.InvokeMethodAsync>( - "inventory-api", - "GetInventoriesByIds", - request, - null, - cancellationToken); - - return inventories.ToDictionary(x => x.Id.ConvertTo()); - } -} diff --git a/tye.yaml b/tye.yaml index af6c599..ff0271c 100644 --- a/tye.yaml +++ b/tye.yaml @@ -8,6 +8,7 @@ name: practical-dapr extensions: - name: dapr log-level: debug + config: appconfig # registry: dapracr.azurecr.io or docker.pkg.github.com/thangchung/practical-dapr registry: vndg services: @@ -20,10 +21,28 @@ services: value: "Y" bindings: - port: 1433 -# - name: redis -# image: redis -# bindings: -# - port: 6973 +- name: redis + image: redis + bindings: + - port: 6379 +- name: zipkin + image: openzipkin/zipkin + bindings: + - port: 9411 + containerPort: 9411 + protocol: http +- name: seq + image: datalust/seq + env: + - name: "ACCEPT_EULA" + value: "Y" + bindings: + - name: ui + port: 5340 + protocol: http + containerPort: 80 + - name: internal + port: 5341 - name: graph-api project: src/GraphApi/CoolStore.GraphApi/CoolStore.GraphApi.csproj - name: identity-api