diff --git a/dapr/quickstarts/.devcontainer/Dockerfile b/dapr/quickstarts/.devcontainer/Dockerfile new file mode 100644 index 0000000..8d6323a --- /dev/null +++ b/dapr/quickstarts/.devcontainer/Dockerfile @@ -0,0 +1,32 @@ +# +# Copyright 2021 The Dapr Authors +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +FROM mcr.microsoft.com/vscode/devcontainers/universal:latest + +# Copy custom first notice message. +COPY first-run-notice.txt /tmp/staging/ +RUN sudo mv -f /tmp/staging/first-run-notice.txt /usr/local/etc/vscode-dev-containers/ \ + && sudo rm -rf /tmp/staging + +# Install minikube +RUN MINIKUBE_URL="https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64" \ + && sudo curl -sSL -o /usr/local/bin/minikube "${MINIKUBE_URL}" \ + && sudo chmod 0755 /usr/local/bin/minikube \ + && MINIKUBE_SHA256=$(curl -sSL "${MINIKUBE_URL}.sha256") \ + && echo "${MINIKUBE_SHA256} */usr/local/bin/minikube" | sha256sum -c - + +# Install Dapr CLI +RUN wget -q https://raw.githubusercontent.com/dapr/cli/master/install/install.sh -O - | /bin/bash + +# Install mechanical-markdown for quickstart validations +RUN pip install mechanical-markdown diff --git a/dapr/quickstarts/.devcontainer/devcontainer.json b/dapr/quickstarts/.devcontainer/devcontainer.json new file mode 100644 index 0000000..e4e07bc --- /dev/null +++ b/dapr/quickstarts/.devcontainer/devcontainer.json @@ -0,0 +1,27 @@ +{ + "name": "Dapr Quickstarts Codespace", + "dockerFile": "Dockerfile", + "extensions": [ + "golang.go", + "ms-azuretools.vscode-dapr", + "ms-azuretools.vscode-docker", + "ms-kubernetes-tools.vscode-kubernetes-tools" + ], + "mounts": [ + // Mount docker-in-docker library volume + "source=codespaces-linux-var-lib-docker,target=/var/lib/docker,type=volume" + ], + // Always run image-defined docker-init.sh to enable docker-in-docker + "overrideCommand": false, + "remoteUser": "codespace", + "runArgs": [ + // Enable ptrace-based debugging for Go in container + "--cap-add=SYS_PTRACE", + "--security-opt", + "seccomp=unconfined", + + // Enable docker-in-docker configuration + "--init", + "--privileged" + ], +} diff --git a/dapr/quickstarts/.devcontainer/first-run-notice.txt b/dapr/quickstarts/.devcontainer/first-run-notice.txt new file mode 100644 index 0000000..c60745e --- /dev/null +++ b/dapr/quickstarts/.devcontainer/first-run-notice.txt @@ -0,0 +1,4 @@ +👋 Welcome to the Dapr Codespace! You are on our Dapr Quickstarts image. +It includes everything needed to run through our tutorials and quickstart applications. + +📚 Dapr docs can be found at: https://docs.dapr.io/ diff --git a/dapr/quickstarts/.github/ISSUE_TEMPLATE/bug_report.md b/dapr/quickstarts/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..8daaa7e --- /dev/null +++ b/dapr/quickstarts/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,26 @@ +--- +name: Bug report +about: Report a bug in Dapr quickstarts +title: '' +labels: kind/bug +assignees: '' + +--- + + + + + +## Expected Behavior + + + + +## Actual Behavior + + + + +## Steps to Reproduce the Problem + + diff --git a/dapr/quickstarts/.github/ISSUE_TEMPLATE/discussion.md b/dapr/quickstarts/.github/ISSUE_TEMPLATE/discussion.md new file mode 100644 index 0000000..bb9b931 --- /dev/null +++ b/dapr/quickstarts/.github/ISSUE_TEMPLATE/discussion.md @@ -0,0 +1,12 @@ +--- +name: Discussion +about: Start a discussion for Dapr quickstarts +title: '' +labels: kind/discussion +assignees: '' + +--- + + + + diff --git a/dapr/quickstarts/.github/ISSUE_TEMPLATE/feature_request.md b/dapr/quickstarts/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..538fd77 --- /dev/null +++ b/dapr/quickstarts/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,14 @@ +--- +name: Feature Request +about: Create a Feature Request for Dapr quickstarts +title: '' +labels: kind/enhancement +assignees: '' + +--- + + + + + +## Describe the feature diff --git a/dapr/quickstarts/.github/ISSUE_TEMPLATE/proposal.md b/dapr/quickstarts/.github/ISSUE_TEMPLATE/proposal.md new file mode 100644 index 0000000..aec9ae5 --- /dev/null +++ b/dapr/quickstarts/.github/ISSUE_TEMPLATE/proposal.md @@ -0,0 +1,14 @@ +--- +name: Proposal +about: Create a proposal for Dapr quickstarts +title: '' +labels: kind/proposal +assignees: '' + +--- + + + + + +## Describe the proposal diff --git a/dapr/quickstarts/.github/ISSUE_TEMPLATE/question.md b/dapr/quickstarts/.github/ISSUE_TEMPLATE/question.md new file mode 100644 index 0000000..605c51d --- /dev/null +++ b/dapr/quickstarts/.github/ISSUE_TEMPLATE/question.md @@ -0,0 +1,14 @@ +--- +name: Question +about: Ask a question about Dapr quickstarts +title: '' +labels: kind/question +assignees: '' + +--- + + + + + +## Ask your question here diff --git a/dapr/quickstarts/.github/pull_request_template.md b/dapr/quickstarts/.github/pull_request_template.md new file mode 100644 index 0000000..fdfbef0 --- /dev/null +++ b/dapr/quickstarts/.github/pull_request_template.md @@ -0,0 +1,18 @@ +# Description + +_Please explain the changes you've made_ + +## Issue reference + +We strive to have all PRs being opened based on an issue, where the problem or feature have been discussed prior to implementation. + +Please reference the issue this PR will close: #_[issue number]_ + +## Checklist + +Please make sure you've completed the relevant tasks for this PR, out of the following list: + +* [ ] The quickstart code compiles correctly +* [ ] You've tested new builds of the quickstart if you changed quickstart code +* [ ] You've updated the quickstart's README if necessary +* [ ] If you have changed the steps for a quickstart be sure that you have updated the automated validation accordingly. All of our quickstarts have annotations that allow them to be executed automatically as code. For more information see [mechanical-markdown](https://github.com/dapr/mechanical-markdown#readme). For user guide with examples see [Examples](https://github.com/dapr/mechanical-markdown/tree/main/examples#mechanical-markdown-by-example). diff --git a/dapr/quickstarts/.github/scripts/get_release_version.py b/dapr/quickstarts/.github/scripts/get_release_version.py new file mode 100644 index 0000000..b21150a --- /dev/null +++ b/dapr/quickstarts/.github/scripts/get_release_version.py @@ -0,0 +1,39 @@ +# +# Copyright 2021 The Dapr Authors +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This script parses release version from Git tag and set the parsed version to +# environment variable, REL_VERSION. + +import os +import sys + +gitRef = os.getenv("GITHUB_REF") +tagRefPrefix = "refs/tags/v" + +with open(os.getenv("GITHUB_ENV"), "a") as githubEnv: + if gitRef is None or not gitRef.startswith(tagRefPrefix): + githubEnv.write("REL_VERSION=edge\n") + print ("This is daily build from {}...".format(gitRef)) + sys.exit(0) + + releaseVersion = gitRef[len(tagRefPrefix):] + releaseNotePath="docs/release_notes/v{}.md".format(releaseVersion) + + if gitRef.find("-rc.") > 0: + print ("Release Candidate build from {}...".format(gitRef)) + else: + # Set LATEST_RELEASE to true + githubEnv.write("LATEST_RELEASE=true\n") + print ("Release build from {}...".format(gitRef)) + + githubEnv.write("REL_VERSION={}\n".format(releaseVersion)) diff --git a/dapr/quickstarts/.github/workflows/build.yml b/dapr/quickstarts/.github/workflows/build.yml new file mode 100644 index 0000000..1a22200 --- /dev/null +++ b/dapr/quickstarts/.github/workflows/build.yml @@ -0,0 +1,177 @@ +# +# Copyright 2021 The Dapr Authors +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +name: samples + +on: + push: + branches: + - master + - feature/new_quickstarts + - release-* + tags: + - v* + paths-ignore: + - '**.md' + workflow_dispatch: +jobs: + build: + name: Build ${{ matrix.target_os }}_${{ matrix.target_arch }} binaries + runs-on: ${{ matrix.os }} + env: + TARGET_OS: ${{ matrix.target_os }} + TARGET_ARCH: ${{ matrix.target_arch }} + DOCKER_CLI_EXPERIMENTAL: enabled + strategy: + matrix: + os: [ubuntu-latest] + target_arch: [arm, arm64, amd64] + include: + - os: ubuntu-latest + target_os: linux + exclude: + - os: windows-latest + target_arch: arm + - os: macOS-latest + target_arch: arm + - os: windows-latest + target_arch: arm64 + - os: macOS-latest + target_arch: arm64 + steps: + - name: Check Docker version + run: docker --version + - name: Check out code + uses: actions/checkout@v2 + - name: Parse release version and set REL_VERSION + run: python ./.github/scripts/get_release_version.py + - name: Set Docker's multiarch/qemu-user-static + run: | + docker run --rm --privileged multiarch/qemu-user-static --reset -p yes + docker buildx create --name mybuilder --driver docker-container --use + - name: Check platforms buildx supports + run: docker buildx inspect --bootstrap + - name: Build docker image + run: | + SAMPLE_LIST=(tutorials/hello-kubernetes tutorials/distributed-calculator tutorials/pub-sub tutorials/bindings tutorials/observability tutorials/secretstore) + for sample in "${SAMPLE_LIST[@]}"; do + echo "Building image for ${sample}..." + pushd ${sample} + make build + popd + done + - name: docker login + run: | + docker login -u ${{ secrets.DOCKER_REGISTRY_ID }} -p ${{ secrets.DOCKER_REGISTRY_PASS }} + - name: publish image to dockerhub + run: | + SAMPLE_LIST=(tutorials/hello-kubernetes tutorials/distributed-calculator tutorials/pub-sub tutorials/bindings tutorials/observability tutorials/secretstore) + for sample in "${SAMPLE_LIST[@]}"; do + echo "Push image for ${sample}..." + pushd ${sample} + make push + popd + done + - name: GitHub container registry login + if: github.event_name != 'pull_request' + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build docker image + env: + SAMPLE_REGISTRY: ghcr.io/dapr/samples + run: | + SAMPLE_LIST=(tutorials/hello-kubernetes tutorials/distributed-calculator tutorials/pub-sub tutorials/bindings tutorials/observability tutorials/secretstore) + for sample in "${SAMPLE_LIST[@]}"; do + echo "Building image for ${sample}..." + pushd ${sample} + make build + popd + done + - name: publish image to ghcr + if: github.event_name != 'pull_request' + env: + SAMPLE_REGISTRY: ghcr.io/dapr/samples + run: | + SAMPLE_LIST=(tutorials/hello-kubernetes tutorials/distributed-calculator tutorials/pub-sub tutorials/bindings tutorials/observability tutorials/secretstore) + for sample in "${SAMPLE_LIST[@]}"; do + echo "Push image for ${sample}..." + pushd ${sample} + make push + popd + done + publish: + name: Publish docker manifest + needs: build + runs-on: ubuntu-latest + env: + DOCKER_CLI_EXPERIMENTAL: enabled + steps: + - name: Check out code + uses: actions/checkout@v2 + - name: Parse release version and set REL_VERSION + run: python ./.github/scripts/get_release_version.py + - name: create docker manifest + run: | + SAMPLE_LIST=(tutorials/hello-kubernetes tutorials/distributed-calculator tutorials/pub-sub tutorials/bindings tutorials/observability tutorials/secretstore) + for sample in "${SAMPLE_LIST[@]}"; do + echo "Building image for ${sample}..." + pushd ${sample} + make manifest-create + popd + done + - name: docker login + run: | + docker login -u ${{ secrets.DOCKER_REGISTRY_ID }} -p ${{ secrets.DOCKER_REGISTRY_PASS }} + - name: publish manifest to dockerhub + run: | + SAMPLE_LIST=(tutorials/hello-kubernetes tutorials/distributed-calculator tutorials/pub-sub tutorials/bindings tutorials/observability tutorials/secretstore) + for sample in "${SAMPLE_LIST[@]}"; do + echo "Push image for ${sample}..." + pushd ${sample} + make manifest-push + popd + done + - name: GitHub container registry login + if: github.event_name != 'pull_request' + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: create ghcr manifest + if: github.event_name != 'pull_request' + env: + SAMPLE_REGISTRY: ghcr.io/dapr/samples + run: | + SAMPLE_LIST=(tutorials/hello-kubernetes tutorials/distributed-calculator tutorials/pub-sub tutorials/bindings tutorials/observability tutorials/secretstore) + for sample in "${SAMPLE_LIST[@]}"; do + echo "Building image for ${sample}..." + pushd ${sample} + make manifest-create + popd + done + - name: publish manifest to ghcr + if: github.event_name != 'pull_request' + env: + SAMPLE_REGISTRY: ghcr.io/dapr/samples + run: | + SAMPLE_LIST=(tutorials/hello-kubernetes tutorials/distributed-calculator tutorials/pub-sub tutorials/bindings tutorials/observability tutorials/secretstore) + for sample in "${SAMPLE_LIST[@]}"; do + echo "Push image for ${sample}..." + pushd ${sample} + make manifest-push + popd + done diff --git a/dapr/quickstarts/.github/workflows/validate.yaml b/dapr/quickstarts/.github/workflows/validate.yaml new file mode 100644 index 0000000..9097805 --- /dev/null +++ b/dapr/quickstarts/.github/workflows/validate.yaml @@ -0,0 +1,182 @@ +# +# Copyright 2021 The Dapr Authors +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +name: Validate Tutorials + +on: + workflow_dispatch: + push: + branches: + - master + - feature/new_quickstarts + - release-* + tags: + - v* + pull_request: + branches: + - master + - feature/new_quickstarts + - release-* +jobs: + deploy: + name: Validate quickstarts on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + env: + DAPR_INSTALL_URL: https://raw.githubusercontent.com/dapr/cli/master/install + GOVER: 1.17 + KUBERNETES_VERSION: v1.21.1 + KIND_VERSION: v0.12.0 + KIND_IMAGE_SHA: sha256:69860bda5563ac81e3c0057d654b5253219618a22ec3a346306239bba8cfa1a6 + strategy: + matrix: + os: [ubuntu-latest, macos-10.15] + steps: + - name: Install docker - MacOS + if: matrix.os == 'macos-10.15' + uses: docker-practice/actions-setup-docker@v1 + with: + docker_buildx: false + docker_version: 20.10 + - name: Configure KinD + if: matrix.os == 'ubuntu-latest' + # Generate a KinD configuration file that uses: + # A couple of worker nodes: this is needed to run both + # ZooKeeper + Kakfa + working-directory: ./ + run: | + cat > kind.yaml <> $GITHUB_ENV + - name: Set up Go ${{ env.GOVER }} + uses: actions/setup-go@v2 + with: + go-version: ${{ env.GOVER }} + - name: Determine latest Dapr Runtime version including Pre-releases + run: | + helm repo add dapr https://dapr.github.io/helm-charts/ && helm repo update && export RUNTIME_VERSION=$(helm search repo dapr/dapr --devel --versions | awk '/dapr\/dapr/ {print $3; exit}' ) + echo "DAPR_RUNTIME_VERSION=$RUNTIME_VERSION" >> $GITHUB_ENV + echo "Found $RUNTIME_VERSION" + shell: bash + - name: Determine latest Dapr Cli version including Pre-releases + run: | + export CLI_VERSION=$(curl "https://api.github.com/repos/dapr/cli/releases?per_page=1&page=1" --header 'authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' | jq '.[0].tag_name'| tr -d '",v') + echo "DAPR_CLI_VERSION=$CLI_VERSION" >> $GITHUB_ENV + echo "Found $CLI_VERSION" + shell: bash + - name: Set up Dapr CLI - Mac/Linux + if: matrix.os != 'windows-latest' + run: wget -q ${{ env.DAPR_INSTALL_URL }}/install.sh -O - | /bin/bash -s ${{ env.DAPR_CLI_VERSION }} + - name: Set up Dapr CLI - Windows + if: matrix.os == 'windows-latest' + run: powershell -Command "\$$script=iwr -useb ${{ env.DAPR_INSTALL_URL }}/install.ps1; \$$block=[ScriptBlock]::Create(\$$script); invoke-command -ScriptBlock \$$block -ArgumentList ${{ env.DAPR_CLI_VERSION }}" + - name: Install Dapr + run: | + export GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} + dapr init --runtime-version=${{ env.DAPR_RUNTIME_VERSION }} + dapr --version + - name: Install Dapr - Kubernetes + run: | + helm repo add bitnami https://charts.bitnami.com/bitnami + helm repo update + helm install redis bitnami/redis + dapr init -k --runtime-version=${{ env.DAPR_RUNTIME_VERSION }} --wait || kubectl get pods --all-namespaces + kubectl get nodes -o wide + for pod in `dapr status -k | awk '/dapr/ {print $1}'`; do kubectl describe pod -l app=$pod -n dapr-system ; kubectl logs -l app=$pod -n dapr-system; done + - name: Check out code + uses: actions/checkout@v2 + - name: Install utilities dependencies + run: | + echo "PATH=$PATH:$HOME/.local/bin" >> $GITHUB_ENV + pip3 install setuptools wheel + pip3 install mechanical-markdown + - name: Validate hello-world + run: | + pushd tutorials/hello-world + make validate + popd + - name: Validate hello-kubernetes + if: matrix.os == 'ubuntu-latest' + run: | + pushd tutorials/hello-kubernetes + make validate + popd + - name: Validate distributed-calculator + if: matrix.os == 'ubuntu-latest' + run: | + pushd tutorials/distributed-calculator + make validate + popd + - name: Validate pub-sub + if: matrix.os == 'ubuntu-latest' + run: | + pushd tutorials/pub-sub + make validate + popd + - name: Validate bindings + if: matrix.os == 'ubuntu-latest' + run: | + pushd tutorials/bindings + make validate + popd + - name: Validate secretstore + if: matrix.os == 'ubuntu-latest' + run: | + pushd tutorials/secretstore + make validate + popd + - name: Validate observability + if: matrix.os == 'ubuntu-latest' + run: | + pushd tutorials/observability + make validate + popd + - name: Linkcheck README.md + run: | + make validate diff --git a/dapr/quickstarts/.github/workflows/validate_new_quickstarts_bindings.yaml b/dapr/quickstarts/.github/workflows/validate_new_quickstarts_bindings.yaml new file mode 100644 index 0000000..74c3e57 --- /dev/null +++ b/dapr/quickstarts/.github/workflows/validate_new_quickstarts_bindings.yaml @@ -0,0 +1,151 @@ +# +# Copyright 2022 The Dapr Authors +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +name: Validate Bindings + +on: + workflow_dispatch: + push: + branches: + - master + - feature/new_quickstarts + - release-* + tags: + - v* + pull_request: + branches: + - master + - feature/new_quickstarts + - release-* +jobs: + deploy: + name: Validate quickstarts on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + env: + DAPR_INSTALL_URL: https://raw.githubusercontent.com/dapr/cli/master/install + GOVER: 1.17 + KUBERNETES_VERSION: v1.21.1 + KIND_VERSION: v0.11.0 + KIND_IMAGE_SHA: sha256:69860bda5563ac81e3c0057d654b5253219618a22ec3a346306239bba8cfa1a6 + strategy: + matrix: + os: [ubuntu-latest, macos-10.15] + steps: + - name: Install docker - MacOS + if: matrix.os == 'macos-10.15' + uses: docker-practice/actions-setup-docker@v1 + with: + docker_buildx: false + docker_version: 20.10 + - name: Set up Go ${{ env.GOVER }} + uses: actions/setup-go@v2 + with: + go-version: ${{ env.GOVER }} + - name: Set up OpenJDK 11 + uses: actions/setup-java@v3 + with: + distribution: 'adopt' + java-version: 11 + - name: Install .NET Core + uses: actions/setup-dotnet@v1.9.0 + with: + dotnet-version: | + 6.0.x + - name: Determine latest Dapr Runtime version including Pre-releases + run: | + helm repo add dapr https://dapr.github.io/helm-charts/ && helm repo update && export RUNTIME_VERSION=$(helm search repo dapr/dapr --devel --versions | awk '/dapr\/dapr/ {print $3; exit}' ) + echo "DAPR_RUNTIME_VERSION=$RUNTIME_VERSION" >> $GITHUB_ENV + echo "Found $RUNTIME_VERSION" + shell: bash + - name: Determine latest Dapr Cli version including Pre-releases + run: | + export CLI_VERSION=$(curl "https://api.github.com/repos/dapr/cli/releases?per_page=1&page=1" --header 'authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' | jq '.[0].tag_name'| tr -d '",v') + echo "DAPR_CLI_VERSION=$CLI_VERSION" >> $GITHUB_ENV + echo "Found $CLI_VERSION" + shell: bash + - name: Set up Dapr CLI - Mac/Linux + if: matrix.os != 'windows-latest' + run: wget -q ${{ env.DAPR_INSTALL_URL }}/install.sh -O - | /bin/bash -s ${{ env.DAPR_CLI_VERSION }} + - name: Set up Dapr CLI - Windows + if: matrix.os == 'windows-latest' + run: powershell -Command "\$$script=iwr -useb ${{ env.DAPR_INSTALL_URL }}/install.ps1; \$$block=[ScriptBlock]::Create(\$$script); invoke-command -ScriptBlock \$$block -ArgumentList ${{ env.DAPR_CLI_VERSION }}" + - name: Install Dapr + run: | + export GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} + dapr init --runtime-version=${{ env.DAPR_RUNTIME_VERSION }} + dapr --version + - name: Check out code + uses: actions/checkout@v2 + - name: Install utilities dependencies + run: | + echo "PATH=$PATH:$HOME/.local/bin" >> $GITHUB_ENV + pip3 install setuptools wheel + pip3 install mechanical-markdown + - name: Set up Postgres + run: | + pushd bindings/db/ + docker compose up -d + popd + - name: Validate Python http Bindings + run: | + pushd bindings/python/http + make validate + popd + - name: Validate Python sdk Bindings + run: | + pushd bindings/python/sdk + make validate + popd + - name: Validate Javascript http Bindings + run: | + pushd bindings/javascript/http + make validate + popd + # - name: Validate Javascript sdk Bindings + # run: | + # pushd bindings/javascript/sdk + # make validate + # popd + - name: Validate Java http Bindings + run: | + pushd bindings/java/http + make validate + popd + - name: Validate Java sdk Bindings + run: | + pushd bindings/java/sdk + make validate + popd + - name: Validate Go http Bindings + run: | + pushd bindings/go/http + make validate + popd + - name: Validate Go sdk Bindings + run: | + pushd bindings/go/sdk + make validate + popd + - name: Validate .NET http Bindings + run: | + pushd bindings/csharp/http + make validate + popd + - name: Validate .NET sdk Bindings + run: | + pushd bindings/csharp/sdk + make validate + popd + - name: Linkcheck README.md + run: | + make validate diff --git a/dapr/quickstarts/.github/workflows/validate_new_quickstarts_pubsub.yaml b/dapr/quickstarts/.github/workflows/validate_new_quickstarts_pubsub.yaml new file mode 100644 index 0000000..61bc1e6 --- /dev/null +++ b/dapr/quickstarts/.github/workflows/validate_new_quickstarts_pubsub.yaml @@ -0,0 +1,104 @@ +# +# Copyright 2021 The Dapr Authors +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +name: Validate PubSub + +on: + workflow_dispatch: + push: + branches: + - master + - feature/new_quickstarts + - release-* + tags: + - v* + pull_request: + branches: + - master + - feature/new_quickstarts + - release-* +jobs: + validate: + name: Validate quickstart for `${{ matrix.quickstart_language }}` with `${{ matrix.quickstart_variant }}` on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + env: + DAPR_DEFAULT_IMAGE_REGISTRY: GHCR + DAPR_INSTALL_URL: https://raw.githubusercontent.com/dapr/cli/master/install + GOVER: 1.17 + KUBERNETES_VERSION: v1.21.1 + KIND_VERSION: v0.11.0 + KIND_IMAGE_SHA: sha256:69860bda5563ac81e3c0057d654b5253219618a22ec3a346306239bba8cfa1a6 + strategy: + matrix: + os: [ubuntu-latest, macos-10.15] + quickstart_language: [go, csharp, javascript, python, java] + quickstart_variant: [http, sdk] + steps: + - name: Install docker - MacOS + if: matrix.os == 'macos-10.15' + uses: docker-practice/actions-setup-docker@v1 + with: + docker_buildx: false + docker_version: 20.10 + - name: Set up Go ${{ env.GOVER }} + uses: actions/setup-go@v2 + with: + go-version: ${{ env.GOVER }} + - name: Set up OpenJDK 11 + uses: actions/setup-java@v3 + with: + distribution: 'adopt' + java-version: 11 + - name: Install .NET Core + uses: actions/setup-dotnet@v1.9.0 + with: + dotnet-version: | + 6.0.x + - name: Determine latest Dapr Runtime version including Pre-releases + run: | + helm repo add dapr https://dapr.github.io/helm-charts/ && helm repo update && export RUNTIME_VERSION=$(helm search repo dapr/dapr --devel --versions | awk '/dapr\/dapr/ {print $3; exit}' ) + echo "DAPR_RUNTIME_VERSION=$RUNTIME_VERSION" >> $GITHUB_ENV + echo "Found $RUNTIME_VERSION" + shell: bash + - name: Determine latest Dapr Cli version including Pre-releases + run: | + export CLI_VERSION=$(curl "https://api.github.com/repos/dapr/cli/releases?per_page=1&page=1" --header 'authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' | jq '.[0].tag_name'| tr -d '",v') + echo "DAPR_CLI_VERSION=$CLI_VERSION" >> $GITHUB_ENV + echo "Found $CLI_VERSION" + shell: bash + - name: Set up Dapr CLI - Mac/Linux + if: matrix.os != 'windows-latest' + run: wget -q ${{ env.DAPR_INSTALL_URL }}/install.sh -O - | /bin/bash -s ${{ env.DAPR_CLI_VERSION }} + - name: Set up Dapr CLI - Windows + if: matrix.os == 'windows-latest' + run: powershell -Command "\$$script=iwr -useb ${{ env.DAPR_INSTALL_URL }}/install.ps1; \$$block=[ScriptBlock]::Create(\$$script); invoke-command -ScriptBlock \$$block -ArgumentList ${{ env.DAPR_CLI_VERSION }}" + - name: Install Dapr + run: | + export GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} + dapr init --runtime-version=${{ env.DAPR_RUNTIME_VERSION }} + dapr --version + - name: Check out code + uses: actions/checkout@v2 + - name: Install utilities dependencies + run: | + echo "PATH=$PATH:$HOME/.local/bin" >> $GITHUB_ENV + pip3 install setuptools wheel + pip3 install mechanical-markdown + - name: Validate ${{ matrix.quickstart_language }} ${{ matrix.quickstart_variant }} pubsub + run: | + pushd pub_sub/${{ matrix.quickstart_language }}/${{ matrix.quickstart_variant }} + make validate + popd + - name: Linkcheck README.md + run: | + make validate \ No newline at end of file diff --git a/dapr/quickstarts/.github/workflows/validate_new_quickstarts_secrets_management.yaml b/dapr/quickstarts/.github/workflows/validate_new_quickstarts_secrets_management.yaml new file mode 100644 index 0000000..1180980 --- /dev/null +++ b/dapr/quickstarts/.github/workflows/validate_new_quickstarts_secrets_management.yaml @@ -0,0 +1,103 @@ +# +# Copyright 2021 The Dapr Authors +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +name: Validate Secrets Management + +on: + workflow_dispatch: + push: + branches: + - master + - feature/new_quickstarts + - release-* + tags: + - v* + pull_request: + branches: + - master + - feature/new_quickstarts + - release-* +jobs: + validate: + name: Validate quickstart for `${{ matrix.quickstart_language }}` with `${{ matrix.quickstart_variant }}` on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + env: + DAPR_INSTALL_URL: https://raw.githubusercontent.com/dapr/cli/master/install + GOVER: 1.17 + KUBERNETES_VERSION: v1.21.1 + KIND_VERSION: v0.11.0 + KIND_IMAGE_SHA: sha256:69860bda5563ac81e3c0057d654b5253219618a22ec3a346306239bba8cfa1a6 + strategy: + matrix: + os: [ubuntu-latest, macos-10.15] + quickstart_language: [go, csharp, javascript, python, java] + quickstart_variant: [http, sdk] + steps: + - name: Install docker - MacOS + if: matrix.os == 'macos-10.15' + uses: docker-practice/actions-setup-docker@v1 + with: + docker_buildx: false + docker_version: 20.10 + - name: Set up Go ${{ env.GOVER }} + uses: actions/setup-go@v2 + with: + go-version: ${{ env.GOVER }} + - name: Set up OpenJDK 11 + uses: actions/setup-java@v3 + with: + distribution: 'adopt' + java-version: 11 + - name: Install .NET Core + uses: actions/setup-dotnet@v1.9.0 + with: + dotnet-version: | + 6.0.x + - name: Determine latest Dapr Runtime version including Pre-releases + run: | + helm repo add dapr https://dapr.github.io/helm-charts/ && helm repo update && export RUNTIME_VERSION=$(helm search repo dapr/dapr --devel --versions | awk '/dapr\/dapr/ {print $3; exit}' ) + echo "DAPR_RUNTIME_VERSION=$RUNTIME_VERSION" >> $GITHUB_ENV + echo "Found $RUNTIME_VERSION" + shell: bash + - name: Determine latest Dapr Cli version including Pre-releases + run: | + export CLI_VERSION=$(curl "https://api.github.com/repos/dapr/cli/releases?per_page=1&page=1" --header 'authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' | jq '.[0].tag_name'| tr -d '",v') + echo "DAPR_CLI_VERSION=$CLI_VERSION" >> $GITHUB_ENV + echo "Found $CLI_VERSION" + shell: bash + - name: Set up Dapr CLI - Mac/Linux + if: matrix.os != 'windows-latest' + run: wget -q ${{ env.DAPR_INSTALL_URL }}/install.sh -O - | /bin/bash -s ${{ env.DAPR_CLI_VERSION }} + - name: Set up Dapr CLI - Windows + if: matrix.os == 'windows-latest' + run: powershell -Command "\$$script=iwr -useb ${{ env.DAPR_INSTALL_URL }}/install.ps1; \$$block=[ScriptBlock]::Create(\$$script); invoke-command -ScriptBlock \$$block -ArgumentList ${{ env.DAPR_CLI_VERSION }}" + - name: Install Dapr + run: | + export GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} + dapr init --runtime-version=${{ env.DAPR_RUNTIME_VERSION }} + dapr --version + - name: Check out code + uses: actions/checkout@v2 + - name: Install utilities dependencies + run: | + echo "PATH=$PATH:$HOME/.local/bin" >> $GITHUB_ENV + pip3 install setuptools wheel + pip3 install mechanical-markdown + - name: Validate ${{ matrix.quickstart_language }} ${{ matrix.quickstart_variant }} secrets management + run: | + pushd secrets_management/${{ matrix.quickstart_language }}/${{ matrix.quickstart_variant }} + make validate + popd + - name: Linkcheck README.md + run: | + make validate diff --git a/dapr/quickstarts/.github/workflows/validate_new_quickstarts_service_invo.yaml b/dapr/quickstarts/.github/workflows/validate_new_quickstarts_service_invo.yaml new file mode 100644 index 0000000..e7ced89 --- /dev/null +++ b/dapr/quickstarts/.github/workflows/validate_new_quickstarts_service_invo.yaml @@ -0,0 +1,103 @@ +# +# Copyright 2021 The Dapr Authors +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +name: Validate Service Invocation + +on: + workflow_dispatch: + push: + branches: + - master + - feature/new_quickstarts + - release-* + tags: + - v* + pull_request: + branches: + - master + - feature/new_quickstarts + - release-* +jobs: + validate: + name: Validate quickstart for `${{ matrix.quickstart_language }}` with `${{ matrix.quickstart_variant }}` on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + env: + DAPR_INSTALL_URL: https://raw.githubusercontent.com/dapr/cli/master/install + GOVER: 1.17 + KUBERNETES_VERSION: v1.21.1 + KIND_VERSION: v0.11.0 + KIND_IMAGE_SHA: sha256:69860bda5563ac81e3c0057d654b5253219618a22ec3a346306239bba8cfa1a6 + strategy: + matrix: + os: [ubuntu-latest, macos-10.15] + quickstart_language: [go, csharp, javascript, python, java] + quickstart_variant: [http] + steps: + - name: Install docker - MacOS + if: matrix.os == 'macos-10.15' + uses: docker-practice/actions-setup-docker@v1 + with: + docker_buildx: false + docker_version: 20.10 + - name: Set up Go ${{ env.GOVER }} + uses: actions/setup-go@v2 + with: + go-version: ${{ env.GOVER }} + - name: Set up OpenJDK 11 + uses: actions/setup-java@v3 + with: + distribution: 'adopt' + java-version: 11 + - name: Install .NET Core + uses: actions/setup-dotnet@v1.9.0 + with: + dotnet-version: | + 6.0.x + - name: Determine latest Dapr Runtime version including Pre-releases + run: | + helm repo add dapr https://dapr.github.io/helm-charts/ && helm repo update && export RUNTIME_VERSION=$(helm search repo dapr/dapr --devel --versions | awk '/dapr\/dapr/ {print $3; exit}' ) + echo "DAPR_RUNTIME_VERSION=$RUNTIME_VERSION" >> $GITHUB_ENV + echo "Found $RUNTIME_VERSION" + shell: bash + - name: Determine latest Dapr Cli version including Pre-releases + run: | + export CLI_VERSION=$(curl "https://api.github.com/repos/dapr/cli/releases?per_page=1&page=1" --header 'authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' | jq '.[0].tag_name'| tr -d '",v') + echo "DAPR_CLI_VERSION=$CLI_VERSION" >> $GITHUB_ENV + echo "Found $CLI_VERSION" + shell: bash + - name: Set up Dapr CLI - Mac/Linux + if: matrix.os != 'windows-latest' + run: wget -q ${{ env.DAPR_INSTALL_URL }}/install.sh -O - | /bin/bash -s ${{ env.DAPR_CLI_VERSION }} + - name: Set up Dapr CLI - Windows + if: matrix.os == 'windows-latest' + run: powershell -Command "\$$script=iwr -useb ${{ env.DAPR_INSTALL_URL }}/install.ps1; \$$block=[ScriptBlock]::Create(\$$script); invoke-command -ScriptBlock \$$block -ArgumentList ${{ env.DAPR_CLI_VERSION }}" + - name: Install Dapr + run: | + export GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} + dapr init --runtime-version=${{ env.DAPR_RUNTIME_VERSION }} + dapr --version + - name: Check out code + uses: actions/checkout@v2 + - name: Install utilities dependencies + run: | + echo "PATH=$PATH:$HOME/.local/bin" >> $GITHUB_ENV + pip3 install setuptools wheel + pip3 install mechanical-markdown + - name: Validate ${{ matrix.quickstart_language }} ${{ matrix.quickstart_variant }} service invocation + run: | + pushd service_invocation/${{ matrix.quickstart_language }}/${{ matrix.quickstart_variant }} + make validate + popd + - name: Linkcheck README.md + run: | + make validate diff --git a/dapr/quickstarts/.github/workflows/validate_new_quickstarts_state_management.yaml b/dapr/quickstarts/.github/workflows/validate_new_quickstarts_state_management.yaml new file mode 100644 index 0000000..81b21fb --- /dev/null +++ b/dapr/quickstarts/.github/workflows/validate_new_quickstarts_state_management.yaml @@ -0,0 +1,103 @@ +# +# Copyright 2022 The Dapr Authors +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +name: Validate State Management + +on: + workflow_dispatch: + push: + branches: + - master + - feature/new_quickstarts + - release-* + tags: + - v* + pull_request: + branches: + - master + - feature/new_quickstarts + - release-* +jobs: + validate: + name: Validate quickstart for `${{ matrix.quickstart_language }}` with `${{ matrix.quickstart_variant }}` on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + env: + DAPR_INSTALL_URL: https://raw.githubusercontent.com/dapr/cli/master/install + GOVER: 1.17 + KUBERNETES_VERSION: v1.21.1 + KIND_VERSION: v0.11.0 + KIND_IMAGE_SHA: sha256:69860bda5563ac81e3c0057d654b5253219618a22ec3a346306239bba8cfa1a6 + strategy: + matrix: + os: [ubuntu-latest, macos-10.15] + quickstart_language: [go, csharp, javascript, python, java] + quickstart_variant: [http, sdk] + steps: + - name: Install docker - MacOS + if: matrix.os == 'macos-10.15' + uses: docker-practice/actions-setup-docker@v1 + with: + docker_buildx: false + docker_version: 20.10 + - name: Set up Go ${{ env.GOVER }} + uses: actions/setup-go@v2 + with: + go-version: ${{ env.GOVER }} + - name: Set up OpenJDK 11 + uses: actions/setup-java@v3 + with: + distribution: 'adopt' + java-version: 11 + - name: Install .NET Core + uses: actions/setup-dotnet@v1.9.0 + with: + dotnet-version: | + 6.0.x + - name: Determine latest Dapr Runtime version including Pre-releases + run: | + helm repo add dapr https://dapr.github.io/helm-charts/ && helm repo update && export RUNTIME_VERSION=$(helm search repo dapr/dapr --devel --versions | awk '/dapr\/dapr/ {print $3; exit}' ) + echo "DAPR_RUNTIME_VERSION=$RUNTIME_VERSION" >> $GITHUB_ENV + echo "Found $RUNTIME_VERSION" + shell: bash + - name: Determine latest Dapr Cli version including Pre-releases + run: | + export CLI_VERSION=$(curl "https://api.github.com/repos/dapr/cli/releases?per_page=1&page=1" --header 'authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' | jq '.[0].tag_name'| tr -d '",v') + echo "DAPR_CLI_VERSION=$CLI_VERSION" >> $GITHUB_ENV + echo "Found $CLI_VERSION" + shell: bash + - name: Set up Dapr CLI - Mac/Linux + if: matrix.os != 'windows-latest' + run: wget -q ${{ env.DAPR_INSTALL_URL }}/install.sh -O - | /bin/bash -s ${{ env.DAPR_CLI_VERSION }} + - name: Set up Dapr CLI - Windows + if: matrix.os == 'windows-latest' + run: powershell -Command "\$$script=iwr -useb ${{ env.DAPR_INSTALL_URL }}/install.ps1; \$$block=[ScriptBlock]::Create(\$$script); invoke-command -ScriptBlock \$$block -ArgumentList ${{ env.DAPR_CLI_VERSION }}" + - name: Install Dapr + run: | + export GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} + dapr init --runtime-version=${{ env.DAPR_RUNTIME_VERSION }} + dapr --version + - name: Check out code + uses: actions/checkout@v2 + - name: Install utilities dependencies + run: | + echo "PATH=$PATH:$HOME/.local/bin" >> $GITHUB_ENV + pip3 install setuptools wheel + pip3 install mechanical-markdown + - name: Validate ${{ matrix.quickstart_language }} ${{ matrix.quickstart_variant }} state management + run: | + pushd state_management/${{ matrix.quickstart_language }}/${{ matrix.quickstart_variant }} + make validate + popd + - name: Linkcheck README.md + run: | + make validate diff --git a/dapr/quickstarts/.gitignore b/dapr/quickstarts/.gitignore new file mode 100644 index 0000000..b9a302a --- /dev/null +++ b/dapr/quickstarts/.gitignore @@ -0,0 +1,42 @@ +# General +.env +*.suo +*.user +_ReSharper.* +bin +obj +github.com + +## Build generated +target/ +node_modules +release.properties +.mvn +mvnw +packages +**/__pycache__/ + +# IDE generated files and directories +*.iml +.idea/ +*.ipr +*.iws +.vs/ +.vscode/ +.code-workspace +.settings/ +.metadata +.project + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar +*.package-lock.json + +# macOS +.DS_Store diff --git a/dapr/quickstarts/CODEOWNERS b/dapr/quickstarts/CODEOWNERS new file mode 100644 index 0000000..01df9d3 --- /dev/null +++ b/dapr/quickstarts/CODEOWNERS @@ -0,0 +1,2 @@ +# These owners are the maintainers and approvers of this repo +* @maintainers-quickstarts @approvers-quickstarts diff --git a/dapr/quickstarts/CONTRIBUTING.md b/dapr/quickstarts/CONTRIBUTING.md new file mode 100644 index 0000000..257e4a6 --- /dev/null +++ b/dapr/quickstarts/CONTRIBUTING.md @@ -0,0 +1,125 @@ +# Contribution Guidelines + +Thank you for your interest in Dapr! + +This project welcomes contributions and suggestions. Most contributions require you to signoff on your commits via +the Developer Certificate of Origin (DCO). When you submit a pull request, a DCO-bot will automatically determine +whether you need to provide signoff for your commit. Please follow the instructions provided by DCO-bot, as pull +requests cannot be merged until the author(s) have provided signoff to fulfill the DCO requirement. +You may find more information on the DCO requirements [below](#developer-certificate-of-origin-signing-your-work). + +This project has adopted the [Contributor Covenant Code of Conduct](https://github.com/dapr/community/blob/master/CODE-OF-CONDUCT.md). + +Contributions come in many forms: submitting issues, writing code, participating in discussions and community calls. + +This document provides the guidelines for how to contribute to the Dapr project. + +## Issues + +This section describes the guidelines for submitting issues + +### Issue Types + +There are 4 types of issues: + +- Issue/Bug: You've found a bug with the code, and want to report it, or create an issue to track the bug. +- Issue/Discussion: You have something on your mind, which requires input form others in a discussion, before it eventually manifests as a proposal. +- Issue/Proposal: Used for items that propose a new idea or functionality. This allows feedback from others before code is written. +- Issue/Question: Use this issue type, if you need help or have a question. + +### Before You File + +Before you file an issue, make sure you've checked the following: + +1. Is it the right repository? + - The Dapr project is distributed across multiple repositories. Check the list of [repositories](https://github.com/dapr) if you aren't sure which repo is the correct one. +1. Check for existing issues + - Before you create a new issue, please do a search in [open issues](https://github.com/dapr/quickstarts/issues) to see if the issue or feature request has already been filed. + - If you find your issue already exists, make relevant comments and add your [reaction](https://github.com/blog/2119-add-reaction-to-pull-requests-issues-and-comments). Use a reaction: + - 👍 up-vote + - 👎 down-vote +1. For bugs + - Check it's not an environment issue. For example, if running on Kubernetes, make sure prerequisites are in place. (state stores, bindings, etc.) + - You have as much data as possible. This usually comes in the form of logs and/or stacktrace. If running on Kubernetes or other environment, look at the logs of the Dapr services (runtime, operator, placement service). More details on how to get logs can be found [here](https://docs.dapr.io/operations/troubleshooting/logs-troubleshooting/). +1. For proposals + - Many changes to the Dapr runtime may require changes to the API. In that case, the best place to discuss the potential feature is the main [Dapr repo](https://github.com/dapr/dapr). + - Other examples could include bindings, state stores or entirely new components. + +## Contributing to Dapr + +This section describes the guidelines for contributing code / docs to Dapr. + +### Pull Requests + +All contributions come through pull requests. To submit a proposed change, we recommend following this workflow: + +1. Make sure there's an issue (bug or proposal) raised, which sets the expectations for the contribution you are about to make. +1. Fork the relevant repo and create a new branch +1. Create your change + - Code changes require tests +1. Update relevant documentation for the change +1. Commit and open a PR +1. Wait for the CI process to finish and make sure all checks are green +1. A maintainer of the project will be assigned, and you can expect a review within a few days + +#### Use work-in-progress PRs for early feedback + +A good way to communicate before investing too much time is to create a "Work-in-progress" PR and share it with your reviewers. The standard way of doing this is to add a "[WIP]" prefix in your PR's title and assign the **do-not-merge** label. This will let people looking at your PR know that it is not well baked yet. + +### Developer Certificate of Origin: Signing your work + +#### Every commit needs to be signed + +The Developer Certificate of Origin (DCO) is a lightweight way for contributors to certify that they wrote or otherwise have the right to submit the code they are contributing to the project. Here is the full text of the [DCO](https://developercertificate.org/), reformatted for readability: +``` +By making a contribution to this project, I certify that: + + (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or + + (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or + + (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. + + (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved. +``` + +Contributors sign-off that they adhere to these requirements by adding a `Signed-off-by` line to commit messages. + +``` +This is my commit message + +Signed-off-by: Random J Developer +``` +Git even has a `-s` command line option to append this automatically to your commit message: +``` +$ git commit -s -m 'This is my commit message' +``` + +Each Pull Request is checked whether or not commits in a Pull Request do contain a valid Signed-off-by line. + +#### I didn't sign my commit, now what?! + +No worries - You can easily replay your changes, sign them and force push them! + +``` +git checkout +git commit --amend --no-edit --signoff +git push --force-with-lease +``` + +### Use of Third-party code + +- All third-party code must be placed in the `vendor/` folder. +- `vendor/` folder is managed by Go modules and stores the source code of third-party Go dependencies. - The `vendor/` folder should not be modified manually. +- Third-party code must include licenses. + +A non-exclusive list of code that must be places in `vendor/`: + +- Open source, free software, or commercially-licensed code. +- Tools or libraries or protocols that are open source, free software, or commercially licensed. + +**Thank You!** - Your contributions to open source, large or small, make projects like this possible. Thank you for taking the time to contribute. + +## Code of Conduct + +This project has adopted the [Contributor Covenant Code of Conduct](https://github.com/dapr/community/blob/master/CODE-OF-CONDUCT.md) diff --git a/dapr/quickstarts/LICENSE b/dapr/quickstarts/LICENSE new file mode 100644 index 0000000..c2c85ba --- /dev/null +++ b/dapr/quickstarts/LICENSE @@ -0,0 +1,204 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2021 The Dapr Authors. + + and others that have contributed code to the public domain. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/dapr/quickstarts/README.md b/dapr/quickstarts/README.md new file mode 100644 index 0000000..e16e18b --- /dev/null +++ b/dapr/quickstarts/README.md @@ -0,0 +1,38 @@ +# Dapr Quickstarts and Tutorials + +[![Build Status](https://github.com/dapr/quickstarts/workflows/samples/badge.svg?event=push&branch=master)](https://github.com/dapr/quickstarts/actions?workflow=samples) +[![Join the chat at https://gitter.im/Dapr/samples](https://badges.gitter.im/Dapr/samples.svg)](https://gitter.im/Dapr/samples?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![License: Apache 2.0](https://img.shields.io/badge/License-Apache-yellow.svg)](https://www.apache.org/licenses/LICENSE-2.0) + +If you are new to Dapr and haven't done so already, it is recommended you go through the Dapr [Getting Started](https://docs.dapr.io/getting-started/install-dapr-cli/) instructions. + +### Quickstarts +Pick a building block API (for example, pub-sub, state management) and rapidly try it out in your favorite language SDK (recommended), or via HTTP. Visit the [Dapr Docs Quickstarts Guide](https://docs.dapr.io/getting-started/quickstarts/) for a comprehensive walkthrough of each example. + +| Dapr Quickstart | Description | +|:--------------------:|:--------------------:| +| [Publish and Subscribe](./pub_sub) | Asynchronous communication between two services using messaging | +| [Service Invocation](./service_invocation) | Asynchronous communication between two services using HTTP | +| [State Management](./state_management/) | Store a service's data as key/value pairs in supported state stores | +| [Bindings](./bindings/) | Work with external systems using input bindings to respond to events and output bindings to call operations| +| [Secrets Management](./secrets_management/) | Securely fetch secrets | +| Actors | Coming soon... | +| Observability | Coming soon... | +| Configuration | Coming soon... | + +### Tutorials +Go deeper into a topic or scenario, oftentimes using building block APIs together to solve problems (for example, build a distributed calculator, build and deploy an app to Kubernetes). + +| Tutorials | Description | +|--------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [Hello-world](./tutorials/hello-world) | Demonstrates how to run Dapr locally. Highlights service invocation and state management. | +| [Hello-kubernetes](./tutorials/hello-kubernetes) | Demonstrates how to run Dapr in Kubernetes. Highlights service invocation and state management. | +| [Distributed-calculator](./tutorials/distributed-calculator) | Demonstrates a distributed calculator application that uses Dapr services to power a React web app. Highlights polyglot (multi-language) programming, service invocation and state management. | +| [Pub-sub](./tutorials/pub-sub) | Demonstrates how to use Dapr to enable pub-sub applications. Uses Redis as a pub-sub component. | +| [Bindings](./tutorials/bindings) | Demonstrates how to use Dapr to create input and output bindings to other components. Uses bindings to Kafka. | +| [Observability](./tutorials/observability) | Demonstrates Dapr tracing capabilities. Uses Zipkin as a tracing component. | +| [Secret Store](./tutorials/secretstore) | Demonstrates the use of Dapr Secrets API to access secret stores. | + +## Code of Conduct + +Please refer to our [Dapr Community Code of Conduct](https://github.com/dapr/community/blob/master/CODE-OF-CONDUCT.md) diff --git a/dapr/quickstarts/bindings/components/binding-cron.yaml b/dapr/quickstarts/bindings/components/binding-cron.yaml new file mode 100644 index 0000000..37b75d8 --- /dev/null +++ b/dapr/quickstarts/bindings/components/binding-cron.yaml @@ -0,0 +1,11 @@ +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: cron + namespace: quickstarts +spec: + type: bindings.cron + version: v1 + metadata: + - name: schedule + value: "@every 10s" # valid cron schedule diff --git a/dapr/quickstarts/bindings/components/binding-postgres.yaml b/dapr/quickstarts/bindings/components/binding-postgres.yaml new file mode 100644 index 0000000..d8f591d --- /dev/null +++ b/dapr/quickstarts/bindings/components/binding-postgres.yaml @@ -0,0 +1,11 @@ +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: sqldb + namespace: quickstarts +spec: + type: bindings.postgres + version: v1 + metadata: + - name: url # Required + value: "user=postgres password=docker host=localhost port=5432 dbname=orders pool_min_conns=1 pool_max_conns=10" diff --git a/dapr/quickstarts/bindings/csharp/http/README.md b/dapr/quickstarts/bindings/csharp/http/README.md new file mode 100644 index 0000000..08e6cad --- /dev/null +++ b/dapr/quickstarts/bindings/csharp/http/README.md @@ -0,0 +1,67 @@ +# Dapr Bindings (HTTP) + +In this quickstart, you'll create a microservice to demonstrate Dapr's bindings API to work with external systems as inputs and outputs. The service listens to input binding events from a system CRON and then outputs the contents of local data to a PostreSql output binding. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/bindings/) link for more information about Dapr and Bindings. + +> **Note:** This example leverages only HTTP REST. If you are looking for the example using the Dapr SDK [click here](../sdk). + +This quickstart includes one service: + +- .NET/C# service `batch` + +### Run and initialize PostgreSQL container + +1. Open a new terminal, change directories to `../../../db`, and run the container with [Docker Compose](https://docs.docker.com/compose/): + + + +```bash +cd ../../db +docker compose up +``` + + + +### Run C# service with Dapr + +2. Open a new terminal window, change directories to `./batch` in the quickstart directory and run: + + + +```bash +cd ./batch +ls +dotnet restore +``` + + +3. Run the C# service app with Dapr: + + + +```bash +dapr run --app-id batch-http --app-port 7001 --components-path ../../../components -- dotnet run +``` + + diff --git a/dapr/quickstarts/bindings/csharp/http/batch/Debug/net6.0/Microsoft.OpenApi.dll b/dapr/quickstarts/bindings/csharp/http/batch/Debug/net6.0/Microsoft.OpenApi.dll new file mode 100755 index 0000000..14f3ded Binary files /dev/null and b/dapr/quickstarts/bindings/csharp/http/batch/Debug/net6.0/Microsoft.OpenApi.dll differ diff --git a/dapr/quickstarts/bindings/csharp/http/batch/Debug/net6.0/Swashbuckle.AspNetCore.Swagger.dll b/dapr/quickstarts/bindings/csharp/http/batch/Debug/net6.0/Swashbuckle.AspNetCore.Swagger.dll new file mode 100755 index 0000000..a30e6cc Binary files /dev/null and b/dapr/quickstarts/bindings/csharp/http/batch/Debug/net6.0/Swashbuckle.AspNetCore.Swagger.dll differ diff --git a/dapr/quickstarts/bindings/csharp/http/batch/Debug/net6.0/Swashbuckle.AspNetCore.SwaggerGen.dll b/dapr/quickstarts/bindings/csharp/http/batch/Debug/net6.0/Swashbuckle.AspNetCore.SwaggerGen.dll new file mode 100755 index 0000000..ae35663 Binary files /dev/null and b/dapr/quickstarts/bindings/csharp/http/batch/Debug/net6.0/Swashbuckle.AspNetCore.SwaggerGen.dll differ diff --git a/dapr/quickstarts/bindings/csharp/http/batch/Debug/net6.0/Swashbuckle.AspNetCore.SwaggerUI.dll b/dapr/quickstarts/bindings/csharp/http/batch/Debug/net6.0/Swashbuckle.AspNetCore.SwaggerUI.dll new file mode 100755 index 0000000..a26f458 Binary files /dev/null and b/dapr/quickstarts/bindings/csharp/http/batch/Debug/net6.0/Swashbuckle.AspNetCore.SwaggerUI.dll differ diff --git a/dapr/quickstarts/bindings/csharp/http/batch/Debug/net6.0/appsettings.Development.json b/dapr/quickstarts/bindings/csharp/http/batch/Debug/net6.0/appsettings.Development.json new file mode 100644 index 0000000..c00584b --- /dev/null +++ b/dapr/quickstarts/bindings/csharp/http/batch/Debug/net6.0/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } + } \ No newline at end of file diff --git a/dapr/quickstarts/bindings/csharp/http/batch/Debug/net6.0/appsettings.json b/dapr/quickstarts/bindings/csharp/http/batch/Debug/net6.0/appsettings.json new file mode 100644 index 0000000..a33e07c --- /dev/null +++ b/dapr/quickstarts/bindings/csharp/http/batch/Debug/net6.0/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" + } \ No newline at end of file diff --git a/dapr/quickstarts/bindings/csharp/http/batch/Debug/net6.0/batch b/dapr/quickstarts/bindings/csharp/http/batch/Debug/net6.0/batch new file mode 100755 index 0000000..29f7d59 Binary files /dev/null and b/dapr/quickstarts/bindings/csharp/http/batch/Debug/net6.0/batch differ diff --git a/dapr/quickstarts/bindings/csharp/http/batch/Debug/net6.0/batch.deps.json b/dapr/quickstarts/bindings/csharp/http/batch/Debug/net6.0/batch.deps.json new file mode 100644 index 0000000..5fbbed5 --- /dev/null +++ b/dapr/quickstarts/bindings/csharp/http/batch/Debug/net6.0/batch.deps.json @@ -0,0 +1,115 @@ +{ + "runtimeTarget": { + "name": ".NETCoreApp,Version=v6.0", + "signature": "" + }, + "compilationOptions": {}, + "targets": { + ".NETCoreApp,Version=v6.0": { + "batch/1.0.0": { + "dependencies": { + "Swashbuckle.AspNetCore": "6.2.3" + }, + "runtime": { + "batch.dll": {} + } + }, + "Microsoft.Extensions.ApiDescription.Server/3.0.0": {}, + "Microsoft.OpenApi/1.2.3": { + "runtime": { + "lib/netstandard2.0/Microsoft.OpenApi.dll": { + "assemblyVersion": "1.2.3.0", + "fileVersion": "1.2.3.0" + } + } + }, + "Swashbuckle.AspNetCore/6.2.3": { + "dependencies": { + "Microsoft.Extensions.ApiDescription.Server": "3.0.0", + "Swashbuckle.AspNetCore.Swagger": "6.2.3", + "Swashbuckle.AspNetCore.SwaggerGen": "6.2.3", + "Swashbuckle.AspNetCore.SwaggerUI": "6.2.3" + } + }, + "Swashbuckle.AspNetCore.Swagger/6.2.3": { + "dependencies": { + "Microsoft.OpenApi": "1.2.3" + }, + "runtime": { + "lib/net6.0/Swashbuckle.AspNetCore.Swagger.dll": { + "assemblyVersion": "6.2.3.0", + "fileVersion": "6.2.3.0" + } + } + }, + "Swashbuckle.AspNetCore.SwaggerGen/6.2.3": { + "dependencies": { + "Swashbuckle.AspNetCore.Swagger": "6.2.3" + }, + "runtime": { + "lib/net6.0/Swashbuckle.AspNetCore.SwaggerGen.dll": { + "assemblyVersion": "6.2.3.0", + "fileVersion": "6.2.3.0" + } + } + }, + "Swashbuckle.AspNetCore.SwaggerUI/6.2.3": { + "runtime": { + "lib/net6.0/Swashbuckle.AspNetCore.SwaggerUI.dll": { + "assemblyVersion": "6.2.3.0", + "fileVersion": "6.2.3.0" + } + } + } + } + }, + "libraries": { + "batch/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + }, + "Microsoft.Extensions.ApiDescription.Server/3.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-LH4OE/76F6sOCslif7+Xh3fS/wUUrE5ryeXAMcoCnuwOQGT5Smw0p57IgDh/pHgHaGz/e+AmEQb7pRgb++wt0w==", + "path": "microsoft.extensions.apidescription.server/3.0.0", + "hashPath": "microsoft.extensions.apidescription.server.3.0.0.nupkg.sha512" + }, + "Microsoft.OpenApi/1.2.3": { + "type": "package", + "serviceable": true, + "sha512": "sha512-Nug3rO+7Kl5/SBAadzSMAVgqDlfGjJZ0GenQrLywJ84XGKO0uRqkunz5Wyl0SDwcR71bAATXvSdbdzPrYRYKGw==", + "path": "microsoft.openapi/1.2.3", + "hashPath": "microsoft.openapi.1.2.3.nupkg.sha512" + }, + "Swashbuckle.AspNetCore/6.2.3": { + "type": "package", + "serviceable": true, + "sha512": "sha512-cnzQDn0Le+hInsw2SYwlOhOCPXpYi/szcvnyqZJ12v+QyrLBwAmWXBg6RIyHB18s/mLeywC+Rg2O9ndz0IUNYQ==", + "path": "swashbuckle.aspnetcore/6.2.3", + "hashPath": "swashbuckle.aspnetcore.6.2.3.nupkg.sha512" + }, + "Swashbuckle.AspNetCore.Swagger/6.2.3": { + "type": "package", + "serviceable": true, + "sha512": "sha512-qOF7j1sL0bWm8g/qqHVPCvkO3JlVvUIB8WfC98kSh6BT5y5DAnBNctfac7XR5EZf+eD7/WasvANncTqwZYfmWQ==", + "path": "swashbuckle.aspnetcore.swagger/6.2.3", + "hashPath": "swashbuckle.aspnetcore.swagger.6.2.3.nupkg.sha512" + }, + "Swashbuckle.AspNetCore.SwaggerGen/6.2.3": { + "type": "package", + "serviceable": true, + "sha512": "sha512-+Xq7WdMCCfcXlnbLJVFNgY8ITdP2TRYIlpbt6IKzDw5FwFxdi9lBfNDtcT+/wkKwX70iBBFmXldnnd02/VO72A==", + "path": "swashbuckle.aspnetcore.swaggergen/6.2.3", + "hashPath": "swashbuckle.aspnetcore.swaggergen.6.2.3.nupkg.sha512" + }, + "Swashbuckle.AspNetCore.SwaggerUI/6.2.3": { + "type": "package", + "serviceable": true, + "sha512": "sha512-bCRI87uKJVb4G+KURWm8LQrL64St04dEFZcF6gIM67Zc0Sr/N47EO83ybLMYOvfNdO1DCv8xwPcrz9J/VEhQ5g==", + "path": "swashbuckle.aspnetcore.swaggerui/6.2.3", + "hashPath": "swashbuckle.aspnetcore.swaggerui.6.2.3.nupkg.sha512" + } + } +} \ No newline at end of file diff --git a/dapr/quickstarts/bindings/csharp/http/batch/Debug/net6.0/batch.dll b/dapr/quickstarts/bindings/csharp/http/batch/Debug/net6.0/batch.dll new file mode 100644 index 0000000..36965eb Binary files /dev/null and b/dapr/quickstarts/bindings/csharp/http/batch/Debug/net6.0/batch.dll differ diff --git a/dapr/quickstarts/bindings/csharp/http/batch/Debug/net6.0/batch.pdb b/dapr/quickstarts/bindings/csharp/http/batch/Debug/net6.0/batch.pdb new file mode 100644 index 0000000..9aa24aa Binary files /dev/null and b/dapr/quickstarts/bindings/csharp/http/batch/Debug/net6.0/batch.pdb differ diff --git a/dapr/quickstarts/bindings/csharp/http/batch/Debug/net6.0/batch.runtimeconfig.json b/dapr/quickstarts/bindings/csharp/http/batch/Debug/net6.0/batch.runtimeconfig.json new file mode 100644 index 0000000..dfb1b77 --- /dev/null +++ b/dapr/quickstarts/bindings/csharp/http/batch/Debug/net6.0/batch.runtimeconfig.json @@ -0,0 +1,19 @@ +{ + "runtimeOptions": { + "tfm": "net6.0", + "frameworks": [ + { + "name": "Microsoft.NETCore.App", + "version": "6.0.0" + }, + { + "name": "Microsoft.AspNetCore.App", + "version": "6.0.0" + } + ], + "configProperties": { + "System.GC.Server": true, + "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false + } + } +} \ No newline at end of file diff --git a/dapr/quickstarts/bindings/csharp/http/batch/Properties/launchSettings.json b/dapr/quickstarts/bindings/csharp/http/batch/Properties/launchSettings.json new file mode 100644 index 0000000..b30873f --- /dev/null +++ b/dapr/quickstarts/bindings/csharp/http/batch/Properties/launchSettings.json @@ -0,0 +1,15 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "CheckoutService": { + "commandName": "Project", + "dotnetRunMessages": "true", + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:7001", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } + } \ No newline at end of file diff --git a/dapr/quickstarts/bindings/csharp/http/batch/appsettings.Development.json b/dapr/quickstarts/bindings/csharp/http/batch/appsettings.Development.json new file mode 100644 index 0000000..c00584b --- /dev/null +++ b/dapr/quickstarts/bindings/csharp/http/batch/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } + } \ No newline at end of file diff --git a/dapr/quickstarts/bindings/csharp/http/batch/appsettings.json b/dapr/quickstarts/bindings/csharp/http/batch/appsettings.json new file mode 100644 index 0000000..a33e07c --- /dev/null +++ b/dapr/quickstarts/bindings/csharp/http/batch/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" + } \ No newline at end of file diff --git a/dapr/quickstarts/bindings/csharp/http/batch/batch.csproj b/dapr/quickstarts/bindings/csharp/http/batch/batch.csproj new file mode 100644 index 0000000..85b297d --- /dev/null +++ b/dapr/quickstarts/bindings/csharp/http/batch/batch.csproj @@ -0,0 +1,14 @@ + + + + Exe + net6.0 + enable + enable + + + + + + + \ No newline at end of file diff --git a/dapr/quickstarts/bindings/csharp/http/batch/program.cs b/dapr/quickstarts/bindings/csharp/http/batch/program.cs new file mode 100644 index 0000000..670fdbb --- /dev/null +++ b/dapr/quickstarts/bindings/csharp/http/batch/program.cs @@ -0,0 +1,74 @@ +/* +Copyright 2021 The Dapr Authors +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +using System; +using System.IO; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; +using Microsoft.AspNetCore.Mvc; + + +//dapr run --app-id batch-http --app-port 7001 --components-path ../../../components -- dotnet run + +var cronBindingName = "cron"; +var sqlBindingName = "sqldb"; + +var baseURL = Environment.GetEnvironmentVariable("BASE_URL") ?? "http://localhost"; +var daprPort = Environment.GetEnvironmentVariable("DAPR_HTTP_PORT") ?? "3500"; +var daprUrl = $"{baseURL}:{daprPort}/v1.0/bindings/{sqlBindingName}"; + +var builder = WebApplication.CreateBuilder(args); +var app = builder.Build(); + +if (app.Environment.IsDevelopment()) {app.UseDeveloperExceptionPage();} + +var httpClient = new HttpClient(); +httpClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json")); + +// Triggered by Dapr input binding +app.MapPost("/" + cronBindingName, async () => { + Console.WriteLine("Processing batch.."); + + string jsonFile = File.ReadAllText("../../../orders.json"); + var ordersArray = JsonSerializer.Deserialize(jsonFile); + foreach(Order ord in ordersArray?.orders ?? new Order[] {}){ + var sqlText = $"insert into orders (orderid, customer, price) values ({ord.OrderId}, '{ord.Customer}', {ord.Price});"; + var payload = new DaprPayload(sql: new DaprPostgresBindingMetadata(cmd: sqlText), operation: "exec"); + var orderJson = JsonSerializer.Serialize(payload); + var content = new StringContent(orderJson, Encoding.UTF8, "application/json"); + + Console.WriteLine(sqlText); + + // Insert order using Dapr output binding via HTTP Post + try { + var resp = await httpClient.PostAsync(daprUrl, content); + resp.EnsureSuccessStatusCode(); + } + catch (HttpRequestException e) { + Console.WriteLine(e.ToString()); + throw e; + } + + } + + Console.WriteLine("Finished processing batch"); + + return Results.Ok(); +}); + +await app.RunAsync(); + +public record DaprPostgresBindingMetadata([property: JsonPropertyName("sql")] string cmd); +public record DaprPayload([property: JsonPropertyName("metadata")] DaprPostgresBindingMetadata sql, [property: JsonPropertyName("operation")] string operation); +public record Order([property: JsonPropertyName("orderid")] int OrderId, [property: JsonPropertyName("customer")] string Customer, [property: JsonPropertyName("price")] float Price); +public record Orders([property: JsonPropertyName("orders")] Order[] orders); \ No newline at end of file diff --git a/dapr/quickstarts/bindings/csharp/http/makefile b/dapr/quickstarts/bindings/csharp/http/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/bindings/csharp/http/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/bindings/csharp/sdk/README.md b/dapr/quickstarts/bindings/csharp/sdk/README.md new file mode 100644 index 0000000..c4f1c27 --- /dev/null +++ b/dapr/quickstarts/bindings/csharp/sdk/README.md @@ -0,0 +1,66 @@ +# Dapr Bindings (Dapr SDK) + +In this quickstart, you'll create a microservice to demonstrate Dapr's bindings API to work with external systems as inputs and outputs. The service listens to input binding events from a system CRON and then outputs the contents of local data to a PostreSql output binding. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/bindings/) link for more information about Dapr and Bindings. + +> **Note:** This example leverages the Dapr SDK. If you are looking for the example using HTTP REST only [click here](../http). + +This quickstart includes one service: + +- .NET/C# service `batch` + +### Run and initialize PostgreSQL container + +1. Open a new terminal, change directories to `../../../db`, and run the container with [Docker Compose](https://docs.docker.com/compose/): + + + +```bash +cd ../../db +docker compose up +``` + + + +### Run C# service with Dapr + +2. Open a new terminal window, change directories to `./batch` in the quickstart directory and run: + + + +```bash +cd ./batch +dotnet restore +``` + + +3. Run the C# service app with Dapr: + + + +```bash +dapr run --app-id batch-sdk --app-port 7002 --components-path ../../../components -- dotnet run +``` + + diff --git a/dapr/quickstarts/bindings/csharp/sdk/batch/Properties/launchSettings.json b/dapr/quickstarts/bindings/csharp/sdk/batch/Properties/launchSettings.json new file mode 100644 index 0000000..38ac0d7 --- /dev/null +++ b/dapr/quickstarts/bindings/csharp/sdk/batch/Properties/launchSettings.json @@ -0,0 +1,15 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "CheckoutService": { + "commandName": "Project", + "dotnetRunMessages": "true", + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:7002", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } + } \ No newline at end of file diff --git a/dapr/quickstarts/bindings/csharp/sdk/batch/appsettings.Development.json b/dapr/quickstarts/bindings/csharp/sdk/batch/appsettings.Development.json new file mode 100644 index 0000000..c00584b --- /dev/null +++ b/dapr/quickstarts/bindings/csharp/sdk/batch/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } + } \ No newline at end of file diff --git a/dapr/quickstarts/bindings/csharp/sdk/batch/appsettings.json b/dapr/quickstarts/bindings/csharp/sdk/batch/appsettings.json new file mode 100644 index 0000000..a33e07c --- /dev/null +++ b/dapr/quickstarts/bindings/csharp/sdk/batch/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" + } \ No newline at end of file diff --git a/dapr/quickstarts/bindings/csharp/sdk/batch/batch.csproj b/dapr/quickstarts/bindings/csharp/sdk/batch/batch.csproj new file mode 100644 index 0000000..170a7e4 --- /dev/null +++ b/dapr/quickstarts/bindings/csharp/sdk/batch/batch.csproj @@ -0,0 +1,15 @@ + + + + Exe + net6.0 + enable + enable + + + + + + + + \ No newline at end of file diff --git a/dapr/quickstarts/bindings/csharp/sdk/batch/program.cs b/dapr/quickstarts/bindings/csharp/sdk/batch/program.cs new file mode 100644 index 0000000..c484cd6 --- /dev/null +++ b/dapr/quickstarts/bindings/csharp/sdk/batch/program.cs @@ -0,0 +1,59 @@ +/* +Copyright 2021 The Dapr Authors +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +using System; +using System.IO; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; +using Microsoft.AspNetCore.Mvc; +using Dapr.Client; + + +// dapr run --app-id batch-sdk --app-port 7002 --components-path ../../../components -- dotnet run + +var cronBindingName = "cron"; +var sqlBindingName = "sqldb"; + +var builder = WebApplication.CreateBuilder(args); +var app = builder.Build(); + +if (app.Environment.IsDevelopment()) {app.UseDeveloperExceptionPage();} + +// Triggered by Dapr input binding +app.MapPost("/" + cronBindingName, async () => { + + Console.WriteLine("Processing batch.."); + string jsonFile = File.ReadAllText("../../../orders.json"); + var ordersArray = JsonSerializer.Deserialize(jsonFile); + using var client = new DaprClientBuilder().Build(); + foreach(Order ord in ordersArray?.orders ?? new Order[] {}){ + var sqlText = $"insert into orders (orderid, customer, price) values ({ord.OrderId}, '{ord.Customer}', {ord.Price});"; + var command = new Dictionary(){ + {"sql", + sqlText} + }; + Console.WriteLine(sqlText); + + // Insert order using Dapr output binding via Dapr Client SDK + await client.InvokeBindingAsync(bindingName: sqlBindingName, operation: "exec", data: "", metadata: command); + } + + Console.WriteLine("Finished processing batch"); + + return Results.Ok(); +}); + +await app.RunAsync(); + +public record Order([property: JsonPropertyName("orderid")] int OrderId, [property: JsonPropertyName("customer")] string Customer, [property: JsonPropertyName("price")] float Price); +public record Orders([property: JsonPropertyName("orders")] Order[] orders); \ No newline at end of file diff --git a/dapr/quickstarts/bindings/csharp/sdk/makefile b/dapr/quickstarts/bindings/csharp/sdk/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/bindings/csharp/sdk/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/bindings/db/Dockerfile b/dapr/quickstarts/bindings/db/Dockerfile new file mode 100644 index 0000000..d626ad2 --- /dev/null +++ b/dapr/quickstarts/bindings/db/Dockerfile @@ -0,0 +1,2 @@ +FROM postgres +COPY orders.sql /docker-entrypoint-initdb.d/ diff --git a/dapr/quickstarts/bindings/db/README.md b/dapr/quickstarts/bindings/db/README.md new file mode 100644 index 0000000..4043a64 --- /dev/null +++ b/dapr/quickstarts/bindings/db/README.md @@ -0,0 +1,30 @@ +# Dapr Bindings - Sample Orders Database + +This quickstart uses a specific sample database called `postgres` to back up orders data. + +The database is implemented using the official base [`postgres`](https://hub.docker.com/_/postgres) container image as a base and customizing it to initialize using the SQL script in `orders.sql`. For convenience, building, running, and setting of customizable environment variables (e.g. DB name and password) is handled in the `docker-compose.yml` file. + +To start the database simply run the following in this folder: +```bash +docker compose up +``` + +To explore the database using the interactive CLI run: +```bash +docker exec -i -t postgres psql --username postgres -p 5432 -h localhost --no-password +``` + +At the prompt change to the `orders` database with: +```bash +\c orders; +``` + +Explore data using: +```bash +select * from orders; +``` + +To clean up, CTRL-C the terminal or run: +```bash +docker compose down +``` diff --git a/dapr/quickstarts/bindings/db/docker-compose.yml b/dapr/quickstarts/bindings/db/docker-compose.yml new file mode 100644 index 0000000..41125bd --- /dev/null +++ b/dapr/quickstarts/bindings/db/docker-compose.yml @@ -0,0 +1,15 @@ +version: '3.4' + +services: + db: + image: samples/postgres + container_name: postgres + ports: + - "5432:5432" + build: + context: . + dockerfile: ./Dockerfile + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: docker + POSTGRES_DB: orders diff --git a/dapr/quickstarts/bindings/db/orders.sql b/dapr/quickstarts/bindings/db/orders.sql new file mode 100644 index 0000000..a8ed738 --- /dev/null +++ b/dapr/quickstarts/bindings/db/orders.sql @@ -0,0 +1,2 @@ +\c orders; +create table orders ( orderid int, customer text, price float ); select * from orders; diff --git a/dapr/quickstarts/bindings/go/http/README.md b/dapr/quickstarts/bindings/go/http/README.md new file mode 100644 index 0000000..7b4a5d5 --- /dev/null +++ b/dapr/quickstarts/bindings/go/http/README.md @@ -0,0 +1,66 @@ +# Dapr Bindings (HTTP) + +In this quickstart, you'll create a microservice to demonstrate Dapr's bindings API to work with external systems as inputs and outputs. The service listens to input binding events from a system CRON and then outputs the contents of local data to a PostreSql output binding. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/bindings/) link for more information about Dapr and Bindings. + +> **Note:** This example leverages only HTTP REST. If you are looking for the example using the Dapr SDK [click here](../sdk). + +This quickstart includes one service: + +- Go service `app` + +### Run and initialize PostgreSQL container + +1. Open a new terminal, change directories to `../../db`, and run the container with [Docker Compose](https://docs.docker.com/compose/): + + + +```bash +cd ../../db +docker compose up +``` + + + +### Run Go service with Dapr + +2. Open a new terminal, change directories to `./batch` in the quickstart directory and run: + + + +```bash +cd ./batch +go build . +``` + + +3. Run the Go service app with Dapr: + + + +```bash +dapr run --app-id batch-http --app-port 6003 --dapr-http-port 3503 --dapr-grpc-port 60003 --components-path ../../../components -- go run . +``` + + diff --git a/dapr/quickstarts/bindings/go/http/batch/app b/dapr/quickstarts/bindings/go/http/batch/app new file mode 100755 index 0000000..c37c8be Binary files /dev/null and b/dapr/quickstarts/bindings/go/http/batch/app differ diff --git a/dapr/quickstarts/bindings/go/http/batch/app.go b/dapr/quickstarts/bindings/go/http/batch/app.go new file mode 100644 index 0000000..6f28dbe --- /dev/null +++ b/dapr/quickstarts/bindings/go/http/batch/app.go @@ -0,0 +1,122 @@ +/* +Copyright 2021 The Dapr Authors +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +/* +dapr run --app-id batch-http --app-port 6003 --dapr-http-port 3503 --dapr-grpc-port 60003 --components-path ../../../components -- go run . +*/ + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "net/http" + "os" + "strconv" + "strings" + + "github.com/gorilla/mux" +) + +var ( + cronBindingName, sqlBindingName string = "cron", "sqldb" +) + +type Orders struct { + Orders []Order `json:"orders"` +} + +type Order struct { + OrderId int `json:"orderid"` + Customer string `json:"customer"` + Price float64 `json:"price"` +} + +func processBatch(w http.ResponseWriter, r *http.Request) { + + fmt.Println("Processing batch..") + + fileContent, err := os.Open("../../../orders.json") + if err != nil { + log.Fatal(err) + return + } + + defer fileContent.Close() + + byteResult, _ := ioutil.ReadAll(fileContent) + + var orders Orders + + json.Unmarshal(byteResult, &orders) + + for i := 0; i < len(orders.Orders); i++ { + err := sqlOutput(orders.Orders[i]) + if err != nil { + log.Fatal(err) + os.Exit(1) + } + } + fmt.Println("Finished processing batch") + +} + +func sqlOutput(order Order) (err error) { + var daprHost, daprHttpPort string + var okHost, okPort bool + + if daprHost, okHost = os.LookupEnv("DAPR_HOST"); !okHost { + daprHost = "http://localhost" + } + + if daprHttpPort, okPort = os.LookupEnv("DAPR_HTTP_PORT"); !okPort { + daprHttpPort = "3503" + } + + var daprUrl string = daprHost + ":" + daprHttpPort + "/v1.0/bindings/" + sqlBindingName + + sqlCmd := fmt.Sprintf("insert into orders (orderid, customer, price) values (%d, '%s', %s);", order.OrderId, order.Customer, strconv.FormatFloat(order.Price, 'f', 2, 64)) + + payload := `{"operation": "exec", "metadata": {"sql": "` + sqlCmd + `" }}` + fmt.Println(sqlCmd) + + client := http.Client{} + // Insert order using Dapr output binding via HTTP Post + req, err := http.NewRequest("POST", daprUrl, strings.NewReader(payload)) + if err != nil { + return err + } + if _, err = client.Do(req); err != nil { + return err + } + return nil +} + +func main() { + var appPort string + var okHost bool + if appPort, okHost = os.LookupEnv("APP_PORT"); !okHost { + appPort = "6003" + } + + r := mux.NewRouter() + + // Triggered by Dapr input binding + r.HandleFunc("/"+cronBindingName, processBatch).Methods("POST") + + if err := http.ListenAndServe(":"+appPort, r); err != nil { + log.Panic(err) + } +} diff --git a/dapr/quickstarts/bindings/go/http/batch/dapr_output_binding b/dapr/quickstarts/bindings/go/http/batch/dapr_output_binding new file mode 100755 index 0000000..cb23c2a Binary files /dev/null and b/dapr/quickstarts/bindings/go/http/batch/dapr_output_binding differ diff --git a/dapr/quickstarts/bindings/go/http/batch/go.mod b/dapr/quickstarts/bindings/go/http/batch/go.mod new file mode 100644 index 0000000..86a4dab --- /dev/null +++ b/dapr/quickstarts/bindings/go/http/batch/go.mod @@ -0,0 +1,5 @@ +module dapr_output_binding + +go 1.18 + +require github.com/gorilla/mux v1.8.0 diff --git a/dapr/quickstarts/bindings/go/http/batch/go.sum b/dapr/quickstarts/bindings/go/http/batch/go.sum new file mode 100644 index 0000000..5350288 --- /dev/null +++ b/dapr/quickstarts/bindings/go/http/batch/go.sum @@ -0,0 +1,2 @@ +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= diff --git a/dapr/quickstarts/bindings/go/http/makefile b/dapr/quickstarts/bindings/go/http/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/bindings/go/http/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/bindings/go/sdk/README.md b/dapr/quickstarts/bindings/go/sdk/README.md new file mode 100644 index 0000000..d46c490 --- /dev/null +++ b/dapr/quickstarts/bindings/go/sdk/README.md @@ -0,0 +1,66 @@ +# Dapr Bindings (Dapr SDK) + +In this quickstart, you'll create a microservice to demonstrate Dapr's bindings API to work with external systems as inputs and outputs. The service listens to input binding events from a system CRON and then outputs the contents of local data to a PostreSql output binding. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/bindings/) link for more information about Dapr and Bindings. + +> **Note:** This example leverages the Dapr SDK. If you are looking for the example using HTTP REST only [click here](../http). + +This quickstart includes one service: + +- Go service `app` + +### Run and initialize PostgreSQL container + +1. Open a new terminal, change directories to `../../db`, and run the container with [Docker Compose](https://docs.docker.com/compose/): + + + +```bash +cd ../../db +docker compose up +``` + + + +### Run Go service with Dapr + +2. Open a new terminal window, change directories to `./batch` in the quickstart directory and run: + + + +```bash +cd ./batch +go build . +``` + + +3. Run the Go service app with Dapr: + + + +```bash +dapr run --app-id batch-sdk --app-port 6002 --dapr-http-port 3502 --dapr-grpc-port 60002 --components-path ../../../components -- go run . +``` + + diff --git a/dapr/quickstarts/bindings/go/sdk/batch/app b/dapr/quickstarts/bindings/go/sdk/batch/app new file mode 100755 index 0000000..85e4a80 Binary files /dev/null and b/dapr/quickstarts/bindings/go/sdk/batch/app differ diff --git a/dapr/quickstarts/bindings/go/sdk/batch/app.go b/dapr/quickstarts/bindings/go/sdk/batch/app.go new file mode 100644 index 0000000..1a74d32 --- /dev/null +++ b/dapr/quickstarts/bindings/go/sdk/batch/app.go @@ -0,0 +1,119 @@ +/* +Copyright 2021 The Dapr Authors +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +/* +dapr run --app-id batch-sdk --app-port 6002 --dapr-http-port 3502 --dapr-grpc-port 60002 --components-path ../../../components -- go run . +*/ + +import ( + "context" + "encoding/json" + "fmt" + "io/ioutil" + "log" + "net/http" + "os" + "strconv" + + dapr "github.com/dapr/go-sdk/client" + "github.com/gorilla/mux" +) + +var ( + cronBindingName, sqlBindingName string = "cron", "sqldb" +) + +type Orders struct { + Orders []Order `json:"orders"` +} + +type Order struct { + OrderId int `json:"orderid"` + Customer string `json:"customer"` + Price float64 `json:"price"` +} + +func processBatch(w http.ResponseWriter, r *http.Request) { + + fmt.Println("Processing batch...") + + fileContent, err := os.Open("../../../orders.json") + if err != nil { + log.Fatal(err) + return + } + + defer fileContent.Close() + + byteResult, _ := ioutil.ReadAll(fileContent) + + var orders Orders + + json.Unmarshal(byteResult, &orders) + + for i := 0; i < len(orders.Orders); i++ { + err := sqlOutput(orders.Orders[i]) + if err != nil { + log.Fatal(err) + os.Exit(1) + } + } + fmt.Println("Finished processing batch") + +} + +func sqlOutput(order Order) (err error) { + + client, err := dapr.NewClient() + if err != nil { + return err + } + + ctx := context.Background() + + sqlCmd := fmt.Sprintf("insert into orders (orderid, customer, price) values (%d, '%s', %s);", order.OrderId, order.Customer, strconv.FormatFloat(order.Price, 'f', 2, 64)) + fmt.Println(sqlCmd) + + // Insert order using Dapr output binding via Dapr SDK + in := &dapr.InvokeBindingRequest{ + Name: sqlBindingName, + Operation: "exec", + Data: []byte(""), + Metadata: map[string]string{"sql": sqlCmd}, + } + err = client.InvokeOutputBinding(ctx, in) + if err != nil { + return err + } + + return nil +} + +func main() { + var appPort string + var okHost bool + if appPort, okHost = os.LookupEnv("APP_PORT"); !okHost { + appPort = "6002" + } + + r := mux.NewRouter() + + // Triggered by Dapr input binding + r.HandleFunc("/"+cronBindingName, processBatch).Methods("POST") + + if err := http.ListenAndServe(":"+appPort, r); err != nil { + log.Panic(err) + } +} diff --git a/dapr/quickstarts/bindings/go/sdk/batch/batch b/dapr/quickstarts/bindings/go/sdk/batch/batch new file mode 100755 index 0000000..565b0c1 Binary files /dev/null and b/dapr/quickstarts/bindings/go/sdk/batch/batch differ diff --git a/dapr/quickstarts/bindings/go/sdk/batch/dapr_output_binding b/dapr/quickstarts/bindings/go/sdk/batch/dapr_output_binding new file mode 100755 index 0000000..8f6390f Binary files /dev/null and b/dapr/quickstarts/bindings/go/sdk/batch/dapr_output_binding differ diff --git a/dapr/quickstarts/bindings/go/sdk/batch/go.mod b/dapr/quickstarts/bindings/go/sdk/batch/go.mod new file mode 100644 index 0000000..8961142 --- /dev/null +++ b/dapr/quickstarts/bindings/go/sdk/batch/go.mod @@ -0,0 +1,21 @@ +module dapr_output_binding + +go 1.18 + +require ( + github.com/dapr/go-sdk v1.5.0 + github.com/gorilla/mux v1.8.0 +) + +require ( + github.com/dapr/dapr v1.8.0 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/pkg/errors v0.9.1 // indirect + golang.org/x/net v0.0.0-20220621193019-9d032be2e588 // indirect + golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect + golang.org/x/text v0.3.7 // indirect + google.golang.org/genproto v0.0.0-20220622171453-ea41d75dfa0f // indirect + google.golang.org/grpc v1.47.0 // indirect + google.golang.org/protobuf v1.28.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/dapr/quickstarts/bindings/go/sdk/batch/go.sum b/dapr/quickstarts/bindings/go/sdk/batch/go.sum new file mode 100644 index 0000000..de4c64e --- /dev/null +++ b/dapr/quickstarts/bindings/go/sdk/batch/go.sum @@ -0,0 +1,153 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/dapr/dapr v1.8.0 h1:ZAAoBe6wuFp7k4tIHB7ajZXVTtGeDeVqIPrldzo3dF0= +github.com/dapr/dapr v1.8.0/go.mod h1:yAsDiK5oecG0htw2S8JG9RFaeHJVdlTfZyOrL57AvRM= +github.com/dapr/go-sdk v1.5.0 h1:OVkrupquJEOL1qRtwKcMVrFKYhw4UJQvgOJNduo2VxE= +github.com/dapr/go-sdk v1.5.0/go.mod h1:Cvz3taCVu22WCNEUbc9/szvG/yJxWPAV4dcaG+zDWA4= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.4 h1:wZRexSlwd7ZXfKINDLsO4r7WBt3gTKONc6K/VesHvHM= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20220621193019-9d032be2e588 h1:9ubFuySsnAJYGyJrZ3koiEv8FyqofCBdz3G9Mbf2YFc= +golang.org/x/net v0.0.0-20220621193019-9d032be2e588/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20220622171453-ea41d75dfa0f h1:kYlCnpX4eB0QEnXm12j4DAX4yrjjhJmsyuWtSSZ+Buo= +google.golang.org/genproto v0.0.0-20220622171453-ea41d75dfa0f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8= +google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/dapr/quickstarts/bindings/go/sdk/makefile b/dapr/quickstarts/bindings/go/sdk/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/bindings/go/sdk/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/bindings/java/http/README.md b/dapr/quickstarts/bindings/java/http/README.md new file mode 100644 index 0000000..2c300e6 --- /dev/null +++ b/dapr/quickstarts/bindings/java/http/README.md @@ -0,0 +1,66 @@ +# Dapr Bindings (HTTP) + +In this quickstart, you'll create a microservice to demonstrate Dapr's bindings API to work with external systems as inputs and outputs. The service listens to input binding events from a system CRON and then outputs the contents of local data to a PostreSql output binding. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/bindings/) link for more information about Dapr and Bindings. + +> **Note:** This example leverages only HTTP REST. If you are looking for the example using the Dapr SDK [click here](../sdk). + +This quickstart includes one service: + +- Java service `batch` + +### Run and initialize PostgreSQL container + +1. Open a new terminal, change directories to `../../db`, and run the container with [Docker Compose](https://docs.docker.com/compose/): + + + +```bash +cd ../../db +docker compose up +``` + + + +### Run Java service with Dapr + +2. Open a new terminal window, change directories to `./batch` in the quickstart directory and run: + + + +```bash +cd ./batch +mvn clean install +``` + + +3. Run the Java service app with Dapr: + + + +```bash +dapr run --app-id batch-http --app-port 8080 --components-path ../../../components -- java -jar target/BatchProcessingService-0.0.1-SNAPSHOT.jar +``` + + diff --git a/dapr/quickstarts/bindings/java/http/batch/pom.xml b/dapr/quickstarts/bindings/java/http/batch/pom.xml new file mode 100644 index 0000000..f677095 --- /dev/null +++ b/dapr/quickstarts/bindings/java/http/batch/pom.xml @@ -0,0 +1,50 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.6.3 + + + com.service + BatchProcessingService + 0.0.1-SNAPSHOT + BatchProcessingService + Quickstart for Dapr Bindings building block + + 11 + + + + org.json + json + 20211205 + + + com.fasterxml.jackson.core + jackson-databind + 2.13.0 + + + org.springframework.boot + spring-boot-starter-web + + + org.projectlombok + lombok + 1.18.22 + true + + + + + + org.springframework.boot + spring-boot-maven-plugin + 2.6.4 + + + + + \ No newline at end of file diff --git a/dapr/quickstarts/bindings/java/http/batch/src/main/java/com/service/BatchProcessingServiceApplication.java b/dapr/quickstarts/bindings/java/http/batch/src/main/java/com/service/BatchProcessingServiceApplication.java new file mode 100644 index 0000000..38a65d3 --- /dev/null +++ b/dapr/quickstarts/bindings/java/http/batch/src/main/java/com/service/BatchProcessingServiceApplication.java @@ -0,0 +1,24 @@ +/* +Copyright 2021 The Dapr Authors +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package com.service; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class BatchProcessingServiceApplication { + public static void main(String[] args) throws Exception { + SpringApplication.run(BatchProcessingServiceApplication.class, args); + } + +} diff --git a/dapr/quickstarts/bindings/java/http/batch/src/main/java/com/service/controller/BatchProcessingServiceController.java b/dapr/quickstarts/bindings/java/http/batch/src/main/java/com/service/controller/BatchProcessingServiceController.java new file mode 100644 index 0000000..e794227 --- /dev/null +++ b/dapr/quickstarts/bindings/java/http/batch/src/main/java/com/service/controller/BatchProcessingServiceController.java @@ -0,0 +1,130 @@ +/* +Copyright 2021 The Dapr Authors +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package com.service.controller; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.json.JSONObject; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.util.List; + +import java.time.Duration; + +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; + +import java.io.InputStream; + +@RestController +public class BatchProcessingServiceController { + + private static final Logger logger = LoggerFactory.getLogger(BatchProcessingServiceController.class); + private static final String cronBindingPath = "/cron"; + private static final String sqlBindingName = "sqldb"; + + private static String DAPR_HOST = System.getenv().getOrDefault("DAPR_HOST", "http://localhost"); + private static String DAPR_HTTP_PORT = System.getenv().getOrDefault("DAPR_HTTP_PORT", "3500"); + String daprUri = DAPR_HOST + ":" + DAPR_HTTP_PORT + "/v1.0/bindings/" + sqlBindingName; + + private static final HttpClient httpClient = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_2) + .connectTimeout(Duration.ofSeconds(10)) + .build(); + + + @PostMapping(path = cronBindingPath, consumes = MediaType.ALL_VALUE) + public ResponseEntity processBatch() throws Exception { + + logger.info("Processing batch.."); + + Orders ordList = this.loadOrdersFromFile("orders.json"); + + try { + + for (Order order : ordList.orders) { + String sqlText = String.format( + "insert into orders (orderid, customer, price) " + + "values (%s, '%s', %s);", + order.orderid, order.customer, order.price); + logger.info(sqlText); + + JSONObject command = new JSONObject(); + command.put("sql", sqlText); + + JSONObject payload = new JSONObject(); + payload.put("metadata", command); + payload.put("operation", "exec"); + + // Invoke sql output binding using Dapr Bindings via HTTP Post + HttpRequest request = HttpRequest.newBuilder() + .POST(HttpRequest.BodyPublishers.ofString(payload.toString())) + .uri(URI.create(daprUri)) + .header("Content-Type", "application/json") + .build(); + + HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + } + + logger.info("Finished processing batch"); + + return ResponseEntity.ok("Finished processing batch"); + + } catch (Exception e) { + logger.error("HTTP client failed:", e); + throw e; + } + + } + + private Orders loadOrdersFromFile(String path) throws Exception { + + ObjectMapper mapper = new ObjectMapper(); + mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true); + + try (InputStream is = getClass().getClassLoader().getResourceAsStream(path)) { + Orders obj = mapper.readValue(is, Orders.class); + return obj; + } catch (Exception e) { + logger.error(e.toString()); + throw e; + } + + + } +} + +@Getter +@Setter +class Order { + public int orderid; + public String customer; + public float price; +} + +@Getter +@Setter +class Orders { + public List orders; +} diff --git a/dapr/quickstarts/bindings/java/http/batch/src/main/resources/application.properties b/dapr/quickstarts/bindings/java/http/batch/src/main/resources/application.properties new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/dapr/quickstarts/bindings/java/http/batch/src/main/resources/application.properties @@ -0,0 +1 @@ + diff --git a/dapr/quickstarts/bindings/java/http/batch/src/main/resources/orders.json b/dapr/quickstarts/bindings/java/http/batch/src/main/resources/orders.json new file mode 100644 index 0000000..6dec201 --- /dev/null +++ b/dapr/quickstarts/bindings/java/http/batch/src/main/resources/orders.json @@ -0,0 +1,20 @@ +{ + "orders": [ + { + "orderid": 1, + "customer": "John Smith", + "price": 100.32 + }, + { + "orderid": 2, + "customer": "Jane Bond", + "price": 15.4 + }, + { + "orderid": 3, + "customer": "Tony James", + "price": 35.56 + } + ] + } + \ No newline at end of file diff --git a/dapr/quickstarts/bindings/java/http/makefile b/dapr/quickstarts/bindings/java/http/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/bindings/java/http/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/bindings/java/sdk/README.md b/dapr/quickstarts/bindings/java/sdk/README.md new file mode 100644 index 0000000..c195d2f --- /dev/null +++ b/dapr/quickstarts/bindings/java/sdk/README.md @@ -0,0 +1,66 @@ +# Dapr Bindings (Dapr SDK) + +In this quickstart, you'll create a microservice to demonstrate Dapr's bindings API to work with external systems as inputs and outputs. The service listens to input binding events from a system CRON and then outputs the contents of local data to a PostreSql output binding. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/bindings/) link for more information about Dapr and Bindings. + +> **Note:** This example leverages the Dapr SDK. If you are looking for the example using HTTP REST only [click here](../http). + +This quickstart includes one service: + +- Java service `batch` + +### Run and initialize PostgreSQL container + +1. Open a new terminal, change directories to `../../db`, and run the container with [Docker Compose](https://docs.docker.com/compose/): + + + +```bash +cd ../../db +docker compose up +``` + + + +### Run Java service with Dapr + +2. Open a new terminal window, change directories to `./batch` in the quickstart directory and run: + + + +```bash +cd ./batch +mvn clean install +``` + + +3. Run the java service app with Dapr: + + + +```bash +dapr run --app-id batch-sdk --app-port 8080 --components-path ../../../components -- java -jar target/BatchProcessingService-0.0.1-SNAPSHOT.jar +``` + + diff --git a/dapr/quickstarts/bindings/java/sdk/batch/pom.xml b/dapr/quickstarts/bindings/java/sdk/batch/pom.xml new file mode 100644 index 0000000..a157fa7 --- /dev/null +++ b/dapr/quickstarts/bindings/java/sdk/batch/pom.xml @@ -0,0 +1,66 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.6.3 + + + com.service + BatchProcessingService + 0.0.1-SNAPSHOT + BatchProcessingService + Quickstart for Dapr Bindings building block + + 11 + + + + com.squareup.okhttp3 + okhttp + 4.9.0 + + + org.springframework.boot + spring-boot-starter-web + + + io.dapr + dapr-sdk-springboot + 1.6.0 + + + io.dapr + dapr-sdk + 1.6.0 + + + org.projectlombok + lombok + 1.18.22 + true + + + com.fasterxml.jackson.core + jackson-databind + 2.13.0 + + + + + + + org.springframework.boot + spring-boot-maven-plugin + 2.6.4 + + + org.apache.maven.plugins + maven-resources-plugin + 3.1.0 + + + + + \ No newline at end of file diff --git a/dapr/quickstarts/bindings/java/sdk/batch/src/main/java/com/service/BatchProcessingServiceApplication.java b/dapr/quickstarts/bindings/java/sdk/batch/src/main/java/com/service/BatchProcessingServiceApplication.java new file mode 100644 index 0000000..38a65d3 --- /dev/null +++ b/dapr/quickstarts/bindings/java/sdk/batch/src/main/java/com/service/BatchProcessingServiceApplication.java @@ -0,0 +1,24 @@ +/* +Copyright 2021 The Dapr Authors +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package com.service; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class BatchProcessingServiceApplication { + public static void main(String[] args) throws Exception { + SpringApplication.run(BatchProcessingServiceApplication.class, args); + } + +} diff --git a/dapr/quickstarts/bindings/java/sdk/batch/src/main/java/com/service/controller/BatchProcessingServiceController.java b/dapr/quickstarts/bindings/java/sdk/batch/src/main/java/com/service/controller/BatchProcessingServiceController.java new file mode 100644 index 0000000..415f925 --- /dev/null +++ b/dapr/quickstarts/bindings/java/sdk/batch/src/main/java/com/service/controller/BatchProcessingServiceController.java @@ -0,0 +1,104 @@ +/* +Copyright 2021 The Dapr Authors +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package com.service.controller; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; + +import io.dapr.client.DaprClient; +import io.dapr.client.DaprClientBuilder; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.io.InputStream; + + +@RestController +public class BatchProcessingServiceController { + + private static final Logger logger = LoggerFactory.getLogger(BatchProcessingServiceController.class); + private static final String cronBindingPath = "/cron"; + private static final String sqlBindingName = "sqldb"; + + @PostMapping(path = cronBindingPath, consumes = MediaType.ALL_VALUE) + public ResponseEntity processBatch() throws Exception { + + logger.info("Processing batch.."); + + Orders ordList = this.loadOrdersFromFile("orders.json"); + + try (DaprClient client = new DaprClientBuilder().build()) { + + for (Order order : ordList.orders) { + String sqlText = String.format( + "insert into orders (orderid, customer, price) " + + "values (%s, '%s', %s);", + order.orderid, order.customer, order.price); + logger.info(sqlText); + + Map metadata = new HashMap(); + metadata.put("sql", sqlText); + + // Invoke sql output binding using Dapr SDK + client.invokeBinding(sqlBindingName, "exec", null, metadata).block(); + } + + logger.info("Finished processing batch"); + + return ResponseEntity.ok("Finished processing batch"); + + } catch (Exception e) { + logger.error("Dapr client failed:", e); + throw e; + } + + } + + private Orders loadOrdersFromFile(String path) throws Exception { + + ObjectMapper mapper = new ObjectMapper(); + mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true); + + try (InputStream is = getClass().getClassLoader().getResourceAsStream(path)) { + Orders obj = mapper.readValue(is, Orders.class); + return obj; + } catch (Exception e) { + logger.error(e.toString()); + throw e; + } + } +} + +@Getter +@Setter +class Order { + public int orderid; + public String customer; + public float price; +} + +@Getter +@Setter +class Orders { + public List orders; +} diff --git a/dapr/quickstarts/bindings/java/sdk/batch/src/main/resources/application.properties b/dapr/quickstarts/bindings/java/sdk/batch/src/main/resources/application.properties new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/dapr/quickstarts/bindings/java/sdk/batch/src/main/resources/application.properties @@ -0,0 +1 @@ + diff --git a/dapr/quickstarts/bindings/java/sdk/batch/src/main/resources/orders.json b/dapr/quickstarts/bindings/java/sdk/batch/src/main/resources/orders.json new file mode 100644 index 0000000..6dec201 --- /dev/null +++ b/dapr/quickstarts/bindings/java/sdk/batch/src/main/resources/orders.json @@ -0,0 +1,20 @@ +{ + "orders": [ + { + "orderid": 1, + "customer": "John Smith", + "price": 100.32 + }, + { + "orderid": 2, + "customer": "Jane Bond", + "price": 15.4 + }, + { + "orderid": 3, + "customer": "Tony James", + "price": 35.56 + } + ] + } + \ No newline at end of file diff --git a/dapr/quickstarts/bindings/java/sdk/makefile b/dapr/quickstarts/bindings/java/sdk/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/bindings/java/sdk/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/bindings/javascript/http/README.md b/dapr/quickstarts/bindings/javascript/http/README.md new file mode 100644 index 0000000..63f88ee --- /dev/null +++ b/dapr/quickstarts/bindings/javascript/http/README.md @@ -0,0 +1,66 @@ +# Dapr Bindings (HTTP) + +In this quickstart, you'll create a microservice to demonstrate Dapr's bindings API to work with external systems as inputs and outputs. The service listens to input binding events from a system CRON and then outputs the contents of local data to a PostreSql output binding. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/bindings/) link for more information about Dapr and Bindings. + +> **Note:** This example leverages only HTTP REST. If you are looking for the example using the Dapr SDK [click here](../sdk). + +This quickstart includes one service: + +- Javascript/Node.js service `bindings` + +### Run and initialize PostgreSQL container + +1. Open a new terminal, change directories to `../../db`, and run the container with [Docker Compose](https://docs.docker.com/compose/): + + + +```bash +cd ../../db +docker compose up +``` + + + +### Run Javascript service with Dapr + +2. Open a new terminal window, change directories to `./batch` in the quickstart directory and run: + + + +```bash +cd ./batch +npm install +``` + + +3. Run the Javascript service app with Dapr: + + + +```bash +dapr run --app-id batch-http --app-port 5001 --dapr-http-port 3500 --components-path ../../../components -- node index.js +``` + + diff --git a/dapr/quickstarts/bindings/javascript/http/batch/index.js b/dapr/quickstarts/bindings/javascript/http/batch/index.js new file mode 100644 index 0000000..164294c --- /dev/null +++ b/dapr/quickstarts/bindings/javascript/http/batch/index.js @@ -0,0 +1,54 @@ +/* +Copyright 2021 The Dapr Authors +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/* +dapr run --app-id batch-http --app-port 5001 --dapr-http-port 3500 --components-path ../../../components -- node index.js +*/ + +import express from "express"; +import fs from 'fs'; +import axios from "axios"; + +const cronBinding = 'cron'; +const sqlBinding = 'sqldb'; + +const DAPR_HOST = process.env.DAPR_HOST || 'http://localhost'; +const DAPR_HTTP_PORT = process.env.DAPR_HTTP_PORT || '3500'; +const SERVER_PORT = process.env.APP_PORT || '5001'; +const daprUrl = `${DAPR_HOST}:${DAPR_HTTP_PORT}/v1.0/bindings/${sqlBinding}`; + + +const app = express(); +app.post('/' + cronBinding, (req, res) => { + const loc = '../../../orders.json'; + fs.readFile(loc, 'utf8', async (err, data) => { + console.log(`Dapr URL is: ${daprUrl}`); + const orders = JSON.parse(data).orders; + orders.forEach(order => { + let sqlCmd = `insert into orders (orderid, customer, price) values (${order.orderid}, '${order.customer}', ${order.price});`; + let payload = `{"operation": "exec", "metadata": {"sql": "${sqlCmd}"}}`; + console.log(sqlCmd); + try { + let resp = axios.post(daprUrl, payload); + } catch (error) { + console.error("SQL binding failed with: " + error.response.data); + throw error + } + + }); + console.log('Finished processing batch'); + }); + res.status(200).send(); +}); + +app.listen(SERVER_PORT, () => console.log(`listening on port ${SERVER_PORT}!`)); diff --git a/dapr/quickstarts/bindings/javascript/http/batch/package.json b/dapr/quickstarts/bindings/javascript/http/batch/package.json new file mode 100644 index 0000000..9bc5a7e --- /dev/null +++ b/dapr/quickstarts/bindings/javascript/http/batch/package.json @@ -0,0 +1,14 @@ +{ + "name": "bindings", + "version": "1.0.0", + "description": "", + "main": "index.js", + "type": "module", + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "axios": "^0.25.0", + "express": "^4.17.2" + } +} \ No newline at end of file diff --git a/dapr/quickstarts/bindings/javascript/http/makefile b/dapr/quickstarts/bindings/javascript/http/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/bindings/javascript/http/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/bindings/javascript/sdk/README.md b/dapr/quickstarts/bindings/javascript/sdk/README.md new file mode 100644 index 0000000..2d9fad4 --- /dev/null +++ b/dapr/quickstarts/bindings/javascript/sdk/README.md @@ -0,0 +1,66 @@ +# Dapr Bindings (Dapr SDK) + +In this quickstart, you'll create a microservice to demonstrate Dapr's bindings API to work with external systems as inputs and outputs. The service listens to input binding events from a system CRON and then outputs the contents of local data to a PostgreSql output binding. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/bindings/) link for more information about Dapr and Bindings. + +> **Note:** This example leverages the Dapr SDK. If you are looking for the example using HTTP REST only [click here](../http). + +This quickstart includes one service: + +- Javascript/Node.js service `bindings` + +### Run and initialize PostgreSQL container + +1. Open a new terminal, change directories to `../../db`, and run the container with [Docker Compose](https://docs.docker.com/compose/): + + + +```bash +cd ../../db +docker compose up +``` + + + +### Run Javascript service with Dapr + +2. Open a new terminal window, change directories to `./batch` in the quickstart directory and run: + + + +```bash +cd ./batch +npm install +``` + + +3. Run the Javascript service app with Dapr: + + + +```bash +dapr run --app-id batch-sdk --app-port 5002 --dapr-http-port 3500 --components-path ../../../components -- node index.js +``` + + diff --git a/dapr/quickstarts/bindings/javascript/sdk/batch/index.js b/dapr/quickstarts/bindings/javascript/sdk/batch/index.js new file mode 100644 index 0000000..3c9a819 --- /dev/null +++ b/dapr/quickstarts/bindings/javascript/sdk/batch/index.js @@ -0,0 +1,58 @@ +/* +Copyright 2021 The Dapr Authors +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/* +dapr run --app-id batch-sdk --app-port 5002 --dapr-http-port 3500 --components-path ../../../components -- node index.js +*/ + +import { DaprClient, DaprServer } from "@dapr/dapr"; +import fs from 'fs'; + +const cronBindingName = "cron"; +const postgresBindingName = "sqldb"; + +const daprHost = process.env.DAPR_HOST || 'http://localhost'; +const daprPort = process.env.DAPR_HTTP_PORT || '3500'; +const serverHost = "127.0.0.1"; +const serverPort = process.env.APP_PORT || '5002'; + +const client = new DaprClient(daprHost, daprPort); + +const server = new DaprServer(serverHost, serverPort, daprHost, daprPort); + +async function start() { + await server.binding.receive(cronBindingName,processBatch); + await server.start(); +} + +start().catch((e) => { + console.error(e); + process.exit(1); +}); + + +async function processBatch(){ + const loc = '../../../orders.json'; + fs.readFile(loc, 'utf8', (err, data) => { + const orders = JSON.parse(data).orders; + orders.forEach(order => { + let sqlCmd = `insert into orders (orderid, customer, price) values (${order.orderid}, '${order.customer}', ${order.price});`; + let payload = `{"sql": "${sqlCmd}"} `; + console.log(payload); + client.binding.send(postgresBindingName, "exec", "", JSON.parse(payload)); + }); + console.log('Finished processing batch'); + }); + return 0; +} + diff --git a/dapr/quickstarts/bindings/javascript/sdk/batch/package.json b/dapr/quickstarts/bindings/javascript/sdk/batch/package.json new file mode 100644 index 0000000..e88e6f5 --- /dev/null +++ b/dapr/quickstarts/bindings/javascript/sdk/batch/package.json @@ -0,0 +1,14 @@ +{ + "name": "bindings", + "version": "1.0.0", + "description": "", + "main": "index.js", + "type": "module", + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@dapr/dapr": "^2.3.0", + "axios": "^0.25.0" + } +} diff --git a/dapr/quickstarts/bindings/javascript/sdk/makefile b/dapr/quickstarts/bindings/javascript/sdk/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/bindings/javascript/sdk/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/bindings/orders.json b/dapr/quickstarts/bindings/orders.json new file mode 100644 index 0000000..6dec201 --- /dev/null +++ b/dapr/quickstarts/bindings/orders.json @@ -0,0 +1,20 @@ +{ + "orders": [ + { + "orderid": 1, + "customer": "John Smith", + "price": 100.32 + }, + { + "orderid": 2, + "customer": "Jane Bond", + "price": 15.4 + }, + { + "orderid": 3, + "customer": "Tony James", + "price": 35.56 + } + ] + } + \ No newline at end of file diff --git a/dapr/quickstarts/bindings/python/http/README.md b/dapr/quickstarts/bindings/python/http/README.md new file mode 100644 index 0000000..e3c3023 --- /dev/null +++ b/dapr/quickstarts/bindings/python/http/README.md @@ -0,0 +1,66 @@ +# Dapr Bindings (HTTP) + +In this quickstart, you'll create a microservice to demonstrate Dapr's bindings API to work with external systems as inputs and outputs. The service listens to input binding events from a system CRON and then outputs the contents of local data to a PostreSql output binding. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/bindings/) link for more information about Dapr and Bindings. + +> **Note:** This example leverages only HTTP REST. If you are looking for the example using the Dapr SDK [click here](../sdk). + +This quickstart includes one service: + +- Python service `batch` + +### Run and initialize PostgreSQL container + +1. Open a new terminal, change directories to `../../db`, and run the container with [Docker Compose](https://docs.docker.com/compose/): + + + +```bash +cd ../../db +docker compose up +``` + + + +### Run Python service with Dapr + +2. Open a new terminal window, change directories to `./batch` in the quickstart directory and run: + + + +```bash +cd ./batch +pip3 install -r requirements.txt +``` + + +3. Run the Python service app with Dapr: + + + +```bash +dapr run --app-id batch-http --app-port 50051 --components-path ../../../components -- python3 app.py +``` + + diff --git a/dapr/quickstarts/bindings/python/http/batch/app.py b/dapr/quickstarts/bindings/python/http/batch/app.py new file mode 100644 index 0000000..af84722 --- /dev/null +++ b/dapr/quickstarts/bindings/python/http/batch/app.py @@ -0,0 +1,73 @@ +# +# Copyright 2021 The Dapr Authors +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# dapr run --app-id batch-http --app-port 50051 +# --components-path ../../../components -- python3 app.py + +import json +from flask import Flask +import requests +import os + +app = Flask(__name__) + +app_port = os.getenv('APP_PORT', '5001') +dapr_port = os.getenv('DAPR_HTTP_PORT', '4001') +base_url = os.getenv('BASE_URL', 'http://localhost') +cron_binding_name = 'cron' +sql_binding_name = 'sqldb' +dapr_url = '%s:%s/v1.0/bindings/%s' % (base_url, + dapr_port, + sql_binding_name) + + +# Triggered by Dapr input binding +@app.route('/' + cron_binding_name, methods=['POST']) +def process_batch(): + + print('Processing batch..', flush=True) + + json_file = open("../../../orders.json", "r") + json_array = json.load(json_file) + + for order_line in json_array['orders']: + sql_output(order_line) + + json_file.close() + + print('Finished processing batch', flush=True) + + return json.dumps({'success': True}), 200, { + 'ContentType': 'application/json'} + + +def sql_output(order_line): + + sqlCmd = ('insert into orders (orderid, customer, price) values ' + + '(%s, \'%s\', %s)' % (order_line['orderid'], + order_line['customer'], + order_line['price'])) + payload = ('{"operation": "exec", "metadata": {"sql" : "%s"} }' % sqlCmd) + + print(sqlCmd, flush=True) + + try: + # Insert order using Dapr output binding via HTTP Post + resp = requests.post(dapr_url, payload) + return resp + + except requests.exceptions.RequestException as e: + print(e, flush=True) + raise SystemExit(e) + + +app.run(port=app_port) diff --git a/dapr/quickstarts/bindings/python/http/batch/requirements.txt b/dapr/quickstarts/bindings/python/http/batch/requirements.txt new file mode 100644 index 0000000..ba0d8cd --- /dev/null +++ b/dapr/quickstarts/bindings/python/http/batch/requirements.txt @@ -0,0 +1,2 @@ +requests +Flask diff --git a/dapr/quickstarts/bindings/python/http/makefile b/dapr/quickstarts/bindings/python/http/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/bindings/python/http/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/bindings/python/sdk/README.md b/dapr/quickstarts/bindings/python/sdk/README.md new file mode 100644 index 0000000..732a0b8 --- /dev/null +++ b/dapr/quickstarts/bindings/python/sdk/README.md @@ -0,0 +1,66 @@ +# Dapr Bindings (Dapr SDK) + +In this quickstart, you'll create a microservice to demonstrate Dapr's bindings API to work with external systems as inputs and outputs. The service listens to input binding events from a system CRON and then outputs the contents of local data to a PostreSql output binding. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/bindings/) link for more information about Dapr and Bindings. + +> **Note:** This example leverages the Dapr SDK. If you are looking for the example using HTTP REST only [click here](../http). + +This quickstart includes one service: + +- Python service `batch` + +### Run and initialize PostgreSQL container + +1. Open a new terminal, change directories to `../../db`, and run the container with [Docker Compose](https://docs.docker.com/compose/): + + + +```bash +cd ../../db +docker compose up +``` + + + +### Run Python service with Dapr + +2. Open a new terminal window, change directories to `./batch` in the quickstart directory and run: + + + +```bash +cd ./batch +pip3 install -r requirements.txt +``` + + +3. Run the Python service app with Dapr: + + + +```bash +dapr run --app-id batch-sdk --app-port 50051 --components-path ../../../components -- python3 app.py +``` + + diff --git a/dapr/quickstarts/bindings/python/sdk/batch/app.py b/dapr/quickstarts/bindings/python/sdk/batch/app.py new file mode 100644 index 0000000..4a7553d --- /dev/null +++ b/dapr/quickstarts/bindings/python/sdk/batch/app.py @@ -0,0 +1,67 @@ +# +# Copyright 2021 The Dapr Authors +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# dapr run --app-id batch-sdk --app-port 50051 +# --components-path ../../../components -- python3 app.py + +import json +from flask import Flask +from dapr.clients import DaprClient +import os + +app = Flask(__name__) +cron_binding_name = 'cron' +sql_binding = 'sqldb' +app_port = os.getenv('APP_PORT', '5001') + + +# Triggered by Dapr input binding +@app.route('/' + cron_binding_name, methods=['POST']) +def process_batch(): + + print('Processing batch..', flush=True) + + json_file = open('../../../orders.json', 'r') + json_array = json.load(json_file) + + for order_line in json_array['orders']: + sql_output(order_line) + + json_file.close() + + print('Finished processing batch') + + return 'Finished processing batch' + + +def sql_output(order_line): + + with DaprClient() as d: + sqlCmd = ('insert into orders (orderid, customer, price) values ' + + '(%s, \'%s\', %s)' % (order_line['orderid'], + order_line['customer'], + order_line['price'])) + payload = {'sql': sqlCmd} + + print(sqlCmd, flush=True) + + try: + # Insert order using Dapr output binding via HTTP Post + resp = d.invoke_binding(binding_name=sql_binding, operation='exec', + binding_metadata=payload, data='') + return resp + except Exception as e: + print(e, flush=True) + raise SystemExit(e) + + +app.run(port=app_port) diff --git a/dapr/quickstarts/bindings/python/sdk/batch/requirements.txt b/dapr/quickstarts/bindings/python/sdk/batch/requirements.txt new file mode 100644 index 0000000..2575cdc --- /dev/null +++ b/dapr/quickstarts/bindings/python/sdk/batch/requirements.txt @@ -0,0 +1,2 @@ +dapr +Flask diff --git a/dapr/quickstarts/bindings/python/sdk/makefile b/dapr/quickstarts/bindings/python/sdk/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/bindings/python/sdk/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/docker.mk b/dapr/quickstarts/docker.mk new file mode 100644 index 0000000..f1e148d --- /dev/null +++ b/dapr/quickstarts/docker.mk @@ -0,0 +1,87 @@ +# +# Copyright 2021 The Dapr Authors +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# Common make targets for samples' Docker images. + +SAMPLE_REGISTRY ?= docker.io/dapriosamples +TARGET_OS ?= linux +TARGET_ARCH ?= amd64 +REL_VERSION ?= latest +ifeq ($(REL_VERSION),edge) + REL_VERSION := latest +endif + +# Docker image build and push setting +DOCKER:=docker +DOCKERFILE:=Dockerfile +DOCKERMUTI_ARCH=linux-amd64 linux-arm linux-arm64 + +.PHONY: build + +BUILD_APPS:=$(foreach ITEM,$(APPS),build-$(ITEM)) +build: $(BUILD_APPS) + +# Generate docker image build targets +define genDockerImageBuild +.PHONY: build-$(1) +build-$(1): + $(DOCKER) build -f $(1)/$(DOCKERFILE) $(1)/. -t $(SAMPLE_REGISTRY)/$(DOCKER_IMAGE_PREFIX)$(1):$(REL_VERSION)-$(TARGET_OS)-$(TARGET_ARCH) --platform $(TARGET_OS)/$(TARGET_ARCH) +endef + +# Generate docker image build targets +$(foreach ITEM,$(APPS),$(eval $(call genDockerImageBuild,$(ITEM)))) + +# push docker image to the registry +.PHONY: push +PUSH_APPS:=$(foreach ITEM,$(APPS),push-$(ITEM)) +push: $(PUSH_APPS) + +# Generate docker image push targets +define genDockerImagePush +.PHONY: push-$(1) +push-$(1): + $(DOCKER) push $(SAMPLE_REGISTRY)/$(DOCKER_IMAGE_PREFIX)$(1):$(REL_VERSION)-$(TARGET_OS)-$(TARGET_ARCH) +endef + +# Generate docker image push targets +$(foreach ITEM,$(APPS),$(eval $(call genDockerImagePush,$(ITEM)))) + +# Create docker manifest +.PHONY: manifest-create +CREATE_MANIFEST_APPS:=$(foreach ITEM,$(APPS),manifest-create-$(ITEM)) +manifest-create: $(CREATE_MANIFEST_APPS) + +# Generate docker manifest create +define genDockerManifestCreate +.PHONY: manifest-create-$(1) +manifest-create-$(1): + $(DOCKER) manifest create $(SAMPLE_REGISTRY)/$(DOCKER_IMAGE_PREFIX)$(1):$(REL_VERSION) $(DOCKERMUTI_ARCH:%=$(SAMPLE_REGISTRY)/$(DOCKER_IMAGE_PREFIX)$(1):$(REL_VERSION)-%) +endef + +# Generate docker manifest create +$(foreach ITEM,$(APPS),$(eval $(call genDockerManifestCreate,$(ITEM)))) + +# Push docker manifest +.PHONY: manifest-push +PUSH_MANIFEST_APPS:=$(foreach ITEM,$(APPS),manifest-push-$(ITEM)) +manifest-push: $(PUSH_MANIFEST_APPS) + +# Generate docker manifest create +define genDockerManifestPush +.PHONY: manifest-push-$(1) +manifest-push-$(1): + $(DOCKER) manifest push $(SAMPLE_REGISTRY)/$(DOCKER_IMAGE_PREFIX)$(1):$(REL_VERSION) +endef + +# Generate docker manifest create +$(foreach ITEM,$(APPS),$(eval $(call genDockerManifestPush,$(ITEM)))) \ No newline at end of file diff --git a/dapr/quickstarts/makefile b/dapr/quickstarts/makefile new file mode 100644 index 0000000..c79fd76 --- /dev/null +++ b/dapr/quickstarts/makefile @@ -0,0 +1,2 @@ + +include validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/pub_sub/Commands b/dapr/quickstarts/pub_sub/Commands new file mode 100644 index 0000000..3220a7c --- /dev/null +++ b/dapr/quickstarts/pub_sub/Commands @@ -0,0 +1,63 @@ +The default pubsub.yaml($HOME/.dapr/components) file is used. And rabbit mq is used for pub sub implementation. Below are the contents of pubsub.yaml file. +The other way to include pubsub.yaml is to create a components directory and create a file called pubsub.yaml and add the below contents to the file. +And then modify the dapr run command to include components directory path using "--components-path". + +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: orderpubsub +spec: + type: pubsub.rabbitmq + version: v1 + metadata: + - name: host + value: "amqp://localhost:5672" + - name: durable + value: "false" + - name: deletedWhenUnused + value: "false" + - name: autoAck + value: "false" + - name: reconnectWait + value: "0" + - name: concurrency + value: parallel +scopes: + - orderprocessing + - checkout + + +Start a RabbitMQ message broker by entering the following command: +docker run -d -p 5672:5672 -p 15672:15672 --name dtc-rabbitmq rabbitmq:3-management-alpine + + + +Java: + +dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 --components-path ../../components mvn spring-boot:run + +dapr run --app-id checkout --app-port 6002 --dapr-http-port 3602 --dapr-grpc-port 60002 --components-path ../../components mvn spring-boot:run + +Python: + +dapr run --app-id orderprocessing --app-port 6001 --dapr-grpc-port 3601 --app-protocol grpc --components-path ../components python3 OrderProcessingService.py + +dapr run --app-id checkout --app-port 6002 --dapr-grpc-port 3602 --app-protocol grpc --components-path ../components python3 CheckoutService.py + +GO: + +dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 --components-path ../../components go run OrderProcessingService.go + +dapr run --app-id checkout --app-port 6002 --dapr-http-port 3602 --dapr-grpc-port 60002 --components-path ../../components go run CheckoutService.go + +CSharp: + +dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 --app-ssl --components-path ../../components dotnet run + +dapr run --app-id checkout --app-port 6002 --dapr-http-port 3602 --dapr-grpc-port 60002 --app-ssl --components-path ../../components dotnet run + +Javascript: + +dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 --components-path ../../components npm start + +dapr run --app-id checkout --app-port 6002 --dapr-http-port 3602 --dapr-grpc-port 60002 --components-path ../../components npm start diff --git a/dapr/quickstarts/pub_sub/components/observability.yaml b/dapr/quickstarts/pub_sub/components/observability.yaml new file mode 100644 index 0000000..4c75af5 --- /dev/null +++ b/dapr/quickstarts/pub_sub/components/observability.yaml @@ -0,0 +1,10 @@ +apiVersion: dapr.io/v1alpha1 +kind: Configuration +metadata: + name: daprConfig + namespace: default +spec: + tracing: + samplingRate: "1" + zipkin: + endpointAddress: "http://localhost:9411/api/v2/spans" diff --git a/dapr/quickstarts/pub_sub/components/pubsub.yaml b/dapr/quickstarts/pub_sub/components/pubsub.yaml new file mode 100644 index 0000000..01ca684 --- /dev/null +++ b/dapr/quickstarts/pub_sub/components/pubsub.yaml @@ -0,0 +1,12 @@ +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: orderpubsub +spec: + type: pubsub.redis + version: v1 + metadata: + - name: redisHost + value: localhost:6379 + - name: redisPassword + value: "" diff --git a/dapr/quickstarts/pub_sub/csharp/http/README.md b/dapr/quickstarts/pub_sub/csharp/http/README.md new file mode 100644 index 0000000..ec28034 --- /dev/null +++ b/dapr/quickstarts/pub_sub/csharp/http/README.md @@ -0,0 +1,93 @@ +# Dapr pub/sub (HTTP Client) + +In this quickstart, you'll create a publisher microservice and a subscriber microservice to demonstrate how Dapr enables a publish-subcribe pattern. The publisher will generate messages of a specific topic, while subscribers will listen for messages of specific topics. See [Why Pub-Sub](#why-pub-sub) to understand when this pattern might be a good choice for your software architecture. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/pubsub/) link for more information about Dapr and Pub-Sub. + +> **Note:** This example leverages HTTPClient only. If you are looking for the example using the Dapr Client SDK (recommended) [click here](../sdk). + +This quickstart includes one publisher: + +- Dotnet client message generator `checkout` + +And one subscriber: + +- Dotnet subscriber `order-processor` + +### Run Dotnet message subscriber with Dapr + +1. Navigate to the directory and install dependencies: + + + +```bash +cd ./order-processor +dotnet restore +dotnet build +``` + +2. Run the Dotnet subscriber app with Dapr: + + + + +```bash +dapr run --app-id order-processor-http --components-path ../../../components/ --app-port 7001 -- dotnet run --project . +``` + + +### Run Dotnet message publisher with Dapr + +1. Navigate to the directory and install dependencies: + + + +```bash +cd ./checkout +dotnet restore +dotnet build +``` + +2. Run the Dotnet publisher app with Dapr: + + + +```bash +dapr run --app-id checkout-http --components-path ../../../components/ -- dotnet run --project . +``` + + + +```bash +dapr stop --app-id order-processor-http +dapr stop --app-id checkout-http +``` diff --git a/dapr/quickstarts/pub_sub/csharp/http/checkout.sln b/dapr/quickstarts/pub_sub/csharp/http/checkout.sln new file mode 100644 index 0000000..703e373 --- /dev/null +++ b/dapr/quickstarts/pub_sub/csharp/http/checkout.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.32112.339 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "checkout", "checkout\checkout.csproj", "{636C3192-12CE-4884-AC45-B7C8A276075A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {636C3192-12CE-4884-AC45-B7C8A276075A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {636C3192-12CE-4884-AC45-B7C8A276075A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {636C3192-12CE-4884-AC45-B7C8A276075A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {636C3192-12CE-4884-AC45-B7C8A276075A}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {49215BFE-48D9-44C4-A8F6-374E24D59DED} + EndGlobalSection +EndGlobal diff --git a/dapr/quickstarts/pub_sub/csharp/http/checkout/Program.cs b/dapr/quickstarts/pub_sub/csharp/http/checkout/Program.cs new file mode 100644 index 0000000..d7e9b18 --- /dev/null +++ b/dapr/quickstarts/pub_sub/csharp/http/checkout/Program.cs @@ -0,0 +1,25 @@ +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; + +var baseURL = (Environment.GetEnvironmentVariable("BASE_URL") ?? "http://localhost") + ":" + (Environment.GetEnvironmentVariable("DAPR_HTTP_PORT") ?? "3500"); //reconfigure cpde to make requests to Dapr sidecar +const string PUBSUBNAME = "orderpubsub"; +const string TOPIC = "orders"; +Console.WriteLine($"Publishing to baseURL: {baseURL}, Pubsub Name: {PUBSUBNAME}, Topic: {TOPIC} "); + +var httpClient = new HttpClient(); +httpClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json")); + +for (int i = 1; i <= 10; i++) { + var order = new Order(i); + var orderJson = JsonSerializer.Serialize(order); + var content = new StringContent(orderJson, Encoding.UTF8, "application/json"); + + // Publish an event/message using Dapr PubSub via HTTP Post + var response = httpClient.PostAsync($"{baseURL}/v1.0/publish/{PUBSUBNAME}/{TOPIC}", content); + Console.WriteLine("Published data: " + order); + + await Task.Delay(TimeSpan.FromSeconds(1)); +} + +public record Order([property: JsonPropertyName("orderId")] int OrderId); diff --git a/dapr/quickstarts/pub_sub/csharp/http/checkout/checkout.csproj b/dapr/quickstarts/pub_sub/csharp/http/checkout/checkout.csproj new file mode 100644 index 0000000..3897747 --- /dev/null +++ b/dapr/quickstarts/pub_sub/csharp/http/checkout/checkout.csproj @@ -0,0 +1,10 @@ + + + + Exe + net6.0 + enable + enable + + + diff --git a/dapr/quickstarts/pub_sub/csharp/http/makefile b/dapr/quickstarts/pub_sub/csharp/http/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/pub_sub/csharp/http/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/pub_sub/csharp/http/order-processor/Program.cs b/dapr/quickstarts/pub_sub/csharp/http/order-processor/Program.cs new file mode 100644 index 0000000..3fe5d88 --- /dev/null +++ b/dapr/quickstarts/pub_sub/csharp/http/order-processor/Program.cs @@ -0,0 +1,29 @@ +using System.Text.Json.Serialization; + +var builder = WebApplication.CreateBuilder(args); + +var app = builder.Build(); + +if (app.Environment.IsDevelopment()) {app.UseDeveloperExceptionPage();} + +// Register Dapr pub/sub subscriptions +app.MapGet("/dapr/subscribe", () => { + var sub = new DaprSubscription(PubsubName: "orderpubsub", Topic: "orders", Route: "orders"); + Console.WriteLine("Dapr pub/sub is subscribed to: " + sub); + return Results.Json(new DaprSubscription[]{sub}); +}); + +// Dapr subscription in /dapr/subscribe sets up this route +app.MapPost("/orders", (DaprData requestData) => { + Console.WriteLine("Subscriber received : " + requestData.Data.OrderId); + return Results.Ok(requestData.Data); +}); + +await app.RunAsync(); + +public record DaprData ([property: JsonPropertyName("data")] T Data); +public record Order([property: JsonPropertyName("orderId")] int OrderId); +public record DaprSubscription( + [property: JsonPropertyName("pubsubname")] string PubsubName, + [property: JsonPropertyName("topic")] string Topic, + [property: JsonPropertyName("route")] string Route); diff --git a/dapr/quickstarts/pub_sub/csharp/http/order-processor/Properties/launchSettings.json b/dapr/quickstarts/pub_sub/csharp/http/order-processor/Properties/launchSettings.json new file mode 100644 index 0000000..673fe7e --- /dev/null +++ b/dapr/quickstarts/pub_sub/csharp/http/order-processor/Properties/launchSettings.json @@ -0,0 +1,15 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "CheckoutService": { + "commandName": "Project", + "dotnetRunMessages": "true", + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:7001", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/dapr/quickstarts/pub_sub/csharp/http/order-processor/appsettings.Development.json b/dapr/quickstarts/pub_sub/csharp/http/order-processor/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/dapr/quickstarts/pub_sub/csharp/http/order-processor/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/dapr/quickstarts/pub_sub/csharp/http/order-processor/appsettings.json b/dapr/quickstarts/pub_sub/csharp/http/order-processor/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/dapr/quickstarts/pub_sub/csharp/http/order-processor/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/dapr/quickstarts/pub_sub/csharp/http/order-processor/order-processor.csproj b/dapr/quickstarts/pub_sub/csharp/http/order-processor/order-processor.csproj new file mode 100644 index 0000000..6f159fe --- /dev/null +++ b/dapr/quickstarts/pub_sub/csharp/http/order-processor/order-processor.csproj @@ -0,0 +1,14 @@ + + + + net6.0 + enable + enable + Linux + + + + + + + diff --git a/dapr/quickstarts/pub_sub/csharp/sdk/README.md b/dapr/quickstarts/pub_sub/csharp/sdk/README.md new file mode 100644 index 0000000..12f5699 --- /dev/null +++ b/dapr/quickstarts/pub_sub/csharp/sdk/README.md @@ -0,0 +1,93 @@ +# Dapr pub/sub + +In this quickstart, you'll create a publisher microservice and a subscriber microservice to demonstrate how Dapr enables a publish-subcribe pattern. The publisher will generate messages of a specific topic, while subscribers will listen for messages of specific topics. See [Why Pub-Sub](https://docs.dapr.io/developing-applications/building-blocks/pubsub/pubsub-overview/) to understand when this pattern might be a good choice for your software architecture. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/pubsub/) link for more information about Dapr and Pub-Sub. + +> **Note:** This example leverages the Dapr client SDK. If you are looking for the example using only HTTP [click here](../http). + +This quickstart includes one publisher: + +- Dotnet client message generator `checkout` + +And one subscriber: + +- Dotnet subscriber `order-processor` + +### Run Dotnet message subscriber with Dapr + +1. Navigate to the directory and install dependencies: + + + +```bash +cd ./order-processor +dotnet restore +dotnet build +``` + +2. Run the Dotnet subscriber app with Dapr: + + + + +```bash +dapr run --app-id order-processor --components-path ../../../components/ --app-port 7002 -- dotnet run --project . +``` + + +### Run Dotnet message publisher with Dapr + +1. Navigate to the directory and install dependencies: + + + +```bash +cd ./checkout +dotnet restore +dotnet build +``` + +2. Run the Dotnet publisher app with Dapr: + + + +```bash +dapr run --app-id checkout-sdk --components-path ../../../components/ -- dotnet run --project . +``` + + + +```bash +dapr stop --app-id order-processor +dapr stop --app-id checkout-sdk +``` diff --git a/dapr/quickstarts/pub_sub/csharp/sdk/checkout/Program.cs b/dapr/quickstarts/pub_sub/csharp/sdk/checkout/Program.cs new file mode 100644 index 0000000..402ae5e --- /dev/null +++ b/dapr/quickstarts/pub_sub/csharp/sdk/checkout/Program.cs @@ -0,0 +1,17 @@ +using System; +using Dapr.Client; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +for (int i = 1; i <= 10; i++) { + var order = new Order(i); + using var client = new DaprClientBuilder().Build(); + + // Publish an event/message using Dapr PubSub + await client.PublishEventAsync("orderpubsub", "orders", order); + Console.WriteLine("Published data: " + order); + + await Task.Delay(TimeSpan.FromSeconds(1)); +} + +public record Order([property: JsonPropertyName("orderId")] int OrderId); diff --git a/dapr/quickstarts/pub_sub/csharp/sdk/checkout/checkout.csproj b/dapr/quickstarts/pub_sub/csharp/sdk/checkout/checkout.csproj new file mode 100644 index 0000000..b21a5af --- /dev/null +++ b/dapr/quickstarts/pub_sub/csharp/sdk/checkout/checkout.csproj @@ -0,0 +1,12 @@ + + + + Exe + net6.0 + + + + + + + diff --git a/dapr/quickstarts/pub_sub/csharp/sdk/makefile b/dapr/quickstarts/pub_sub/csharp/sdk/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/pub_sub/csharp/sdk/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/pub_sub/csharp/sdk/order-processor/Program.cs b/dapr/quickstarts/pub_sub/csharp/sdk/order-processor/Program.cs new file mode 100644 index 0000000..069871e --- /dev/null +++ b/dapr/quickstarts/pub_sub/csharp/sdk/order-processor/Program.cs @@ -0,0 +1,24 @@ +using System.Text.Json.Serialization; +using Dapr; + +var builder = WebApplication.CreateBuilder(args); + +var app = builder.Build(); + +// Dapr will send serialized event object vs. being raw CloudEvent +app.UseCloudEvents(); + +// needed for Dapr pub/sub routing +app.MapSubscribeHandler(); + +if (app.Environment.IsDevelopment()) {app.UseDeveloperExceptionPage();} + +// Dapr subscription in [Topic] routes orders topic to this route +app.MapPost("/orders", [Topic("orderpubsub", "orders")] (Order order) => { + Console.WriteLine("Subscriber received : " + order); + return Results.Ok(order); +}); + +await app.RunAsync(); + +public record Order([property: JsonPropertyName("orderId")] int OrderId); diff --git a/dapr/quickstarts/pub_sub/csharp/sdk/order-processor/Properties/launchSettings.json b/dapr/quickstarts/pub_sub/csharp/sdk/order-processor/Properties/launchSettings.json new file mode 100644 index 0000000..42d0d41 --- /dev/null +++ b/dapr/quickstarts/pub_sub/csharp/sdk/order-processor/Properties/launchSettings.json @@ -0,0 +1,15 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "CheckoutService": { + "commandName": "Project", + "dotnetRunMessages": "true", + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:7002", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/dapr/quickstarts/pub_sub/csharp/sdk/order-processor/appsettings.Development.json b/dapr/quickstarts/pub_sub/csharp/sdk/order-processor/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/dapr/quickstarts/pub_sub/csharp/sdk/order-processor/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/dapr/quickstarts/pub_sub/csharp/sdk/order-processor/appsettings.json b/dapr/quickstarts/pub_sub/csharp/sdk/order-processor/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/dapr/quickstarts/pub_sub/csharp/sdk/order-processor/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/dapr/quickstarts/pub_sub/csharp/sdk/order-processor/order-processor.csproj b/dapr/quickstarts/pub_sub/csharp/sdk/order-processor/order-processor.csproj new file mode 100644 index 0000000..31e7d92 --- /dev/null +++ b/dapr/quickstarts/pub_sub/csharp/sdk/order-processor/order-processor.csproj @@ -0,0 +1,15 @@ + + + + net6.0 + enable + enable + Linux + + + + + + + + diff --git a/dapr/quickstarts/pub_sub/go/http/README.md b/dapr/quickstarts/pub_sub/go/http/README.md new file mode 100644 index 0000000..7c11d4a --- /dev/null +++ b/dapr/quickstarts/pub_sub/go/http/README.md @@ -0,0 +1,89 @@ +# Dapr pub/sub + +In this quickstart, you'll create a publisher microservice and a subscriber microservice to demonstrate how Dapr enables a publish-subcribe API. The publisher generates messages for a specific topic, while subscribers listen for messages on specific topics. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/pubsub/) link for more information about Dapr and Pub-Sub. + +> **Note:** This example leverages HTTP `requests` only. If you are looking for the example using the Dapr Client SDK (recommended) [click here](../sdk/). + +This quickstart includes one publisher: + +- Go client message generator `checkout` + +And one subscriber: + +- Go subscriber `order-processor` + +### Run Go message subscriber with Dapr + +1. Navigate to the directory and install dependencies: + + + +```bash +cd ./order-processor +go build . +``` + + +2. Run the Go subscriber app with Dapr: + + + + +```bash +cd ./order-processor +dapr run --app-port 6001 --app-id order-processor --app-protocol http --dapr-http-port 3501 --components-path ../../../components -- go run . +``` + + + +### Run Go message publisher with Dapr + +1. Navigate to the directory and install dependencies: + + + +```bash +cd ./checkout +go build . +``` + +2. Run the Go publisher app with Dapr: + + + +```bash +cd ./checkout +dapr run --app-id checkout-http --app-protocol http --dapr-http-port 3500 --components-path ../../../components -- go run . +``` + + + +```bash +dapr stop --app-id checkout-http +dapr stop --app-id order-processor +``` diff --git a/dapr/quickstarts/pub_sub/go/http/checkout/app.go b/dapr/quickstarts/pub_sub/go/http/checkout/app.go new file mode 100644 index 0000000..d710191 --- /dev/null +++ b/dapr/quickstarts/pub_sub/go/http/checkout/app.go @@ -0,0 +1,43 @@ +package main + +import ( + "fmt" + "log" + "net/http" + "os" + "strconv" + "strings" + "time" +) + +const PUBSUB_NAME = "orderpubsub" +const PUBSUB_TOPIC = "orders" + +func main() { + daprHost := "http://localhost" + if value, ok := os.LookupEnv("DAPR_HOST"); ok { + daprHost = value + } + daprHttpPost := "3500" + if value, ok := os.LookupEnv("DAPR_HTTP_PORT"); ok { + daprHttpPost = value + } + for i := 1; i <= 10; i++ { + order := `{"orderId":` + strconv.Itoa(i) + `}` + client := http.Client{} + req, err := http.NewRequest("POST", daprHost+":"+daprHttpPost+"/v1.0/publish/"+PUBSUB_NAME+"/"+PUBSUB_TOPIC, strings.NewReader(order)) + if err != nil { + log.Fatal(err.Error()) + os.Exit(1) + } + + // Publish an event using Dapr pub/sub + if _, err = client.Do(req); err != nil { + log.Fatal(err) + } + + fmt.Println("Published data: ", order) + + time.Sleep(1000) + } +} diff --git a/dapr/quickstarts/pub_sub/go/http/checkout/dapr_example b/dapr/quickstarts/pub_sub/go/http/checkout/dapr_example new file mode 100755 index 0000000..f7eb333 Binary files /dev/null and b/dapr/quickstarts/pub_sub/go/http/checkout/dapr_example differ diff --git a/dapr/quickstarts/pub_sub/go/http/checkout/go.mod b/dapr/quickstarts/pub_sub/go/http/checkout/go.mod new file mode 100644 index 0000000..a81a53c --- /dev/null +++ b/dapr/quickstarts/pub_sub/go/http/checkout/go.mod @@ -0,0 +1,3 @@ +module dapr_example + +go 1.18 diff --git a/dapr/quickstarts/pub_sub/go/http/checkout/go.sum b/dapr/quickstarts/pub_sub/go/http/checkout/go.sum new file mode 100644 index 0000000..e69de29 diff --git a/dapr/quickstarts/pub_sub/go/http/makefile b/dapr/quickstarts/pub_sub/go/http/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/pub_sub/go/http/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/pub_sub/go/http/order-processor/app.go b/dapr/quickstarts/pub_sub/go/http/order-processor/app.go new file mode 100644 index 0000000..5e2415f --- /dev/null +++ b/dapr/quickstarts/pub_sub/go/http/order-processor/app.go @@ -0,0 +1,79 @@ +package main + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "net/http" + "os" + + "github.com/gorilla/mux" +) + +type JSONObj struct { + PubsubName string `json:"pubsubName"` + Topic string `json:"topic"` + Route string `json:"route"` +} + +type Result struct { + Data string `json:"data"` +} + +func getOrder(w http.ResponseWriter, r *http.Request) { + jsonData := []JSONObj{ + { + PubsubName: "orderpubsub", + Topic: "orders", + Route: "orders", + }, + } + jsonBytes, err := json.Marshal(jsonData) + if err != nil { + log.Fatal("Error in reading the result obj") + } + _, err = w.Write(jsonBytes) + if err != nil { + log.Fatal("Error in writing the result obj") + } +} + +func postOrder(w http.ResponseWriter, r *http.Request) { + data, err := ioutil.ReadAll(r.Body) + if err != nil { + log.Fatal(err) + } + var result Result + err = json.Unmarshal(data, &result) + if err != nil { + log.Fatal(err) + } + fmt.Println("Subscriber received: ", string(result.Data)) + obj, err := json.Marshal(data) + if err != nil { + log.Fatal("Error in reading the result obj") + } + _, err = w.Write(obj) + if err != nil { + log.Fatal("Error in writing the result obj") + } +} + +func main() { + appPort := "6001" + if value, ok := os.LookupEnv("APP_PORT"); ok { + appPort = value + } + + r := mux.NewRouter() + + r.HandleFunc("/dapr/subscribe", getOrder).Methods("GET") + + // Dapr subscription routes orders topic to this route + r.HandleFunc("/orders", postOrder).Methods("POST") + + if err := http.ListenAndServe(":"+appPort, r); err != nil { + log.Panic(err) + } +} diff --git a/dapr/quickstarts/pub_sub/go/http/order-processor/dapr_example b/dapr/quickstarts/pub_sub/go/http/order-processor/dapr_example new file mode 100755 index 0000000..10b7f10 Binary files /dev/null and b/dapr/quickstarts/pub_sub/go/http/order-processor/dapr_example differ diff --git a/dapr/quickstarts/pub_sub/go/http/order-processor/go.mod b/dapr/quickstarts/pub_sub/go/http/order-processor/go.mod new file mode 100644 index 0000000..66749be --- /dev/null +++ b/dapr/quickstarts/pub_sub/go/http/order-processor/go.mod @@ -0,0 +1,5 @@ +module dapr_example + +go 1.18 + +require github.com/gorilla/mux v1.8.0 diff --git a/dapr/quickstarts/pub_sub/go/http/order-processor/go.sum b/dapr/quickstarts/pub_sub/go/http/order-processor/go.sum new file mode 100644 index 0000000..5350288 --- /dev/null +++ b/dapr/quickstarts/pub_sub/go/http/order-processor/go.sum @@ -0,0 +1,2 @@ +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= diff --git a/dapr/quickstarts/pub_sub/go/sdk/README.md b/dapr/quickstarts/pub_sub/go/sdk/README.md new file mode 100644 index 0000000..6f2f361 --- /dev/null +++ b/dapr/quickstarts/pub_sub/go/sdk/README.md @@ -0,0 +1,88 @@ +# Dapr pub/sub + +In this quickstart, you'll create a publisher microservice and a subscriber microservice to demonstrate how Dapr enables a publish-subcribe pattern. The publisher generates messages of a specific topic, while subscribers listen for messages of specific topics. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/pubsub/) link for more information about Dapr and Pub-Sub. + +> **Note:** This example leverages the Dapr client SDK. If you are looking for the example using only HTTP `requests` [click here](../http). + +This quickstart includes one publisher: + +- Go client message generator `checkout` + +And one subscriber: + +- Go subscriber `order-processor` + +### Run Go message subscriber with Dapr + +1. Navigate to the directory and install dependencies: + + + +```bash +cd ./order-processor +go build . +``` + + +2. Run the Go subscriber app with Dapr: + + + +```bash +cd ./order-processor +dapr run --app-port 6002 --app-id order-processor-sdk --app-protocol http --dapr-http-port 3501 --components-path ../../../components -- go run . +``` + + + +### Run Go message publisher with Dapr + +1. Navigate to the directory and install dependencies: + + + +```bash +cd ./checkout +go build . +``` + +2. Run the Go publisher app with Dapr: + + + +```bash +cd ./checkout +dapr run --app-id checkout-sdk --app-protocol http --dapr-http-port 3500 --components-path ../../../components -- go run . +``` + + + +```bash +dapr stop --app-id checkout-sdk +dapr stop --app-id order-processor-sdk +``` diff --git a/dapr/quickstarts/pub_sub/go/sdk/checkout/app.go b/dapr/quickstarts/pub_sub/go/sdk/checkout/app.go new file mode 100644 index 0000000..0fdd819 --- /dev/null +++ b/dapr/quickstarts/pub_sub/go/sdk/checkout/app.go @@ -0,0 +1,36 @@ +package main + +import ( + "context" + "fmt" + "strconv" + "time" + + dapr "github.com/dapr/go-sdk/client" +) + +var ( + PUBSUB_NAME = "orderpubsub" + PUBSUB_TOPIC = "orders" +) + +func main() { + client, err := dapr.NewClient() + if err != nil { + panic(err) + } + defer client.Close() + ctx := context.Background() + for i := 1; i <= 10; i++ { + order := `{"orderId":` + strconv.Itoa(i) + `}` + + // Publish an event using Dapr pub/sub + if err := client.PublishEvent(ctx, PUBSUB_NAME, PUBSUB_TOPIC, []byte(order)); err != nil { + panic(err) + } + + fmt.Println("Published data: ", order) + + time.Sleep(1000) + } +} diff --git a/dapr/quickstarts/pub_sub/go/sdk/checkout/dapr_example b/dapr/quickstarts/pub_sub/go/sdk/checkout/dapr_example new file mode 100755 index 0000000..fc8bbe5 Binary files /dev/null and b/dapr/quickstarts/pub_sub/go/sdk/checkout/dapr_example differ diff --git a/dapr/quickstarts/pub_sub/go/sdk/checkout/go.mod b/dapr/quickstarts/pub_sub/go/sdk/checkout/go.mod new file mode 100644 index 0000000..951a92e --- /dev/null +++ b/dapr/quickstarts/pub_sub/go/sdk/checkout/go.mod @@ -0,0 +1,18 @@ +module dapr_example + +go 1.18 + +require github.com/dapr/go-sdk v1.5.0 + +require ( + github.com/dapr/dapr v1.8.0 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/pkg/errors v0.9.1 // indirect + golang.org/x/net v0.0.0-20220621193019-9d032be2e588 // indirect + golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect + golang.org/x/text v0.3.7 // indirect + google.golang.org/genproto v0.0.0-20220622171453-ea41d75dfa0f // indirect + google.golang.org/grpc v1.47.0 // indirect + google.golang.org/protobuf v1.28.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/dapr/quickstarts/pub_sub/go/sdk/checkout/go.sum b/dapr/quickstarts/pub_sub/go/sdk/checkout/go.sum new file mode 100644 index 0000000..7e7b155 --- /dev/null +++ b/dapr/quickstarts/pub_sub/go/sdk/checkout/go.sum @@ -0,0 +1,151 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/dapr/dapr v1.8.0 h1:ZAAoBe6wuFp7k4tIHB7ajZXVTtGeDeVqIPrldzo3dF0= +github.com/dapr/dapr v1.8.0/go.mod h1:yAsDiK5oecG0htw2S8JG9RFaeHJVdlTfZyOrL57AvRM= +github.com/dapr/go-sdk v1.5.0 h1:OVkrupquJEOL1qRtwKcMVrFKYhw4UJQvgOJNduo2VxE= +github.com/dapr/go-sdk v1.5.0/go.mod h1:Cvz3taCVu22WCNEUbc9/szvG/yJxWPAV4dcaG+zDWA4= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.4 h1:wZRexSlwd7ZXfKINDLsO4r7WBt3gTKONc6K/VesHvHM= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20220621193019-9d032be2e588 h1:9ubFuySsnAJYGyJrZ3koiEv8FyqofCBdz3G9Mbf2YFc= +golang.org/x/net v0.0.0-20220621193019-9d032be2e588/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20220622171453-ea41d75dfa0f h1:kYlCnpX4eB0QEnXm12j4DAX4yrjjhJmsyuWtSSZ+Buo= +google.golang.org/genproto v0.0.0-20220622171453-ea41d75dfa0f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8= +google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/dapr/quickstarts/pub_sub/go/sdk/makefile b/dapr/quickstarts/pub_sub/go/sdk/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/pub_sub/go/sdk/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/pub_sub/go/sdk/order-processor/app.go b/dapr/quickstarts/pub_sub/go/sdk/order-processor/app.go new file mode 100644 index 0000000..383008b --- /dev/null +++ b/dapr/quickstarts/pub_sub/go/sdk/order-processor/app.go @@ -0,0 +1,38 @@ +package main + +import ( + "context" + "fmt" + "log" + "net/http" + "os" + + "github.com/dapr/go-sdk/service/common" + daprd "github.com/dapr/go-sdk/service/http" +) + +var sub = &common.Subscription{ + PubsubName: "orderpubsub", + Topic: "orders", + Route: "/orders", +} + +func main() { + appPort := "6002" + if value, ok := os.LookupEnv("APP_PORT"); ok { + appPort = value + } + + s := daprd.NewService(":" + appPort) + if err := s.AddTopicEventHandler(sub, eventHandler); err != nil { + log.Fatalf("error adding topic subscription: %v", err) + } + if err := s.Start(); err != nil && err != http.ErrServerClosed { + log.Fatalf("error listenning: %v", err) + } +} + +func eventHandler(ctx context.Context, e *common.TopicEvent) (retry bool, err error) { + fmt.Println("Subscriber received: ", e.Data) + return false, nil +} diff --git a/dapr/quickstarts/pub_sub/go/sdk/order-processor/dapr_example b/dapr/quickstarts/pub_sub/go/sdk/order-processor/dapr_example new file mode 100755 index 0000000..edb184b Binary files /dev/null and b/dapr/quickstarts/pub_sub/go/sdk/order-processor/dapr_example differ diff --git a/dapr/quickstarts/pub_sub/go/sdk/order-processor/go.mod b/dapr/quickstarts/pub_sub/go/sdk/order-processor/go.mod new file mode 100644 index 0000000..f6c67a7 --- /dev/null +++ b/dapr/quickstarts/pub_sub/go/sdk/order-processor/go.mod @@ -0,0 +1,19 @@ +module dapr_example + +go 1.18 + +require github.com/dapr/go-sdk v1.5.0 + +require ( + github.com/dapr/dapr v1.8.0 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/gorilla/mux v1.8.0 // indirect + github.com/pkg/errors v0.9.1 // indirect + golang.org/x/net v0.0.0-20220621193019-9d032be2e588 // indirect + golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect + golang.org/x/text v0.3.7 // indirect + google.golang.org/genproto v0.0.0-20220622171453-ea41d75dfa0f // indirect + google.golang.org/grpc v1.47.0 // indirect + google.golang.org/protobuf v1.28.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/dapr/quickstarts/pub_sub/go/sdk/order-processor/go.sum b/dapr/quickstarts/pub_sub/go/sdk/order-processor/go.sum new file mode 100644 index 0000000..5903a69 --- /dev/null +++ b/dapr/quickstarts/pub_sub/go/sdk/order-processor/go.sum @@ -0,0 +1,154 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/dapr/dapr v1.8.0 h1:ZAAoBe6wuFp7k4tIHB7ajZXVTtGeDeVqIPrldzo3dF0= +github.com/dapr/dapr v1.8.0/go.mod h1:yAsDiK5oecG0htw2S8JG9RFaeHJVdlTfZyOrL57AvRM= +github.com/dapr/go-sdk v1.5.0 h1:OVkrupquJEOL1qRtwKcMVrFKYhw4UJQvgOJNduo2VxE= +github.com/dapr/go-sdk v1.5.0/go.mod h1:Cvz3taCVu22WCNEUbc9/szvG/yJxWPAV4dcaG+zDWA4= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.4 h1:wZRexSlwd7ZXfKINDLsO4r7WBt3gTKONc6K/VesHvHM= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20220621193019-9d032be2e588 h1:9ubFuySsnAJYGyJrZ3koiEv8FyqofCBdz3G9Mbf2YFc= +golang.org/x/net v0.0.0-20220621193019-9d032be2e588/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20220622171453-ea41d75dfa0f h1:kYlCnpX4eB0QEnXm12j4DAX4yrjjhJmsyuWtSSZ+Buo= +google.golang.org/genproto v0.0.0-20220622171453-ea41d75dfa0f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8= +google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/dapr/quickstarts/pub_sub/java/http/README.md b/dapr/quickstarts/pub_sub/java/http/README.md new file mode 100644 index 0000000..00ff1c6 --- /dev/null +++ b/dapr/quickstarts/pub_sub/java/http/README.md @@ -0,0 +1,93 @@ +# Dapr pub/sub + +In this quickstart, there is a publisher microservice `checkout` and a subscriber microservice `order-processor` to demonstrate how Dapr enables a publish-subscribe pattern. `checkout` generates messages and publishes to a specific orders topic, and `order-processor` subscribers listen for messages of topic orders. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/pubsub/) link for more information about Dapr and Pub-Sub. + +> **Note:** This example leverages HTTPClient only. If you are looking for the example using the Dapr Client SDK (recommended) [click here](../sdk). + +This quickstart includes one publisher: + +- Java client message generator `checkout` + +And one subscriber: + +- Java subscriber `order-processor` + +## Pre-requisites + +* [Dapr and Dapr Cli](https://docs.dapr.io/getting-started/install-dapr-cli/). +* Java JDK 11 (or greater): + * [Microsoft JDK 11](https://docs.microsoft.com/en-us/java/openjdk/download#openjdk-11) + * [Oracle JDK 11](https://www.oracle.com/technetwork/java/javase/downloads/index.html#JDK11) + * [OpenJDK 11](https://jdk.java.net/11/) +* [Apache Maven](https://maven.apache.org/install.html) version 3.x. + +### Run Java message subscriber app with Dapr + +1. Navigate to directory and install dependencies: + + +```bash +cd ./order-processor +mvn clean install +``` + + +2. Run the Java subscriber app with Dapr: + +```bash +cd ./order-processor + dapr run --app-port 8080 --app-id order-processor-http --components-path ../../../components -- java -jar target/OrderProcessingService-0.0.1-SNAPSHOT.jar +``` + +### Run Java message publisher app with Dapr + +1. Navigate to the directory and install dependencies: + + + +```bash +cd ./checkout +mvn clean install +``` + + +2. Run the Java publisher app with Dapr: + + +```bash +cd ./checkout + dapr run --app-id checkout-http --components-path ../../../components -- java -jar target/CheckoutService-0.0.1-SNAPSHOT.jar +``` + + +```bash +dapr stop --app-id checkout-http +dapr stop --app-id order-processor-http +``` diff --git a/dapr/quickstarts/pub_sub/java/http/checkout/pom.xml b/dapr/quickstarts/pub_sub/java/http/checkout/pom.xml new file mode 100644 index 0000000..075b225 --- /dev/null +++ b/dapr/quickstarts/pub_sub/java/http/checkout/pom.xml @@ -0,0 +1,55 @@ + + + 4.0.0 + + com.service + CheckoutService + CheckoutService + 0.0.1-SNAPSHOT + Demo for Dapr pubsub component + + 11 + 11 + 1.6.1 + + + + org.json + json + 20211205 + + + org.slf4j + slf4j-api + ${slf4jVersion} + + + org.slf4j + slf4j-simple + ${slf4jVersion} + + + + + + org.springframework.boot + spring-boot-maven-plugin + 2.6.4 + + + + repackage + + + + com.service.CheckoutServiceApplication + + + + + + + + + diff --git a/dapr/quickstarts/pub_sub/java/http/checkout/src/main/java/com/service/CheckoutServiceApplication.java b/dapr/quickstarts/pub_sub/java/http/checkout/src/main/java/com/service/CheckoutServiceApplication.java new file mode 100644 index 0000000..73008ca --- /dev/null +++ b/dapr/quickstarts/pub_sub/java/http/checkout/src/main/java/com/service/CheckoutServiceApplication.java @@ -0,0 +1,46 @@ +package com.service; + +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.time.Duration; +import java.util.concurrent.TimeUnit; + +public class CheckoutServiceApplication { + private static final Logger logger = LoggerFactory.getLogger(CheckoutServiceApplication.class); + private static final HttpClient httpClient = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_2) + .connectTimeout(Duration.ofSeconds(10)) + .build(); + + private static final String PUBSUB_NAME = "orderpubsub"; + private static final String TOPIC = "orders"; + private static String DAPR_HOST = System.getenv().getOrDefault("DAPR_HOST", "http://localhost"); + private static String DAPR_HTTP_PORT = System.getenv().getOrDefault("DAPR_HTTP_PORT", "3500"); + + public static void main(String[] args) throws InterruptedException, IOException { + String uri = DAPR_HOST + ":" + DAPR_HTTP_PORT + "/v1.0/publish/" + PUBSUB_NAME + "/" + TOPIC; + for (int i = 0; i <= 10; i++) { + int orderId = i; + JSONObject obj = new JSONObject(); + obj.put("orderId", orderId); + + // Publish an event/message using Dapr PubSub via HTTP Post + HttpRequest request = HttpRequest.newBuilder() + .POST(HttpRequest.BodyPublishers.ofString(obj.toString())) + .uri(URI.create(uri)) + .header("Content-Type", "application/json") + .build(); + + HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + logger.info("Published data: {}", orderId); + TimeUnit.MILLISECONDS.sleep(3000); + } + } +} diff --git a/dapr/quickstarts/pub_sub/java/http/makefile b/dapr/quickstarts/pub_sub/java/http/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/pub_sub/java/http/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/pub_sub/java/http/order-processor/pom.xml b/dapr/quickstarts/pub_sub/java/http/order-processor/pom.xml new file mode 100644 index 0000000..5a2081c --- /dev/null +++ b/dapr/quickstarts/pub_sub/java/http/order-processor/pom.xml @@ -0,0 +1,41 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.6.3 + + + com.service + OrderProcessingService + 0.0.1-SNAPSHOT + OrderProcessingService + Demo for Dapr pubsub component + + 11 + + + + org.springframework.boot + spring-boot-starter-web + + + org.projectlombok + lombok + 1.18.22 + true + + + + + + org.springframework.boot + spring-boot-maven-plugin + 2.6.4 + + + + + diff --git a/dapr/quickstarts/pub_sub/java/http/order-processor/src/main/java/com/service/OrderProcessingServiceApplication.java b/dapr/quickstarts/pub_sub/java/http/order-processor/src/main/java/com/service/OrderProcessingServiceApplication.java new file mode 100644 index 0000000..733f414 --- /dev/null +++ b/dapr/quickstarts/pub_sub/java/http/order-processor/src/main/java/com/service/OrderProcessingServiceApplication.java @@ -0,0 +1,11 @@ +package com.service; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class OrderProcessingServiceApplication { + public static void main(String[] args) { + SpringApplication.run(OrderProcessingServiceApplication.class, args); + } +} diff --git a/dapr/quickstarts/pub_sub/java/http/order-processor/src/main/java/com/service/controller/OrderProcessingServiceController.java b/dapr/quickstarts/pub_sub/java/http/order-processor/src/main/java/com/service/controller/OrderProcessingServiceController.java new file mode 100644 index 0000000..3ea5d08 --- /dev/null +++ b/dapr/quickstarts/pub_sub/java/http/order-processor/src/main/java/com/service/controller/OrderProcessingServiceController.java @@ -0,0 +1,47 @@ +package com.service.controller; + +import com.service.model.DaprSubscription; +import com.service.model.Order; +import com.service.model.SubscriptionData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class OrderProcessingServiceController { + private static final Logger logger = LoggerFactory.getLogger(OrderProcessingServiceController.class); + + /** + * Register Dapr pub/sub subscriptions. + * + * @return DaprSubscription Object containing pubsub name, topic and route for subscription. + */ + @GetMapping(path = "/dapr/subscribe", produces = MediaType.APPLICATION_JSON_VALUE) + public DaprSubscription[] getSubscription() { + DaprSubscription daprSubscription = DaprSubscription.builder() + .pubSubName("orderpubsub") + .topic("orders") + .route("orders") + .build(); + logger.info("Subscribed to Pubsubname {} and topic {}", "orderpubsub", "orders"); + DaprSubscription[] arr = new DaprSubscription[]{daprSubscription}; + return arr; + } + + /** + * Dapr subscription in /dapr/subscribe sets up this route. + * + * @param body Request body + * @return ResponseEntity Returns ResponseEntity.ok() + */ + @PostMapping(path = "/orders", consumes = MediaType.ALL_VALUE) + public ResponseEntity processOrders(@RequestBody SubscriptionData body) { + logger.info("Subscriber received: "+ body.getData().getOrderId()); + return ResponseEntity.ok().build(); + } +} diff --git a/dapr/quickstarts/pub_sub/java/http/order-processor/src/main/java/com/service/model/DaprSubscription.java b/dapr/quickstarts/pub_sub/java/http/order-processor/src/main/java/com/service/model/DaprSubscription.java new file mode 100644 index 0000000..d4a887e --- /dev/null +++ b/dapr/quickstarts/pub_sub/java/http/order-processor/src/main/java/com/service/model/DaprSubscription.java @@ -0,0 +1,12 @@ +package com.service.model; + +import lombok.Builder; +import lombok.Getter; + +@Builder +@Getter +public class DaprSubscription { + private String pubSubName; + private String topic; + private String route; +} diff --git a/dapr/quickstarts/pub_sub/java/http/order-processor/src/main/java/com/service/model/Order.java b/dapr/quickstarts/pub_sub/java/http/order-processor/src/main/java/com/service/model/Order.java new file mode 100644 index 0000000..47437b7 --- /dev/null +++ b/dapr/quickstarts/pub_sub/java/http/order-processor/src/main/java/com/service/model/Order.java @@ -0,0 +1,13 @@ +package com.service.model; + +import lombok.Getter; +import lombok.Setter; + +/** + * A class to represent the data object that is being passed to subscribe endpoint. + */ +@Getter +@Setter +public class Order { + private int orderId; +} diff --git a/dapr/quickstarts/pub_sub/java/http/order-processor/src/main/java/com/service/model/SubscriptionData.java b/dapr/quickstarts/pub_sub/java/http/order-processor/src/main/java/com/service/model/SubscriptionData.java new file mode 100644 index 0000000..eb14939 --- /dev/null +++ b/dapr/quickstarts/pub_sub/java/http/order-processor/src/main/java/com/service/model/SubscriptionData.java @@ -0,0 +1,15 @@ +package com.service.model; + +import lombok.Getter; +import lombok.Setter; + +/** + * Helps in unpacking a cloud event which Checkout application generates. + * + * @param Type of data being passed to subscriber end point(orderProcessor app) + */ +@Getter +@Setter +public class SubscriptionData { + private T data; +} diff --git a/dapr/quickstarts/pub_sub/java/http/order-processor/src/main/resources/application.properties b/dapr/quickstarts/pub_sub/java/http/order-processor/src/main/resources/application.properties new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/dapr/quickstarts/pub_sub/java/http/order-processor/src/main/resources/application.properties @@ -0,0 +1 @@ + diff --git a/dapr/quickstarts/pub_sub/java/sdk/README.md b/dapr/quickstarts/pub_sub/java/sdk/README.md new file mode 100644 index 0000000..b90266d --- /dev/null +++ b/dapr/quickstarts/pub_sub/java/sdk/README.md @@ -0,0 +1,94 @@ +# Dapr pub/sub + + In this quickstart, there is a publisher microservice `checkout` and a subscriber microservice `order-processor` to demonstrate how Dapr enables a publish-subscribe pattern. `checkout` generates messages and publishes to a specific orders topic, and `order-processor` subscribers listen for messages of topic orders. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/pubsub/) link for more information about Dapr and Pub-Sub. + +> **Note:** This example leverages the Dapr client SDK. If you are looking for the example using only HTTP [click here](../http). + +This quickstart includes one publisher: + +- Java client message generator `checkout` + +And one subscriber: + +- Java subscriber `order-processor` + +## Pre-requisites + +* [Dapr and Dapr Cli](https://docs.dapr.io/getting-started/install-dapr-cli/). +* Java JDK 11 (or greater): + * [Microsoft JDK 11](https://docs.microsoft.com/en-us/java/openjdk/download#openjdk-11) + * [Oracle JDK 11](https://www.oracle.com/technetwork/java/javase/downloads/index.html#JDK11) + * [OpenJDK 11](https://jdk.java.net/11/) +* [Apache Maven](https://maven.apache.org/install.html) version 3.x. + +### Run Java message subscriber app with Dapr + +1. Navigate to directory and install dependencies: + + +```bash +cd ./order-processor +mvn clean install +``` + + +2. Run the Java subscriber app with Dapr: + +```bash +cd ./order-processor + dapr run --app-port 8080 --app-id order-processor-sdk --components-path ../../../components -- java -jar target/OrderProcessingService-0.0.1-SNAPSHOT.jar +``` + + +### Run Java message publisher app with Dapr + +1. Navigate to the directory and install dependencies: + + + +```bash +cd ./checkout +mvn clean install +``` + + +2. Run the Java publisher app with Dapr: + + +```bash +cd ./checkout +dapr run --app-id checkout-sdk --components-path ../../../components -- java -jar target/CheckoutService-0.0.1-SNAPSHOT.jar +``` + + +```bash +dapr stop --app-id checkout-sdk +dapr stop --app-id order-processor-sdk +``` diff --git a/dapr/quickstarts/pub_sub/java/sdk/checkout/pom.xml b/dapr/quickstarts/pub_sub/java/sdk/checkout/pom.xml new file mode 100644 index 0000000..6e77bf4 --- /dev/null +++ b/dapr/quickstarts/pub_sub/java/sdk/checkout/pom.xml @@ -0,0 +1,67 @@ + + + 4.0.0 + + com.service + CheckoutService + CheckoutService + 0.0.1-SNAPSHOT + Demo for Dapr pubsub component + + 11 + 11 + 1.6.1 + + + + io.dapr + dapr-sdk + 1.6.0 + + + com.squareup.okhttp3 + okhttp + 4.9.0 + + + org.slf4j + slf4j-api + ${slf4jVersion} + + + org.slf4j + slf4j-simple + ${slf4jVersion} + + + org.projectlombok + lombok + 1.18.22 + true + + + + + + + org.springframework.boot + spring-boot-maven-plugin + 2.6.4 + + + + repackage + + + + com.service.CheckoutServiceApplication + + + + + + + + + diff --git a/dapr/quickstarts/pub_sub/java/sdk/checkout/src/main/java/com/service/CheckoutServiceApplication.java b/dapr/quickstarts/pub_sub/java/sdk/checkout/src/main/java/com/service/CheckoutServiceApplication.java new file mode 100644 index 0000000..db8dfab --- /dev/null +++ b/dapr/quickstarts/pub_sub/java/sdk/checkout/src/main/java/com/service/CheckoutServiceApplication.java @@ -0,0 +1,38 @@ +package com.service; + +import io.dapr.client.DaprClient; +import io.dapr.client.DaprClientBuilder; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.util.concurrent.TimeUnit; + +public class CheckoutServiceApplication { + private static final Logger logger = LoggerFactory.getLogger(CheckoutServiceApplication.class); + + public static void main(String[] args) throws InterruptedException{ + String TOPIC_NAME = "orders"; + String PUBSUB_NAME = "orderpubsub"; + DaprClient client = new DaprClientBuilder().build(); + + for (int i = 0; i <= 10; i++) { + int orderId = i; + Order order = new Order(orderId); + + // Publish an event/message using Dapr PubSub + client.publishEvent( + PUBSUB_NAME, + TOPIC_NAME, + order).block(); + logger.info("Published data: " + order.getOrderId()); + TimeUnit.MILLISECONDS.sleep(5000); + } + } +} + +@AllArgsConstructor +@Getter +class Order { + private int orderId; +} diff --git a/dapr/quickstarts/pub_sub/java/sdk/makefile b/dapr/quickstarts/pub_sub/java/sdk/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/pub_sub/java/sdk/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/pub_sub/java/sdk/order-processor/pom.xml b/dapr/quickstarts/pub_sub/java/sdk/order-processor/pom.xml new file mode 100644 index 0000000..7c5a8a8 --- /dev/null +++ b/dapr/quickstarts/pub_sub/java/sdk/order-processor/pom.xml @@ -0,0 +1,57 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.6.3 + + + com.service + OrderProcessingService + 0.0.1-SNAPSHOT + OrderProcessingService + Demo for Dapr pubsub component + + 11 + + + + org.springframework.boot + spring-boot-starter-web + + + io.dapr + dapr-sdk-springboot + 1.6.0 + + + io.dapr + dapr-sdk + 1.6.0 + + + org.projectlombok + lombok + 1.18.22 + true + + + + + + + org.springframework.boot + spring-boot-maven-plugin + 2.6.4 + + + org.apache.maven.plugins + maven-resources-plugin + 3.1.0 + + + + + diff --git a/dapr/quickstarts/pub_sub/java/sdk/order-processor/src/main/java/com/service/OrderProcessingServiceApplication.java b/dapr/quickstarts/pub_sub/java/sdk/order-processor/src/main/java/com/service/OrderProcessingServiceApplication.java new file mode 100644 index 0000000..a0178b9 --- /dev/null +++ b/dapr/quickstarts/pub_sub/java/sdk/order-processor/src/main/java/com/service/OrderProcessingServiceApplication.java @@ -0,0 +1,12 @@ +package com.service; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class OrderProcessingServiceApplication { + public static void main(String[] args) throws Exception { + SpringApplication.run(OrderProcessingServiceApplication.class, args); + } + +} diff --git a/dapr/quickstarts/pub_sub/java/sdk/order-processor/src/main/java/com/service/controller/OrderProcessingServiceController.java b/dapr/quickstarts/pub_sub/java/sdk/order-processor/src/main/java/com/service/controller/OrderProcessingServiceController.java new file mode 100644 index 0000000..2adde48 --- /dev/null +++ b/dapr/quickstarts/pub_sub/java/sdk/order-processor/src/main/java/com/service/controller/OrderProcessingServiceController.java @@ -0,0 +1,40 @@ +package com.service.controller; + +import io.dapr.Topic; +import io.dapr.client.domain.CloudEvent; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import reactor.core.publisher.Mono; + +@RestController +public class OrderProcessingServiceController { + + private static final Logger logger = LoggerFactory.getLogger(OrderProcessingServiceController.class); + + @Topic(name = "orders", pubsubName = "orderpubsub") + @PostMapping(path = "/orders", consumes = MediaType.ALL_VALUE) + public Mono getCheckout(@RequestBody(required = false) CloudEvent cloudEvent) { + return Mono.fromSupplier(() -> { + try { + logger.info("Subscriber received: " + cloudEvent.getData().getOrderId()); + return ResponseEntity.ok("SUCCESS"); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + } +} + +@Getter +@Setter +class Order { + private int orderId; +} \ No newline at end of file diff --git a/dapr/quickstarts/pub_sub/java/sdk/order-processor/src/main/resources/application.properties b/dapr/quickstarts/pub_sub/java/sdk/order-processor/src/main/resources/application.properties new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/dapr/quickstarts/pub_sub/java/sdk/order-processor/src/main/resources/application.properties @@ -0,0 +1 @@ + diff --git a/dapr/quickstarts/pub_sub/javascript/http/README.md b/dapr/quickstarts/pub_sub/javascript/http/README.md new file mode 100644 index 0000000..d24585a --- /dev/null +++ b/dapr/quickstarts/pub_sub/javascript/http/README.md @@ -0,0 +1,87 @@ +# Dapr pub/sub + +In this quickstart, you'll create a publisher microservice and a subscriber microservice to demonstrate how Dapr enables a publish-subcribe pattern. The publisher will generate messages of a specific topic, while subscribers will listen for messages of specific topics. See [Why Pub-Sub](#why-pub-sub) to understand when this pattern might be a good choice for your software architecture. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/pubsub/) link for more information about Dapr and Pub-Sub. + +> **Note:** This example leverages HTTP `requests` only. If you are looking for the example using the Dapr Client SDK (recommended) [click here](../sdk/). + +This quickstart includes one publisher: + +- Node client message generator `checkout` + +And one subscriber: + +- Node subscriber `order-processor` + +### Run Node message subscriber with Dapr + +1. Install dependencies: + + + +```bash +cd ./order-processor +npm install +``` + +2. Run the Node publisher app with Dapr: + + + +```bash +dapr run --app-id order-processor-http --components-path ../../../components/ --app-port 5001 -- node . +``` + + + +### Run Node message publisher with Dapr + +1. Install dependencies: + + + +```bash +cd ./checkout +npm install +``` + +2. Run the Node publisher app with Dapr: + + + +```bash +dapr run --app-id checkout-http --components-path ../../../components/ --app-port 5001 -- node . +``` + + + +```bash +dapr stop --app-id checkout-http +dapr stop --app-id order-processor-http +``` diff --git a/dapr/quickstarts/pub_sub/javascript/http/checkout/index.js b/dapr/quickstarts/pub_sub/javascript/http/checkout/index.js new file mode 100644 index 0000000..446ec26 --- /dev/null +++ b/dapr/quickstarts/pub_sub/javascript/http/checkout/index.js @@ -0,0 +1,29 @@ +import axios from "axios"; + +const DAPR_HOST = process.env.DAPR_HOST || "http://localhost"; +const DAPR_HTTP_PORT = process.env.DAPR_HTTP_PORT || "3500"; +const PUBSUB_NAME = "orderpubsub"; +const PUBSUB_TOPIC = "orders"; + +async function main() { + for(var i = 1; i <= 10; i++) { + const order = {orderId: i}; + + // Publish an event using Dapr pub/sub + await axios.post(`${DAPR_HOST}:${DAPR_HTTP_PORT}/v1.0/publish/${PUBSUB_NAME}/${PUBSUB_TOPIC}`, order) + .then(function (response) { + console.log("Published data: " + response.config.data); + }) + .catch(function (error) { + console.log(error); + }); + + await sleep(1000); + } +} + +async function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +main().catch(e => console.error(e)) \ No newline at end of file diff --git a/dapr/quickstarts/pub_sub/javascript/http/checkout/package.json b/dapr/quickstarts/pub_sub/javascript/http/checkout/package.json new file mode 100644 index 0000000..1f73820 --- /dev/null +++ b/dapr/quickstarts/pub_sub/javascript/http/checkout/package.json @@ -0,0 +1,17 @@ +{ + "name": "checkout", + "version": "1.0.0", + "description": "", + "main": "index.js", + "type": "module", + "scripts": { + "start": "node index.js", + "start:dapr": "dapr run --app-id checkout --app-protocol http --dapr-http-port 3500 --components-path ../../../components -- npm run start" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "axios": "^0.25.0" + } +} diff --git a/dapr/quickstarts/pub_sub/javascript/http/makefile b/dapr/quickstarts/pub_sub/javascript/http/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/pub_sub/javascript/http/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/pub_sub/javascript/http/order-processor/index.js b/dapr/quickstarts/pub_sub/javascript/http/order-processor/index.js new file mode 100644 index 0000000..c31102e --- /dev/null +++ b/dapr/quickstarts/pub_sub/javascript/http/order-processor/index.js @@ -0,0 +1,25 @@ +import express from 'express'; +import bodyParser from 'body-parser'; + +const APP_PORT = process.env.APP_PORT ?? '5001'; + +const app = express(); +app.use(bodyParser.json({ type: 'application/*+json' })); + +app.get('/dapr/subscribe', (_req, res) => { + res.json([ + { + pubsubname: "orderpubsub", + topic: "orders", + route: "orders" + } + ]); +}); + +// Dapr subscription routes orders topic to this route +app.post('/orders', (req, res) => { + console.log("Subscriber received:", req.body.data); + res.sendStatus(200); +}); + +app.listen(APP_PORT); \ No newline at end of file diff --git a/dapr/quickstarts/pub_sub/javascript/http/order-processor/package.json b/dapr/quickstarts/pub_sub/javascript/http/order-processor/package.json new file mode 100644 index 0000000..4019c04 --- /dev/null +++ b/dapr/quickstarts/pub_sub/javascript/http/order-processor/package.json @@ -0,0 +1,17 @@ +{ + "name": "order-processor", + "version": "1.0.0", + "description": "", + "main": "index.js", + "type": "module", + "scripts": { + "start": "node index.js", + "start:dapr": "dapr run --app-port 5001 --app-id order-processing --app-protocol http --dapr-http-port 3501 --components-path ../../../components -- npm run start" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "express": "^4.17.2" + } +} diff --git a/dapr/quickstarts/pub_sub/javascript/sdk/README.md b/dapr/quickstarts/pub_sub/javascript/sdk/README.md new file mode 100644 index 0000000..7ba92b2 --- /dev/null +++ b/dapr/quickstarts/pub_sub/javascript/sdk/README.md @@ -0,0 +1,87 @@ +# Dapr pub/sub + +In this quickstart, you'll create a publisher microservice and a subscriber microservice to demonstrate how Dapr enables a publish-subcribe pattern. The publisher will generate messages of a specific topic, while subscribers will listen for messages of specific topics. See [Why Pub-Sub](#why-pub-sub) to understand when this pattern might be a good choice for your software architecture. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/pubsub/) link for more information about Dapr and Pub-Sub. + +> **Note:** This example leverages the Dapr client SDK. If you are looking for the example using only HTTP `requests` [click here](../http). + +This quickstart includes one publisher: + +- Node client message generator `checkout` + +And one subscriber: + +- Node subscriber `order-processor` + +### Run Node message subscriber with Dapr + +1. Install dependencies: + + + +```bash +cd ./order-processor +npm install +``` + +3. Run the Node publisher app with Dapr: + + + +```bash +dapr run --app-port 5002 --app-id order-processing-sdk --app-protocol http --dapr-http-port 3501 --components-path ../../../components -- npm run start +``` + + + +### Run Node message publisher with Dapr + +1. Install dependencies: + + + +```bash +cd ./checkout +npm install +``` + +3. Run the Node publisher app with Dapr: + + + +```bash +dapr run --app-id checkout-sdk --app-protocol http --dapr-http-port 3500 --components-path ../../../components -- npm run start +``` + + + +```bash +dapr stop --app-id checkout-sdk +dapr stop --app-id order-processor-sdk +``` diff --git a/dapr/quickstarts/pub_sub/javascript/sdk/checkout/index.js b/dapr/quickstarts/pub_sub/javascript/sdk/checkout/index.js new file mode 100644 index 0000000..fe2ecab --- /dev/null +++ b/dapr/quickstarts/pub_sub/javascript/sdk/checkout/index.js @@ -0,0 +1,25 @@ +import { DaprClient } from '@dapr/dapr'; + +const DAPR_HOST = process.env.DAPR_HOST || "http://localhost"; +const DAPR_HTTP_PORT = process.env.DAPR_HTTP_PORT || "3500"; +const PUBSUB_NAME = "orderpubsub"; +const PUBSUB_TOPIC = "orders"; + +async function main() { + const client = new DaprClient(DAPR_HOST, DAPR_HTTP_PORT); + + for(var i = 1; i <= 10; i++) { + const order = {orderId: i}; + + // Publish an event using Dapr pub/sub + await client.pubsub.publish(PUBSUB_NAME, PUBSUB_TOPIC, order); + console.log("Published data: " + JSON.stringify(order)); + + await sleep(1000); + } +} +async function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +main().catch(e => console.error(e)) \ No newline at end of file diff --git a/dapr/quickstarts/pub_sub/javascript/sdk/checkout/package.json b/dapr/quickstarts/pub_sub/javascript/sdk/checkout/package.json new file mode 100644 index 0000000..5a0c595 --- /dev/null +++ b/dapr/quickstarts/pub_sub/javascript/sdk/checkout/package.json @@ -0,0 +1,17 @@ +{ + "name": "checkout", + "version": "1.0.0", + "description": "", + "main": "index.js", + "type": "module", + "scripts": { + "start": "node index.js", + "start:dapr": "dapr run --app-id checkout --app-protocol http --dapr-http-port 3500 --components-path ../../../components -- npm run start" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@dapr/dapr": "^2.3.0" + } +} diff --git a/dapr/quickstarts/pub_sub/javascript/sdk/makefile b/dapr/quickstarts/pub_sub/javascript/sdk/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/pub_sub/javascript/sdk/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/pub_sub/javascript/sdk/order-processor/index.js b/dapr/quickstarts/pub_sub/javascript/sdk/order-processor/index.js new file mode 100644 index 0000000..8a1bd9f --- /dev/null +++ b/dapr/quickstarts/pub_sub/javascript/sdk/order-processor/index.js @@ -0,0 +1,17 @@ +import { DaprServer } from '@dapr/dapr'; + +const DAPR_HOST = process.env.DAPR_HOST || "http://localhost"; +const DAPR_HTTP_PORT = process.env.DAPR_HTTP_PORT || "3501"; +const SERVER_HOST = process.env.SERVER_HOST || "127.0.0.1"; +const SERVER_PORT = process.env.APP_PORT || 5002; + +async function main() { + const server = new DaprServer(SERVER_HOST, SERVER_PORT, DAPR_HOST, DAPR_HTTP_PORT); + + // Dapr subscription routes orders topic to this route + server.pubsub.subscribe("orderpubsub", "orders", (data) => console.log("Subscriber received: " + JSON.stringify(data))); + + await server.start(); +} + +main().catch(e => console.error(e)); \ No newline at end of file diff --git a/dapr/quickstarts/pub_sub/javascript/sdk/order-processor/package.json b/dapr/quickstarts/pub_sub/javascript/sdk/order-processor/package.json new file mode 100644 index 0000000..7852574 --- /dev/null +++ b/dapr/quickstarts/pub_sub/javascript/sdk/order-processor/package.json @@ -0,0 +1,17 @@ +{ + "name": "order-processor", + "version": "1.0.0", + "description": "", + "main": "index.js", + "type": "module", + "scripts": { + "start": "node index.js", + "start:dapr": "dapr run --app-port 5001 --app-id order-processing --app-protocol http --dapr-http-port 3501 --components-path ../../../components -- npm run start" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@dapr/dapr": "^2.3.0" + } +} diff --git a/dapr/quickstarts/pub_sub/python/http/README.md b/dapr/quickstarts/pub_sub/python/http/README.md new file mode 100644 index 0000000..1f6c36c --- /dev/null +++ b/dapr/quickstarts/pub_sub/python/http/README.md @@ -0,0 +1,88 @@ +# Dapr pub/sub (HTTP requests client) + +In this quickstart, you'll create a publisher microservice and a subscriber microservice to demonstrate how Dapr enables a publish-subcribe pattern. The publisher will generate messages of a specific topic, while subscribers will listen for messages of specific topics. See [Why Pub-Sub](#why-pub-sub) to understand when this pattern might be a good choice for your software architecture. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/pubsub/) link for more information about Dapr and Pub-Sub. + +> **Note:** This example leverages HTTP `requests` only. If you are looking for the example using the Dapr Client SDK (recommended) [click here](../sdk/). + +This quickstart includes one publisher: + +- Python client message generator `checkout` + +And one subscriber: + +- Python subscriber `order-processor` + +### Run Python message subscriber with Dapr + +2. Install dependencies: + + + +```bash +cd ./order-processor +pip3 install -r requirements.txt +``` + +3. Run the Python subscriber app with Dapr: + + + + +```bash +dapr run --app-id order-processor-http --components-path ../../../components/ --app-port 6001 -- python3 app.py +``` + + + +### Run Python message publisher with Dapr + +1. Install dependencies: + + + +```bash +cd ./checkout +pip3 install -r requirements.txt +``` + +3. Run the Python publisher app with Dapr: + + + +```bash +dapr run --app-id checkout-http --components-path ../../../components/ -- python3 app.py +``` + + + +```bash +dapr stop --app-id checkout-http +dapr stop --app-id order-processor-http +``` diff --git a/dapr/quickstarts/pub_sub/python/http/checkout/app.py b/dapr/quickstarts/pub_sub/python/http/checkout/app.py new file mode 100644 index 0000000..6a24203 --- /dev/null +++ b/dapr/quickstarts/pub_sub/python/http/checkout/app.py @@ -0,0 +1,27 @@ +import json +import time +import random +import logging +import requests +import os + +logging.basicConfig(level=logging.INFO) + +base_url = os.getenv('BASE_URL', 'http://localhost') + ':' + os.getenv( + 'DAPR_HTTP_PORT', '3500') +PUBSUB_NAME = 'orderpubsub' +TOPIC = 'orders' +logging.info('Publishing to baseURL: %s, Pubsub Name: %s, Topic: %s' % ( + base_url, PUBSUB_NAME, TOPIC)) + +for i in range(1, 10): + order = {'orderId': i} + + # Publish an event/message using Dapr PubSub via HTTP Post + result = requests.post( + url='%s/v1.0/publish/%s/%s' % (base_url, PUBSUB_NAME, TOPIC), + json=order + ) + logging.info('Published data: ' + json.dumps(order)) + + time.sleep(1) diff --git a/dapr/quickstarts/pub_sub/python/http/checkout/requirements.txt b/dapr/quickstarts/pub_sub/python/http/checkout/requirements.txt new file mode 100644 index 0000000..f229360 --- /dev/null +++ b/dapr/quickstarts/pub_sub/python/http/checkout/requirements.txt @@ -0,0 +1 @@ +requests diff --git a/dapr/quickstarts/pub_sub/python/http/makefile b/dapr/quickstarts/pub_sub/python/http/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/pub_sub/python/http/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/pub_sub/python/http/order-processor/app.py b/dapr/quickstarts/pub_sub/python/http/order-processor/app.py new file mode 100644 index 0000000..0ac2042 --- /dev/null +++ b/dapr/quickstarts/pub_sub/python/http/order-processor/app.py @@ -0,0 +1,30 @@ +from flask import Flask, request, jsonify +import json +import os + +app = Flask(__name__) + +app_port = os.getenv('APP_PORT', '6001') + +# Register Dapr pub/sub subscriptions +@app.route('/dapr/subscribe', methods=['GET']) +def subscribe(): + subscriptions = [{ + 'pubsubname': 'orderpubsub', + 'topic': 'orders', + 'route': 'orders' + }] + print('Dapr pub/sub is subscribed to: ' + json.dumps(subscriptions)) + return jsonify(subscriptions) + + +# Dapr subscription in /dapr/subscribe sets up this route +@app.route('/orders', methods=['POST']) +def orders_subscriber(): + event_orderid = request.json['data']['orderId'] + print('Subscriber received : ' + json.dumps(event_orderid), flush=True) + return json.dumps({'success': True}), 200, { + 'ContentType': 'application/json'} + + +app.run(port=app_port) diff --git a/dapr/quickstarts/pub_sub/python/http/order-processor/requirements.txt b/dapr/quickstarts/pub_sub/python/http/order-processor/requirements.txt new file mode 100644 index 0000000..289bc5e --- /dev/null +++ b/dapr/quickstarts/pub_sub/python/http/order-processor/requirements.txt @@ -0,0 +1,3 @@ +Flask +dapr +cloudevents diff --git a/dapr/quickstarts/pub_sub/python/sdk/README.md b/dapr/quickstarts/pub_sub/python/sdk/README.md new file mode 100644 index 0000000..8c84eea --- /dev/null +++ b/dapr/quickstarts/pub_sub/python/sdk/README.md @@ -0,0 +1,88 @@ +# Dapr pub/sub + +In this quickstart, you'll create a publisher microservice and a subscriber microservice to demonstrate how Dapr enables a publish-subcribe pattern. The publisher will generate messages of a specific topic, while subscribers will listen for messages of specific topics. See [Why Pub-Sub](#why-pub-sub) to understand when this pattern might be a good choice for your software architecture. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/pubsub/) link for more information about Dapr and Pub-Sub. + +> **Note:** This example leverages the Dapr client SDK. If you are looking for the example using only HTTP `requests` [click here](../http). + +This quickstart includes one publisher: + +- Python client message generator `checkout` + +And one subscriber: + +- Python subscriber `order-processor` + +### Run Python message subscriber with Dapr + + + +```bash +cd ./order-processor +pip3 install -r requirements.txt +``` + + + +2. Run the Python subscriber app with Dapr: + + + +```bash +dapr run --app-id order-processor-sdk --components-path ../../../components/ --app-port 6001 -- uvicorn app:app --port 6002 +``` + + + +### Run Python message publisher with Dapr + +1. Install dependencies: + + + +```bash +cd ./checkout +pip3 install -r requirements.txt +``` + + +3. Run the Python publisher app with Dapr: + + + +```bash +dapr run --app-id checkout-sdk --components-path ../../../components/ -- python3 app.py +``` + + + +```bash +dapr stop --app-id checkout-sdk +dapr stop --app-id order-processor-sdk +``` diff --git a/dapr/quickstarts/pub_sub/python/sdk/checkout/app.py b/dapr/quickstarts/pub_sub/python/sdk/checkout/app.py new file mode 100644 index 0000000..1d16af6 --- /dev/null +++ b/dapr/quickstarts/pub_sub/python/sdk/checkout/app.py @@ -0,0 +1,21 @@ +from dapr.clients import DaprClient +import json +import time +import logging + +logging.basicConfig(level=logging.INFO) + +for i in range(1, 10): + order = {'orderId': i} + + with DaprClient() as client: + # Publish an event/message using Dapr PubSub + result = client.publish_event( + pubsub_name='orderpubsub', + topic_name='orders', + data=json.dumps(order), + data_content_type='application/json', + ) + + logging.info('Published data: ' + json.dumps(order)) + time.sleep(1) diff --git a/dapr/quickstarts/pub_sub/python/sdk/checkout/requirements.txt b/dapr/quickstarts/pub_sub/python/sdk/checkout/requirements.txt new file mode 100644 index 0000000..53c5975 --- /dev/null +++ b/dapr/quickstarts/pub_sub/python/sdk/checkout/requirements.txt @@ -0,0 +1 @@ +dapr diff --git a/dapr/quickstarts/pub_sub/python/sdk/makefile b/dapr/quickstarts/pub_sub/python/sdk/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/pub_sub/python/sdk/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/pub_sub/python/sdk/order-processor-fastapi/app.py b/dapr/quickstarts/pub_sub/python/sdk/order-processor-fastapi/app.py new file mode 100644 index 0000000..580bddf --- /dev/null +++ b/dapr/quickstarts/pub_sub/python/sdk/order-processor-fastapi/app.py @@ -0,0 +1,26 @@ +from dapr.ext.fastapi import DaprApp +from fastapi import FastAPI +from pydantic import BaseModel + +app = FastAPI() +dapr_app = DaprApp(app) + + +class CloudEvent(BaseModel): + datacontenttype: str + source: str + topic: str + pubsubname: str + data: dict + id: str + specversion: str + tracestate: str + type: str + traceid: str + + +# Dapr subscription routes orders topic to this route +@dapr_app.subscribe(pubsub='orderpubsub', topic='orders') +def orders_subscriber(event: CloudEvent): + print('Subscriber received : %s' % event.data['orderId'], flush=True) + return {'success': True} diff --git a/dapr/quickstarts/pub_sub/python/sdk/order-processor-fastapi/requirements.txt b/dapr/quickstarts/pub_sub/python/sdk/order-processor-fastapi/requirements.txt new file mode 100644 index 0000000..bb1755b --- /dev/null +++ b/dapr/quickstarts/pub_sub/python/sdk/order-processor-fastapi/requirements.txt @@ -0,0 +1,4 @@ +fastapi +dapr-ext-fastapi==1.5.0 +cloudevents +uvicorn diff --git a/dapr/quickstarts/pub_sub/python/sdk/order-processor/app.py b/dapr/quickstarts/pub_sub/python/sdk/order-processor/app.py new file mode 100644 index 0000000..6a5a362 --- /dev/null +++ b/dapr/quickstarts/pub_sub/python/sdk/order-processor/app.py @@ -0,0 +1,31 @@ +from flask import Flask, request, jsonify +from cloudevents.http import from_http +import json +import os + +app = Flask(__name__) + +app_port = os.getenv('APP_PORT', '6002') + +# Register Dapr pub/sub subscriptions +@app.route('/dapr/subscribe', methods=['GET']) +def subscribe(): + subscriptions = [{ + 'pubsubname': 'orderpubsub', + 'topic': 'orders', + 'route': 'orders' + }] + print('Dapr pub/sub is subscribed to: ' + json.dumps(subscriptions)) + return jsonify(subscriptions) + + +# Dapr subscription in /dapr/subscribe sets up this route +@app.route('/orders', methods=['POST']) +def orders_subscriber(): + event = from_http(request.headers, request.get_data()) + print('Subscriber received : %s' % event.data['orderId'], flush=True) + return json.dumps({'success': True}), 200, { + 'ContentType': 'application/json'} + + +app.run(port=app_port) diff --git a/dapr/quickstarts/pub_sub/python/sdk/order-processor/requirements.txt b/dapr/quickstarts/pub_sub/python/sdk/order-processor/requirements.txt new file mode 100644 index 0000000..5a32fc7 --- /dev/null +++ b/dapr/quickstarts/pub_sub/python/sdk/order-processor/requirements.txt @@ -0,0 +1,4 @@ +Flask +dapr +cloudevents +uvicorn diff --git a/dapr/quickstarts/samples-diagrams.pptx b/dapr/quickstarts/samples-diagrams.pptx new file mode 100644 index 0000000..8831c99 Binary files /dev/null and b/dapr/quickstarts/samples-diagrams.pptx differ diff --git a/dapr/quickstarts/secrets_management/components/local-secret-store.yaml b/dapr/quickstarts/secrets_management/components/local-secret-store.yaml new file mode 100644 index 0000000..3ad432e --- /dev/null +++ b/dapr/quickstarts/secrets_management/components/local-secret-store.yaml @@ -0,0 +1,13 @@ +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: localsecretstore + namespace: default +spec: + type: secretstores.local.file + version: v1 + metadata: + - name: secretsFile + value: secrets.json + - name: nestedSeparator + value: ":" diff --git a/dapr/quickstarts/secrets_management/csharp/http/README.md b/dapr/quickstarts/secrets_management/csharp/http/README.md new file mode 100644 index 0000000..e05836a --- /dev/null +++ b/dapr/quickstarts/secrets_management/csharp/http/README.md @@ -0,0 +1,48 @@ +# Dapr secrets management (HTTP Client) + +In this quickstart, you'll create a microservice to demonstrate Dapr's secrets management API. The service fetches secret from a secret store. See [Why secrets management](#why-secrets-management) to understand when to use this API. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/secrets/) link for more information about Dapr and Secrets Management. + +> **Note:** This example leverages HTTP `requests` only. If you are looking for the example using the Dapr Client SDK (recommended) [click here](../sdk/). + +This quickstart includes one service: + +- Dotnet client service `order-processor` + +### Run Dotnet service with Dapr + +1. Open a new terminal window and navigate to `order-processor` directory: + + + +```bash +cd ./order-processor +dotnet restore +dotnet build +``` + + +2. Run the Dotnet service app with Dapr: + + + +```bash +cd ./order-processor +dapr run --app-id order-processor --components-path ../../../components/ -- dotnet run +``` + + + +```bash +dapr stop --app-id order-processor +``` diff --git a/dapr/quickstarts/secrets_management/csharp/http/makefile b/dapr/quickstarts/secrets_management/csharp/http/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/secrets_management/csharp/http/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/secrets_management/csharp/http/order-processor/Program.cs b/dapr/quickstarts/secrets_management/csharp/http/order-processor/Program.cs new file mode 100644 index 0000000..95b507c --- /dev/null +++ b/dapr/quickstarts/secrets_management/csharp/http/order-processor/Program.cs @@ -0,0 +1,15 @@ +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; + +var baseURL = (Environment.GetEnvironmentVariable("BASE_URL") ?? "http://localhost") + ":" ++ (Environment.GetEnvironmentVariable("DAPR_HTTP_PORT") ?? "3500"); +const string DAPR_SECRET_STORE = "localsecretstore"; +const string SECRET_NAME = "secret"; + +var httpClient = new HttpClient(); +httpClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json")); + +// Get secret from a local secret store +var secret = await httpClient.GetStringAsync($"{baseURL}/v1.0/secrets/{DAPR_SECRET_STORE}/{SECRET_NAME}"); +Console.WriteLine("Fetched Secret: " + secret); \ No newline at end of file diff --git a/dapr/quickstarts/secrets_management/csharp/http/order-processor/Program.csproj b/dapr/quickstarts/secrets_management/csharp/http/order-processor/Program.csproj new file mode 100644 index 0000000..3897747 --- /dev/null +++ b/dapr/quickstarts/secrets_management/csharp/http/order-processor/Program.csproj @@ -0,0 +1,10 @@ + + + + Exe + net6.0 + enable + enable + + + diff --git a/dapr/quickstarts/secrets_management/csharp/http/order-processor/secrets.json b/dapr/quickstarts/secrets_management/csharp/http/order-processor/secrets.json new file mode 100644 index 0000000..105ba89 --- /dev/null +++ b/dapr/quickstarts/secrets_management/csharp/http/order-processor/secrets.json @@ -0,0 +1,3 @@ +{ + "secret": "YourPasskeyHere" +} diff --git a/dapr/quickstarts/secrets_management/csharp/sdk/README.md b/dapr/quickstarts/secrets_management/csharp/sdk/README.md new file mode 100644 index 0000000..6bc06aa --- /dev/null +++ b/dapr/quickstarts/secrets_management/csharp/sdk/README.md @@ -0,0 +1,48 @@ +# Dapr secrets management + +In this quickstart, you'll create a microservice to demonstrate Dapr's secrets management API. The service fetches secret from a secret store. See [Why secrets management](#why-secrets-management) to understand when to use this API. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/secrets/) link for more information about Dapr and Secrets Management. + +> **Note:** This example leverages the Dapr client SDK. If you are looking for the example using only HTTP [click here](../http). + +This quickstart includes one service: + +- Dotnet client service `order-processor` + +### Run Dotnet service with Dapr + +1. Open a new terminal window and navigate to `order-processor` directory: + + + +```bash +cd ./order-processor +dotnet restore +dotnet build +``` + + +2. Run the Dotnet service app with Dapr: + + + +```bash +cd ./order-processor +dapr run --app-id order-processor --components-path ../../../components/ -- dotnet run +``` + + + +```bash +dapr stop --app-id order-processor +``` diff --git a/dapr/quickstarts/secrets_management/csharp/sdk/makefile b/dapr/quickstarts/secrets_management/csharp/sdk/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/secrets_management/csharp/sdk/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/secrets_management/csharp/sdk/order-processor/Program.cs b/dapr/quickstarts/secrets_management/csharp/sdk/order-processor/Program.cs new file mode 100644 index 0000000..4038c56 --- /dev/null +++ b/dapr/quickstarts/secrets_management/csharp/sdk/order-processor/Program.cs @@ -0,0 +1,11 @@ +using System; +using Dapr.Client; + +const string DAPR_SECRET_STORE = "localsecretstore"; +const string SECRET_NAME = "secret"; +var client = new DaprClientBuilder().Build(); + +// Get secret from a local secret store +var secret = await client.GetSecretAsync(DAPR_SECRET_STORE, SECRET_NAME); +var secretValue = string.Join(", ", secret); +Console.WriteLine($"Fetched Secret: {secretValue}"); \ No newline at end of file diff --git a/dapr/quickstarts/secrets_management/csharp/sdk/order-processor/Program.csproj b/dapr/quickstarts/secrets_management/csharp/sdk/order-processor/Program.csproj new file mode 100644 index 0000000..b21a5af --- /dev/null +++ b/dapr/quickstarts/secrets_management/csharp/sdk/order-processor/Program.csproj @@ -0,0 +1,12 @@ + + + + Exe + net6.0 + + + + + + + diff --git a/dapr/quickstarts/secrets_management/csharp/sdk/order-processor/secrets.json b/dapr/quickstarts/secrets_management/csharp/sdk/order-processor/secrets.json new file mode 100644 index 0000000..105ba89 --- /dev/null +++ b/dapr/quickstarts/secrets_management/csharp/sdk/order-processor/secrets.json @@ -0,0 +1,3 @@ +{ + "secret": "YourPasskeyHere" +} diff --git a/dapr/quickstarts/secrets_management/go/http/README.md b/dapr/quickstarts/secrets_management/go/http/README.md new file mode 100644 index 0000000..1918a6c --- /dev/null +++ b/dapr/quickstarts/secrets_management/go/http/README.md @@ -0,0 +1,35 @@ +# Dapr secrets management (HTTP Client) + +In this quickstart, you'll create a microservice to demonstrate Dapr's secrets management API. The service fetches a secret from a secret store. See [Why secrets management](#why-secrets-management) to understand when to use this API. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/secrets/) link for more information about Dapr and Secrets Management. + +> **Note:** This example leverages HTTP `requests` only. If you are looking for the example using the Dapr Client SDK (recommended) [click here](../sdk/). + +This quickstart includes one service: + +- Go client service `order-processor` + +### Run Go service with Dapr + +1. Run the Go service app with Dapr: + + + +```bash +cd ./order-processor +dapr run --app-id order-processor --components-path ../../../components/ -- go run . +``` + + + +```bash +dapr stop --app-id order-processor +``` \ No newline at end of file diff --git a/dapr/quickstarts/secrets_management/go/http/makefile b/dapr/quickstarts/secrets_management/go/http/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/secrets_management/go/http/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/secrets_management/go/http/order-processor/app.go b/dapr/quickstarts/secrets_management/go/http/order-processor/app.go new file mode 100644 index 0000000..07da0e5 --- /dev/null +++ b/dapr/quickstarts/secrets_management/go/http/order-processor/app.go @@ -0,0 +1,30 @@ +package main + +import ( + "fmt" + "io/ioutil" + "net/http" + "os" +) + +func main() { + var DAPR_HOST, DAPR_HTTP_PORT string + var okHost, okPort bool + if DAPR_HOST, okHost = os.LookupEnv("DAPR_HOST"); !okHost { + DAPR_HOST = "http://localhost" + } + if DAPR_HTTP_PORT, okPort = os.LookupEnv("DAPR_HTTP_PORT"); !okPort { + DAPR_HTTP_PORT = "3500" + } + + const DAPR_SECRET_STORE = "localsecretstore" + const SECRET_NAME = "secret" + // Get secret from a local secret store + getResponse, err := http.Get(DAPR_HOST + ":" + DAPR_HTTP_PORT + "/v1.0/secrets/" + DAPR_SECRET_STORE + "/" + SECRET_NAME) + if err != nil { + fmt.Print(err.Error()) + os.Exit(1) + } + result, _ := ioutil.ReadAll(getResponse.Body) + fmt.Println("Fetched Secret: ", string(result)) +} diff --git a/dapr/quickstarts/secrets_management/go/http/order-processor/dapr_example b/dapr/quickstarts/secrets_management/go/http/order-processor/dapr_example new file mode 100755 index 0000000..7f66366 Binary files /dev/null and b/dapr/quickstarts/secrets_management/go/http/order-processor/dapr_example differ diff --git a/dapr/quickstarts/secrets_management/go/http/order-processor/go.mod b/dapr/quickstarts/secrets_management/go/http/order-processor/go.mod new file mode 100644 index 0000000..a81a53c --- /dev/null +++ b/dapr/quickstarts/secrets_management/go/http/order-processor/go.mod @@ -0,0 +1,3 @@ +module dapr_example + +go 1.18 diff --git a/dapr/quickstarts/secrets_management/go/http/order-processor/go.sum b/dapr/quickstarts/secrets_management/go/http/order-processor/go.sum new file mode 100644 index 0000000..e69de29 diff --git a/dapr/quickstarts/secrets_management/go/http/order-processor/secrets.json b/dapr/quickstarts/secrets_management/go/http/order-processor/secrets.json new file mode 100644 index 0000000..105ba89 --- /dev/null +++ b/dapr/quickstarts/secrets_management/go/http/order-processor/secrets.json @@ -0,0 +1,3 @@ +{ + "secret": "YourPasskeyHere" +} diff --git a/dapr/quickstarts/secrets_management/go/sdk/README.md b/dapr/quickstarts/secrets_management/go/sdk/README.md new file mode 100644 index 0000000..2a4e1f2 --- /dev/null +++ b/dapr/quickstarts/secrets_management/go/sdk/README.md @@ -0,0 +1,35 @@ +# Dapr secrets management (HTTP Client) + +In this quickstart, you'll create a microservice to demonstrate Dapr's secrets management API. The service fetches a secret from a secret store. See [Why secrets management](#why-secrets-management) to understand when to use this API. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/secrets/) link for more information about Dapr and Secrets Management. + +> **Note:** This example leverages HTTP `requests` only. If you are looking for the example using the Dapr Client SDK (recommended) [click here](../sdk/). + +This quickstart includes one service: + +- Go client service `order-processor` + +### Run Go service with Dapr + +1. Run the Go service app with Dapr: + + + +```bash +cd ./order-processor +dapr run --app-id order-processor --components-path ../../../components/ -- go run . +``` + + + +```bash +dapr stop --app-id order-processor +``` diff --git a/dapr/quickstarts/secrets_management/go/sdk/makefile b/dapr/quickstarts/secrets_management/go/sdk/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/secrets_management/go/sdk/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/secrets_management/go/sdk/order-processor/app.go b/dapr/quickstarts/secrets_management/go/sdk/order-processor/app.go new file mode 100644 index 0000000..462c29d --- /dev/null +++ b/dapr/quickstarts/secrets_management/go/sdk/order-processor/app.go @@ -0,0 +1,23 @@ +package main + +import ( + "context" + "fmt" + + dapr "github.com/dapr/go-sdk/client" +) + +func main() { + client, err := dapr.NewClient() + const DAPR_SECRET_STORE = "localsecretstore" + const SECRET_NAME = "secret" + if err != nil { + panic(err) + } + defer client.Close() + ctx := context.Background() + secret, err := client.GetSecret(ctx, DAPR_SECRET_STORE, SECRET_NAME, nil) + if secret != nil { + fmt.Println("Fetched Secret: ", secret[SECRET_NAME]) + } +} diff --git a/dapr/quickstarts/secrets_management/go/sdk/order-processor/dapr_example b/dapr/quickstarts/secrets_management/go/sdk/order-processor/dapr_example new file mode 100755 index 0000000..f600449 Binary files /dev/null and b/dapr/quickstarts/secrets_management/go/sdk/order-processor/dapr_example differ diff --git a/dapr/quickstarts/secrets_management/go/sdk/order-processor/go.mod b/dapr/quickstarts/secrets_management/go/sdk/order-processor/go.mod new file mode 100644 index 0000000..951a92e --- /dev/null +++ b/dapr/quickstarts/secrets_management/go/sdk/order-processor/go.mod @@ -0,0 +1,18 @@ +module dapr_example + +go 1.18 + +require github.com/dapr/go-sdk v1.5.0 + +require ( + github.com/dapr/dapr v1.8.0 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/pkg/errors v0.9.1 // indirect + golang.org/x/net v0.0.0-20220621193019-9d032be2e588 // indirect + golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect + golang.org/x/text v0.3.7 // indirect + google.golang.org/genproto v0.0.0-20220622171453-ea41d75dfa0f // indirect + google.golang.org/grpc v1.47.0 // indirect + google.golang.org/protobuf v1.28.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/dapr/quickstarts/secrets_management/go/sdk/order-processor/go.sum b/dapr/quickstarts/secrets_management/go/sdk/order-processor/go.sum new file mode 100644 index 0000000..7e7b155 --- /dev/null +++ b/dapr/quickstarts/secrets_management/go/sdk/order-processor/go.sum @@ -0,0 +1,151 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/dapr/dapr v1.8.0 h1:ZAAoBe6wuFp7k4tIHB7ajZXVTtGeDeVqIPrldzo3dF0= +github.com/dapr/dapr v1.8.0/go.mod h1:yAsDiK5oecG0htw2S8JG9RFaeHJVdlTfZyOrL57AvRM= +github.com/dapr/go-sdk v1.5.0 h1:OVkrupquJEOL1qRtwKcMVrFKYhw4UJQvgOJNduo2VxE= +github.com/dapr/go-sdk v1.5.0/go.mod h1:Cvz3taCVu22WCNEUbc9/szvG/yJxWPAV4dcaG+zDWA4= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.4 h1:wZRexSlwd7ZXfKINDLsO4r7WBt3gTKONc6K/VesHvHM= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20220621193019-9d032be2e588 h1:9ubFuySsnAJYGyJrZ3koiEv8FyqofCBdz3G9Mbf2YFc= +golang.org/x/net v0.0.0-20220621193019-9d032be2e588/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20220622171453-ea41d75dfa0f h1:kYlCnpX4eB0QEnXm12j4DAX4yrjjhJmsyuWtSSZ+Buo= +google.golang.org/genproto v0.0.0-20220622171453-ea41d75dfa0f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8= +google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/dapr/quickstarts/secrets_management/go/sdk/order-processor/secrets.json b/dapr/quickstarts/secrets_management/go/sdk/order-processor/secrets.json new file mode 100644 index 0000000..105ba89 --- /dev/null +++ b/dapr/quickstarts/secrets_management/go/sdk/order-processor/secrets.json @@ -0,0 +1,3 @@ +{ + "secret": "YourPasskeyHere" +} diff --git a/dapr/quickstarts/secrets_management/java/http/README.md b/dapr/quickstarts/secrets_management/java/http/README.md new file mode 100644 index 0000000..4a8f260 --- /dev/null +++ b/dapr/quickstarts/secrets_management/java/http/README.md @@ -0,0 +1,47 @@ +# Dapr secrets management (HTTP Client) + +In this quickstart, you'll create a microservice to demonstrate Dapr's secrets management API. The service fetches secret from a secret store. See [Why secrets management](#why-secrets-management) to understand when to use this API. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/secrets/) link for more information about Dapr and Secrets Management. + +> **Note:** This example leverages HTTP `requests` only. If you are looking for the example using the Dapr Client SDK (recommended) [click here](../sdk/). + +This quickstart includes one service: + +- Java client service `order-processor` + +### Run Java service with Dapr + +1. Navigate to folder and install dependencies: + + + +```bash +cd ./order-processor +mvn clean install +``` + + +2. Run the Java service app with Dapr: + + + +```bash +cd ./order-processor +dapr run --app-id order-processor --components-path ../../../components/ -- java -jar target/OrderProcessingService-0.0.1-SNAPSHOT.jar +``` + + + +```bash +dapr stop --app-id order-processor +``` diff --git a/dapr/quickstarts/secrets_management/java/http/makefile b/dapr/quickstarts/secrets_management/java/http/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/secrets_management/java/http/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/secrets_management/java/http/order-processor/pom.xml b/dapr/quickstarts/secrets_management/java/http/order-processor/pom.xml new file mode 100644 index 0000000..03a56ee --- /dev/null +++ b/dapr/quickstarts/secrets_management/java/http/order-processor/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + + com.service + OrderProcessingService + 0.0.1-SNAPSHOT + OrderProcessingService + Demo for Dapr secret mgmt component + + + UTF-8 + + 11 + 11 + + + + + org.springframework.boot + spring-boot-maven-plugin + 2.6.3 + + + + repackage + + + + com.service.OrderProcessingServiceApplication + + + + + + + + diff --git a/dapr/quickstarts/secrets_management/java/http/order-processor/secrets.json b/dapr/quickstarts/secrets_management/java/http/order-processor/secrets.json new file mode 100644 index 0000000..105ba89 --- /dev/null +++ b/dapr/quickstarts/secrets_management/java/http/order-processor/secrets.json @@ -0,0 +1,3 @@ +{ + "secret": "YourPasskeyHere" +} diff --git a/dapr/quickstarts/secrets_management/java/http/order-processor/src/main/java/com/service/OrderProcessingServiceApplication.java b/dapr/quickstarts/secrets_management/java/http/order-processor/src/main/java/com/service/OrderProcessingServiceApplication.java new file mode 100644 index 0000000..b56c6ec --- /dev/null +++ b/dapr/quickstarts/secrets_management/java/http/order-processor/src/main/java/com/service/OrderProcessingServiceApplication.java @@ -0,0 +1,29 @@ +package com.service; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.time.Duration; + +public class OrderProcessingServiceApplication { + private static final String DAPR_HOST = System.getenv().getOrDefault("DAPR_HOST", "http://localhost"); + private static final String DAPR_HTTP_PORT = System.getenv().getOrDefault("DAPR_HTTP_PORT", "3500"); + private static final String SECRET_STORE_NAME = System.getenv().getOrDefault("SECRET_STORE", "localsecretstore"); + private static final String SECRET_NAME = "secret"; + private static final HttpClient httpClient = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_2) + .connectTimeout(Duration.ofSeconds(10)) + .build(); + + public static void main(String[] args) throws URISyntaxException, IOException, InterruptedException { + URI secretStoreURI = new URI(DAPR_HOST + ":" + DAPR_HTTP_PORT + "/v1.0/secrets/" + SECRET_STORE_NAME + "/" + SECRET_NAME); + HttpRequest request = HttpRequest.newBuilder() + .uri(secretStoreURI) + .build(); + HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println("Fetched Secret: " + response.body().toString()); + } +} diff --git a/dapr/quickstarts/secrets_management/java/sdk/README.md b/dapr/quickstarts/secrets_management/java/sdk/README.md new file mode 100644 index 0000000..90dd3ed --- /dev/null +++ b/dapr/quickstarts/secrets_management/java/sdk/README.md @@ -0,0 +1,47 @@ +# Dapr secrets management (HTTP Client) + +In this quickstart, you'll create a microservice to demonstrate Dapr's secrets management API. The service fetches secret from a secret store. See [Why secrets management](#why-secrets-management) to understand when to use this API. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/secrets/) link for more information about Dapr and Secrets Management. + +> **Note:** This example leverages the Dapr client SDK. If you are looking for the example using only HTTP [click here](../http). + +This quickstart includes one service: + +- Java client service `order-processor` + +### Run Java service with Dapr + +1. Navigate to folder and install dependencies: + + + +```bash +cd ./order-processor +mvn clean install +``` + + +2. Run the Java service app with Dapr: + + + +```bash +cd ./order-processor +dapr run --app-id order-processor --components-path ../../../components/ -- java -jar target/OrderProcessingService-0.0.1-SNAPSHOT.jar +``` + + + +```bash +dapr stop --app-id order-processor +``` diff --git a/dapr/quickstarts/secrets_management/java/sdk/makefile b/dapr/quickstarts/secrets_management/java/sdk/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/secrets_management/java/sdk/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/secrets_management/java/sdk/order-processor/pom.xml b/dapr/quickstarts/secrets_management/java/sdk/order-processor/pom.xml new file mode 100644 index 0000000..50ef811 --- /dev/null +++ b/dapr/quickstarts/secrets_management/java/sdk/order-processor/pom.xml @@ -0,0 +1,47 @@ + + + 4.0.0 + + com.service + OrderProcessingService + 0.0.1-SNAPSHOT + OrderProcessingService + Demo for Dapr secret mgmt component + + + UTF-8 + + 11 + 11 + + + + io.dapr + dapr-sdk + 1.6.0 + + + + + + org.springframework.boot + spring-boot-maven-plugin + 2.6.3 + + + + repackage + + + + com.service.OrderProcessingServiceApplication + + + + + + + + diff --git a/dapr/quickstarts/secrets_management/java/sdk/order-processor/secrets.json b/dapr/quickstarts/secrets_management/java/sdk/order-processor/secrets.json new file mode 100644 index 0000000..105ba89 --- /dev/null +++ b/dapr/quickstarts/secrets_management/java/sdk/order-processor/secrets.json @@ -0,0 +1,3 @@ +{ + "secret": "YourPasskeyHere" +} diff --git a/dapr/quickstarts/secrets_management/java/sdk/order-processor/src/main/java/com.service/OrderProcessingServiceApplication.java b/dapr/quickstarts/secrets_management/java/sdk/order-processor/src/main/java/com.service/OrderProcessingServiceApplication.java new file mode 100644 index 0000000..5269613 --- /dev/null +++ b/dapr/quickstarts/secrets_management/java/sdk/order-processor/src/main/java/com.service/OrderProcessingServiceApplication.java @@ -0,0 +1,16 @@ +package com.service; + +import io.dapr.client.DaprClient; +import io.dapr.client.DaprClientBuilder; + +import java.util.Map; + +public class OrderProcessingServiceApplication { + private static final String SECRET_STORE_NAME = "localsecretstore"; + + public static void main(String[] args) throws InterruptedException { + DaprClient client = new DaprClientBuilder().build(); + Map secret = client.getSecret(SECRET_STORE_NAME, "secret").block(); + System.out.println("Fetched Secret: " + secret); + } +} diff --git a/dapr/quickstarts/secrets_management/javascript/http/.gitignore b/dapr/quickstarts/secrets_management/javascript/http/.gitignore new file mode 100644 index 0000000..8afc710 --- /dev/null +++ b/dapr/quickstarts/secrets_management/javascript/http/.gitignore @@ -0,0 +1,5 @@ +##lint files +*.cjs + +##node modules +node_modules \ No newline at end of file diff --git a/dapr/quickstarts/secrets_management/javascript/http/README.md b/dapr/quickstarts/secrets_management/javascript/http/README.md new file mode 100644 index 0000000..0eca5e8 --- /dev/null +++ b/dapr/quickstarts/secrets_management/javascript/http/README.md @@ -0,0 +1,47 @@ +# Dapr secrets management (HTTP Client) + +In this quickstart, you'll create a microservice to demonstrate Dapr's secrets management API. The service fetches secret from a secret store. See [Why secrets management](#why-secrets-management) to understand when to use this API. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/secrets/) link for more information about Dapr and Secrets Management. + +> **Note:** This example leverages HTTP `requests` only. If you are looking for the example using the Dapr Client SDK (recommended) [click here](../sdk/). + +This quickstart includes one service: + +- Node client service `order-processor` + +### Run Node service with Dapr + +1. Navigate to folder and install dependencies: + + + +```bash +cd ./order-processor +npm install +``` + + +2. Run the Node service app with Dapr: + + + +```bash +dapr run --app-id order-processor --components-path ../../../components/ -- npm start +``` + + + +```bash +dapr stop --app-id order-processor +``` diff --git a/dapr/quickstarts/secrets_management/javascript/http/makefile b/dapr/quickstarts/secrets_management/javascript/http/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/secrets_management/javascript/http/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/secrets_management/javascript/http/order-processor/index.js b/dapr/quickstarts/secrets_management/javascript/http/order-processor/index.js new file mode 100644 index 0000000..9a5f3a6 --- /dev/null +++ b/dapr/quickstarts/secrets_management/javascript/http/order-processor/index.js @@ -0,0 +1,13 @@ +import axios from "axios"; + +const base_url = process.env.DAPR_HOST || "http://localhost" + ':' + process.env.DAPR_HTTP_PORT || "3500" +const DAPR_SECRET_STORE = "localsecretstore"; +const SECRET_NAME = "secret"; + +async function main() { + // Get secret from a local secret store + const secret = await axios.get(`${base_url}/v1.0/secrets/${DAPR_SECRET_STORE}/${SECRET_NAME}`); + console.log("Fetched Secret: ", secret.data); +} + +main().catch(e => console.error(e)) \ No newline at end of file diff --git a/dapr/quickstarts/secrets_management/javascript/http/order-processor/package.json b/dapr/quickstarts/secrets_management/javascript/http/order-processor/package.json new file mode 100644 index 0000000..20c68c9 --- /dev/null +++ b/dapr/quickstarts/secrets_management/javascript/http/order-processor/package.json @@ -0,0 +1,21 @@ +{ + "name": "order-processor", + "version": "1.0.0", + "description": "", + "main": "index.js", + "type": "module", + "scripts": { + "start": "node index.js", + "start:dapr": "dapr run --app-id checkout --app-protocol http --dapr-http-port 3500 -- npm run start" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "axios": "^0.25.0" + }, + "devDependencies": { + "eslint": "^8.8.0", + "eslint-plugin-react": "^7.28.0" + } +} diff --git a/dapr/quickstarts/secrets_management/javascript/http/order-processor/secrets.json b/dapr/quickstarts/secrets_management/javascript/http/order-processor/secrets.json new file mode 100644 index 0000000..105ba89 --- /dev/null +++ b/dapr/quickstarts/secrets_management/javascript/http/order-processor/secrets.json @@ -0,0 +1,3 @@ +{ + "secret": "YourPasskeyHere" +} diff --git a/dapr/quickstarts/secrets_management/javascript/sdk/.gitignore b/dapr/quickstarts/secrets_management/javascript/sdk/.gitignore new file mode 100644 index 0000000..8afc710 --- /dev/null +++ b/dapr/quickstarts/secrets_management/javascript/sdk/.gitignore @@ -0,0 +1,5 @@ +##lint files +*.cjs + +##node modules +node_modules \ No newline at end of file diff --git a/dapr/quickstarts/secrets_management/javascript/sdk/README.md b/dapr/quickstarts/secrets_management/javascript/sdk/README.md new file mode 100644 index 0000000..54f8329 --- /dev/null +++ b/dapr/quickstarts/secrets_management/javascript/sdk/README.md @@ -0,0 +1,51 @@ +# Dapr secrets management + +In this quickstart, you'll create a microservice to demonstrate Dapr's secrets management API. The service fetches secret from a secret store. See [Why secrets management](#why-secrets-management) to understand when to use this API. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/secrets/) link for more information about Dapr and Secrets Management. + +> **Note:** This example leverages the Dapr client SDK. If you are looking for the example using only HTTP [click here](../http). + +This quickstart includes one service: + +- Node client service `order-processor` + +This quickstart includes one service: + +- Node client service `order-processor` + +### Run Node service with Dapr + +1. Navigate to folder and install dependencies: + + + +```bash +cd ./order-processor +npm install +``` + + +2. Run the Node service app with Dapr: + + + +```bash +dapr run --app-id order-processor --components-path ../../../components/ -- npm start +``` + + + +```bash +dapr stop --app-id order-processor +``` diff --git a/dapr/quickstarts/secrets_management/javascript/sdk/makefile b/dapr/quickstarts/secrets_management/javascript/sdk/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/secrets_management/javascript/sdk/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/secrets_management/javascript/sdk/order-processor/index.js b/dapr/quickstarts/secrets_management/javascript/sdk/order-processor/index.js new file mode 100644 index 0000000..841c7e8 --- /dev/null +++ b/dapr/quickstarts/secrets_management/javascript/sdk/order-processor/index.js @@ -0,0 +1,14 @@ +import { DaprClient, CommunicationProtocolEnum } from '@dapr/dapr'; + +const DAPR_HOST = process.env.DAPR_HOST || "http://localhost"; +const DAPR_HTTP_PORT = process.env.DAPR_HTTP_PORT || "3500"; +const DAPR_SECRET_STORE = "localsecretstore"; +const SECRET_NAME = "secret"; + +async function main() { + const client = new DaprClient(DAPR_HOST, DAPR_HTTP_PORT, CommunicationProtocolEnum.HTTP); + const secret = await client.secret.get(DAPR_SECRET_STORE, SECRET_NAME); + console.log("Fetched Secret: " + JSON.stringify(secret)); +} + +main().catch(e => console.error(e)) \ No newline at end of file diff --git a/dapr/quickstarts/secrets_management/javascript/sdk/order-processor/package.json b/dapr/quickstarts/secrets_management/javascript/sdk/order-processor/package.json new file mode 100644 index 0000000..3b04924 --- /dev/null +++ b/dapr/quickstarts/secrets_management/javascript/sdk/order-processor/package.json @@ -0,0 +1,21 @@ +{ + "name": "order-processor", + "version": "1.0.0", + "description": "", + "main": "index.js", + "type": "module", + "scripts": { + "start": "node index.js", + "start:dapr": "dapr run --app-id checkout --app-protocol http --dapr-http-port 3500 -- npm run start" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@dapr/dapr": "^2.3.0" + }, + "devDependencies": { + "eslint": "^8.8.0", + "eslint-plugin-react": "^7.28.0" + } +} diff --git a/dapr/quickstarts/secrets_management/javascript/sdk/order-processor/secrets.json b/dapr/quickstarts/secrets_management/javascript/sdk/order-processor/secrets.json new file mode 100644 index 0000000..105ba89 --- /dev/null +++ b/dapr/quickstarts/secrets_management/javascript/sdk/order-processor/secrets.json @@ -0,0 +1,3 @@ +{ + "secret": "YourPasskeyHere" +} diff --git a/dapr/quickstarts/secrets_management/python/http/README.md b/dapr/quickstarts/secrets_management/python/http/README.md new file mode 100644 index 0000000..c6dde04 --- /dev/null +++ b/dapr/quickstarts/secrets_management/python/http/README.md @@ -0,0 +1,47 @@ +# Dapr secrets management (HTTP client) + +In this quickstart, you'll create a microservice to demonstrate Dapr's secrets management API. The service fetches secret from a secret store. See [Why secrets management](#why-secrets-management) to understand when to use this API. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/secrets/) link for more information about Dapr and Secrets Management. + +> **Note:** This example leverages HTTP `requests` only. If you are looking for the example using the Dapr Client SDK (recommended) [click here](../sdk/). + +This quickstart includes one service: + +- Python service `order-processor` + +### Run Python service with Dapr + +1. Open a new terminal window and navigate to `order-processor` directory: + + + +```bash +cd ./order-processor +pip3 install -r requirements.txt +``` + + +2. Run the Python service app with Dapr: + + + +```bash +cd ./order-processor +dapr run --app-id order-processor --components-path ../../../components/ -- python3 app.py +``` + + + +```bash +dapr stop --app-id order-processor +``` diff --git a/dapr/quickstarts/secrets_management/python/http/makefile b/dapr/quickstarts/secrets_management/python/http/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/secrets_management/python/http/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/secrets_management/python/http/order-processor/app.py b/dapr/quickstarts/secrets_management/python/http/order-processor/app.py new file mode 100644 index 0000000..873f223 --- /dev/null +++ b/dapr/quickstarts/secrets_management/python/http/order-processor/app.py @@ -0,0 +1,17 @@ +import time +import logging +import requests +import os + +logging.basicConfig(level=logging.INFO) + +base_url = os.getenv('BASE_URL', 'http://localhost') + ':' + os.getenv( + 'DAPR_HTTP_PORT', '3500') +DAPR_SECRET_STORE = 'localsecretstore' +SECRET_NAME = 'secret' + +# Get secret from a local secret store +secret = requests.get( + url='%s/v1.0/secrets/%s/%s' % (base_url, DAPR_SECRET_STORE, SECRET_NAME) +) +logging.info('Fetched Secret: ' + str(secret.json())) diff --git a/dapr/quickstarts/secrets_management/python/http/order-processor/requirements.txt b/dapr/quickstarts/secrets_management/python/http/order-processor/requirements.txt new file mode 100644 index 0000000..f229360 --- /dev/null +++ b/dapr/quickstarts/secrets_management/python/http/order-processor/requirements.txt @@ -0,0 +1 @@ +requests diff --git a/dapr/quickstarts/secrets_management/python/http/order-processor/secrets.json b/dapr/quickstarts/secrets_management/python/http/order-processor/secrets.json new file mode 100644 index 0000000..105ba89 --- /dev/null +++ b/dapr/quickstarts/secrets_management/python/http/order-processor/secrets.json @@ -0,0 +1,3 @@ +{ + "secret": "YourPasskeyHere" +} diff --git a/dapr/quickstarts/secrets_management/python/sdk/README.md b/dapr/quickstarts/secrets_management/python/sdk/README.md new file mode 100644 index 0000000..ef267c3 --- /dev/null +++ b/dapr/quickstarts/secrets_management/python/sdk/README.md @@ -0,0 +1,49 @@ +# Dapr secrets management + +In this quickstart, you'll create a microservice to demonstrate Dapr's secrets management API. The service fetches secret from a secret store. See [Why secrets management](#why-secrets-management) to understand when to use this API. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/secrets/) link for more information about Dapr and Secrets Management. + +> **Note:** This example leverages the Dapr client SDK. If you are looking for the example using only HTTP `requests` [click here](../http). + +This quickstart includes one service: + +- Python service `order-processor` + +### Run Python service with Dapr + +1. Open a new terminal window and navigate to `order-processor` directory: + + + +```bash +cd ./order-processor +pip3 install -r requirements.txt +``` + + +2. Run the Python service app with Dapr: + + + +```bash +cd ./order-processor +dapr run --app-id order-processor --components-path ../../../components/ -- python3 app.py +``` + + + +```bash +dapr stop --app-id order-processor +``` diff --git a/dapr/quickstarts/secrets_management/python/sdk/makefile b/dapr/quickstarts/secrets_management/python/sdk/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/secrets_management/python/sdk/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/secrets_management/python/sdk/order-processor/app.py b/dapr/quickstarts/secrets_management/python/sdk/order-processor/app.py new file mode 100644 index 0000000..093d663 --- /dev/null +++ b/dapr/quickstarts/secrets_management/python/sdk/order-processor/app.py @@ -0,0 +1,10 @@ +import logging +from dapr.clients import DaprClient + +logging.basicConfig(level=logging.INFO) + +DAPR_SECRET_STORE = 'localsecretstore' +SECRET_NAME = 'secret' +with DaprClient() as client: + secret = client.get_secret(store_name=DAPR_SECRET_STORE, key=SECRET_NAME) + logging.info('Fetched Secret: %s', secret.secret) diff --git a/dapr/quickstarts/secrets_management/python/sdk/order-processor/requirements.txt b/dapr/quickstarts/secrets_management/python/sdk/order-processor/requirements.txt new file mode 100644 index 0000000..53c5975 --- /dev/null +++ b/dapr/quickstarts/secrets_management/python/sdk/order-processor/requirements.txt @@ -0,0 +1 @@ +dapr diff --git a/dapr/quickstarts/secrets_management/python/sdk/order-processor/secrets.json b/dapr/quickstarts/secrets_management/python/sdk/order-processor/secrets.json new file mode 100644 index 0000000..105ba89 --- /dev/null +++ b/dapr/quickstarts/secrets_management/python/sdk/order-processor/secrets.json @@ -0,0 +1,3 @@ +{ + "secret": "YourPasskeyHere" +} diff --git a/dapr/quickstarts/service_invocation/csharp/http/README.md b/dapr/quickstarts/service_invocation/csharp/http/README.md new file mode 100644 index 0000000..7dbe666 --- /dev/null +++ b/dapr/quickstarts/service_invocation/csharp/http/README.md @@ -0,0 +1,90 @@ +# Service Invocation + +In this quickstart, you'll create a checkout service and an order processor service to demonstrate how to use the service invocation API. The checkout service uses Dapr's http proxying capability to invoke a method on the order processing service. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/service-invocation/) link for more information about Dapr and service invocation. + +This quickstart includes one checkout service: + +- Dotnet client service `checkout` + +And one order processor service: + +- Dotnet order-processor service `order-processor` + +### Run Dotnet order-processor with Dapr + +1. Open a new terminal window and navigate to `order-processor` directory and install dependencies: + + + +```bash +cd ./order-processor +dotnet restore +dotnet build +``` + + + +2. Run the Dotnet order-processor app with Dapr: + + + +```bash +cd ./order-processor +dapr run --app-port 7001 --app-id order-processor --app-protocol http --dapr-http-port 3501 -- dotnet run +``` + + + +### Run Dotnet checkout with Dapr + +1. Open a new terminal window and navigate to the `checkout` directory and install dependencies: + + + +```bash +cd ./checkout +dotnet restore +dotnet build +``` + + + +2. Run the Dotnet checkout app with Dapr: + + + +```bash +cd ./checkout +dapr run --app-id checkout --app-protocol http --dapr-http-port 3500 -- dotnet run +``` + + + +```bash +dapr stop --app-id order-processor +``` diff --git a/dapr/quickstarts/service_invocation/csharp/http/checkout.sln b/dapr/quickstarts/service_invocation/csharp/http/checkout.sln new file mode 100644 index 0000000..703e373 --- /dev/null +++ b/dapr/quickstarts/service_invocation/csharp/http/checkout.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.32112.339 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "checkout", "checkout\checkout.csproj", "{636C3192-12CE-4884-AC45-B7C8A276075A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {636C3192-12CE-4884-AC45-B7C8A276075A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {636C3192-12CE-4884-AC45-B7C8A276075A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {636C3192-12CE-4884-AC45-B7C8A276075A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {636C3192-12CE-4884-AC45-B7C8A276075A}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {49215BFE-48D9-44C4-A8F6-374E24D59DED} + EndGlobalSection +EndGlobal diff --git a/dapr/quickstarts/service_invocation/csharp/http/checkout/Program.cs b/dapr/quickstarts/service_invocation/csharp/http/checkout/Program.cs new file mode 100644 index 0000000..5362071 --- /dev/null +++ b/dapr/quickstarts/service_invocation/csharp/http/checkout/Program.cs @@ -0,0 +1,24 @@ +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; + +var baseURL = (Environment.GetEnvironmentVariable("BASE_URL") ?? "http://localhost") + ":" + (Environment.GetEnvironmentVariable("DAPR_HTTP_PORT") ?? "3500"); + +var client = new HttpClient(); +client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json")); +// Adding app id as part of the header +client.DefaultRequestHeaders.Add("dapr-app-id", "order-processor"); + +for (int i = 1; i <= 10; i++) { + var order = new Order(i); + var orderJson = JsonSerializer.Serialize(order); + var content = new StringContent(orderJson, Encoding.UTF8, "application/json"); + + // Invoking a service + var response = await client.PostAsync($"{baseURL}/orders", content); + Console.WriteLine("Order passed: " + order); + + await Task.Delay(TimeSpan.FromSeconds(1)); +} + +public record Order([property: JsonPropertyName("orderId")] int OrderId); diff --git a/dapr/quickstarts/service_invocation/csharp/http/checkout/checkout.csproj b/dapr/quickstarts/service_invocation/csharp/http/checkout/checkout.csproj new file mode 100644 index 0000000..3897747 --- /dev/null +++ b/dapr/quickstarts/service_invocation/csharp/http/checkout/checkout.csproj @@ -0,0 +1,10 @@ + + + + Exe + net6.0 + enable + enable + + + diff --git a/dapr/quickstarts/service_invocation/csharp/http/makefile b/dapr/quickstarts/service_invocation/csharp/http/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/service_invocation/csharp/http/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/service_invocation/csharp/http/order-processor/Program.cs b/dapr/quickstarts/service_invocation/csharp/http/order-processor/Program.cs new file mode 100644 index 0000000..53501ac --- /dev/null +++ b/dapr/quickstarts/service_invocation/csharp/http/order-processor/Program.cs @@ -0,0 +1,18 @@ +var builder = WebApplication.CreateBuilder(args); + +var app = builder.Build(); + +if (app.Environment.IsDevelopment()) +{ + app.UseDeveloperExceptionPage(); +} + +app.MapPost("/orders", (Order order) => +{ + Console.WriteLine("Order received : " + order); + return order.ToString(); +}); + +await app.RunAsync(); + +public record Order(int orderId); diff --git a/dapr/quickstarts/service_invocation/csharp/http/order-processor/Properties/launchSettings.json b/dapr/quickstarts/service_invocation/csharp/http/order-processor/Properties/launchSettings.json new file mode 100644 index 0000000..673fe7e --- /dev/null +++ b/dapr/quickstarts/service_invocation/csharp/http/order-processor/Properties/launchSettings.json @@ -0,0 +1,15 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "CheckoutService": { + "commandName": "Project", + "dotnetRunMessages": "true", + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:7001", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/dapr/quickstarts/service_invocation/csharp/http/order-processor/appsettings.Development.json b/dapr/quickstarts/service_invocation/csharp/http/order-processor/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/dapr/quickstarts/service_invocation/csharp/http/order-processor/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/dapr/quickstarts/service_invocation/csharp/http/order-processor/appsettings.json b/dapr/quickstarts/service_invocation/csharp/http/order-processor/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/dapr/quickstarts/service_invocation/csharp/http/order-processor/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/dapr/quickstarts/service_invocation/csharp/http/order-processor/order-processor.csproj b/dapr/quickstarts/service_invocation/csharp/http/order-processor/order-processor.csproj new file mode 100644 index 0000000..6f159fe --- /dev/null +++ b/dapr/quickstarts/service_invocation/csharp/http/order-processor/order-processor.csproj @@ -0,0 +1,14 @@ + + + + net6.0 + enable + enable + Linux + + + + + + + diff --git a/dapr/quickstarts/service_invocation/go/http/README.md b/dapr/quickstarts/service_invocation/go/http/README.md new file mode 100644 index 0000000..fdd02f8 --- /dev/null +++ b/dapr/quickstarts/service_invocation/go/http/README.md @@ -0,0 +1,88 @@ +# Service Invocation + +In this quickstart, you'll create a checkout service and an order processor service to demonstrate how to use the service invocation API. The checkout service uses Dapr's http proxying capability to invoke a method on the order processing service. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/service-invocation/) link for more information about Dapr and service invocation. + +This quickstart includes one checkout service: + +- Go client service `checkout` + +And one order processor service: + +- Go order-processor service `order-processor` + +### Run Go order-processor with Dapr + +1. Open a new terminal window and navigate to `order-processor` directory: + + + +```bash +cd ./order-processor +go build . +``` + + + +2. Run the Go order-processor app with Dapr: + + + +```bash +cd ./order-processor +dapr run --app-port 6001 --app-id order-processor --app-protocol http --dapr-http-port 3501 -- go run . +``` + + + +### Run Go checkout with Dapr + +1. Open a new terminal window and navigate to `checkout` directory: + + + +```bash +cd ./checkout +go build . +``` + + +2. Run the Go checkout app with Dapr: + + + +```bash +cd ./checkout +dapr run --app-id checkout --app-protocol http --dapr-http-port 3500 -- go run . +``` + + + +```bash +dapr stop --app-id checkout +dapr stop --app-id order-processor +``` diff --git a/dapr/quickstarts/service_invocation/go/http/checkout/app.go b/dapr/quickstarts/service_invocation/go/http/checkout/app.go new file mode 100644 index 0000000..a917245 --- /dev/null +++ b/dapr/quickstarts/service_invocation/go/http/checkout/app.go @@ -0,0 +1,48 @@ +package main + +import ( + "fmt" + "io/ioutil" + "log" + "net/http" + "os" + "strconv" + "strings" +) + +func main() { + var DAPR_HOST, DAPR_HTTP_PORT string + var okHost, okPort bool + if DAPR_HOST, okHost = os.LookupEnv("DAPR_HOST"); !okHost { + DAPR_HOST = "http://localhost" + } + if DAPR_HTTP_PORT, okPort = os.LookupEnv("DAPR_HTTP_PORT"); !okPort { + DAPR_HTTP_PORT = "3500" + } + for i := 1; i <= 10; i++ { + order := "{\"orderId\":" + strconv.Itoa(i) + "}" + client := &http.Client{} + req, err := http.NewRequest("POST", DAPR_HOST+":"+DAPR_HTTP_PORT+"/orders", strings.NewReader(order)) + if err != nil { + fmt.Print(err.Error()) + os.Exit(1) + } + // Adding app id as part of th header + req.Header.Add("dapr-app-id", "order-processor") + + // Invoking a service + response, err := client.Do(req) + + if err != nil { + fmt.Print(err.Error()) + os.Exit(1) + } + + result, err := ioutil.ReadAll(response.Body) + if err != nil { + log.Fatal(err) + } + + fmt.Println("Order passed: ", string(result)) + } +} diff --git a/dapr/quickstarts/service_invocation/go/http/checkout/go.mod b/dapr/quickstarts/service_invocation/go/http/checkout/go.mod new file mode 100644 index 0000000..0c937a4 --- /dev/null +++ b/dapr/quickstarts/service_invocation/go/http/checkout/go.mod @@ -0,0 +1,4 @@ +// module dapr_example +module checkout + +go 1.18 diff --git a/dapr/quickstarts/service_invocation/go/http/checkout/go.sum b/dapr/quickstarts/service_invocation/go/http/checkout/go.sum new file mode 100644 index 0000000..e69de29 diff --git a/dapr/quickstarts/service_invocation/go/http/makefile b/dapr/quickstarts/service_invocation/go/http/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/service_invocation/go/http/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/service_invocation/go/http/order-processor/app.go b/dapr/quickstarts/service_invocation/go/http/order-processor/app.go new file mode 100644 index 0000000..d1ca75f --- /dev/null +++ b/dapr/quickstarts/service_invocation/go/http/order-processor/app.go @@ -0,0 +1,28 @@ +package main + +import ( + "fmt" + "io/ioutil" + "log" + "net/http" + + "github.com/gorilla/mux" +) + +func getOrder(w http.ResponseWriter, r *http.Request) { + data, err := ioutil.ReadAll(r.Body) + if err != nil { + log.Fatal(err) + } + fmt.Println("Order received : ", string(data)) + _, err = w.Write(data) + if err != nil { + log.Println("Error in writing the result obj") + } +} + +func main() { + r := mux.NewRouter() + r.HandleFunc("/orders", getOrder).Methods("POST") + _ = http.ListenAndServe(":6001", r) +} diff --git a/dapr/quickstarts/service_invocation/go/http/order-processor/go.mod b/dapr/quickstarts/service_invocation/go/http/order-processor/go.mod new file mode 100644 index 0000000..66749be --- /dev/null +++ b/dapr/quickstarts/service_invocation/go/http/order-processor/go.mod @@ -0,0 +1,5 @@ +module dapr_example + +go 1.18 + +require github.com/gorilla/mux v1.8.0 diff --git a/dapr/quickstarts/service_invocation/go/http/order-processor/go.sum b/dapr/quickstarts/service_invocation/go/http/order-processor/go.sum new file mode 100644 index 0000000..5350288 --- /dev/null +++ b/dapr/quickstarts/service_invocation/go/http/order-processor/go.sum @@ -0,0 +1,2 @@ +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= diff --git a/dapr/quickstarts/service_invocation/java/http/README.md b/dapr/quickstarts/service_invocation/java/http/README.md new file mode 100644 index 0000000..557f44b --- /dev/null +++ b/dapr/quickstarts/service_invocation/java/http/README.md @@ -0,0 +1,95 @@ +# Service Invocation + +In this quickstart, you'll create a checkout service and an order processor service to demonstrate how to use the service invocation API. The checkout service uses Dapr's http proxying capability to invoke a method on the order processing service. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/service-invocation/) link for more information about Dapr and service invocation. + +This quickstart includes one checkout service: + +- Java client service `checkout` + +And one order processor service: + +- Java order-processor service `order-processor` + +## Pre-requisites + +* [Dapr and Dapr Cli](https://docs.dapr.io/getting-started/install-dapr-cli/). +* Java JDK 11 (or greater): + * [Microsoft JDK 11](https://docs.microsoft.com/en-us/java/openjdk/download#openjdk-11) + * [Oracle JDK 11](https://www.oracle.com/technetwork/java/javase/downloads/index.html#JDK11) + * [OpenJDK 11](https://jdk.java.net/11/) +* [Apache Maven](https://maven.apache.org/install.html) version 3.x. + +### Run Java order-processor with Dapr + +1. Open a new terminal window and navigate to `order-processor` directory and install dependencies: + + + +```bash +cd ./order-processor +mvn clean install +``` + + +2. Run the Java order-processor app with Dapr: + + + +```bash +cd ./order-processor +dapr run --app-id order-processor --app-port 9001 --app-protocol http --dapr-http-port 3501 -- java -jar target/OrderProcessingService-0.0.1-SNAPSHOT.jar +``` + + + +### Run Java checkout service with Dapr + +1. Open a new terminal window and navigate to `checkout` directory and install dependencies: + + + +```bash +cd ./checkout +mvn clean install +``` + + +2. Run the Java checkout app with Dapr: + + + +```bash +cd ./checkout +dapr run --app-id checkout --app-protocol http --dapr-http-port 3500 -- java -jar target/CheckoutService-0.0.1-SNAPSHOT.jar +``` + + + +```bash +dapr stop --app-id checkout +dapr stop --app-id order-processor +``` diff --git a/dapr/quickstarts/service_invocation/java/http/checkout/pom.xml b/dapr/quickstarts/service_invocation/java/http/checkout/pom.xml new file mode 100644 index 0000000..2b6dfca --- /dev/null +++ b/dapr/quickstarts/service_invocation/java/http/checkout/pom.xml @@ -0,0 +1,45 @@ + + + 4.0.0 + + com.service + CheckoutService + 0.0.1-SNAPSHOT + CheckoutService + Demo for Dapr service invocation component + + 11 + 11 + 1.6.1 + + + + org.json + json + 20211205 + + + + + + org.springframework.boot + spring-boot-maven-plugin + 2.6.3 + + + + repackage + + + + com.service.CheckoutServiceApplication + + + + + + + + + diff --git a/dapr/quickstarts/service_invocation/java/http/checkout/src/main/java/com/service/CheckoutServiceApplication.java b/dapr/quickstarts/service_invocation/java/http/checkout/src/main/java/com/service/CheckoutServiceApplication.java new file mode 100644 index 0000000..87ae3cf --- /dev/null +++ b/dapr/quickstarts/service_invocation/java/http/checkout/src/main/java/com/service/CheckoutServiceApplication.java @@ -0,0 +1,40 @@ +package com.service; + +import org.json.JSONObject; + +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.time.Duration; +import java.util.concurrent.TimeUnit; + +public class CheckoutServiceApplication { + private static final HttpClient httpClient = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_2) + .connectTimeout(Duration.ofSeconds(10)) + .build(); + + private static final String DAPR_HTTP_PORT = System.getenv().getOrDefault("DAPR_HTTP_PORT", "3500"); + + public static void main(String[] args) throws InterruptedException, IOException { + String dapr_url = "http://localhost:" + DAPR_HTTP_PORT + "/orders"; + for (int i=1; i<=10; i++) { + int orderId = i; + JSONObject obj = new JSONObject(); + obj.put("orderId", orderId); + + HttpRequest request = HttpRequest.newBuilder() + .POST(HttpRequest.BodyPublishers.ofString(obj.toString())) + .uri(URI.create(dapr_url)) + .header("Content-Type", "application/json") + .header("dapr-app-id", "order-processor") + .build(); + + HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println("Order passed: "+ orderId); + TimeUnit.MILLISECONDS.sleep(1000); + } + } +} diff --git a/dapr/quickstarts/service_invocation/java/http/makefile b/dapr/quickstarts/service_invocation/java/http/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/service_invocation/java/http/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/service_invocation/java/http/order-processor/pom.xml b/dapr/quickstarts/service_invocation/java/http/order-processor/pom.xml new file mode 100644 index 0000000..942165d --- /dev/null +++ b/dapr/quickstarts/service_invocation/java/http/order-processor/pom.xml @@ -0,0 +1,39 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.6.3 + + + com.service + OrderProcessingService + 0.0.1-SNAPSHOT + OrderProcessingService + Demo for Dapr service invocation component + + 11 + + + + org.springframework.boot + spring-boot-starter-web + + + org.projectlombok + lombok + true + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/dapr/quickstarts/service_invocation/java/http/order-processor/src/main/java/com/service/OrderProcessingServiceApplication.java b/dapr/quickstarts/service_invocation/java/http/order-processor/src/main/java/com/service/OrderProcessingServiceApplication.java new file mode 100644 index 0000000..733f414 --- /dev/null +++ b/dapr/quickstarts/service_invocation/java/http/order-processor/src/main/java/com/service/OrderProcessingServiceApplication.java @@ -0,0 +1,11 @@ +package com.service; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class OrderProcessingServiceApplication { + public static void main(String[] args) { + SpringApplication.run(OrderProcessingServiceApplication.class, args); + } +} diff --git a/dapr/quickstarts/service_invocation/java/http/order-processor/src/main/java/com/service/controller/OrderProcessingServiceController.java b/dapr/quickstarts/service_invocation/java/http/order-processor/src/main/java/com/service/controller/OrderProcessingServiceController.java new file mode 100644 index 0000000..a0ddec6 --- /dev/null +++ b/dapr/quickstarts/service_invocation/java/http/order-processor/src/main/java/com/service/controller/OrderProcessingServiceController.java @@ -0,0 +1,16 @@ +package com.service.controller; + +import com.service.model.Order; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class OrderProcessingServiceController { + @PostMapping(path = "/orders", consumes = MediaType.ALL_VALUE) + public String processOrders(@RequestBody Order body) { + System.out.println("Order received: "+ body.getOrderId()); + return "CID" + body.getOrderId(); + } +} diff --git a/dapr/quickstarts/service_invocation/java/http/order-processor/src/main/java/com/service/model/Order.java b/dapr/quickstarts/service_invocation/java/http/order-processor/src/main/java/com/service/model/Order.java new file mode 100644 index 0000000..803e66c --- /dev/null +++ b/dapr/quickstarts/service_invocation/java/http/order-processor/src/main/java/com/service/model/Order.java @@ -0,0 +1,10 @@ +package com.service.model; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class Order { + private int orderId; +} diff --git a/dapr/quickstarts/service_invocation/java/http/order-processor/src/main/resources/application.properties b/dapr/quickstarts/service_invocation/java/http/order-processor/src/main/resources/application.properties new file mode 100644 index 0000000..1432221 --- /dev/null +++ b/dapr/quickstarts/service_invocation/java/http/order-processor/src/main/resources/application.properties @@ -0,0 +1 @@ +server.port=${port:9001} diff --git a/dapr/quickstarts/service_invocation/javascript/http/README.md b/dapr/quickstarts/service_invocation/javascript/http/README.md new file mode 100644 index 0000000..5cfde66 --- /dev/null +++ b/dapr/quickstarts/service_invocation/javascript/http/README.md @@ -0,0 +1,89 @@ +# Service Invocation + +In this quickstart, you'll create a checkout service and an order processor service to demonstrate how to use the service invocation API. The checkout service uses Dapr's http proxying capability to invoke a method on the order processing service. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/service-invocation/) link for more information about Dapr and service invocation. + +This quickstart includes one checkout service: + +- Node client service `checkout` + +And one order-processor service: + +- Node order-processor service `order-processor` + +### Run Node order-processor with Dapr + +1. Open a new terminal window and navigate to `order-processor` directory and install dependencies: + + + +```bash +cd ./order-processor +npm install +``` + + + +3. Run the Node order-processor app with Dapr: + + + +```bash +cd ./order-processor +dapr run --app-port 5001 --app-id order-processor --app-protocol http --dapr-http-port 3501 -- npm start +``` + + + +### Run Node checkout with Dapr + +1. Open a new terminal window and navigate to `checkout` directory and install dependencies: + + + +```bash +cd ./checkout +npm install +``` + + + +2. Run the Node checkout app with Dapr: + + + +```bash +cd ./checkout +dapr run --app-id checkout --app-protocol http --dapr-http-port 3500 -- npm start +``` + + + +```bash +dapr stop --app-id checkout +dapr stop --app-id order-processor +``` diff --git a/dapr/quickstarts/service_invocation/javascript/http/checkout/index.js b/dapr/quickstarts/service_invocation/javascript/http/checkout/index.js new file mode 100644 index 0000000..a56bb8e --- /dev/null +++ b/dapr/quickstarts/service_invocation/javascript/http/checkout/index.js @@ -0,0 +1,29 @@ +import axios from "axios"; + +const DAPR_HOST = process.env.DAPR_HOST || "http://localhost"; +const DAPR_HTTP_PORT = process.env.DAPR_HTTP_PORT || "3500"; + +async function main() { + // Adding app id as part of the header + let axiosConfig = { + headers: { + "dapr-app-id": "order-processor" + } + }; + + for(var i = 1; i <= 10; i++) { + const order = {orderId: i}; + + // Invoking a service + const res = await axios.post(`${DAPR_HOST}:${DAPR_HTTP_PORT}/orders`, order , axiosConfig); + console.log("Order passed: " + res.config.data); + + await sleep(1000); + } +} + +async function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +main().catch(e => console.error(e)) \ No newline at end of file diff --git a/dapr/quickstarts/service_invocation/javascript/http/checkout/package.json b/dapr/quickstarts/service_invocation/javascript/http/checkout/package.json new file mode 100644 index 0000000..d8793ce --- /dev/null +++ b/dapr/quickstarts/service_invocation/javascript/http/checkout/package.json @@ -0,0 +1,21 @@ +{ + "name": "checkout", + "version": "1.0.0", + "description": "", + "main": "index.js", + "type": "module", + "scripts": { + "start": "node index.js", + "start:dapr": "dapr run --app-id checkout --app-protocol http --dapr-http-port 3500 -- npm run start" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "axios": "^0.26.0" + }, + "devDependencies": { + "eslint": "^8.9.0", + "eslint-plugin-react": "^7.28.0" + } +} diff --git a/dapr/quickstarts/service_invocation/javascript/http/makefile b/dapr/quickstarts/service_invocation/javascript/http/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/service_invocation/javascript/http/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/service_invocation/javascript/http/order-processor/index.js b/dapr/quickstarts/service_invocation/javascript/http/order-processor/index.js new file mode 100644 index 0000000..7b6c9c1 --- /dev/null +++ b/dapr/quickstarts/service_invocation/javascript/http/order-processor/index.js @@ -0,0 +1,11 @@ +import express from 'express'; + +const app = express(); +app.use(express.json()); + +app.post('/orders', (req, res) => { + console.log("Order received:", req.body); + res.sendStatus(200); +}); + +app.listen(5001); \ No newline at end of file diff --git a/dapr/quickstarts/service_invocation/javascript/http/order-processor/package.json b/dapr/quickstarts/service_invocation/javascript/http/order-processor/package.json new file mode 100644 index 0000000..cee87ad --- /dev/null +++ b/dapr/quickstarts/service_invocation/javascript/http/order-processor/package.json @@ -0,0 +1,21 @@ +{ + "name": "order-processor", + "version": "1.0.0", + "description": "", + "main": "index.js", + "type": "module", + "scripts": { + "start": "node index.js", + "start:dapr": "dapr run --app-port 5001 --app-id order-processing --app-protocol http --dapr-http-port 3501 -- npm run start" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "express": "^4.17.2" + }, + "devDependencies": { + "eslint": "^8.9.0", + "eslint-plugin-react": "^7.28.0" + } +} diff --git a/dapr/quickstarts/service_invocation/python/http/README.md b/dapr/quickstarts/service_invocation/python/http/README.md new file mode 100644 index 0000000..ad3d117 --- /dev/null +++ b/dapr/quickstarts/service_invocation/python/http/README.md @@ -0,0 +1,89 @@ +# Service Invocation + +In this quickstart, you'll create a checkout service and an order processor service to demonstrate how to use the service invocation API. The checkout service uses Dapr's http proxying capability to invoke a method on the order processing service. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/service-invocation/) link for more information about Dapr and service invocation. + +This quickstart includes one checkout service: + +- Python client service `checkout` + +And one order processor service: + +- Python order-processor service `order-processor` + +### Run Python order-processor with Dapr + +1. Open a new terminal window and navigate to `order-processor` directory and install dependencies: + + + +```bash +cd ./order-processor +pip3 install -r requirements.txt +``` + + + +2. Run the Python order-processor app with Dapr: + + + +```bash +cd ./order-processor +dapr run --app-port 8001 --app-id order-processor --app-protocol http --dapr-http-port 3501 -- python3 app.py +``` + + + +### Run Python checkout with Dapr + +1. Open a new terminal window and navigate to `checkout` directory and install dependencies: + + + +```bash +cd ./checkout +pip3 install -r requirements.txt +``` + + + +2. Run the Python checkout app with Dapr: + + + +```bash +cd ./checkout +dapr run --app-id checkout --app-protocol http --dapr-http-port 3500 -- python3 app.py +``` + + + +```bash +dapr stop --app-id checkout +dapr stop --app-id order-processor +``` diff --git a/dapr/quickstarts/service_invocation/python/http/checkout/app.py b/dapr/quickstarts/service_invocation/python/http/checkout/app.py new file mode 100644 index 0000000..84a9e1d --- /dev/null +++ b/dapr/quickstarts/service_invocation/python/http/checkout/app.py @@ -0,0 +1,25 @@ +import json +import time +import logging +import requests +import os + +logging.basicConfig(level=logging.INFO) + +base_url = os.getenv('BASE_URL', 'http://localhost') + ':' + os.getenv( + 'DAPR_HTTP_PORT', '3500') +# Adding app id as part of the header +headers = {'dapr-app-id': 'order-processor', 'content-type': 'application/json'} + +for i in range(1, 11): + order = {'orderId': i} + + # Invoking a service + result = requests.post( + url='%s/orders' % (base_url), + data=json.dumps(order), + headers=headers + ) + print('Order passed: ' + json.dumps(order)) + + time.sleep(1) diff --git a/dapr/quickstarts/service_invocation/python/http/checkout/requirements.txt b/dapr/quickstarts/service_invocation/python/http/checkout/requirements.txt new file mode 100644 index 0000000..f229360 --- /dev/null +++ b/dapr/quickstarts/service_invocation/python/http/checkout/requirements.txt @@ -0,0 +1 @@ +requests diff --git a/dapr/quickstarts/service_invocation/python/http/makefile b/dapr/quickstarts/service_invocation/python/http/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/service_invocation/python/http/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/service_invocation/python/http/order-processor/app.py b/dapr/quickstarts/service_invocation/python/http/order-processor/app.py new file mode 100644 index 0000000..dc52e45 --- /dev/null +++ b/dapr/quickstarts/service_invocation/python/http/order-processor/app.py @@ -0,0 +1,15 @@ +from flask import Flask, request +import json + +app = Flask(__name__) + + +@app.route('/orders', methods=['POST']) +def getOrder(): + data = request.json + print('Order received : ' + json.dumps(data), flush=True) + return json.dumps({'success': True}), 200, { + 'ContentType': 'application/json'} + + +app.run(port=8001) diff --git a/dapr/quickstarts/service_invocation/python/http/order-processor/requirements.txt b/dapr/quickstarts/service_invocation/python/http/order-processor/requirements.txt new file mode 100644 index 0000000..0649e2d --- /dev/null +++ b/dapr/quickstarts/service_invocation/python/http/order-processor/requirements.txt @@ -0,0 +1,2 @@ +Flask +dapr diff --git a/dapr/quickstarts/state_management/components/statestore.yaml b/dapr/quickstarts/state_management/components/statestore.yaml new file mode 100644 index 0000000..0681c0c --- /dev/null +++ b/dapr/quickstarts/state_management/components/statestore.yaml @@ -0,0 +1,14 @@ +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: statestore +spec: + type: state.redis + version: v1 + metadata: + - name: redisHost + value: localhost:6379 + - name: redisPassword + value: "" + - name: actorStateStore + value: "true" \ No newline at end of file diff --git a/dapr/quickstarts/state_management/csharp/http/README.md b/dapr/quickstarts/state_management/csharp/http/README.md new file mode 100644 index 0000000..b46d369 --- /dev/null +++ b/dapr/quickstarts/state_management/csharp/http/README.md @@ -0,0 +1,51 @@ +# Dapr state management (HTTP Client) + +In this quickstart, you'll create a microservice to demonstrate Dapr's state management API. The service generates messages to store in a state store. See [Why state management](#why-state-management) to understand when to use this API. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/state-management/) link for more information about Dapr and State Management. + +> **Note:** This example leverages HTTP `requests` only. If you are looking for the example using the Dapr Client SDK (recommended) [click here](../sdk/). + +This quickstart includes one service: + +- Dotnet client service `order-processor` + +### Run Dotnet service with Dapr + +1. Open a new terminal window and navigate to `order-processor` directory: + + + +```bash +cd ./order-processor +dotnet restore +dotnet build +``` + + +2. Run the Dotnet service app with Dapr: + + + +```bash +cd ./order-processor +dapr run --app-id order-processor --components-path ../../../components/ -- dotnet run +``` + + + +```bash +dapr stop --app-id order-processor +``` diff --git a/dapr/quickstarts/state_management/csharp/http/makefile b/dapr/quickstarts/state_management/csharp/http/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/state_management/csharp/http/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/state_management/csharp/http/order-processor/Program.cs b/dapr/quickstarts/state_management/csharp/http/order-processor/Program.cs new file mode 100644 index 0000000..cce83d2 --- /dev/null +++ b/dapr/quickstarts/state_management/csharp/http/order-processor/Program.cs @@ -0,0 +1,40 @@ +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; + +var baseURL = (Environment.GetEnvironmentVariable("BASE_URL") ?? "http://localhost") + ":" ++ (Environment.GetEnvironmentVariable("DAPR_HTTP_PORT") ?? "3500"); +const string DAPR_STATE_STORE = "statestore"; + +var httpClient = new HttpClient(); +httpClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json")); + +for (int i = 1; i <= 10; i++) { + var orderId = i; + var order = new Order(orderId); + var orderJson = JsonSerializer.Serialize( + new[] { + new { + key = orderId.ToString(), + value = order + } + } + ); + var state = new StringContent(orderJson, Encoding.UTF8, "application/json"); + + // Save state into a state store + await httpClient.PostAsync($"{baseURL}/v1.0/state/{DAPR_STATE_STORE}", state); + Console.WriteLine("Saving Order: " + order); + + // Get state from a state store + var response = await httpClient.GetStringAsync($"{baseURL}/v1.0/state/{DAPR_STATE_STORE}/{orderId.ToString()}"); + Console.WriteLine("Getting Order: " + response); + + // Delete state from the state store + await httpClient.DeleteAsync($"{baseURL}/v1.0/state/{DAPR_STATE_STORE}/{orderId.ToString()}"); + Console.WriteLine("Deleting Order: " + order); + + await Task.Delay(TimeSpan.FromSeconds(1)); +} + +public record Order([property: JsonPropertyName("orderId")] int orderId); \ No newline at end of file diff --git a/dapr/quickstarts/state_management/csharp/http/order-processor/Program.csproj b/dapr/quickstarts/state_management/csharp/http/order-processor/Program.csproj new file mode 100644 index 0000000..3897747 --- /dev/null +++ b/dapr/quickstarts/state_management/csharp/http/order-processor/Program.csproj @@ -0,0 +1,10 @@ + + + + Exe + net6.0 + enable + enable + + + diff --git a/dapr/quickstarts/state_management/csharp/sdk/README.md b/dapr/quickstarts/state_management/csharp/sdk/README.md new file mode 100644 index 0000000..eac9836 --- /dev/null +++ b/dapr/quickstarts/state_management/csharp/sdk/README.md @@ -0,0 +1,51 @@ +# Dapr state management + +In this quickstart, you'll create a microservice to demonstrate Dapr's state management API. The service generates messages to store data in a state store. See [Why state management](#why-state-management) to understand when this pattern might be a good choice for your software architecture. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/state-management/) link for more information about Dapr and State Management. + +> **Note:** This example leverages the Dapr client SDK. If you are looking for the example using only HTTP [click here](../http). + +This quickstart includes one service: + +- Dotnet client service `order-processor` + +### Run Dotnet service with Dapr + +1. Open a new terminal window and navigate to `order-processor` directory: + + + +```bash +cd ./order-processor +dotnet restore +dotnet build +``` + + +2. Run the Dotnet service app with Dapr: + + + +```bash +cd ./order-processor +dapr run --app-id order-processor --components-path ../../../components/ -- dotnet run +``` + + + +```bash +dapr stop --app-id order-processor +``` diff --git a/dapr/quickstarts/state_management/csharp/sdk/makefile b/dapr/quickstarts/state_management/csharp/sdk/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/state_management/csharp/sdk/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/state_management/csharp/sdk/order-processor/Program.cs b/dapr/quickstarts/state_management/csharp/sdk/order-processor/Program.cs new file mode 100644 index 0000000..b6fcce2 --- /dev/null +++ b/dapr/quickstarts/state_management/csharp/sdk/order-processor/Program.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; +using Dapr.Client; +using Microsoft.AspNetCore.Mvc; +using System.Threading; +using System.Text.Json; +using System.Text; +using System.Text.Json.Serialization; + +string DAPR_STORE_NAME = "statestore"; +var client = new DaprClientBuilder().Build(); +for (int i = 1; i <= 10; i++) { + var orderId = i; + var order = new Order(orderId); + + // Save state into the state store + await client.SaveStateAsync(DAPR_STORE_NAME, orderId.ToString(), order.ToString()); + Console.WriteLine("Saving Order: " + order); + + // Get state from the state store + var result = await client.GetStateAsync(DAPR_STORE_NAME, orderId.ToString()); + Console.WriteLine("Getting Order: " + result); + + // Delete state from the state store + await client.DeleteStateAsync(DAPR_STORE_NAME, orderId.ToString()); + Console.WriteLine("Deleting Order: " + order); + + await Task.Delay(TimeSpan.FromSeconds(5)); +} + +public record Order([property: JsonPropertyName("orderId")] int orderId); diff --git a/dapr/quickstarts/state_management/csharp/sdk/order-processor/Program.csproj b/dapr/quickstarts/state_management/csharp/sdk/order-processor/Program.csproj new file mode 100644 index 0000000..b21a5af --- /dev/null +++ b/dapr/quickstarts/state_management/csharp/sdk/order-processor/Program.csproj @@ -0,0 +1,12 @@ + + + + Exe + net6.0 + + + + + + + diff --git a/dapr/quickstarts/state_management/go/http/README.md b/dapr/quickstarts/state_management/go/http/README.md new file mode 100644 index 0000000..abd77fb --- /dev/null +++ b/dapr/quickstarts/state_management/go/http/README.md @@ -0,0 +1,50 @@ +# Dapr state management (HTTP Client) + +In this quickstart, you'll create a microservice to demonstrate Dapr's state management API. The service generates messages to store data in a state store. See [Why state management](#why-state-management) to understand when this pattern might be a good choice for your software architecture. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/state-management/) link for more information about Dapr and State Management. + +> **Note:** This example leverages HTTP `requests` only. If you are looking for the example using the Dapr Client SDK (recommended) [click here](../sdk/). + +This quickstart includes one service: + +- Go client service `order-processor` + +### Run Go service with Dapr + +1. Open a new terminal window and navigate to `order-processor` directory: + + + +```bash +cd ./order-processor +go build . +``` + + +2. Run the Go service app with Dapr: + + + +```bash +cd ./order-processor +dapr run --app-id order-processor --components-path ../../../components -- go run . +``` + + + +```bash +dapr stop --app-id order-processor +``` diff --git a/dapr/quickstarts/state_management/go/http/makefile b/dapr/quickstarts/state_management/go/http/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/state_management/go/http/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/state_management/go/http/order-processor/app b/dapr/quickstarts/state_management/go/http/order-processor/app new file mode 100755 index 0000000..f6433db Binary files /dev/null and b/dapr/quickstarts/state_management/go/http/order-processor/app differ diff --git a/dapr/quickstarts/state_management/go/http/order-processor/app.go b/dapr/quickstarts/state_management/go/http/order-processor/app.go new file mode 100644 index 0000000..d544d50 --- /dev/null +++ b/dapr/quickstarts/state_management/go/http/order-processor/app.go @@ -0,0 +1,55 @@ +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "log" + "net/http" + "os" + "strconv" + "time" +) + +func main() { + var DAPR_HOST, DAPR_HTTP_PORT string + var okHost, okPort bool + if DAPR_HOST, okHost = os.LookupEnv("DAPR_HOST"); !okHost { + DAPR_HOST = "http://localhost" + } + if DAPR_HTTP_PORT, okPort = os.LookupEnv("DAPR_HTTP_PORT"); !okPort { + DAPR_HTTP_PORT = "3500" + } + + DAPR_STATE_STORE := "statestore" + for i := 1; i <= 10; i++ { + orderId := i + order := "{\"orderId\":" + strconv.Itoa(orderId) + "}" + state, _ := json.Marshal([]map[string]string{ + {"key": strconv.Itoa(orderId), "value": order}, + }) + responseBody := bytes.NewBuffer(state) + + // Save state into a state store + _, _ = http.Post(DAPR_HOST+":"+DAPR_HTTP_PORT+"/v1.0/state/"+DAPR_STATE_STORE, "application/json", responseBody) + log.Println("Saving Order: " + order) + + // Get state from a state store + getResponse, err := http.Get(DAPR_HOST + ":" + DAPR_HTTP_PORT + "/v1.0/state/" + DAPR_STATE_STORE + "/" + strconv.Itoa(orderId)) + if err != nil { + fmt.Print(err.Error()) + os.Exit(1) + } + result, _ := ioutil.ReadAll(getResponse.Body) + fmt.Println("Getting Order: ", string(result)) + + // Delete state from the state store + req, _ := http.NewRequest(http.MethodDelete, DAPR_HOST+":"+DAPR_HTTP_PORT+"/v1.0/state/"+DAPR_STATE_STORE+"/"+strconv.Itoa(orderId), nil) + client := &http.Client{} + _, _ = client.Do(req) + log.Println("Deleting Order: " + order) + + time.Sleep(5000) + } +} diff --git a/dapr/quickstarts/state_management/go/http/order-processor/dapr_example b/dapr/quickstarts/state_management/go/http/order-processor/dapr_example new file mode 100755 index 0000000..d5f674f Binary files /dev/null and b/dapr/quickstarts/state_management/go/http/order-processor/dapr_example differ diff --git a/dapr/quickstarts/state_management/go/http/order-processor/go.mod b/dapr/quickstarts/state_management/go/http/order-processor/go.mod new file mode 100644 index 0000000..a81a53c --- /dev/null +++ b/dapr/quickstarts/state_management/go/http/order-processor/go.mod @@ -0,0 +1,3 @@ +module dapr_example + +go 1.18 diff --git a/dapr/quickstarts/state_management/go/http/order-processor/go.sum b/dapr/quickstarts/state_management/go/http/order-processor/go.sum new file mode 100644 index 0000000..e69de29 diff --git a/dapr/quickstarts/state_management/go/sdk/README.md b/dapr/quickstarts/state_management/go/sdk/README.md new file mode 100644 index 0000000..89368fa --- /dev/null +++ b/dapr/quickstarts/state_management/go/sdk/README.md @@ -0,0 +1,50 @@ +# Dapr state management + +In this quickstart, you'll create a microservice to demonstrate Dapr's state management API. The service generates messages to store data in a state store. See [Why state management](#why-state-management) to understand when this pattern might be a good choice for your software architecture. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/state-management/) link for more information about Dapr and State Management. + +> **Note:** This example leverages the Dapr client SDK. If you are looking for the example using only HTTP `requests` [click here](../http). + +This quickstart includes one service: + +- Go client service `order-processor` + +### Run Go service with Dapr + +1. Open a new terminal window and navigate to `order-processor` directory: + + + +```bash +cd ./order-processor +go build . +``` + + +2. Run the Go service app with Dapr: + + + +```bash +cd ./order-processor +dapr run --app-id order-processor --components-path ../../../components -- go run . +``` + + + +```bash +dapr stop --app-id order-processor +``` diff --git a/dapr/quickstarts/state_management/go/sdk/makefile b/dapr/quickstarts/state_management/go/sdk/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/state_management/go/sdk/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/state_management/go/sdk/order-processor/app b/dapr/quickstarts/state_management/go/sdk/order-processor/app new file mode 100755 index 0000000..4ed7554 Binary files /dev/null and b/dapr/quickstarts/state_management/go/sdk/order-processor/app differ diff --git a/dapr/quickstarts/state_management/go/sdk/order-processor/app.go b/dapr/quickstarts/state_management/go/sdk/order-processor/app.go new file mode 100644 index 0000000..b0797fd --- /dev/null +++ b/dapr/quickstarts/state_management/go/sdk/order-processor/app.go @@ -0,0 +1,38 @@ +package main + +import ( + "context" + "fmt" + "log" + "strconv" + "time" + + dapr "github.com/dapr/go-sdk/client" +) + +func main() { + for i := 1; i <= 10; i++ { + orderId := i + order := "{\"orderId\":" + strconv.Itoa(orderId) + "}" + client, err := dapr.NewClient() + STATE_STORE_NAME := "statestore" + if err != nil { + panic(err) + } + ctx := context.Background() + + // Save state into the state store + _ = client.SaveState(ctx, STATE_STORE_NAME, strconv.Itoa(orderId), []byte(order), nil) + log.Print("Saving Order: " + string(order)) + + // Get state from the state store + result, _ := client.GetState(ctx, STATE_STORE_NAME, strconv.Itoa(orderId), nil) + fmt.Println("Getting Order: " + string(result.Value)) + + // Delete state from the state store + _ = client.DeleteState(ctx, STATE_STORE_NAME, strconv.Itoa(orderId), nil) + log.Print("Deleting Order: " + string(order)) + + time.Sleep(5000) + } +} diff --git a/dapr/quickstarts/state_management/go/sdk/order-processor/dapr_example b/dapr/quickstarts/state_management/go/sdk/order-processor/dapr_example new file mode 100755 index 0000000..a757fb5 Binary files /dev/null and b/dapr/quickstarts/state_management/go/sdk/order-processor/dapr_example differ diff --git a/dapr/quickstarts/state_management/go/sdk/order-processor/go.mod b/dapr/quickstarts/state_management/go/sdk/order-processor/go.mod new file mode 100644 index 0000000..951a92e --- /dev/null +++ b/dapr/quickstarts/state_management/go/sdk/order-processor/go.mod @@ -0,0 +1,18 @@ +module dapr_example + +go 1.18 + +require github.com/dapr/go-sdk v1.5.0 + +require ( + github.com/dapr/dapr v1.8.0 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/pkg/errors v0.9.1 // indirect + golang.org/x/net v0.0.0-20220621193019-9d032be2e588 // indirect + golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect + golang.org/x/text v0.3.7 // indirect + google.golang.org/genproto v0.0.0-20220622171453-ea41d75dfa0f // indirect + google.golang.org/grpc v1.47.0 // indirect + google.golang.org/protobuf v1.28.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/dapr/quickstarts/state_management/go/sdk/order-processor/go.sum b/dapr/quickstarts/state_management/go/sdk/order-processor/go.sum new file mode 100644 index 0000000..7e7b155 --- /dev/null +++ b/dapr/quickstarts/state_management/go/sdk/order-processor/go.sum @@ -0,0 +1,151 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/dapr/dapr v1.8.0 h1:ZAAoBe6wuFp7k4tIHB7ajZXVTtGeDeVqIPrldzo3dF0= +github.com/dapr/dapr v1.8.0/go.mod h1:yAsDiK5oecG0htw2S8JG9RFaeHJVdlTfZyOrL57AvRM= +github.com/dapr/go-sdk v1.5.0 h1:OVkrupquJEOL1qRtwKcMVrFKYhw4UJQvgOJNduo2VxE= +github.com/dapr/go-sdk v1.5.0/go.mod h1:Cvz3taCVu22WCNEUbc9/szvG/yJxWPAV4dcaG+zDWA4= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.4 h1:wZRexSlwd7ZXfKINDLsO4r7WBt3gTKONc6K/VesHvHM= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20220621193019-9d032be2e588 h1:9ubFuySsnAJYGyJrZ3koiEv8FyqofCBdz3G9Mbf2YFc= +golang.org/x/net v0.0.0-20220621193019-9d032be2e588/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20220622171453-ea41d75dfa0f h1:kYlCnpX4eB0QEnXm12j4DAX4yrjjhJmsyuWtSSZ+Buo= +google.golang.org/genproto v0.0.0-20220622171453-ea41d75dfa0f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8= +google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/dapr/quickstarts/state_management/java/http/README.md b/dapr/quickstarts/state_management/java/http/README.md new file mode 100644 index 0000000..7019ce5 --- /dev/null +++ b/dapr/quickstarts/state_management/java/http/README.md @@ -0,0 +1,61 @@ +# Dapr state management (HTTP Client) + +In this quickstart, there is a `order-processor` microservice to demonstrate Dapr's state management API. The service generates messages to store in a state store. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/state-management/) link for more information about Dapr and State Management. + +> **Note:** This example leverages HTTP `requests` only. If you are looking for the example using the Dapr Client SDK (recommended) [click here](../sdk/). + +## Pre-requisites + +* [Dapr and Dapr Cli](https://docs.dapr.io/getting-started/install-dapr-cli/). +* Java JDK 11 (or greater): + * [Microsoft JDK 11](https://docs.microsoft.com/en-us/java/openjdk/download#openjdk-11) + * [Oracle JDK 11](https://www.oracle.com/technetwork/java/javase/downloads/index.html#JDK11) + * [OpenJDK 11](https://jdk.java.net/11/) +* [Apache Maven](https://maven.apache.org/install.html) version 3.x. + +This quickstart includes one service: + +- Java client service `order-processor` + +### Run Java service with Dapr + +1. Open a new terminal window and navigate to `order-processor` directory: + + + +```bash +cd ./order-processor +mvn clean install +``` + + +2. Run the Java service app with Dapr: + + + +```bash +cd ./order-processor +dapr run --app-id order-processor --components-path ../../../components/ -- java -jar target/OrderProcessingService-0.0.1-SNAPSHOT.jar +``` + + + +```bash +dapr stop --app-id order-processor +``` diff --git a/dapr/quickstarts/state_management/java/http/makefile b/dapr/quickstarts/state_management/java/http/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/state_management/java/http/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/state_management/java/http/order-processor/pom.xml b/dapr/quickstarts/state_management/java/http/order-processor/pom.xml new file mode 100644 index 0000000..b26ab59 --- /dev/null +++ b/dapr/quickstarts/state_management/java/http/order-processor/pom.xml @@ -0,0 +1,51 @@ + + + 4.0.0 + + com.service + OrderProcessingService + 0.0.1-SNAPSHOT + OrderProcessingService + Demo for Dapr state mgmt component + + 11 + 11 + 1.6.1 + + + + org.projectlombok + lombok + 1.18.24 + true + + + com.fasterxml.jackson.core + jackson-databind + 2.9.8 + + + + + + org.springframework.boot + spring-boot-maven-plugin + 2.6.3 + + + + repackage + + + + com.service.OrderProcessingServiceApplication + + + + + + + + diff --git a/dapr/quickstarts/state_management/java/http/order-processor/src/main/java/com/service/OrderProcessingServiceApplication.java b/dapr/quickstarts/state_management/java/http/order-processor/src/main/java/com/service/OrderProcessingServiceApplication.java new file mode 100644 index 0000000..5e5d4ba --- /dev/null +++ b/dapr/quickstarts/state_management/java/http/order-processor/src/main/java/com/service/OrderProcessingServiceApplication.java @@ -0,0 +1,78 @@ +package com.service; + +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.time.Duration; +import java.util.concurrent.TimeUnit; + +public class OrderProcessingServiceApplication { + private static final String DAPR_STATE_STORE = "statestore"; + private static String DAPR_HOST = System.getenv().getOrDefault("DAPR_HOST", "http://localhost"); + private static String DAPR_HTTP_PORT = System.getenv().getOrDefault("DAPR_HTTP_PORT", "3500"); + private static final HttpClient httpClient = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_2) + .connectTimeout(Duration.ofSeconds(10)) + .build(); + + public static void main(String[] args) throws IOException, URISyntaxException, InterruptedException { + URI baseUrl = new URI(DAPR_HOST+":"+DAPR_HTTP_PORT); + URI stateStoreUrl = new URI(baseUrl + "/v1.0/state/"+DAPR_STATE_STORE); + for (int i = 1; i <= 10; i++) { + int orderId = i; + Order order = new Order(orderId); + State state = new State(String.valueOf(orderId), order); + State[] payload = new State[] {state}; + ObjectMapper objectMapper = new ObjectMapper(); + + // Save state into a state store + HttpRequest request = HttpRequest.newBuilder() + .POST(HttpRequest.BodyPublishers.ofString(objectMapper.writeValueAsString(payload))) + .uri(stateStoreUrl) + .build(); + HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println("Saving order: "+ order.getOrderId()); + + // Get state from a state store + URI getStateURL = new URI(baseUrl + "/v1.0/state/"+DAPR_STATE_STORE+"/"+String.valueOf(orderId)); + request = HttpRequest.newBuilder() + .GET() + .uri(getStateURL) + .build(); + + response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println("Order saved: "+ response.body()); + + // Delete state from the state store + URI deleteStateURI = new URI(baseUrl + "/v1.0/state/" + DAPR_STATE_STORE + "/" + String.valueOf(orderId)); + request = HttpRequest.newBuilder() + .DELETE() + .uri(deleteStateURI) + .build(); + response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + System.out.println("Deleting order: "+ order.getOrderId()); + System.out.println("Deletion Status code :"+ response.statusCode()); + TimeUnit.MILLISECONDS.sleep(1000); + } + } +} + +@AllArgsConstructor +@Getter +class Order { + private int orderId; +} + +@AllArgsConstructor +@Getter +class State { + private String key; + private Order value; +} diff --git a/dapr/quickstarts/state_management/java/sdk/README.md b/dapr/quickstarts/state_management/java/sdk/README.md new file mode 100644 index 0000000..52ce8b9 --- /dev/null +++ b/dapr/quickstarts/state_management/java/sdk/README.md @@ -0,0 +1,60 @@ +# Dapr state management (HTTP Client) + +In this quickstart, there is a `order-processor` microservice to demonstrate Dapr's state management API. The service generates messages to store in a state store. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/state-management/) link for more information about Dapr and State Management. + +> **Note:** This example leverages Dapr SDK client. If you are looking for the example using the HTTP Client [click here](../http/). + +## Pre-requisites + +* [Dapr and Dapr Cli](https://docs.dapr.io/getting-started/install-dapr-cli/). +* Java JDK 11 (or greater): + * [Microsoft JDK 11](https://docs.microsoft.com/en-us/java/openjdk/download#openjdk-11) + * [Oracle JDK 11](https://www.oracle.com/technetwork/java/javase/downloads/index.html#JDK11) + * [OpenJDK 11](https://jdk.java.net/11/) +* [Apache Maven](https://maven.apache.org/install.html) version 3.x. + +This quickstart includes one service: + +- Java client service `order-processor` + +### Run Java service with Dapr + +1. Open a new terminal window and navigate to `order-processor` directory: + + + +```bash +cd ./order-processor +mvn clean install +``` + + +2. Run the Java service app with Dapr: + + + +```bash +cd ./order-processor +dapr run --app-id order-processor --components-path ../../../components/ -- java -jar target/OrderProcessingService-0.0.1-SNAPSHOT.jar +``` + + + +```bash +dapr stop --app-id order-processor +``` diff --git a/dapr/quickstarts/state_management/java/sdk/makefile b/dapr/quickstarts/state_management/java/sdk/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/state_management/java/sdk/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/state_management/java/sdk/order-processor/pom.xml b/dapr/quickstarts/state_management/java/sdk/order-processor/pom.xml new file mode 100644 index 0000000..f808273 --- /dev/null +++ b/dapr/quickstarts/state_management/java/sdk/order-processor/pom.xml @@ -0,0 +1,52 @@ + + + 4.0.0 + + com.service + OrderProcessingService + 0.0.1-SNAPSHOT + OrderProcessingService + Demo for Dapr state mgmt component + + 11 + 11 + 1.6.1 + + + + io.dapr + dapr-sdk + 1.6.0 + + + org.projectlombok + lombok + 1.18.22 + true + + + + + + + org.springframework.boot + spring-boot-maven-plugin + 2.6.3 + + + + repackage + + + + com.service.OrderProcessingServiceApplication + + + + + + + + + diff --git a/dapr/quickstarts/state_management/java/sdk/order-processor/src/main/java/com/service/OrderProcessingServiceApplication.java b/dapr/quickstarts/state_management/java/sdk/order-processor/src/main/java/com/service/OrderProcessingServiceApplication.java new file mode 100644 index 0000000..8fbedf4 --- /dev/null +++ b/dapr/quickstarts/state_management/java/sdk/order-processor/src/main/java/com/service/OrderProcessingServiceApplication.java @@ -0,0 +1,41 @@ +package com.service; + +import io.dapr.client.DaprClient; +import io.dapr.client.DaprClientBuilder; +import io.dapr.client.domain.State; +import lombok.Getter; +import lombok.Setter; +import java.util.concurrent.TimeUnit; + +public class OrderProcessingServiceApplication { + private static final String DAPR_STATE_STORE = "statestore"; + + public static void main(String[] args) throws Exception { + try (DaprClient client = new DaprClientBuilder().build()) { + for (int i = 1; i <= 10; i++) { + int orderId = i; + Order order = new Order(); + order.setOrderId(orderId); + + // Save state into the state store + client.saveState(DAPR_STATE_STORE, String.valueOf(orderId), order).block(); + System.out.println("Saving Order: " + order.getOrderId()); + + // Get state from the state store + State response = client.getState(DAPR_STATE_STORE, String.valueOf(orderId), Order.class).block(); + System.out.println("Getting Order: " + response.getValue().getOrderId()); + + // Delete state from the state store + client.deleteState(DAPR_STATE_STORE, String.valueOf(orderId)).block(); + System.out.println("Deleting Order: " + orderId); + TimeUnit.MILLISECONDS.sleep(1000); + } + } + } +} + +@Getter +@Setter +class Order { + private int orderId; +} diff --git a/dapr/quickstarts/state_management/javascript/http/.gitignore b/dapr/quickstarts/state_management/javascript/http/.gitignore new file mode 100644 index 0000000..8afc710 --- /dev/null +++ b/dapr/quickstarts/state_management/javascript/http/.gitignore @@ -0,0 +1,5 @@ +##lint files +*.cjs + +##node modules +node_modules \ No newline at end of file diff --git a/dapr/quickstarts/state_management/javascript/http/README.md b/dapr/quickstarts/state_management/javascript/http/README.md new file mode 100644 index 0000000..2f2748b --- /dev/null +++ b/dapr/quickstarts/state_management/javascript/http/README.md @@ -0,0 +1,50 @@ +# Dapr state management (HTTP Client) + +In this quickstart, you'll create a microservice to demonstrate Dapr's state management API. The service generates messages to store data in a state store. See [Why state management](#why-state-management) to understand when this pattern might be a good choice for your software architecture. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/state-management/) link for more information about Dapr and State Management. + +> **Note:** This example leverages HTTP `requests` only. If you are looking for the example using the Dapr Client SDK (recommended) [click here](../sdk/). + +This quickstart includes one service: + +- Node client service `order-processor` + +### Run Node service with Dapr + +1. Navigate to folder and install dependencies: + + + +```bash +cd ./order-processor +npm install +``` + + +2. Run the Node service app with Dapr: + + + +```bash +dapr run --app-id order-processor --components-path ../../../components/ -- npm start +``` + + + +```bash +dapr stop --app-id order-processor +``` diff --git a/dapr/quickstarts/state_management/javascript/http/makefile b/dapr/quickstarts/state_management/javascript/http/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/state_management/javascript/http/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/state_management/javascript/http/order-processor/index.js b/dapr/quickstarts/state_management/javascript/http/order-processor/index.js new file mode 100644 index 0000000..9ff08f4 --- /dev/null +++ b/dapr/quickstarts/state_management/javascript/http/order-processor/index.js @@ -0,0 +1,55 @@ +import axios from "axios" + +const protocol = process.env.DAPR_PROTOCOL ?? "http" +const DAPR_HOST = process.env.DAPR_HOST ?? "localhost" + +let port +switch (protocol) { + case "http": { + port = process.env.DAPR_HTTP_PORT + break + } + case "grpc": { + port = process.env.DAPR_GRPC_PORT + break + } + default: { + port = 3500 + } +} + +const DAPR_STATE_STORE_NAME = 'statestore' +const stateStoreBaseUrl = `${protocol}://${DAPR_HOST}:${port}/v1.0/state/${DAPR_STATE_STORE_NAME}` + +async function main() { + // For each loop, Save order, Get order, and Delete order + for (let i = 1; i <= 10; i++) { + const order = { orderId: i.toString() } + const state = [ + { + key: order.orderId, + value: order + } + ] + + // Save state into a state store + await axios.post(`${stateStoreBaseUrl}`, state) + console.log("Saving Order: ", order) + + // Get state from a state store + const orderResponse = await axios.get(`${stateStoreBaseUrl}/${order.orderId}`) + console.log("Getting Order: ", orderResponse.data) + + // Delete state from the state store + await axios.delete(`${stateStoreBaseUrl}/${order.orderId}`, state) + console.log("Deleting Order: ", order) + + await sleep(500) + } +} + +async function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)) +} + +main().catch(e => console.error(e)) \ No newline at end of file diff --git a/dapr/quickstarts/state_management/javascript/http/order-processor/package.json b/dapr/quickstarts/state_management/javascript/http/order-processor/package.json new file mode 100644 index 0000000..843e460 --- /dev/null +++ b/dapr/quickstarts/state_management/javascript/http/order-processor/package.json @@ -0,0 +1,21 @@ +{ + "name": "order-processor", + "version": "1.0.0", + "description": "", + "main": "index.js", + "type": "module", + "scripts": { + "start": "node .", + "start:dapr": "dapr run --app-id checkout --app-protocol http --dapr-http-port 3500 -- npm run start" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "axios": "^0.25.0" + }, + "devDependencies": { + "eslint": "^8.8.0", + "eslint-plugin-react": "^7.28.0" + } +} diff --git a/dapr/quickstarts/state_management/javascript/sdk/.gitignore b/dapr/quickstarts/state_management/javascript/sdk/.gitignore new file mode 100644 index 0000000..8afc710 --- /dev/null +++ b/dapr/quickstarts/state_management/javascript/sdk/.gitignore @@ -0,0 +1,5 @@ +##lint files +*.cjs + +##node modules +node_modules \ No newline at end of file diff --git a/dapr/quickstarts/state_management/javascript/sdk/README.md b/dapr/quickstarts/state_management/javascript/sdk/README.md new file mode 100644 index 0000000..9f08718 --- /dev/null +++ b/dapr/quickstarts/state_management/javascript/sdk/README.md @@ -0,0 +1,54 @@ +# Dapr state management + +In this quickstart, you'll create a microservice to demonstrate Dapr's state management API. The service generates messages to store data in a state store. See [Why state management](#why-state-management) to understand when this pattern might be a good choice for your software architecture. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/state-management/) link for more information about Dapr and State Management. + +> **Note:** This example leverages the Dapr client SDK. If you are looking for the example using only HTTP [click here](../http). + +This quickstart includes one service: + +- Node client service `order-processor` + +This quickstart includes one service: + +- Node client service `order-processor` + +### Run Node service with Dapr + +1. Navigate to folder and install dependencies: + + + +```bash +cd ./order-processor +npm install +``` + + +2. Run the Node service app with Dapr: + + + +```bash +dapr run --app-id order-processor --components-path ../../../components/ -- npm start +``` + + + +```bash +dapr stop --app-id order-processor +``` diff --git a/dapr/quickstarts/state_management/javascript/sdk/makefile b/dapr/quickstarts/state_management/javascript/sdk/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/state_management/javascript/sdk/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/state_management/javascript/sdk/order-processor/index.js b/dapr/quickstarts/state_management/javascript/sdk/order-processor/index.js new file mode 100644 index 0000000..9b1ac98 --- /dev/null +++ b/dapr/quickstarts/state_management/javascript/sdk/order-processor/index.js @@ -0,0 +1,59 @@ +import { CommunicationProtocolEnum, DaprClient } from "@dapr/dapr" + +const protocol = (process.env.DAPR_PROTOCOL === "grpc") + ? CommunicationProtocolEnum.GRPC + : CommunicationProtocolEnum.HTTP + +const host = process.env.DAPR_HOST ?? "localhost" + +let port +switch (protocol) { + case CommunicationProtocolEnum.HTTP: { + port = process.env.DAPR_HTTP_PORT + break + } + case CommunicationProtocolEnum.GRPC: { + port = process.env.DAPR_GRPC_PORT + break + } + default: { + port = 3500 + } +} + +const DAPR_STATE_STORE_NAME = "statestore" + +async function main() { + const client = new DaprClient(host, port, protocol) + + // For each loop, Save order, Get order, and Delete order + for (let i = 1; i <= 10; i++) { + const order = { orderId: i.toString() } + const state = [ + { + key: order.orderId, + value: order + } + ] + + // Save state into a state store + await client.state.save(DAPR_STATE_STORE_NAME, state) + console.log("Saving Order: ", order) + + // Get state from a state store + const savedOrder = await client.state.get(DAPR_STATE_STORE_NAME, order.orderId) + console.log("Getting Order: ", savedOrder) + + // Delete state from the state store + await client.state.delete(DAPR_STATE_STORE_NAME, order.orderId) + console.log("Deleting Order: ", order) + + await sleep(500) + } +} + +async function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)) +} + +main().catch(e => console.error(e)) \ No newline at end of file diff --git a/dapr/quickstarts/state_management/javascript/sdk/order-processor/package.json b/dapr/quickstarts/state_management/javascript/sdk/order-processor/package.json new file mode 100644 index 0000000..5416676 --- /dev/null +++ b/dapr/quickstarts/state_management/javascript/sdk/order-processor/package.json @@ -0,0 +1,21 @@ +{ + "name": "order-processor", + "version": "1.0.0", + "description": "", + "main": "index.js", + "type": "module", + "scripts": { + "start": "node .", + "start:dapr": "dapr run --app-id checkout --app-protocol http --dapr-http-port 3500 -- npm run start" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@dapr/dapr": "^2.3.0" + }, + "devDependencies": { + "eslint": "^8.8.0", + "eslint-plugin-react": "^7.28.0" + } +} diff --git a/dapr/quickstarts/state_management/python/http/README.md b/dapr/quickstarts/state_management/python/http/README.md new file mode 100644 index 0000000..921b211 --- /dev/null +++ b/dapr/quickstarts/state_management/python/http/README.md @@ -0,0 +1,50 @@ +# Dapr state management (HTTP client) + +In this quickstart, you'll create a microservice to demonstrate Dapr's state management API. The service generates messages to store data in a state store. See [Why state management](#why-state-management) to understand when this pattern might be a good choice for your software architecture. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/state-management/) link for more information about Dapr and State Management. + +> **Note:** This example leverages HTTP `requests` only. If you are looking for the example using the Dapr Client SDK (recommended) [click here](../sdk/). + +This quickstart includes one service: + +- Python service `order-processor` + +### Run Python service with Dapr + +1. Open a new terminal window and navigate to `order-processor` directory: + + + +```bash +cd ./order-processor +pip3 install -r requirements.txt +``` + + +2. Run the Python service app with Dapr: + + + +```bash +cd ./order-processor +dapr run --app-id order-processor --components-path ../../../components/ -- python3 app.py +``` + + + +```bash +dapr stop --app-id order-processor +``` diff --git a/dapr/quickstarts/state_management/python/http/makefile b/dapr/quickstarts/state_management/python/http/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/state_management/python/http/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/state_management/python/http/order-processor/app.py b/dapr/quickstarts/state_management/python/http/order-processor/app.py new file mode 100644 index 0000000..5adca22 --- /dev/null +++ b/dapr/quickstarts/state_management/python/http/order-processor/app.py @@ -0,0 +1,40 @@ +import time +import logging +import requests +import os + +logging.basicConfig(level=logging.INFO) + +base_url = os.getenv('BASE_URL', 'http://localhost') + ':' + os.getenv( + 'DAPR_HTTP_PORT', '3500') +DAPR_STATE_STORE = 'statestore' + +for i in range(1, 10): + orderId = str(i) + order = {'orderId': orderId} + state = [{ + 'key': orderId, + 'value': order + }] + + # Save state into a state store + result = requests.post( + url='%s/v1.0/state/%s' % (base_url, DAPR_STATE_STORE), + json=state + ) + logging.info('Saving Order: %s', order) + + # Get state from a state store + result = requests.get( + url='%s/v1.0/state/%s/%s' % (base_url, DAPR_STATE_STORE, orderId) + ) + logging.info('Getting Order: ' + str(result.json())) + + # Delete state from the state store + result = requests.delete( + url='%s/v1.0/state/%s' % (base_url, DAPR_STATE_STORE), + json=state + ) + logging.info('Deleted Order: %s', order) + + time.sleep(1) diff --git a/dapr/quickstarts/state_management/python/http/order-processor/requirements.txt b/dapr/quickstarts/state_management/python/http/order-processor/requirements.txt new file mode 100644 index 0000000..f229360 --- /dev/null +++ b/dapr/quickstarts/state_management/python/http/order-processor/requirements.txt @@ -0,0 +1 @@ +requests diff --git a/dapr/quickstarts/state_management/python/sdk/README.md b/dapr/quickstarts/state_management/python/sdk/README.md new file mode 100644 index 0000000..43e5891 --- /dev/null +++ b/dapr/quickstarts/state_management/python/sdk/README.md @@ -0,0 +1,50 @@ +# Dapr state management + +In this quickstart, you'll create a microservice to demonstrate Dapr's state management API. The service generates messages to store data in a state store. See [Why state management](#why-state-management) to understand when this pattern might be a good choice for your software architecture. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/state-management/) link for more information about Dapr and State Managenment. + +> **Note:** This example leverages the Dapr client SDK. If you are looking for the example using only HTTP `requests` [click here](../http). + +This quickstart includes one service: + +- Python service `order-processor` + +### Run Python service with Dapr + +1. Open a new terminal window and navigate to `order-processor` directory: + + + +```bash +cd ./order-processor +pip3 install -r requirements.txt +``` + + +2. Run the Python service app with Dapr: + + + +```bash +cd ./order-processor +dapr run --app-id order-processor --components-path ../../../components/ -- python3 app.py +``` + + + +```bash +dapr stop --app-id order-processor +``` diff --git a/dapr/quickstarts/state_management/python/sdk/makefile b/dapr/quickstarts/state_management/python/sdk/makefile new file mode 100644 index 0000000..e7a8826 --- /dev/null +++ b/dapr/quickstarts/state_management/python/sdk/makefile @@ -0,0 +1,2 @@ +include ../../../docker.mk +include ../../../validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/state_management/python/sdk/order-processor/app.py b/dapr/quickstarts/state_management/python/sdk/order-processor/app.py new file mode 100644 index 0000000..f727c5f --- /dev/null +++ b/dapr/quickstarts/state_management/python/sdk/order-processor/app.py @@ -0,0 +1,25 @@ +from time import sleep +import logging +from dapr.clients import DaprClient + +logging.basicConfig(level=logging.INFO) + +DAPR_STORE_NAME = "statestore" +for i in range(1, 10): + orderId = str(i) + order = {'orderId': orderId} + with DaprClient() as client: + + # Save state into the state store + client.save_state(DAPR_STORE_NAME, orderId, str(order)) + logging.info('Saving Order: %s', order) + + # Get state from the state store + result = client.get_state(DAPR_STORE_NAME, orderId) + logging.info('Result after get: ' + str(result.data)) + + # Delete state from the state store + client.delete_state(store_name=DAPR_STORE_NAME, key=orderId) + logging.info('Deleting Order: %s', order) + + sleep(1) diff --git a/dapr/quickstarts/state_management/python/sdk/order-processor/requirements.txt b/dapr/quickstarts/state_management/python/sdk/order-processor/requirements.txt new file mode 100644 index 0000000..53c5975 --- /dev/null +++ b/dapr/quickstarts/state_management/python/sdk/order-processor/requirements.txt @@ -0,0 +1 @@ +dapr diff --git a/dapr/quickstarts/tutorials/bindings/README.md b/dapr/quickstarts/tutorials/bindings/README.md new file mode 100644 index 0000000..2ec07c5 --- /dev/null +++ b/dapr/quickstarts/tutorials/bindings/README.md @@ -0,0 +1,495 @@ +# Dapr Bindings + +In this quickstart, you'll create two microservices, one with an input binding and another with an output binding. You'll bind to Kafka, but note that there are a myriad of components that Dapr can bind to ([see Dapr components](https://docs.dapr.io/concepts/components-concept/)). + +This quickstart includes two microservices: + +- Node.js microservice that utilizes an input binding +- Python microservice that utilizes an output binding + +The bindings connect to Kafka, allowing us to push messages into a Kafka instance (from the Python microservice) and receive message from that instance (from the Node microservice) without having to know where the instance is hosted. Instead, connect through the sidecars using the Dapr API. See architecture diagram to see how the components interconnect locally: + +![Architecture Diagram](./img/Bindings_Standalone.png) + + +Dapr allows us to deploy the same microservices from the local machines to Kubernetes. Correspondingly, this quickstart has instructions for deploying this project [locally](#Run-Locally) or in [Kubernetes](#Run-in-Kubernetes). + +## Prerequisites + +### Prerequisites to Run Locally + +- [Dapr CLI with Dapr initialized](https://docs.dapr.io/getting-started/install-dapr-cli/) +- [Node.js version 14 or greater](https://nodejs.org/en/) +- [Python 3.4 or greater](https://www.python.org/) + +### Prerequisites to Run in Kubernetes + +- [Dapr enabled Kubernetes cluster](https://docs.dapr.io/operations/hosting/kubernetes/kubernetes-deploy/) + +## Run Locally + +### Clone the quickstarts repository +Clone this quickstarts repository to your local machine: + +```bash +git clone [-b ] https://github.com/dapr/quickstarts.git +``` + +>**Note**: See https://github.com/dapr/quickstarts#supported-dapr-runtime-version for supported tags. Use `git clone https://github.com/dapr/quickstarts.git` when using the edge version of dapr runtime. + +### Run Kafka Docker Container Locally + +In order to run the Kafka bindings quickstart locally, you will run the [Kafka broker server](https://github.com/wurstmeister/kafka-docker) in a docker container on your machine. + +1. To run the container locally, run: + + + +```bash +docker-compose -f ./docker-compose-single-kafka.yml up -d +``` + +2. To see the container running locally, run: + +```bash +docker ps +``` + + + +The output should be similar to this: + +```bash +342d3522ca14 kafka-docker_kafka "start-kafka.sh" 14 hours ago Up About +a minute 0.0.0.0:9092->9092/tcp kafka-docker_kafka_1 +0cd69dbe5e65 wurstmeister/zookeeper "/bin/sh -c '/usr/sb…" 8 days ago Up About +a minute 22/tcp, 2888/tcp, 3888/tcp, 0.0.0.0:2181->2181/tcp kafka-docker_zookeeper_1 +``` + +### Run Node Microservice with Input Binding + +Now that you have Kafka running locally on your machine, you'll need to run the microservices. You'll start by running the Node microservice that uses input bindings: + +1. Navigate to Node subscriber directory in your CLI: + + +```bash +cd nodeapp +``` + +2. Install dependencies: + + + +```bash +npm install +``` + + + +3. Run Node quickstart app with Dapr: + + + +```bash +dapr run --app-id bindings-nodeapp --app-port 3000 node app.js --components-path ../components +``` + + + +### Run Python Microservice with Output Binding + +Next, run the Python microservice that uses output bindings + +1. Open a new CLI window and navigate to Python subscriber directory in your CLI: + +```bash +cd pythonapp +``` + +2. Install dependencies: + + + +```bash +pip3 install requests +``` + + + +3. Run Python quickstart app with Dapr: + + + +```bash +dapr run --app-id bindings-pythonapp python3 app.py --components-path ../components +``` + + + +### Observe Logs + +1. Observe the Python logs, which show a successful output binding with Kafka: + +```bash +[0m?[94;1m== APP == {'data': {'orderId': 1}} +[0m?[94;1m== APP == +[0m?[94;1m== APP == {'data': {'orderId': 2}} +[0m?[94;1m== APP == +[0m?[94;1m== APP == {'data': {'orderId': 3}} +[0m?[94;1m== APP == +``` + +2. Observe the Node logs, which show a successful input binding with Kafka: + +```bash +[0m?[94;1m== APP == { orderId: 1 } +[0m?[94;1m== APP == Hello from Kafka! +[0m?[94;1m== APP == { orderId: 2 } +[0m?[94;1m== APP == Hello from Kafka! +[0m?[94;1m== APP == { orderId: 3 } +[0m?[94;1m== APP == Hello from Kafka! +``` + +### Cleanup + +To cleanly stop the dapr microservices, run: + + + +```bash +dapr stop --app-id bindings-nodeapp +``` + +```bash +dapr stop --app-id bindings-pythonapp +``` + +Once you're done, you can spin down your local Kafka Docker Container by running: + +```bash +docker-compose -f ./docker-compose-single-kafka.yml down +``` + + + +## Run in Kubernetes + +### Setting up a Kafka in Kubernetes + +1. Install Kafka via [bitnami/kafka](https://bitnami.com/stack/kafka/helm) + + + + +```bash +helm repo add bitnami https://charts.bitnami.com/bitnami +``` + +```bash +helm repo update +``` + +```bash +kubectl create ns kafka +``` + +```bash +helm install dapr-kafka bitnami/kafka --wait --namespace kafka -f ./kafka-non-persistence.yaml +``` + + + +2. Wait until kafka pods are running + +```bash +kubectl -n kafka get pods -w + +NAME READY STATUS RESTARTS AGE +dapr-kafka-0 1/1 Running 0 2m7s +dapr-kafka-zookeeper-0 1/1 Running 0 2m57s +``` + +### Deploy Assets + +Now that the Kafka binding is set up, deploy the assets. + +1. In your CLI window, in the bindings directory run: + + + +```bash +kubectl apply -f ./deploy +``` + +This will deploy bindings-nodeapp and bindings-pythonapp microservices. It will also apply the Kafka bindings component configuration you set up in the last step. + +Kubernetes deployments are asyncronous. This means you'll need to wait for the deployment to complete before moving on to the next steps. You can do so with the following command: + +```bash +kubectl rollout status deploy/bindings-nodeapp +``` + +```bash +kubectl rollout status deploy/bindings-pythonapp +``` + + + +2. Run `kubectl get pods` to see that pods were correctly provisioned. + +### Observe Logs + +1. Observe the Python app logs, which show a successful output binding with Kafka: + +```bash +kubectl get pods +``` + +The output should look like this: + +```bash +NAME READY STATUS RESTARTS AGE +bindings-nodeapp-699489b8b6-mqhrj 2/2 Running 0 4s +bindings-pythonapp-644489969b-c8lg5 2/2 Running 0 4m9s +``` + +Look at the Python app logs by running: + + + +```bash +kubectl logs --selector app=bindingspythonapp -c python --tail=-1 +``` + + + +```bash +... +{'data': {'orderId': 10}, 'operation': 'create'} + +{'data': {'orderId': 11}, 'operation': 'create'} + +... +``` + +2. Observe the Node app logs, which show a successful input bining with Kafka: + +```bash +kubectl get pods +``` + +The output should look like this: + +```bash +NAME READY STATUS RESTARTS AGE +bindings-nodeapp-699489b8b6-mqhrj 2/2 Running 0 4s +bindings-pythonapp-644489969b-c8lg5 2/2 Running 0 4m9s +``` + +Look at the Node app logs by running: + + + +```bash +kubectl logs --selector app=bindingsnodeapp -c node --tail=-1 +``` + + + +The output should look like this: + +```bash +Node App listening on port 3000! +... +Hello from Kafka! +{ orderId: 240 } +Hello from Kafka! +{ orderId: 241 } +... +``` + +### Cleanup + +Once you're done, you can spin down your Kubernetes resources by running: + + + +```bash +kubectl delete -f ./deploy +``` + +This will spin down each resource defined by the .yaml files in the `deploy` directory, including the kafka component. + +Once you delete all quickstart apps, delete Kafka in the cluster. + +```bash +helm uninstall dapr-kafka --namespace kafka +``` + +And finally, you can delete the kafka namespace + +```bash +kubectl delete ns kafka +``` + + + +## How it Works + +Now that you've run the quickstart locally and/or in Kubernetes, let's unpack how this all works. The app is broken up into input binding app and output binding app: + +### Kafka Bindings yaml + +Before looking at the application code, let's see the Kafka bindings component yamls([local](./components/kafka_bindings.yaml), and [Kubernetes](./deploy/kafka_bindings.yaml)), which specify `brokers` for Kafka connection, `topics` and `consumerGroup` for consumer, and `publishTopic` for publisher topic. + +> See the howtos in [references](#references) for the details on input and output bindings + + +This configuration yaml creates `sample-topic` component to set up Kafka input and output bindings through the Kafka `sample` topic. + +```yaml +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: sample-topic +spec: + type: bindings.kafka + version: v1 + metadata: + # Kafka broker connection setting + - name: brokers + value: [kafka broker address] + # consumer configuration: topic and consumer group + - name: topics + value: sample + - name: consumerGroup + value: group1 + # publisher configuration: topic + - name: publishTopic + value: sample + - name: authRequired + value: "false" +``` + +### Node Input binding app + +Navigate to the `nodeapp` directory and open `app.js`, the code for the Node.js input bindings sample app. Here you're exposing one API endpoint using `express`. The API name must be identical to the component name which is specified in Kafka bindings component yaml. Then Dapr runtime will consume the event from `sample` topic and then send the POST request to Node app with the event payload. + +```js +app.post('/sample-topic', (req, res) => { + console.log("Hello from Kafka!"); + console.log(req.body); + res.status(200).send(); +}); +``` + +### Python Output binding app + +Navigate to the `pythonapp` directory and open `app.py`, the code for the output bindings sample app. This sends POST request to Dapr http endpoint `http://localhost:3500/v1.0/bindings/` with the event payload every second. This app uses `sample-topic` bindings component name as ``. Then Dapr runtime will send the event to `sample` topic which is specified in the above Kafka bindings component yaml. + +```python +dapr_url = "http://localhost:{}/v1.0/bindings/sample-topic".format(dapr_port) +n = 0 +while True: + n += 1 + payload = { "data": {"orderId": n}, "operation": "create" } + print(payload, flush=True) + try: + response = requests.post(dapr_url, json=payload) + print(response.text, flush=True) + + except Exception as e: + print(e) + + time.sleep(1) +``` + +## Related links + +- Learn more about bindings in the [Dapr docs](https://docs.dapr.io/developing-applications/building-blocks/bindings/) +- How to [create an event-driven app using input bindings](https://docs.dapr.io/developing-applications/building-blocks/bindings/howto-triggers/) +- How to [send events to external systems using Output Bindings](https://docs.dapr.io/developing-applications/building-blocks/bindings/howto-bindings/) + +## Next Steps + +- Explore additional [quickstarts](../../README.md#quickstarts). diff --git a/dapr/quickstarts/tutorials/bindings/components/kafka_bindings.yaml b/dapr/quickstarts/tutorials/bindings/components/kafka_bindings.yaml new file mode 100644 index 0000000..3aa06a7 --- /dev/null +++ b/dapr/quickstarts/tutorials/bindings/components/kafka_bindings.yaml @@ -0,0 +1,21 @@ +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: sample-topic +spec: + type: bindings.kafka + version: v1 + metadata: + # Kafka broker connection setting + - name: brokers + value: localhost:9092 + # consumer configuration: topic and consumer group + - name: topics + value: sample + - name: consumerGroup + value: group1 + # publisher configuration: topic + - name: publishTopic + value: sample + - name: authRequired + value: "false" \ No newline at end of file diff --git a/dapr/quickstarts/tutorials/bindings/deploy/kafka_bindings.yaml b/dapr/quickstarts/tutorials/bindings/deploy/kafka_bindings.yaml new file mode 100644 index 0000000..a4bad35 --- /dev/null +++ b/dapr/quickstarts/tutorials/bindings/deploy/kafka_bindings.yaml @@ -0,0 +1,21 @@ +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: sample-topic +spec: + type: bindings.kafka + version: v1 + metadata: + # Kafka broker connection setting + - name: brokers + value: dapr-kafka.kafka:9092 + # consumer configuration: topic and consumer group + - name: topics + value: sample + - name: consumerGroup + value: group1 + # publisher configuration: topic + - name: publishTopic + value: sample + - name: authRequired + value: "false" \ No newline at end of file diff --git a/dapr/quickstarts/tutorials/bindings/deploy/node.yaml b/dapr/quickstarts/tutorials/bindings/deploy/node.yaml new file mode 100644 index 0000000..c24c3c9 --- /dev/null +++ b/dapr/quickstarts/tutorials/bindings/deploy/node.yaml @@ -0,0 +1,42 @@ +kind: Service +apiVersion: v1 +metadata: + name: bindings-nodeapp + labels: + app: bindingsnodeapp +spec: + selector: + app: bindingsnodeapp + ports: + - protocol: TCP + port: 80 + targetPort: 3000 + type: LoadBalancer + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: bindings-nodeapp + labels: + app: bindingsnodeapp +spec: + replicas: 1 + selector: + matchLabels: + app: bindingsnodeapp + template: + metadata: + labels: + app: bindingsnodeapp + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "bindings-nodeapp" + dapr.io/app-port: "3000" + spec: + containers: + - name: node + image: ghcr.io/dapr/samples/bindings-nodeapp:latest + ports: + - containerPort: 3000 + imagePullPolicy: Always diff --git a/dapr/quickstarts/tutorials/bindings/deploy/python.yaml b/dapr/quickstarts/tutorials/bindings/deploy/python.yaml new file mode 100644 index 0000000..f1d6c3d --- /dev/null +++ b/dapr/quickstarts/tutorials/bindings/deploy/python.yaml @@ -0,0 +1,22 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: bindings-pythonapp + labels: + app: bindingspythonapp +spec: + replicas: 1 + selector: + matchLabels: + app: bindingspythonapp + template: + metadata: + labels: + app: bindingspythonapp + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "bindings-pythonapp" + spec: + containers: + - name: python + image: ghcr.io/dapr/samples/bindings-pythonapp:latest diff --git a/dapr/quickstarts/tutorials/bindings/docker-compose-single-kafka.yml b/dapr/quickstarts/tutorials/bindings/docker-compose-single-kafka.yml new file mode 100644 index 0000000..2e89b4f --- /dev/null +++ b/dapr/quickstarts/tutorials/bindings/docker-compose-single-kafka.yml @@ -0,0 +1,14 @@ +version: '2' +services: + zookeeper: + image: ghcr.io/dapr/3rdparty/zookeeper:latest + ports: + - "2181:2181" + kafka: + image: ghcr.io/dapr/3rdparty/kafka:latest + ports: + - "9092:9092" + environment: + KAFKA_ADVERTISED_HOST_NAME: 127.0.0.1 + KAFKA_CREATE_TOPICS: "sample:1:1" + KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 diff --git a/dapr/quickstarts/tutorials/bindings/img/Bindings_Standalone.png b/dapr/quickstarts/tutorials/bindings/img/Bindings_Standalone.png new file mode 100644 index 0000000..6175117 Binary files /dev/null and b/dapr/quickstarts/tutorials/bindings/img/Bindings_Standalone.png differ diff --git a/dapr/quickstarts/tutorials/bindings/kafka-non-persistence.yaml b/dapr/quickstarts/tutorials/bindings/kafka-non-persistence.yaml new file mode 100644 index 0000000..aa233f6 --- /dev/null +++ b/dapr/quickstarts/tutorials/bindings/kafka-non-persistence.yaml @@ -0,0 +1,50 @@ +# +# Copyright 2021 The Dapr Authors +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +replicas: 1 + +# Disable persistent storage +persistence: + enabled: false +zookeeper: + persistence: + enabled: false + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: kubernetes.io/os + operator: In + values: + - linux + - key: kubernetes.io/arch + operator: In + values: + - amd64 + +autoCreateTopicsEnable: true + +affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: kubernetes.io/os + operator: In + values: + - linux + - key: kubernetes.io/arch + operator: In + values: + - amd64 diff --git a/dapr/quickstarts/tutorials/bindings/makefile b/dapr/quickstarts/tutorials/bindings/makefile new file mode 100644 index 0000000..b2dcea3 --- /dev/null +++ b/dapr/quickstarts/tutorials/bindings/makefile @@ -0,0 +1,5 @@ +DOCKER_IMAGE_PREFIX ?=bindings- +APPS ?=nodeapp pythonapp + +include ../docker.mk +include ../validate.mk diff --git a/dapr/quickstarts/tutorials/bindings/nodeapp/.gitignore b/dapr/quickstarts/tutorials/bindings/nodeapp/.gitignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/dapr/quickstarts/tutorials/bindings/nodeapp/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/dapr/quickstarts/tutorials/bindings/nodeapp/Dockerfile b/dapr/quickstarts/tutorials/bindings/nodeapp/Dockerfile new file mode 100644 index 0000000..a58e6f1 --- /dev/null +++ b/dapr/quickstarts/tutorials/bindings/nodeapp/Dockerfile @@ -0,0 +1,7 @@ +FROM node:17-alpine +WORKDIR /app +COPY . /app +RUN rm -rf ./components +RUN npm install +EXPOSE 3000 +CMD [ "node", "app.js" ] \ No newline at end of file diff --git a/dapr/quickstarts/tutorials/bindings/nodeapp/app.js b/dapr/quickstarts/tutorials/bindings/nodeapp/app.js new file mode 100644 index 0000000..1bf40f0 --- /dev/null +++ b/dapr/quickstarts/tutorials/bindings/nodeapp/app.js @@ -0,0 +1,30 @@ +// +// Copyright 2021 The Dapr Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +const express = require('express'); +const bodyParser = require('body-parser'); +const port = process.env.APP_PORT ?? '3000' ; + + +require('isomorphic-fetch'); + +const app = express(); +app.use(bodyParser.json()); + +app.post('/sample-topic', (req, res) => { + console.log("Hello from Kafka!"); + console.log(req.body); + res.status(200).send(); +}); + +app.listen(port, () => console.log(`Node App listening on port ${port}!`)); \ No newline at end of file diff --git a/dapr/quickstarts/tutorials/bindings/nodeapp/package.json b/dapr/quickstarts/tutorials/bindings/nodeapp/package.json new file mode 100644 index 0000000..c996da6 --- /dev/null +++ b/dapr/quickstarts/tutorials/bindings/nodeapp/package.json @@ -0,0 +1,17 @@ +{ + "name": "node_server", + "version": "1.0.0", + "private": true, + "description": "", + "main": "app.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "body-parser": "^1.18.3", + "express": "^4.16.4", + "isomorphic-fetch": "^3.0.0" + } +} diff --git a/dapr/quickstarts/tutorials/bindings/pythonapp/Dockerfile b/dapr/quickstarts/tutorials/bindings/pythonapp/Dockerfile new file mode 100644 index 0000000..fd2f429 --- /dev/null +++ b/dapr/quickstarts/tutorials/bindings/pythonapp/Dockerfile @@ -0,0 +1,7 @@ +FROM python:3.7-alpine +WORKDIR /app +COPY . /app +RUN rm -rf ./components +RUN pip install requests +ENTRYPOINT ["python"] +CMD ["app.py"] diff --git a/dapr/quickstarts/tutorials/bindings/pythonapp/app.py b/dapr/quickstarts/tutorials/bindings/pythonapp/app.py new file mode 100644 index 0000000..1683c85 --- /dev/null +++ b/dapr/quickstarts/tutorials/bindings/pythonapp/app.py @@ -0,0 +1,33 @@ +# +# Copyright 2021 The Dapr Authors +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import time +import requests +import os + +dapr_port = os.getenv("DAPR_HTTP_PORT", 3500) + +dapr_url = "http://localhost:{}/v1.0/bindings/sample-topic".format(dapr_port) +n = 0 +while True: + n += 1 + payload = { "data": {"orderId": n}, "operation": "create" } + print(payload, flush=True) + try: + response = requests.post(dapr_url, json=payload) + print(response, flush=True) + + except Exception as e: + print(e, flush=True) + + time.sleep(1) diff --git a/dapr/quickstarts/tutorials/distributed-calculator/README.md b/dapr/quickstarts/tutorials/distributed-calculator/README.md new file mode 100644 index 0000000..60dd41d --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/README.md @@ -0,0 +1,694 @@ +# Distributed calculator + +This quickstart shows method invocation and state persistent capabilities of Dapr through a distributed calculator where each operation is powered by a different service written in a different language/framework: + +- **Addition**: Go [mux](https://github.com/gorilla/mux) application +- **Multiplication**: Python [flask](https://flask.palletsprojects.com/en/2.2.x/) application +- **Division**: Node [Express](https://expressjs.com/) application +- **Subtraction**: [.NET Core](https://docs.microsoft.com/en-us/dotnet/core/) application + +The front-end application consists of a server and a client written in [React](https://reactjs.org/). +Kudos to [ahfarmer](https://github.com/ahfarmer) whose [React calculator](https://github.com/ahfarmer/calculator) + +The following architecture diagram illustrates the components that make up this quickstart: + +![Architecture Diagram](./img/Architecture_Diagram.png) + +## Prerequisites for running the quickstart +Clone the quickstarts repository + ```bash + git clone [-b ] https://github.com/dapr/quickstarts.git + ``` +> **Note**: See https://github.com/dapr/quickstarts#supported-dapr-runtime-version for supported tags. Use `git clone https://github.com/dapr/quickstarts.git` when using the edge version of dapr runtime. + +### - Run locally +1. Install [.Net Core SDK 3.1](https://dotnet.microsoft.com/download) +2. Install [Dapr CLI](https://github.com/dapr/cli) +3. Install [Go](https://golang.org/doc/install) +4. Install [Python3](https://www.python.org/downloads/) +5. Install [Npm](https://www.npmjs.com/get-npm) +6. Install [Node](https://nodejs.org/en/download/) + +### - Run in Kubernetes environment +1. Dapr-enabled Kubernetes cluster. Follow [these instructions](https://docs.dapr.io/operations/hosting/kubernetes/kubernetes-deploy/) to set this up. + + +## Running the quickstart locally + +These instructions start the four calculator operator apps (add, subtract, multiply and divide) along with the dapr sidecar locally and then run the front end app which persists the state in a local redis state store. + + +1. Add App - Open a terminal window and navigate to the go directory and follow the steps below: + + + +- Install the gorilla/mux package: Run: + ```bash + go get -u github.com/gorilla/mux + ``` + + + + + +- Build the app. Run: + ```bash + go build . + ``` + + + + + +- Run dapr using the command: + ```bash + dapr run --app-id addapp --app-port 6000 --dapr-http-port 3503 go run app.go + ``` + + + +2. Subtract App - Open a terminal window and navigate to the csharp directory and follow the steps below: + +- Set environment variable to use non-default app port 7001 + ```bash + #Linux/Mac OS: + export ASPNETCORE_URLS="http://localhost:7001" + ``` + + ```bash + #Windows: + set ASPNETCORE_URLS=http://localhost:7001 + ``` + + + +- Build the app. Run: + ```bash + dotnet build + ``` + + + + + +- Navigate to ./bin/Debug/netcoreapp3.1 and start Dapr using command: + ```bash + dapr run --app-id subtractapp --app-port 7001 --dapr-http-port 3504 dotnet Subtract.dll + ``` + + + + +3. Divide App - Open a terminal window and navigate to the node directory and follow the steps below: + + + +- Install dependencies by running the command: + ```bash + npm install + ``` + + + + + +- Start Dapr using the command below + ```bash + dapr run --app-id divideapp --app-port 4000 --dapr-http-port 3502 node app.js + ``` + + + +4. Multiply App - Open a terminal window and navigate to the python directory and follow the steps below: + + + +- Install required packages + ```bash + pip3 install wheel python-dotenv flask_cors flask + ``` + + + +- Set environment variable to use non-default app port 5000 + ```bash + #Linux/Mac OS: + export FLASK_RUN_PORT=5001 + + #Windows: + set FLASK_RUN_PORT=5001 + ``` + + + +- Start dapr using the command: + ```bash + dapr run --app-id multiplyapp --app-port 5001 --dapr-http-port 3501 flask run + ``` + + + +5. Frontend Calculator app - Open a terminal window and navigate to the react-calculator directory and follow the steps below: + + + + +- Install the required modules + ```bash + npm install + npm run buildclient + ``` + + + + + + +- Start Dapr using command below: + ```bash + dapr run --app-id frontendapp --app-port 8080 --dapr-http-port 3507 node server.js + ``` + + + + +6. Open a browser window and go to http://localhost:8080/. From here, you can enter the different operations. + + ![Calculator Screenshot](./img/calculator-screenshot.JPG) + +7. Open your browser's console window (using F12 key) to see the logs produced as you use the calculator. Note that each time you click a button, you see logs that indicate state persistence and the different apps that are contacted to perform the operation. + + + + + + + +8. **Optional:** Curl Validate + + + +- To make sure all the apps are working, you can run the following curl commands which will test all the operations: + ```bash + curl -s http://localhost:8080/calculate/add -H Content-Type:application/json --data @operands.json + ``` + + ```bash + curl -s http://localhost:8080/calculate/subtract -H Content-Type:application/json --data @operands.json + ``` + + ```bash + curl -s http://localhost:8080/calculate/divide -H Content-Type:application/json --data @operands.json + ``` + + ```bash + curl -s http://localhost:8080/calculate/multiply -H Content-Type:application/json --data @operands.json + ``` + + ```bash + curl -s http://localhost:8080/persist -H Content-Type:application/json --data @persist.json + ``` + + ```bash + curl -s http://localhost:8080/state + ``` + + + +- You should get the following output: + ```bash + 86 + 18 + 1.5294117647058822 + 1768 + + {"operation":null,"total":"54","next":null} + ``` + + + +9. Cleanup + +- Cleanup microservices + + ```bash + dapr stop --app-id addapp + ``` + + ```bash + dapr stop --app-id subtractapp + ``` + + ```bash + dapr stop --app-id divideapp + ``` + + ```bash + dapr stop --app-id multiplyapp + ``` + + ```bash + dapr stop --app-id frontendapp + ``` + + + + + +- Uninstall node modules by navigating to the node directory and run: + ``` + npm uninstall + ``` + + + +## Running the quickstart in a Kubernetes environment +1. Navigate to the deploy directory in this quickstart directory: `cd deploy` + > **Note**: `appconfig.yaml` is not used directly for this quickstart but is present for the [observability quickstart](../observability). +2. Follow [these instructions](https://docs.dapr.io/getting-started/tutorials/configure-state-pubsub/#step-1-create-a-redis-store) to create and configure a Redis store +3. Deploy all of your resources: + + + + +```bash +kubectl apply -f . +``` + + + + > **Note**: Services could also be deployed one-by-one by specifying the .yaml file: `kubectl apply -f go-adder.yaml`. + +Each of the services will spin up a pod with two containers: one for your service and one for the Dapr sidecar. It will also configure a service for each sidecar and an external IP for the front-end, which allows us to connect to it externally. + +4. Kubernetes deployments are asyncronous. This means you'll need to wait for the deployment to complete before moving on to the next steps. You can do so with the following commands: + + + +```bash +kubectl rollout status deploy/addapp +``` + +```bash +kubectl rollout status deploy/subtractapp +``` + +```bash +kubectl rollout status deploy/divideapp +``` + +```bash +kubectl rollout status deploy/multiplyapp +``` + +```bash +kubectl rollout status deploy/calculator-front-end +``` + + +You can view the status of the running pods with: + +```bash +kubectl get pods +``` + + + +When everything is running properly, you'll see output like this: + +``` +NAME READY STATUS RESTARTS AGE +addapp-5ff9586df6-5bpll 2/2 Running 0 16s +calculator-front-end-56dc959b58-bb8vw 2/2 Running 0 16s +divideapp-c64f744d6-wljcc 2/2 Running 0 16s +multiplyapp-6989454d77-tkd6c 2/2 Running 0 16s +subtractapp-869b74f676-9mw94 2/2 Running 0 16s +``` + +5. Next, setup access to your service + +There are several different ways to access a Kubernetes service depending on which platform you are using. Port forwarding is one consistent way to access a service, whether it is hosted locally or on a cloud Kubernetes provider like AKS. + + + +```bash +kubectl port-forward service/calculator-front-end 8000:80 +``` + + + +This will make your service available on http://localhost:8000. Navigate to this address with your browser and voilà! You have a working distributed calculator! + +> **Optional**: If you are using a public cloud provider, you can substitue your EXTERNAL-IP address instead of port forwarding. You can find it with: + +```bash +kubectl get svc +``` + +```bash +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +dapr-api ClusterIP 10.103.71.22 80/TCP 135m +dapr-placement ClusterIP 10.103.53.127 80/TCP 135m +dapr-sidecar-injector ClusterIP 10.104.220.35 443/TCP 135m +addapp-dapr ClusterIP 10.0.1.170 80/TCP,50001/TCP 2m +calculator-front-end LoadBalancer 10.0.155.131 40.80.152.125 80:32633/TCP 3m +calculator-front-end-dapr ClusterIP 10.0.230.219 80/TCP,50001/TCP 3m +divideapp-dapr ClusterIP 10.0.240.3 80/TCP,50001/TCP 1m +kubernetes ClusterIP 10.0.0.1 443/TCP 33d +multiplyapp-dapr ClusterIP 10.0.217.211 80/TCP,50001/TCP 1m +subtractapp-dapr ClusterIP 10.0.146.253 80/TCP,50001/TCP 2m +``` + +Each service ending in "-dapr" represents your services respective sidecars, while the `calculator-front-end` service represents the external load balancer for the React calculator front-end. + + + + + + + + + +![Calculator Screenshot](./img/calculator-screenshot.JPG) + +6. Open your browser's console window (using F12 key) to see the logs produced as you use the calculator. Note that each time you click a button, you see logs that indicate state persistence: + +```js +Persisting State: +{total: "21", next: "2", operation: "x"} +``` + +`total`, `next`, and `operation` reflect the three pieces of state a calculator needs to operate. The app persists these to a Redis store (see [Simplified State Management](#simplified-state-management) section below). By persisting these, you can refresh the page or take down the front-end pod and still jump right back where you were. Try it! Enter something into the calculator and refresh the page. The calculator should have retained the state, and the console should read: + +```js +Rehydrating State: +{total: "21", next: "2", operation: "x"} +``` + +Also note that each time you enter a full equation (e.g. "126 ÷ 3 =") the logs indicate that a call is made to the service: + +```js +Calling divide service +``` + +The client code calls to an Express server, which routes the calls through Dapr to the back-end services. In this case the divide endpoint is called on the nodejs application. + +7. **Optional:** If your environment doesn't have easy access to a browser, or you just like using curl + +Then you can use the following curl commands to make sure each one of the microservies is working: + + + +```bash +curl -s http://localhost:8000/calculate/add -H Content-Type:application/json --data @operands.json +``` + +```bash +curl -s http://localhost:8000/calculate/subtract -H Content-Type:application/json --data @operands.json +``` + +```bash +curl -s http://localhost:8000/calculate/divide -H Content-Type:application/json --data @operands.json +``` + +```bash +curl -s http://localhost:8000/calculate/multiply -H Content-Type:application/json --data @operands.json +``` + + + + + +```bash +curl -s http://localhost:8000/persist -H Content-Type:application/json --data @persist.json +``` + +```bash +curl -s http://localhost:8000/state +``` + + + +You should get the following output: + + ```bash + 86 + 18 + 1.5294117647058822 + 1768 + + {"operation":null,"total":"54","next":null} + ``` + +## Cleanup + +### Kubernetes environment cleanup +- Once you're done, you can spin down your Kubernetes resources by navigating to the `./deploy` directory and running: + + + + ```bash + kubectl delete -f . + ``` + + + +This will spin down each resource defined by the .yaml files in the `deploy` directory, including the state component. + +## The Role of Dapr + +This quickstart demonstrates how to use Dapr as a programming model for simplifying the development of distributed systems. In this quickstart, Dapr is enabling polyglot programming, service discovery and simplified state management. + +### Polyglot programming + +Each service in this quickstart is written in a different programming language, but they're used together in the same larger application. Dapr itself is language agnostic - none of the services have to include any dependency in order to work with Dapr. This empowers developers to build each service however they want, using the best language for the job or for a particular dev team. + +### Service invocation + +When the front-end server calls the respective operation services (see `server.js` code below), it doesn't need to know what IP address they live at or how they were built. Instead it calls their local dapr side-car by name, which knows how to invoke the method on the service, taking advantage of the platform’s service discovery mechanism, in this case Kubernetes DNS resolution. + +The code below shows calls to the "add" and "subtract" services via the Dapr URLs: +```js +const daprUrl = `http://localhost:${daprPort}/v1.0/invoke`; + +app.post('/calculate/add', async (req, res) => { + const appResponse = await axios.post(`${daprUrl}/addapp/method/add`, req.body); + return res.send(`${appResponse.data}`); +}); + + +app.post('/calculate/subtract', async (req, res) => { + const appResponse = await axios.post(`${daprUrl}/subtractapp/method/subtract`, req.body); + return res.send(`${appResponse.data}`); +}); +... +``` + +Microservice applications are dynamic with scaling, updates and failures causing services to change their network endpoints. Dapr enables you to call service endpoints with a consistent URL syntax, utilizing the hosting platform’s service discovery capabilities to resolve the endpoint location. + +Learn more about Dapr [service invocation](https://docs.dapr.io/developing-applications/building-blocks/service-invocation/). + +### Simplified state management + +Dapr sidecars provide [state management](https://docs.dapr.io/developing-applications/building-blocks/state-management/). In this quickstart, the calculator's state is persisted each time a new button is clicked. This means a user can refresh the page, close the page or even take down the `calculator-front-end` pod, and still retain the same state when they next open it. Dapr adds a layer of indirection so that the app doesn't need to know where it's persisting state. It doesn't have to keep track of keys, handle retry logic or worry about state provider specific configuration. All it has to do is GET or POST against its Dapr sidecar's state endpoint: `http://localhost:3500/v1.0/state/${stateStoreName}`. + +Take a look at `server.js` in the `react-calculator` directory. Note that it exposes two state endpoints for the React client to get and set state: the GET `/state` endpoint and the POST `/persist` endpoint. Both forward client calls to the Dapr state endpoint: + +```js +const stateUrl = `http://localhost:${daprPort}/v1.0/state/${stateStoreName}`; +``` + +Our client persists state by simply POSTing JSON key-value pairs (see `react-calculator/client/src/component/App.js`): + +```js + const state = [{ + key: "calculatorState", + value + }]; + + fetch("/persist", { + method: "POST", + body: JSON.stringify(state), + headers: { + "Content-Type": "application/json" + } + }); +``` + +## [Optional Steps] VS Code Debugging + +If you are using Visual Studio Code, you can debug this application using the preconfigured launch.json and task.json files in the .vscode folder. +The .vscode folder has already been modified in the project to allow users to launch a compound configuration called "Full Dapr App" which will run all applications and allow you to debug in VS Code. + +For more information on how to configure the files visit [How-To: Debug multiple Dapr applications](https://docs.dapr.io/developing-applications/ides/vscode/vscode-how-to-debug-multiple-dapr-apps/) + +> **Note**: You still need to edit your environment variables for Flask and ASPNETCORE_URLS +**Note**: Dapr offers a preview [Dapr Visual Studio Code extension](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-dapr) for local development which enables users a variety of features related to better managing their Dapr applications and debugging of your Dapr applications for all supported Dapr languages which are .NET, Go, PHP, Python and Java. + + +## Next Steps + +- Explore additional [quickstarts](../../README.md#quickstarts). diff --git a/dapr/quickstarts/tutorials/distributed-calculator/csharp/.dockerignore b/dapr/quickstarts/tutorials/distributed-calculator/csharp/.dockerignore new file mode 100644 index 0000000..cd42ee3 --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/csharp/.dockerignore @@ -0,0 +1,2 @@ +bin/ +obj/ diff --git a/dapr/quickstarts/tutorials/distributed-calculator/csharp/.gitignore b/dapr/quickstarts/tutorials/distributed-calculator/csharp/.gitignore new file mode 100644 index 0000000..1746e32 --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/csharp/.gitignore @@ -0,0 +1,2 @@ +bin +obj diff --git a/dapr/quickstarts/tutorials/distributed-calculator/csharp/Controllers/SubtractController.cs b/dapr/quickstarts/tutorials/distributed-calculator/csharp/Controllers/SubtractController.cs new file mode 100644 index 0000000..5b1d35e --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/csharp/Controllers/SubtractController.cs @@ -0,0 +1,33 @@ +// +// Copyright 2021 The Dapr Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System; +using Microsoft.AspNetCore.Mvc; +using Subtract.Models; + +namespace Subtract.Controllers +{ + [Route("[controller]")] + [ApiController] + public class SubtractController : ControllerBase + { + + //POST: /subtract + [HttpPost] + public decimal Subtract(Operands operands) + { + Console.WriteLine($"Subtracting {operands.OperandTwo} from {operands.OperandOne}"); + return Decimal.Parse(operands.OperandOne) - Decimal.Parse(operands.OperandTwo); + } + } +} diff --git a/dapr/quickstarts/tutorials/distributed-calculator/csharp/Dockerfile b/dapr/quickstarts/tutorials/distributed-calculator/csharp/Dockerfile new file mode 100644 index 0000000..b75dcde --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/csharp/Dockerfile @@ -0,0 +1,7 @@ +# Note: we cannot do a staged dotnet docker build here for arm/arm64. + +# Build runtime image +FROM mcr.microsoft.com/dotnet/aspnet:3.1 +WORKDIR /app +COPY /out . +ENTRYPOINT ["dotnet", "Subtract.dll"] diff --git a/dapr/quickstarts/tutorials/distributed-calculator/csharp/Models/Operands.cs b/dapr/quickstarts/tutorials/distributed-calculator/csharp/Models/Operands.cs new file mode 100644 index 0000000..b9463fe --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/csharp/Models/Operands.cs @@ -0,0 +1,19 @@ +// +// Copyright 2021 The Dapr Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +namespace Subtract.Models { + public class Operands { + public string OperandOne { get; set;} + public string OperandTwo { get; set;} + } +} \ No newline at end of file diff --git a/dapr/quickstarts/tutorials/distributed-calculator/csharp/Program.cs b/dapr/quickstarts/tutorials/distributed-calculator/csharp/Program.cs new file mode 100644 index 0000000..48d7df1 --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/csharp/Program.cs @@ -0,0 +1,33 @@ +// +// Copyright 2021 The Dapr Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; + +namespace Subtract +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/dapr/quickstarts/tutorials/distributed-calculator/csharp/Properties/launchSettings.json b/dapr/quickstarts/tutorials/distributed-calculator/csharp/Properties/launchSettings.json new file mode 100644 index 0000000..f9aefa5 --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/csharp/Properties/launchSettings.json @@ -0,0 +1,30 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:4927", + "sslPort": 44366 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "api/values", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Subtract": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "api/subtract", + "applicationUrl": "https://localhost:7001;http://localhost:7000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} \ No newline at end of file diff --git a/dapr/quickstarts/tutorials/distributed-calculator/csharp/Startup.cs b/dapr/quickstarts/tutorials/distributed-calculator/csharp/Startup.cs new file mode 100644 index 0000000..da0f80a --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/csharp/Startup.cs @@ -0,0 +1,52 @@ +// +// Copyright 2021 The Dapr Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace Subtract +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); + } + } +} diff --git a/dapr/quickstarts/tutorials/distributed-calculator/csharp/Subtract.csproj b/dapr/quickstarts/tutorials/distributed-calculator/csharp/Subtract.csproj new file mode 100644 index 0000000..b062337 --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/csharp/Subtract.csproj @@ -0,0 +1,10 @@ + + + + netcoreapp3.1 + + + + + + diff --git a/dapr/quickstarts/tutorials/distributed-calculator/csharp/appsettings.Development.json b/dapr/quickstarts/tutorials/distributed-calculator/csharp/appsettings.Development.json new file mode 100644 index 0000000..e203e94 --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/csharp/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + } +} diff --git a/dapr/quickstarts/tutorials/distributed-calculator/csharp/appsettings.json b/dapr/quickstarts/tutorials/distributed-calculator/csharp/appsettings.json new file mode 100644 index 0000000..def9159 --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/csharp/appsettings.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/dapr/quickstarts/tutorials/distributed-calculator/deploy/appconfig.yaml b/dapr/quickstarts/tutorials/distributed-calculator/deploy/appconfig.yaml new file mode 100644 index 0000000..a06140b --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/deploy/appconfig.yaml @@ -0,0 +1,9 @@ +apiVersion: dapr.io/v1alpha1 +kind: Configuration +metadata: + name: appconfig +spec: + tracing: + samplingRate: "1" + zipkin: + endpointAddress: "http://zipkin.default.svc.cluster.local:9411/api/v2/spans" diff --git a/dapr/quickstarts/tutorials/distributed-calculator/deploy/dotnet-subtractor.yaml b/dapr/quickstarts/tutorials/distributed-calculator/deploy/dotnet-subtractor.yaml new file mode 100644 index 0000000..58c9585 --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/deploy/dotnet-subtractor.yaml @@ -0,0 +1,27 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: subtractapp + labels: + app: subtract +spec: + replicas: 1 + selector: + matchLabels: + app: subtract + template: + metadata: + labels: + app: subtract + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "subtractapp" + dapr.io/app-port: "80" + dapr.io/config: "appconfig" + spec: + containers: + - name: subtract + image: ghcr.io/dapr/samples/distributed-calculator-csharp:latest + ports: + - containerPort: 80 + imagePullPolicy: Always diff --git a/dapr/quickstarts/tutorials/distributed-calculator/deploy/go-adder.yaml b/dapr/quickstarts/tutorials/distributed-calculator/deploy/go-adder.yaml new file mode 100644 index 0000000..086cb99 --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/deploy/go-adder.yaml @@ -0,0 +1,30 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: addapp + labels: + app: add +spec: + replicas: 1 + selector: + matchLabels: + app: add + template: + metadata: + labels: + app: add + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "addapp" + dapr.io/app-port: "6000" + dapr.io/config: "appconfig" + spec: + containers: + - name: add + image: ghcr.io/dapr/samples/distributed-calculator-go:latest + env: + - name: APP_PORT + value: "6000" + ports: + - containerPort: 6000 + imagePullPolicy: Always diff --git a/dapr/quickstarts/tutorials/distributed-calculator/deploy/node-divider.yaml b/dapr/quickstarts/tutorials/distributed-calculator/deploy/node-divider.yaml new file mode 100644 index 0000000..279867a --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/deploy/node-divider.yaml @@ -0,0 +1,30 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: divideapp + labels: + app: divide +spec: + replicas: 1 + selector: + matchLabels: + app: divide + template: + metadata: + labels: + app: divide + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "divideapp" + dapr.io/app-port: "4000" + dapr.io/config: "appconfig" + spec: + containers: + - name: divide + image: ghcr.io/dapr/samples/distributed-calculator-node:latest + env: + - name: APP_PORT + value: "4000" + ports: + - containerPort: 4000 + imagePullPolicy: Always diff --git a/dapr/quickstarts/tutorials/distributed-calculator/deploy/python-multiplier.yaml b/dapr/quickstarts/tutorials/distributed-calculator/deploy/python-multiplier.yaml new file mode 100644 index 0000000..b82cb9a --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/deploy/python-multiplier.yaml @@ -0,0 +1,30 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: multiplyapp + labels: + app: multiply +spec: + replicas: 1 + selector: + matchLabels: + app: multiply + template: + metadata: + labels: + app: multiply + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "multiplyapp" + dapr.io/app-port: "5001" + dapr.io/config: "appconfig" + spec: + containers: + - name: multiply + image: ghcr.io/dapr/samples/distributed-calculator-slow-python:latest + env: + - name: "APP_PORT" + value: "5001" + ports: + - containerPort: 5001 + imagePullPolicy: Always diff --git a/dapr/quickstarts/tutorials/distributed-calculator/deploy/react-calculator.yaml b/dapr/quickstarts/tutorials/distributed-calculator/deploy/react-calculator.yaml new file mode 100644 index 0000000..f72f5de --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/deploy/react-calculator.yaml @@ -0,0 +1,43 @@ +kind: Service +apiVersion: v1 +metadata: + name: calculator-front-end + labels: + app: calculator-front-end +spec: + selector: + app: calculator-front-end + ports: + - protocol: TCP + port: 80 + targetPort: 8080 + type: LoadBalancer + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: calculator-front-end + labels: + app: calculator-front-end +spec: + replicas: 1 + selector: + matchLabels: + app: calculator-front-end + template: + metadata: + labels: + app: calculator-front-end + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "calculator-front-end" + dapr.io/app-port: "8080" + dapr.io/config: "appconfig" + spec: + containers: + - name: calculator-front-end + image: ghcr.io/dapr/samples/distributed-calculator-react-calculator:latest + ports: + - containerPort: 8080 + imagePullPolicy: Always diff --git a/dapr/quickstarts/tutorials/distributed-calculator/deploy/redis.yaml b/dapr/quickstarts/tutorials/distributed-calculator/deploy/redis.yaml new file mode 100644 index 0000000..de9be2a --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/deploy/redis.yaml @@ -0,0 +1,21 @@ +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: statestore +spec: + type: state.redis + version: v1 + metadata: + # These settings will work out of the box if you use `helm install + # bitnami/redis`. If you have your own setup, replace + # `redis-master:6379` with your own Redis master address, and the + # Redis password with your own Secret's name. For more information, + # see https://docs.dapr.io/operations/components/component-secrets . + - name: redisHost + value: redis-master:6379 + - name: redisPassword + secretKeyRef: + name: redis + key: redis-password +auth: + secretStore: kubernetes diff --git a/dapr/quickstarts/tutorials/distributed-calculator/go/.gitignore b/dapr/quickstarts/tutorials/distributed-calculator/go/.gitignore new file mode 100644 index 0000000..7a0b7f0 --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/go/.gitignore @@ -0,0 +1 @@ +app \ No newline at end of file diff --git a/dapr/quickstarts/tutorials/distributed-calculator/go/Dockerfile b/dapr/quickstarts/tutorials/distributed-calculator/go/Dockerfile new file mode 100644 index 0000000..3e37367 --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/go/Dockerfile @@ -0,0 +1,11 @@ +#first stage - builder +FROM golang:1.15-buster as builder +WORKDIR /dir +COPY app.go . +RUN go get -d -v +RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . +#second stage +FROM debian:buster-slim +WORKDIR /root/ +COPY --from=builder /dir/app . +CMD ["./app"] diff --git a/dapr/quickstarts/tutorials/distributed-calculator/go/app.go b/dapr/quickstarts/tutorials/distributed-calculator/go/app.go new file mode 100644 index 0000000..16617f1 --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/go/app.go @@ -0,0 +1,49 @@ +// +// Copyright 2021 The Dapr Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package main + +import ( + "encoding/json" + "fmt" + "log" + "net/http" + "os" + + "github.com/gorilla/mux" +) + +type Operands struct { + OperandOne float32 `json:"operandOne,string"` + OperandTwo float32 `json:"operandTwo,string"` +} + +func add(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.Header().Set("Access-Control-Allow-Origin", "*") + var operands Operands + json.NewDecoder(r.Body).Decode(&operands) + fmt.Printf("Adding %f to %f\n", operands.OperandOne, operands.OperandTwo) + json.NewEncoder(w).Encode(operands.OperandOne + operands.OperandTwo) +} + +func main() { + appPort := "6000" + if value, ok := os.LookupEnv("APP_PORT"); ok { + appPort = value + } + router := mux.NewRouter() + + router.HandleFunc("/add", add).Methods("POST", "OPTIONS") + log.Fatal(http.ListenAndServe(":"+appPort, router)) +} diff --git a/dapr/quickstarts/tutorials/distributed-calculator/go/go b/dapr/quickstarts/tutorials/distributed-calculator/go/go new file mode 100755 index 0000000..3de1a25 Binary files /dev/null and b/dapr/quickstarts/tutorials/distributed-calculator/go/go differ diff --git a/dapr/quickstarts/tutorials/distributed-calculator/go/go.mod b/dapr/quickstarts/tutorials/distributed-calculator/go/go.mod new file mode 100644 index 0000000..935f0c6 --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/go/go.mod @@ -0,0 +1,5 @@ +module github.com/dapr/quickstarts/distributed-calculator/go + +go 1.18 + +require github.com/gorilla/mux v1.8.0 diff --git a/dapr/quickstarts/tutorials/distributed-calculator/go/go.sum b/dapr/quickstarts/tutorials/distributed-calculator/go/go.sum new file mode 100644 index 0000000..5350288 --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/go/go.sum @@ -0,0 +1,2 @@ +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= diff --git a/dapr/quickstarts/tutorials/distributed-calculator/img/Architecture_Diagram.png b/dapr/quickstarts/tutorials/distributed-calculator/img/Architecture_Diagram.png new file mode 100644 index 0000000..b723c98 Binary files /dev/null and b/dapr/quickstarts/tutorials/distributed-calculator/img/Architecture_Diagram.png differ diff --git a/dapr/quickstarts/tutorials/distributed-calculator/img/calculator-screenshot.JPG b/dapr/quickstarts/tutorials/distributed-calculator/img/calculator-screenshot.JPG new file mode 100644 index 0000000..1f6b327 Binary files /dev/null and b/dapr/quickstarts/tutorials/distributed-calculator/img/calculator-screenshot.JPG differ diff --git a/dapr/quickstarts/tutorials/distributed-calculator/makefile b/dapr/quickstarts/tutorials/distributed-calculator/makefile new file mode 100644 index 0000000..2708bc1 --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/makefile @@ -0,0 +1,16 @@ +DOCKER_IMAGE_PREFIX ?= distributed-calculator- +APPS ?= node python go csharp react-calculator + +include ../docker.mk +include ../validate.mk + +TARGET_DOTNET_PLATFORM = $(TARGET_ARCH) +ifeq ($(TARGET_DOTNET_PLATFORM),amd64) + TARGET_DOTNET_PLATFORM = x64 +endif + +build-csharp-local: + cd csharp && dotnet restore -r linux-$(TARGET_DOTNET_PLATFORM) + cd csharp && dotnet publish -c Release -o out + +build-csharp: build-csharp-local diff --git a/dapr/quickstarts/tutorials/distributed-calculator/node/.gitignore b/dapr/quickstarts/tutorials/distributed-calculator/node/.gitignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/node/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/dapr/quickstarts/tutorials/distributed-calculator/node/Dockerfile b/dapr/quickstarts/tutorials/distributed-calculator/node/Dockerfile new file mode 100644 index 0000000..0c7ab85 --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/node/Dockerfile @@ -0,0 +1,6 @@ +FROM node:17-alpine +WORKDIR /usr/src/app +COPY . . +RUN npm install +EXPOSE 4000 +CMD [ "node", "app.js" ] \ No newline at end of file diff --git a/dapr/quickstarts/tutorials/distributed-calculator/node/app.js b/dapr/quickstarts/tutorials/distributed-calculator/node/app.js new file mode 100644 index 0000000..b8cf839 --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/node/app.js @@ -0,0 +1,33 @@ +// +// Copyright 2021 The Dapr Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +const express = require('express'); +const bodyParser = require('body-parser'); +const app = express(); +app.use(bodyParser.json()); +const cors = require('cors'); +const port = process.env.APP_PORT ?? '4000'; + +app.use(cors()); + +app.post('/divide', (req, res) => { + let args = req.body; + const [operandOne, operandTwo] = [Number(args['operandOne']), Number(args['operandTwo'])]; + + console.log(`Dividing ${operandOne} by ${operandTwo}`); + + let result = operandOne / operandTwo; + res.send(result.toString()); +}); + +app.listen(port, () => console.log(`Listening on port ${port}!`)); \ No newline at end of file diff --git a/dapr/quickstarts/tutorials/distributed-calculator/node/package.json b/dapr/quickstarts/tutorials/distributed-calculator/node/package.json new file mode 100644 index 0000000..5fed6d9 --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/node/package.json @@ -0,0 +1,17 @@ +{ + "name": "node-divide", + "version": "0.0.1", + "private": true, + "description": "", + "main": "app.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "body-parser": "^1.19.0", + "cors": "^2.8.5", + "express": "^4.17.1" + } +} diff --git a/dapr/quickstarts/tutorials/distributed-calculator/operands.json b/dapr/quickstarts/tutorials/distributed-calculator/operands.json new file mode 100644 index 0000000..7ffce44 --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/operands.json @@ -0,0 +1 @@ +{"operandOne":"52","operandTwo":"34"} diff --git a/dapr/quickstarts/tutorials/distributed-calculator/persist.json b/dapr/quickstarts/tutorials/distributed-calculator/persist.json new file mode 100644 index 0000000..9f1a83b --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/persist.json @@ -0,0 +1 @@ +[{"key":"calculatorState","value":{"total":"54","next":null,"operation":null}}] diff --git a/dapr/quickstarts/tutorials/distributed-calculator/python/.gitignore b/dapr/quickstarts/tutorials/distributed-calculator/python/.gitignore new file mode 100644 index 0000000..ba0430d --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/python/.gitignore @@ -0,0 +1 @@ +__pycache__/ \ No newline at end of file diff --git a/dapr/quickstarts/tutorials/distributed-calculator/python/Dockerfile b/dapr/quickstarts/tutorials/distributed-calculator/python/Dockerfile new file mode 100644 index 0000000..fa0d808 --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/python/Dockerfile @@ -0,0 +1,7 @@ +FROM python:3.7-alpine +COPY . /app +WORKDIR /app +RUN pip install flask flask_cors +ENTRYPOINT ["python"] +EXPOSE 5001 +CMD ["app.py"] diff --git a/dapr/quickstarts/tutorials/distributed-calculator/python/app.py b/dapr/quickstarts/tutorials/distributed-calculator/python/app.py new file mode 100644 index 0000000..5fcd9e2 --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/python/app.py @@ -0,0 +1,33 @@ +# +# Copyright 2021 The Dapr Authors +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import flask +from flask import request, jsonify +from flask_cors import CORS +import math +import sys +import os + +appPort = os.getenv("APP_PORT","5001") + +app = flask.Flask(__name__) +CORS(app) + +@app.route('/multiply', methods=['POST']) +def multiply(): + content = request.json + [operand_one, operand_two] = [float(content['operandOne']), float(content['operandTwo'])] + print(f"Calculating {operand_one} * {operand_two}", flush=True) + return jsonify(math.ceil(operand_one * operand_two * 100000)/100000) + +app.run(host="0.0.0.0",port=appPort) diff --git a/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/.dockerignore b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/.dockerignore new file mode 100644 index 0000000..dc49089 --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/.dockerignore @@ -0,0 +1,2 @@ +node_modules +client/node_modules \ No newline at end of file diff --git a/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/.gitignore b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/.gitignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/Dockerfile b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/Dockerfile new file mode 100644 index 0000000..945cad5 --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/Dockerfile @@ -0,0 +1,12 @@ +FROM node:17-alpine +WORKDIR /usr/src/app +COPY . . +RUN npm install + +# Build the client +RUN cd client && npm i && npm run build + +EXPOSE 8080 +EXPOSE 3000 + +CMD [ "npm", "run", "start" ] diff --git a/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/.gitignore b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/.gitignore new file mode 100644 index 0000000..4d29575 --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/.gitignore @@ -0,0 +1,23 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# production +/build + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/LICENSE b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/LICENSE new file mode 100644 index 0000000..124df0f --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Andrew H Farmer + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/README.md b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/README.md new file mode 100644 index 0000000..9d9614c --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/README.md @@ -0,0 +1,68 @@ +This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). + +## Available Scripts + +In the project directory, you can run: + +### `npm start` + +Runs the app in the development mode.
+Open [http://localhost:3000](http://localhost:3000) to view it in the browser. + +The page will reload if you make edits.
+You will also see any lint errors in the console. + +### `npm test` + +Launches the test runner in the interactive watch mode.
+See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. + +### `npm run build` + +Builds the app for production to the `build` folder.
+It correctly bundles React in production mode and optimizes the build for the best performance. + +The build is minified and the filenames include the hashes.
+Your app is ready to be deployed! + +See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. + +### `npm run eject` + +**Note: this is a one-way operation. Once you `eject`, you can’t go back!** + +If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. + +Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. + +You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. + +## Learn More + +You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). + +To learn React, check out the [React documentation](https://reactjs.org/). + +### Code Splitting + +This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting + +### Analyzing the Bundle Size + +This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size + +### Making a Progressive Web App + +This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app + +### Advanced Configuration + +This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration + +### Deployment + +This section has moved here: https://facebook.github.io/create-react-app/docs/deployment + +### `npm run build` fails to minify + +This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify diff --git a/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/package.json b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/package.json new file mode 100644 index 0000000..0080d98 --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/package.json @@ -0,0 +1,36 @@ +{ + "name": "client", + "version": "0.1.0", + "private": true, + "proxy": "http://localhost:8080/", + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject" + }, + "dependencies": { + "react": "^16.9.0", + "react-dom": "^16.9.0", + "react-scripts": "^5.0.0", + "mini-css-extract-plugin": "2.4.5" + }, + "resolutions": { + "mini-css-extract-plugin": "2.4.5" + }, + "eslintConfig": { + "extends": "react-app" + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + } +} diff --git a/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/public/favicon.ico b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/public/favicon.ico new file mode 100644 index 0000000..a11777c Binary files /dev/null and b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/public/favicon.ico differ diff --git a/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/public/index.html b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/public/index.html new file mode 100644 index 0000000..dd1ccfd --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/public/index.html @@ -0,0 +1,38 @@ + + + + + + + + + + + React App + + + +
+ + + diff --git a/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/public/manifest.json b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/public/manifest.json new file mode 100644 index 0000000..1f2f141 --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/public/manifest.json @@ -0,0 +1,15 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/component/App.css b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/component/App.css new file mode 100644 index 0000000..7760541 --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/component/App.css @@ -0,0 +1,6 @@ +.component-app { + display: flex; + flex-direction: column; + flex-wrap: wrap; + height: 100%; +} diff --git a/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/component/App.js b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/component/App.js new file mode 100644 index 0000000..400ec04 --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/component/App.js @@ -0,0 +1,61 @@ +import React from "react"; +import Display from "./Display"; +import ButtonPanel from "./ButtonPanel"; +import calculate from "../logic/calculate"; +import "./App.css"; + +export default class App extends React.Component { + state = { + total: null, + next: null, + operation: null, + }; + + async componentDidMount() { + const savedState = await this.getState(); + if (savedState) { + console.log("Rehydrating State:"); + console.log(JSON.stringify(savedState)); + this.setState(savedState); + } + } + + handleClick = async (buttonName) => { + let value = await calculate(this.state, buttonName); + this.setState(value); + this.persistState(this.state); + }; + + persistState = (value) => { + console.log(`Persisting State:`); + console.log(JSON.stringify(value)); + + const state = [{ + key: "calculatorState", + value + }]; + + fetch("/persist", { + method: "POST", + body: JSON.stringify(state), + headers: { + "Content-Type": "application/json" + } + }); + } + + getState = async () => { + const rawResponse = await fetch("/state"); + const calculatorState = await rawResponse.json(); + return calculatorState; + } + + render() { + return ( +
+ + +
+ ); + } +} diff --git a/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/component/App.test.js b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/component/App.test.js new file mode 100644 index 0000000..c9ac3ef --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/component/App.test.js @@ -0,0 +1,8 @@ +import React from "react"; +import ReactDOM from "react-dom"; +import App from "./App"; + +it("renders without crashing", () => { + const div = document.createElement("div"); + ReactDOM.render(, div); +}); diff --git a/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/component/Button.css b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/component/Button.css new file mode 100644 index 0000000..a0b0e70 --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/component/Button.css @@ -0,0 +1,27 @@ +.component-button { + display: inline-flex; + width: 25%; + flex: 1 0 auto; +} + +.component-button.wide { + width: 50%; +} + +.component-button button { + background-color: #e0e0e0; + border: 0; + font-size: 1.5rem; + margin: 0 1px 0 0; + flex: 1 0 auto; + padding: 0; +} + +.component-button:last-child button { + margin-right: 0; +} + +.component-button.orange button { + background-color: #f5923e; + color: white; +} diff --git a/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/component/Button.js b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/component/Button.js new file mode 100644 index 0000000..0fe78c7 --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/component/Button.js @@ -0,0 +1,30 @@ +import React from "react"; +import PropTypes from "prop-types"; +import "./Button.css"; + +export default class Button extends React.Component { + static propTypes = { + name: PropTypes.string, + orange: PropTypes.bool, + wide: PropTypes.bool, + clickHandler: PropTypes.func, + }; + + handleClick = () => { + this.props.clickHandler(this.props.name); + }; + + render() { + const className = [ + "component-button", + this.props.orange ? "orange" : "", + this.props.wide ? "wide" : "", + ]; + + return ( +
+ +
+ ); + } +} diff --git a/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/component/ButtonPanel.css b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/component/ButtonPanel.css new file mode 100644 index 0000000..10e4bbe --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/component/ButtonPanel.css @@ -0,0 +1,14 @@ +.component-button-panel { + background-color: #858694; + display: flex; + flex-direction: row; + flex-wrap: wrap; + flex: 1 0 auto; +} + +.component-button-panel > div { + width: 100%; + margin-bottom: 1px; + flex: 1 0 auto; + display: flex; +} diff --git a/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/component/ButtonPanel.js b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/component/ButtonPanel.js new file mode 100644 index 0000000..994bca4 --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/component/ButtonPanel.js @@ -0,0 +1,51 @@ +import Button from "./Button"; +import React from "react"; +import PropTypes from "prop-types"; + +import "./ButtonPanel.css"; + +export default class ButtonPanel extends React.Component { + static propTypes = { + clickHandler: PropTypes.func, + }; + + handleClick = buttonName => { + this.props.clickHandler(buttonName); + }; + + render() { + return ( +
+
+
+
+
+
+
+
+
+
+
+
+ ); + } +} diff --git a/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/component/Display.css b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/component/Display.css new file mode 100644 index 0000000..d3196e0 --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/component/Display.css @@ -0,0 +1,13 @@ +.component-display { + background-color: #858694; + color: white; + text-align: right; + font-weight: 200; + flex: 0 0 auto; + width: 100%; +} + +.component-display > div { + font-size: 2.5rem; + padding: 0.2rem 0.7rem 0.1rem 0.5rem; +} diff --git a/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/component/Display.js b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/component/Display.js new file mode 100644 index 0000000..31a942d --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/component/Display.js @@ -0,0 +1,18 @@ +import React from "react"; +import PropTypes from "prop-types"; + +import "./Display.css"; + +export default class Display extends React.Component { + static propTypes = { + value: PropTypes.string, + }; + + render() { + return ( +
+
{this.props.value}
+
+ ); + } +} diff --git a/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/index.css b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/index.css new file mode 100644 index 0000000..d46132f --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/index.css @@ -0,0 +1,50 @@ +html { + height: 100%; + font-size: 10px; +} + +body { + background-color: black; + margin: 0; + padding: 0; + font-family: sans-serif; + height: 100%; +} + +#root { + height: 100%; +} + +body .github-fork-ribbon:before { + background-color: #333; +} + +@media screen and (max-width: 400px) { + .github-fork-ribbon { + display: none; + } +} + +@media (min-width: 400px) and (min-height: 400px) { + html { + font-size: 20px; + } +} + +@media (min-width: 500px) and (min-height: 500px) { + html { + font-size: 30px; + } +} + +@media (min-width: 600px) and (min-height: 600px) { + html { + font-size: 40px; + } +} + +@media (min-width: 800px) and (min-height: 800px) { + html { + font-size: 50px; + } +} diff --git a/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/index.js b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/index.js new file mode 100644 index 0000000..d9f7532 --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/index.js @@ -0,0 +1,6 @@ +import React from "react"; +import ReactDOM from "react-dom"; +import App from "./component/App"; +import "./index.css"; + +ReactDOM.render(, document.getElementById("root")); diff --git a/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/logic/calculate.js b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/logic/calculate.js new file mode 100644 index 0000000..330fa83 --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/logic/calculate.js @@ -0,0 +1,150 @@ +import Big from "big.js"; + +import operate from "./operate"; +import isNumber from "./isNumber"; + +/** + * Given a button name and a calculator data object, return an updated + * calculator data object. + * + * Calculator data object contains: + * total:String the running total + * next:String the next number to be operated on with the total + * operation:String +, -, etc. + */ +export default async function calculate(obj, buttonName) { + if (buttonName === "AC") { + return { + total: null, + next: null, + operation: null, + }; + } + + if (isNumber(buttonName)) { + if (buttonName === "0" && obj.next === "0") { + return {}; + } + // If there is an operation, update next + if (obj.operation) { + if (obj.next) { + return { + next: obj.next + buttonName + }; + } + return { + next: buttonName + }; + } + // If there is no operation, update next and clear the value + if (obj.next) { + const next = obj.next === "0" ? buttonName : obj.next + buttonName; + return { + next, + total: null, + }; + } + return { + next: buttonName, + total: null, + }; + } + + if (buttonName === "%") { + if (obj.operation && obj.next) { + let result = await operate(obj.total, obj.next, obj.operation); + return { + total: Big(result) + .div(Big("100")) + .toString(), + next: null, + operation: null, + }; + } + if (obj.next) { + return { + next: Big(obj.next) + .div(Big("100")) + .toString(), + }; + } + return {}; + } + + if (buttonName === ".") { + if (obj.next) { + // ignore a . if the next number already has one + if (obj.next.includes(".")) { + return {}; + } + return { + next: obj.next + "." + }; + } + return { + next: "0." + }; + } + + if (buttonName === "=") { + if (obj.next && obj.operation) { + const total = await operate(obj.total, obj.next, obj.operation); + return { + total, + next: null, + operation: null, + }; + } else { + // '=' with no operation, nothing to do + return {}; + } + } + + if (buttonName === "+/-") { + if (obj.next) { + return { + next: (-1 * parseFloat(obj.next)).toString() + }; + } + if (obj.total) { + return { + total: (-1 * parseFloat(obj.total)).toString() + }; + } + return {}; + } + + // Button must be an operation + + // When the user presses an operation button without having entered + // a number first, do nothing. + // if (!obj.next && !obj.total) { + // return {}; + // } + + // User pressed an operation button and there is an existing operation + if (obj.operation) { + const total = await operate(obj.total, obj.next, obj.operation); + return { + total, + next: null, + operation: buttonName, + }; + } + + // no operation yet, but the user typed one + + // The user hasn't typed a number yet, just save the operation + if (!obj.next) { + return { + operation: buttonName + }; + } + + // save the operation and shift 'next' into 'total' + return { + total: obj.next, + next: null, + operation: buttonName, + }; +} \ No newline at end of file diff --git a/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/logic/calculate.test.js b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/logic/calculate.test.js new file mode 100644 index 0000000..939e833 --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/logic/calculate.test.js @@ -0,0 +1,173 @@ +import calculate from "./calculate"; +import chai from "chai"; + +// https://github.com/chaijs/chai/issues/469 +chai.config.truncateThreshold = 0; + +const expect = chai.expect; + +function pressButtons(buttons) { + const value = {}; + buttons.forEach(button => { + Object.assign(value, calculate(value, button)); + }); + // no need to distinguish between null and undefined values + Object.keys(value).forEach(key => { + if (value[key] === null) { + delete value[key]; + } + }); + return value; +} + +function expectButtons(buttons, expectation) { + expect(pressButtons(buttons)).to.deep.equal(expectation); +} + +function test(buttons, expectation, only = false) { + const func = only ? it.only : it; + func(`buttons ${buttons.join(",")} -> ${JSON.stringify(expectation)}`, () => { + expectButtons(buttons, expectation); + }); +} + +describe("calculate", function() { + test(["6"], { next: "6" }); + + test(["6", "6"], { next: "66" }); + + test(["6", "+", "6"], { + next: "6", + total: "6", + operation: "+", + }); + + test(["6", "+", "6", "="], { + total: "12", + }); + + test(["0", "0", "+", "0", "="], { + total: "0", + }); + + test(["6", "+", "6", "=", "9"], { + next: "9", + }); + + test(["3", "+", "6", "=", "+"], { + total: "9", + operation: "+", + }); + + test(["3", "+", "6", "=", "+", "9"], { + total: "9", + operation: "+", + next: "9", + }); + + test(["3", "+", "6", "=", "+", "9", "="], { + total: "18", + }); + + // When '=' is pressed and there is not enough information to complete + // an operation, the '=' should be disregarded. + test(["3", "+", "=", "3", "="], { + total: "6", + }); + + test(["+"], { + operation: "+", + }); + + test(["+", "2"], { + next: "2", + operation: "+", + }); + + test(["+", "2", "+"], { + total: "2", + operation: "+", + }); + + test(["+", "2", "+", "+"], { + total: "2", + operation: "+", + }); + + test(["+", "2", "+", "5"], { + next: "5", + total: "2", + operation: "+", + }); + + test(["+", "2", "5"], { + next: "25", + operation: "+", + }); + + test(["+", "2", "5"], { + next: "25", + operation: "+", + }); + + test(["+", "6", "+", "5", "="], { + total: "11", + }); + + test(["0", ".", "4"], { + next: "0.4", + }); + + test([".", "4"], { + next: "0.4", + }); + + test([".", "4", "-", ".", "2"], { + total: "0.4", + next: "0.2", + operation: "-", + }); + + test([".", "4", "-", ".", "2", "="], { + total: "0.2", + }); + + // should clear the operator when AC is pressed + test(["1", "+", "2", "AC"], {}); + test(["+", "2", "AC"], {}); + + test(["4", "%"], { + next: "0.04", + }); + + test(["4", "%", "x", "2", "="], { + total: "0.08", + }); + + test(["4", "%", "x", "2"], { + total: "0.04", + operation: "x", + next: "2", + }); + + // the percentage sign should also act as '=' + test(["2", "x", "2", "%"], { + total: "0.04", + }); + + //Test that pressing the multiplication or division sign multiple times should not affect the current computation + test(["2", "x", "x"], { + total: "2", + operation: "x" + }); + + test(["2", "÷", "÷"], { + total: "2", + operation: "÷" + }); + + test(["2", "÷", "x", "+", "-", "x"], { + total: "2", + operation: 'x' + }); +}); diff --git a/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/logic/isNumber.js b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/logic/isNumber.js new file mode 100644 index 0000000..0f4c40d --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/logic/isNumber.js @@ -0,0 +1,3 @@ +export default function isNumber(item) { + return /[0-9]+/.test(item); +} diff --git a/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/logic/operate.js b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/logic/operate.js new file mode 100644 index 0000000..f105f37 --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/logic/operate.js @@ -0,0 +1,30 @@ +const operationMap = { + "+": "add", + "-": "subtract", + "x": "multiply", + "÷": "divide" +}; + +export default async function operate(operandOne, operandTwo, operationSymbol) { + + operandOne = operandOne || "0"; + operandTwo = operandTwo || (operationSymbol === "÷" || operationSymbol === 'x' ? "1" : "0"); //If dividing or multiplying, then 1 maintains current value in cases of null + + const operation = operationMap[operationSymbol]; + console.log(`Calling ${operation} service`); + + const rawResponse = await fetch(`/calculate/${operation}`, { + method: 'POST', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + operandOne, + operandTwo + }), + }); + const response = await rawResponse.json(); + + return response.toString(); +} \ No newline at end of file diff --git a/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/serviceWorker.js b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/serviceWorker.js new file mode 100644 index 0000000..f8c7e50 --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/client/src/serviceWorker.js @@ -0,0 +1,135 @@ +// This optional code is used to register a service worker. +// register() is not called by default. + +// This lets the app load faster on subsequent visits in production, and gives +// it offline capabilities. However, it also means that developers (and users) +// will only see deployed updates on subsequent visits to a page, after all the +// existing tabs open on the page have been closed, since previously cached +// resources are updated in the background. + +// To learn more about the benefits of this model and instructions on how to +// opt-in, read https://bit.ly/CRA-PWA + +const isLocalhost = Boolean( + window.location.hostname === 'localhost' || + // [::1] is the IPv6 localhost address. + window.location.hostname === '[::1]' || + // 127.0.0.1/8 is considered localhost for IPv4. + window.location.hostname.match( + /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ + ) +); + +export function register(config) { + if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { + // The URL constructor is available in all browsers that support SW. + const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); + if (publicUrl.origin !== window.location.origin) { + // Our service worker won't work if PUBLIC_URL is on a different origin + // from what our page is served on. This might happen if a CDN is used to + // serve assets; see https://github.com/facebook/create-react-app/issues/2374 + return; + } + + window.addEventListener('load', () => { + const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; + + if (isLocalhost) { + // This is running on localhost. Let's check if a service worker still exists or not. + checkValidServiceWorker(swUrl, config); + + // Add some additional logging to localhost, pointing developers to the + // service worker/PWA documentation. + navigator.serviceWorker.ready.then(() => { + console.log( + 'This web app is being served cache-first by a service ' + + 'worker. To learn more, visit https://bit.ly/CRA-PWA' + ); + }); + } else { + // Is not localhost. Just register service worker + registerValidSW(swUrl, config); + } + }); + } +} + +function registerValidSW(swUrl, config) { + navigator.serviceWorker + .register(swUrl) + .then(registration => { + registration.onupdatefound = () => { + const installingWorker = registration.installing; + if (installingWorker == null) { + return; + } + installingWorker.onstatechange = () => { + if (installingWorker.state === 'installed') { + if (navigator.serviceWorker.controller) { + // At this point, the updated precached content has been fetched, + // but the previous service worker will still serve the older + // content until all client tabs are closed. + console.log( + 'New content is available and will be used when all ' + + 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' + ); + + // Execute callback + if (config && config.onUpdate) { + config.onUpdate(registration); + } + } else { + // At this point, everything has been precached. + // It's the perfect time to display a + // "Content is cached for offline use." message. + console.log('Content is cached for offline use.'); + + // Execute callback + if (config && config.onSuccess) { + config.onSuccess(registration); + } + } + } + }; + }; + }) + .catch(error => { + console.error('Error during service worker registration:', error); + }); +} + +function checkValidServiceWorker(swUrl, config) { + // Check if the service worker can be found. If it can't reload the page. + fetch(swUrl) + .then(response => { + // Ensure service worker exists, and that we really are getting a JS file. + const contentType = response.headers.get('content-type'); + if ( + response.status === 404 || + (contentType != null && contentType.indexOf('javascript') === -1) + ) { + // No service worker found. Probably a different app. Reload the page. + navigator.serviceWorker.ready.then(registration => { + registration.unregister().then(() => { + window.location.reload(); + }); + }); + } else { + // Service worker found. Proceed as normal. + registerValidSW(swUrl, config); + } + }) + .catch(() => { + console.log( + 'No internet connection found. App is running in offline mode.' + ); + }); +} + +export function unregister() { + if ('serviceWorker' in navigator) { + navigator.serviceWorker.ready.then(registration => { + registration.unregister(); + }); + } +} diff --git a/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/package.json b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/package.json new file mode 100644 index 0000000..96b9f45 --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/package.json @@ -0,0 +1,18 @@ +{ + "name": "react-docker-app", + "version": "1.0.0", + "scripts": { + "client": "cd client && yarn start", + "server": "nodemon server.js", + "dev": "concurrently --kill-others-on-fail \"yarn server\" \"yarn client\"", + "start": "node server.js", + "buildclient": "cd client && npm install && npm install --only=dev --no-shrinkwrap && npm run build" + }, + "dependencies": { + "express": "^4.17.3", + "axios": "^0.26.0" + }, + "devDependencies": { + "concurrently": "^4.0.1" + } +} diff --git a/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/server.js b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/server.js new file mode 100644 index 0000000..fb5c12a --- /dev/null +++ b/dapr/quickstarts/tutorials/distributed-calculator/react-calculator/server.js @@ -0,0 +1,108 @@ +const express = require('express'); +const path = require('path'); +const bodyParser = require('body-parser'); +const axios = require('axios'); + +const app = express(); + +app.use(express.json()); + +const port = 8080; +const daprPort = process.env.DAPR_HTTP_PORT ?? 3500; + +const daprUrl = `http://localhost:${daprPort}/v1.0/invoke`; + +// The name of the state store is specified in the components yaml file. +// For this sample, state store name is specified in the file at: https://github.com/dapr/quickstarts/blob/master/hello-kubernetes/deploy/redis.yaml#L4 +const stateStoreName = `statestore`; +const stateUrl = `http://localhost:${daprPort}/v1.0/state/${stateStoreName}`; + +/** +The following routes forward requests (using pipe) from our React client to our dapr-enabled services. Our Dapr sidecar lives on localhost:. We invoke other Dapr enabled services by calling /v1.0/invoke//method/. +*/ + +app.post('/calculate/add', async (req, res) => { + try { + // Invoke Dapr add app + const appResponse = await axios.post(`${daprUrl}/addapp/method/add`, req.body); + + // Return expected string result to client + return res.send(`${appResponse.data}`); + } catch (err) { + console.log(err); + } +}); + +app.post('/calculate/subtract', async (req, res) => { + try { + // Invoke Dapr subtract app + console.log("subtract app** 1") + const appResponse = await axios.post(`${daprUrl}/subtractapp/method/subtract`, req.body); + console.log("subtract app** 2") + // Return expected string result to client + return res.send(`${appResponse.data}`); + } catch (err) { + console.log(err); + } +}); + +app.post('/calculate/multiply', async (req, res) => { + try { + // Dapr invoke multiply app + const appResponse = await axios.post(`${daprUrl}/multiplyapp/method/multiply`, req.body); + + // Return expected string result to client + return res.send(`${appResponse.data}`); + } catch (err) { + console.log(err); + } +}); + +app.post('/calculate/divide', async (req, res) => { + try { + // Dapr invoke divide app + const appResponse = await axios.post(`${daprUrl}/divideapp/method/divide`, req.body); + + // Return expected string result to client + return res.send(`${appResponse.data}`); + } catch (err) { + console.log(err); + } +}); + +// Forward state retrieval to Dapr state endpoint +app.get('/state', async (req, res) => { + try { + // Getting Dapr state + const apiResponse = await axios.get(`${stateUrl}/calculatorState`); + + return res.send(apiResponse.data); + } catch (err) { + console.log(err); + } +}); + +// Forward state persistence to Dapr state endpoint +app.post('/persist', async (req, res) => { + try { + // Saving Dapr state + const apiResponse = await axios.post(stateUrl, req.body); + return res.send(apiResponse.data); + } catch (err) { + console.log(err); + } +}); + +// Serve static files +app.use(express.static(path.join(__dirname, 'client/build'))); + +// For default home request route to React client +app.get('/', async function (_req, res) { + try { + return await res.sendFile(path.join(__dirname, 'client/build', 'index.html')); + } catch (err) { + console.log(err); + } +}); + +app.listen(process.env.PORT || port, () => console.log(`Listening on port ${port}!`)); diff --git a/dapr/quickstarts/tutorials/docker.mk b/dapr/quickstarts/tutorials/docker.mk new file mode 100644 index 0000000..f1e148d --- /dev/null +++ b/dapr/quickstarts/tutorials/docker.mk @@ -0,0 +1,87 @@ +# +# Copyright 2021 The Dapr Authors +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# Common make targets for samples' Docker images. + +SAMPLE_REGISTRY ?= docker.io/dapriosamples +TARGET_OS ?= linux +TARGET_ARCH ?= amd64 +REL_VERSION ?= latest +ifeq ($(REL_VERSION),edge) + REL_VERSION := latest +endif + +# Docker image build and push setting +DOCKER:=docker +DOCKERFILE:=Dockerfile +DOCKERMUTI_ARCH=linux-amd64 linux-arm linux-arm64 + +.PHONY: build + +BUILD_APPS:=$(foreach ITEM,$(APPS),build-$(ITEM)) +build: $(BUILD_APPS) + +# Generate docker image build targets +define genDockerImageBuild +.PHONY: build-$(1) +build-$(1): + $(DOCKER) build -f $(1)/$(DOCKERFILE) $(1)/. -t $(SAMPLE_REGISTRY)/$(DOCKER_IMAGE_PREFIX)$(1):$(REL_VERSION)-$(TARGET_OS)-$(TARGET_ARCH) --platform $(TARGET_OS)/$(TARGET_ARCH) +endef + +# Generate docker image build targets +$(foreach ITEM,$(APPS),$(eval $(call genDockerImageBuild,$(ITEM)))) + +# push docker image to the registry +.PHONY: push +PUSH_APPS:=$(foreach ITEM,$(APPS),push-$(ITEM)) +push: $(PUSH_APPS) + +# Generate docker image push targets +define genDockerImagePush +.PHONY: push-$(1) +push-$(1): + $(DOCKER) push $(SAMPLE_REGISTRY)/$(DOCKER_IMAGE_PREFIX)$(1):$(REL_VERSION)-$(TARGET_OS)-$(TARGET_ARCH) +endef + +# Generate docker image push targets +$(foreach ITEM,$(APPS),$(eval $(call genDockerImagePush,$(ITEM)))) + +# Create docker manifest +.PHONY: manifest-create +CREATE_MANIFEST_APPS:=$(foreach ITEM,$(APPS),manifest-create-$(ITEM)) +manifest-create: $(CREATE_MANIFEST_APPS) + +# Generate docker manifest create +define genDockerManifestCreate +.PHONY: manifest-create-$(1) +manifest-create-$(1): + $(DOCKER) manifest create $(SAMPLE_REGISTRY)/$(DOCKER_IMAGE_PREFIX)$(1):$(REL_VERSION) $(DOCKERMUTI_ARCH:%=$(SAMPLE_REGISTRY)/$(DOCKER_IMAGE_PREFIX)$(1):$(REL_VERSION)-%) +endef + +# Generate docker manifest create +$(foreach ITEM,$(APPS),$(eval $(call genDockerManifestCreate,$(ITEM)))) + +# Push docker manifest +.PHONY: manifest-push +PUSH_MANIFEST_APPS:=$(foreach ITEM,$(APPS),manifest-push-$(ITEM)) +manifest-push: $(PUSH_MANIFEST_APPS) + +# Generate docker manifest create +define genDockerManifestPush +.PHONY: manifest-push-$(1) +manifest-push-$(1): + $(DOCKER) manifest push $(SAMPLE_REGISTRY)/$(DOCKER_IMAGE_PREFIX)$(1):$(REL_VERSION) +endef + +# Generate docker manifest create +$(foreach ITEM,$(APPS),$(eval $(call genDockerManifestPush,$(ITEM)))) \ No newline at end of file diff --git a/dapr/quickstarts/tutorials/hello-kubernetes/README.md b/dapr/quickstarts/tutorials/hello-kubernetes/README.md new file mode 100644 index 0000000..3b703ad --- /dev/null +++ b/dapr/quickstarts/tutorials/hello-kubernetes/README.md @@ -0,0 +1,397 @@ +# Hello Kubernetes + +This tutorial will get you up and running with Dapr in a Kubernetes cluster. You will be deploying the same applications from [Hello World](../hello-world). To recap, the Python App generates messages and the Node app consumes and persists them. The following architecture diagram illustrates the components that make up this quickstart: + +![Architecture Diagram](./img/Architecture_Diagram.png) + +## Prerequisites + +This quickstart requires you to have the following installed on your machine: + +- [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) +- A Kubernetes cluster, such as [Minikube](https://docs.dapr.io/operations/hosting/kubernetes/cluster/setup-minikube/), [AKS](https://docs.dapr.io/operations/hosting/kubernetes/cluster/setup-aks/) or [GKE](https://cloud.google.com/kubernetes-engine/) + +Also, unless you have already done so, clone the repository with the quickstarts and `cd` into the right directory: + +``` +git clone [-b ] https://github.com/dapr/quickstarts.git +cd quickstarts/tutorials/hello-kubernetes +``` + +> **Note**: See https://github.com/dapr/quickstarts#supported-dapr-runtime-version for supported tags. Use `git clone https://github.com/dapr/quickstarts.git` when using the edge version of dapr runtime. + +## Step 1 - Setup Dapr on your Kubernetes cluster + +The first thing you need is an RBAC enabled Kubernetes cluster. This could be running on your machine using Minikube, or it could be a fully-fledged cluster in Azure using [AKS](https://azure.microsoft.com/en-us/services/kubernetes-service/). + +Once you have a cluster, follow the steps below to deploy Dapr to it. For more details, see [Deploy Dapr on a Kubernetes cluster](https://docs.dapr.io/operations/hosting/kubernetes/kubernetes-deploy/). + +> Please note, the CLI will install to the dapr-system namespace by default. If this namespace does not exist, the CLI will create it. +> If you need to deploy to a different namespace, you can use `-n mynamespace`. + +``` +dapr init --kubernetes --wait +``` + +Sample output: + +``` +⌛ Making the jump to hyperspace... + Note: To install Dapr using Helm, see here: https://docs.dapr.io/getting-started/install-dapr-kubernetes/#install-with-helm-advanced + +✅ Deploying the Dapr control plane to your cluster... +✅ Success! Dapr has been installed to namespace dapr-system. To verify, run `dapr status -k' in your terminal. To get started, go here: https://aka.ms/dapr-getting-started +``` + +> Without the `--wait` flag the Dapr CLI will exit as soon as the kubernetes deployments are created. Kubernetes deployments are asyncronous by default, so we use `--wait` here to make sure the dapr control plane is completely deployed and running before continuing. + + + +```bash +dapr status -k +``` + + + +You will see output like the following. All services should show `True` in the HEALTHY column and `Running` in the STATUS column before you continue. + +``` + NAME NAMESPACE HEALTHY STATUS REPLICAS VERSION AGE CREATED + dapr-operator dapr-system True Running 1 1.0.1 13s 2021-03-08 11:00.21 + dapr-placement-server dapr-system True Running 1 1.0.1 13s 2021-03-08 11:00.21 + dapr-dashboard dapr-system True Running 1 0.6.0 13s 2021-03-08 11:00.21 + dapr-sentry dapr-system True Running 1 1.0.1 13s 2021-03-08 11:00.21 + dapr-sidecar-injector dapr-system True Running 1 1.0.1 13s 2021-03-08 11:00.21 +``` + +## Step 2 - Create and configure a state store + +Dapr can use a number of different state stores (Redis, CosmosDB, DynamoDB, Cassandra, etc) to persist and retrieve state. This demo will use Redis. + +1. Follow [these steps](https://docs.dapr.io/getting-started/tutorials/configure-state-pubsub/#step-1-create-a-redis-store) to create a Redis store. +2. Once your store is created, add the keys to the `redis.yaml` file in the `deploy` directory. + > **Note:** the `redis.yaml` file provided in this quickstart will work securely out-of-the-box with a Redis installed with `helm install bitnami/redis`. If you have your own Redis setup, replace the `redisHost` value with your own Redis master address, and the redisPassword with your own Secret. You can learn more [here](https://docs.dapr.io/operations/components/component-secrets/). +3. Apply the `redis.yaml` file and observe that your state store was successfully configured! + + + +```bash +kubectl apply -f ./deploy/redis.yaml +``` + + + +```bash +component.dapr.io/statestore created +``` + +## Step 3 - Deploy the Node.js app with the Dapr sidecar + + + +```bash +kubectl apply -f ./deploy/node.yaml +``` + +Kubernetes deployments are asyncronous. This means you'll need to wait for the deployment to complete before moving on to the next steps. You can do so with the following command: + +```bash +kubectl rollout status deploy/nodeapp +``` + + + +This will deploy the Node.js app to Kubernetes. The Dapr control plane will automatically inject the Dapr sidecar to the Pod. If you take a look at the `node.yaml` file, you will see how Dapr is enabled for that deployment: + +`dapr.io/enabled: true` - this tells the Dapr control plane to inject a sidecar to this deployment. + +`dapr.io/app-id: nodeapp` - this assigns a unique id or name to the Dapr application, so it can be sent messages to and communicated with by other Dapr apps. + +`dapr.io/enable-api-logging: "true"` - this is added to node.yaml file by default to see the API logs. + +You'll also see the container image that you're deploying. If you want to update the code and deploy a new image, see **Next Steps** section. + +There are several different ways to access a Kubernetes service depending on which platform you are using. Port forwarding is one consistent way to access a service, whether it is hosted locally or on a cloud Kubernetes provider like AKS. + + + +```bash +kubectl port-forward service/nodeapp 8080:80 +``` + + + +This will make your service available on http://localhost:8080. + +> **Optional**: If you are using a public cloud provider, you can substitue your EXTERNAL-IP address instead of port forwarding. You can find it with: + +```bash +kubectl get svc nodeapp +``` + +## Step 4 - Verify Service + +To call the service that you set up port forwarding to, from a command prompt run: + + + +```bash +curl http://localhost:8080/ports +``` + + + +Expected output: + +``` +{"DAPR_HTTP_PORT":"3500","DAPR_GRPC_PORT":"50001"} +``` + +Next submit an order to the app + + + +```bash +curl --request POST --data "@sample.json" --header Content-Type:application/json http://localhost:8080/neworder +``` + + + +Expected output: +Empty reply from server + +Confirm the order was persisted by requesting it from the app + + + +```bash +curl http://localhost:8080/order +``` + +Expected output: + +```json +{ "orderId": "42" } +``` + + + +> **Optional**: Now it would be a good time to get acquainted with the [Dapr dashboard](https://docs.dapr.io/reference/cli/dapr-dashboard/). Which is a convenient interface to check status, information and logs of applications running on Dapr. The following command will make it available on http://localhost:9999/. + +```bash +dapr dashboard -k -p 9999 +``` + +## Step 5 - Deploy the Python app with the Dapr sidecar + +Next, take a quick look at the Python app. Navigate to the Python app in the kubernetes quickstart: `cd quickstarts/tutorials/hello-kubernetes/python` and open `app.py`. + +At a quick glance, this is a basic Python app that posts JSON messages to `localhost:3500`, which is the default listening port for Dapr. You can invoke the Node.js application's `neworder` endpoint by posting to `v1.0/invoke/nodeapp/method/neworder`. The message contains some `data` with an orderId that increments once per second: + +```python +n = 0 +while True: + n += 1 + message = {"data": {"orderId": n}} + + try: + response = requests.post(dapr_url, json=message) + except Exception as e: + print(e) + + time.sleep(1) +``` + + + +Deploy the Python app to your Kubernetes cluster: + +```bash +kubectl apply -f ./deploy/python.yaml +``` + +As with above, the following command will wait for the deployment to complete: + +```bash +kubectl rollout status deploy/pythonapp +``` + + + +## Step 6 - Observe messages + +Now that the Node.js and Python applications are deployed, watch messages come through: + +Get the logs of the Node.js app: + + + +```bash +kubectl logs --selector=app=node -c node --tail=-1 +``` + + + +If all went well, you should see logs like this: + +``` +Got a new order! Order ID: 1 +Successfully persisted state +Got a new order! Order ID: 2 +Successfully persisted state +Got a new order! Order ID: 3 +Successfully persisted state +``` + +## Step 7 - Observe API call logs + +Now that the Node.js and Python applications are deployed, watch API call logs come through: + +Get the API call logs of the node app: + + + +```bash +kubectl logs --selector=app=node -c daprd --tail=-1 +``` + + + +When save state API calls are made, you should see logs similar to this: + +``` +time="2022-04-25T22:46:09.82121774Z" level=info msg="HTTP API Called: POST /v1.0/state/statestore" app_id=nodeapp instance=nodeapp-7dd6648dd4-7hpmh scope=dapr.runtime.http-info type=log ver=1.7.2 +time="2022-04-25T22:46:10.828764787Z" level=info msg="HTTP API Called: POST /v1.0/state/statestore" app_id=nodeapp instance=nodeapp-7dd6648dd4-7hpmh scope=dapr.runtime.http-info type=log ver=1.7.2 +``` + +Get the API call logs of the Python app: + + + +```bash +kubectl logs --selector=app=python -c daprd --tail=-1 +``` + + +``` +time="2022-04-27T02:47:49.972688145Z" level=info msg="HTTP API Called: POST /neworder" app_id=pythonapp instance=pythonapp-545df48d55-jvj52 scope=dapr.runtime.http-info type=log ver=1.7.2 +time="2022-04-27T02:47:50.984994545Z" level=info msg="HTTP API Called: POST /neworder" app_id=pythonapp instance=pythonapp-545df48d55-jvj52 scope=dapr.runtime.http-info type=log ver=1.7.2 +``` + +## Step 8 - Confirm successful persistence + +Call the Node.js app's order endpoint to get the latest order. Grab the external IP address that you saved before and, append "/order" and perform a GET request against it (enter it into your browser, use Postman, or curl it!): + +``` +curl $NODE_APP/order +{"orderID":"42"} +``` + +You should see the latest JSON in response! + +## Step 9 - Cleanup + +Once you're done, you can spin down your Kubernetes resources by navigating to the `./deploy` directory and running: + + + +```bash +kubectl delete -f . +``` + + + +This will spin down each resource defined by the .yaml files in the `deploy` directory, including the state component. + +## Deploying your code + +Now that you're successfully working with Dapr, you probably want to update the code to fit your scenario. The Node.js and Python apps that make up this quickstart are deployed from container images hosted on a private [Azure Container Registry](https://azure.microsoft.com/en-us/services/container-registry/). To create new images with updated code, you'll first need to install docker on your machine. Next, follow these steps: + +1. Update Node or Python code as you see fit! +2. Navigate to the directory of the app you want to build a new image for. +3. Run `docker build -t . `. You can name your image whatever you like. If you're planning on hosting it on docker hub, then it should start with `/`. +4. Once your image has built you can see it on your machines by running `docker images`. +5. To publish your docker image to docker hub (or another registry), first login: `docker login`. Then run`docker push `. +6. Update your .yaml file to reflect the new image name. +7. Deploy your updated Dapr enabled app: `kubectl apply -f .yaml`. + +## Related links + +- [Guidelines for production ready deployments on Kubernetes](https://docs.dapr.io/operations/hosting/kubernetes/kubernetes-production/) + +## Next steps + +- Explore additional [quickstarts](../../README.md#quickstarts) and deploy them locally or on Kubernetes. diff --git a/dapr/quickstarts/tutorials/hello-kubernetes/deploy/node.yaml b/dapr/quickstarts/tutorials/hello-kubernetes/deploy/node.yaml new file mode 100644 index 0000000..4db9990 --- /dev/null +++ b/dapr/quickstarts/tutorials/hello-kubernetes/deploy/node.yaml @@ -0,0 +1,46 @@ +kind: Service +apiVersion: v1 +metadata: + name: nodeapp + labels: + app: node +spec: + selector: + app: node + ports: + - protocol: TCP + port: 80 + targetPort: 3000 + type: LoadBalancer + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nodeapp + labels: + app: node +spec: + replicas: 1 + selector: + matchLabels: + app: node + template: + metadata: + labels: + app: node + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "nodeapp" + dapr.io/app-port: "3000" + dapr.io/enable-api-logging: "true" + spec: + containers: + - name: node + image: ghcr.io/dapr/samples/hello-k8s-node:latest + env: + - name: APP_PORT + value: "3000" + ports: + - containerPort: 3000 + imagePullPolicy: Always diff --git a/dapr/quickstarts/tutorials/hello-kubernetes/deploy/python.yaml b/dapr/quickstarts/tutorials/hello-kubernetes/deploy/python.yaml new file mode 100644 index 0000000..f5f1bd8 --- /dev/null +++ b/dapr/quickstarts/tutorials/hello-kubernetes/deploy/python.yaml @@ -0,0 +1,23 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: pythonapp + labels: + app: python +spec: + replicas: 1 + selector: + matchLabels: + app: python + template: + metadata: + labels: + app: python + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "pythonapp" + dapr.io/enable-api-logging: "true" + spec: + containers: + - name: python + image: ghcr.io/dapr/samples/hello-k8s-python:latest diff --git a/dapr/quickstarts/tutorials/hello-kubernetes/deploy/redis.yaml b/dapr/quickstarts/tutorials/hello-kubernetes/deploy/redis.yaml new file mode 100644 index 0000000..de9be2a --- /dev/null +++ b/dapr/quickstarts/tutorials/hello-kubernetes/deploy/redis.yaml @@ -0,0 +1,21 @@ +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: statestore +spec: + type: state.redis + version: v1 + metadata: + # These settings will work out of the box if you use `helm install + # bitnami/redis`. If you have your own setup, replace + # `redis-master:6379` with your own Redis master address, and the + # Redis password with your own Secret's name. For more information, + # see https://docs.dapr.io/operations/components/component-secrets . + - name: redisHost + value: redis-master:6379 + - name: redisPassword + secretKeyRef: + name: redis + key: redis-password +auth: + secretStore: kubernetes diff --git a/dapr/quickstarts/tutorials/hello-kubernetes/img/Architecture_Diagram.png b/dapr/quickstarts/tutorials/hello-kubernetes/img/Architecture_Diagram.png new file mode 100644 index 0000000..77713b2 Binary files /dev/null and b/dapr/quickstarts/tutorials/hello-kubernetes/img/Architecture_Diagram.png differ diff --git a/dapr/quickstarts/tutorials/hello-kubernetes/makefile b/dapr/quickstarts/tutorials/hello-kubernetes/makefile new file mode 100644 index 0000000..57a3867 --- /dev/null +++ b/dapr/quickstarts/tutorials/hello-kubernetes/makefile @@ -0,0 +1,5 @@ +DOCKER_IMAGE_PREFIX ?=hello-k8s- +APPS ?=node python + +include ../docker.mk +include ../validate.mk diff --git a/dapr/quickstarts/tutorials/hello-kubernetes/node/.gitignore b/dapr/quickstarts/tutorials/hello-kubernetes/node/.gitignore new file mode 100644 index 0000000..cfe9975 --- /dev/null +++ b/dapr/quickstarts/tutorials/hello-kubernetes/node/.gitignore @@ -0,0 +1,2 @@ +node_modules +.dockerfiles diff --git a/dapr/quickstarts/tutorials/hello-kubernetes/node/Dockerfile b/dapr/quickstarts/tutorials/hello-kubernetes/node/Dockerfile new file mode 100644 index 0000000..5c3326e --- /dev/null +++ b/dapr/quickstarts/tutorials/hello-kubernetes/node/Dockerfile @@ -0,0 +1,6 @@ +FROM node:17-alpine +WORKDIR /app +COPY . . +RUN npm install +EXPOSE 3000 +CMD [ "node", "app.js" ] diff --git a/dapr/quickstarts/tutorials/hello-kubernetes/node/app.js b/dapr/quickstarts/tutorials/hello-kubernetes/node/app.js new file mode 100644 index 0000000..ec7c623 --- /dev/null +++ b/dapr/quickstarts/tutorials/hello-kubernetes/node/app.js @@ -0,0 +1,79 @@ +// +// Copyright 2021 The Dapr Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +const express = require('express'); +const bodyParser = require('body-parser'); +require('isomorphic-fetch'); + +const app = express(); +app.use(bodyParser.json()); + +// These ports are injected automatically into the container. +const daprPort = process.env.DAPR_HTTP_PORT ?? "3500"; +const daprGRPCPort = process.env.DAPR_GRPC_PORT ?? "50001"; + +const stateStoreName = `statestore`; +const stateUrl = `http://localhost:${daprPort}/v1.0/state/${stateStoreName}`; +const port = process.env.APP_PORT ?? "3000"; + +app.get('/order', async (_req, res) => { + try { + const response = await fetch(`${stateUrl}/order`); + if (!response.ok) { + throw "Could not get state."; + } + const orders = await response.text(); + res.send(orders); + } + catch (error) { + console.log(error); + res.status(500).send({message: error}); + } +}); + +app.post('/neworder', async (req, res) => { + const data = req.body.data; + const orderId = data.orderId; + console.log("Got a new order! Order ID: " + orderId); + + const state = [{ + key: "order", + value: data + }]; + + try { + const response = await fetch(stateUrl, { + method: "POST", + body: JSON.stringify(state), + headers: { + "Content-Type": "application/json" + } + }); + if (!response.ok) { + throw "Failed to persist state."; + } + console.log("Successfully persisted state."); + res.status(200).send(); + } catch (error) { + console.log(error); + res.status(500).send({message: error}); + } +}); + +app.get('/ports', (_req, res) => { + console.log("DAPR_HTTP_PORT: " + daprPort); + console.log("DAPR_GRPC_PORT: " + daprGRPCPort); + res.status(200).send({DAPR_HTTP_PORT: daprPort, DAPR_GRPC_PORT: daprGRPCPort }) +}); + +app.listen(port, () => console.log(`Node App listening on port ${port}!`)); \ No newline at end of file diff --git a/dapr/quickstarts/tutorials/hello-kubernetes/node/package.json b/dapr/quickstarts/tutorials/hello-kubernetes/node/package.json new file mode 100644 index 0000000..726b448 --- /dev/null +++ b/dapr/quickstarts/tutorials/hello-kubernetes/node/package.json @@ -0,0 +1,17 @@ +{ + "name": "node_server", + "version": "1.0.0", + "private": true, + "description": "", + "main": "app.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "body-parser": "^1.18.3", + "express": "^4.16.4", + "isomorphic-fetch": "^2.2.1" + } +} diff --git a/dapr/quickstarts/tutorials/hello-kubernetes/python/.gitignore b/dapr/quickstarts/tutorials/hello-kubernetes/python/.gitignore new file mode 100644 index 0000000..96a4f9a --- /dev/null +++ b/dapr/quickstarts/tutorials/hello-kubernetes/python/.gitignore @@ -0,0 +1 @@ +.dockerfiles diff --git a/dapr/quickstarts/tutorials/hello-kubernetes/python/Dockerfile b/dapr/quickstarts/tutorials/hello-kubernetes/python/Dockerfile new file mode 100644 index 0000000..50e0537 --- /dev/null +++ b/dapr/quickstarts/tutorials/hello-kubernetes/python/Dockerfile @@ -0,0 +1,6 @@ +FROM python:3.7-alpine +WORKDIR /app +COPY . . +RUN pip install requests +ENTRYPOINT ["python"] +CMD ["app.py"] diff --git a/dapr/quickstarts/tutorials/hello-kubernetes/python/app.py b/dapr/quickstarts/tutorials/hello-kubernetes/python/app.py new file mode 100644 index 0000000..c9264af --- /dev/null +++ b/dapr/quickstarts/tutorials/hello-kubernetes/python/app.py @@ -0,0 +1,34 @@ +# +# Copyright 2021 The Dapr Authors +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import os +import requests +import time + +dapr_port = os.getenv("DAPR_HTTP_PORT", 3500) +dapr_url = "http://localhost:{}/neworder".format(dapr_port) + +n = 0 +while True: + n += 1 + message = {"data": {"orderId": n}} + + try: + response = requests.post(dapr_url, json=message, timeout=5, headers = {"dapr-app-id": "nodeapp"} ) + if not response.ok: + print("HTTP %d => %s" % (response.status_code, + response.content.decode("utf-8")), flush=True) + except Exception as e: + print(e, flush=True) + + time.sleep(1) diff --git a/dapr/quickstarts/tutorials/hello-kubernetes/sample.json b/dapr/quickstarts/tutorials/hello-kubernetes/sample.json new file mode 100644 index 0000000..dbad3ab --- /dev/null +++ b/dapr/quickstarts/tutorials/hello-kubernetes/sample.json @@ -0,0 +1 @@ +{"data":{"orderId":"42"}} diff --git a/dapr/quickstarts/tutorials/hello-world/.gitignore b/dapr/quickstarts/tutorials/hello-world/.gitignore new file mode 100644 index 0000000..d37b538 --- /dev/null +++ b/dapr/quickstarts/tutorials/hello-world/.gitignore @@ -0,0 +1,2 @@ +node_modules +!.vscode \ No newline at end of file diff --git a/dapr/quickstarts/tutorials/hello-world/.vscode/launch.json b/dapr/quickstarts/tutorials/hello-world/.vscode/launch.json new file mode 100644 index 0000000..d252746 --- /dev/null +++ b/dapr/quickstarts/tutorials/hello-world/.vscode/launch.json @@ -0,0 +1,34 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "pwa-node", + "request": "launch", + "name": "Nodeapp with Dapr", + "skipFiles": [ + "/**" + ], + "program": "${workspaceFolder}/node/app.js", + "preLaunchTask": "daprd-debug-node", + "postDebugTask": "daprd-down-node" + }, + { + "type": "python", + "request": "launch", + "name": "Pythonapp with Dapr", + "program": "${workspaceFolder}/python/app.py", + "console": "integratedTerminal", + "preLaunchTask": "daprd-debug-python", + "postDebugTask": "daprd-down-python" + } + ], + "compounds": [ + { + "name": "Node/Python Dapr", + "configurations": ["Nodeapp with Dapr","Pythonapp with Dapr"] + } + ] +} \ No newline at end of file diff --git a/dapr/quickstarts/tutorials/hello-world/.vscode/tasks.json b/dapr/quickstarts/tutorials/hello-world/.vscode/tasks.json new file mode 100644 index 0000000..32a2da1 --- /dev/null +++ b/dapr/quickstarts/tutorials/hello-world/.vscode/tasks.json @@ -0,0 +1,31 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "appId": "nodeapp", + "appPort": 3000, + "httpPort": 3500, + "metricsPort": 9090, + "label": "daprd-debug-node", + "type": "daprd" + }, + { + "appId": "nodeapp", + "label": "daprd-down-node", + "type": "daprd-down" + }, + { + "appId": "pythonapp", + "httpPort": 53109, + "grpcPort": 53317, + "metricsPort": 9091, + "label": "daprd-debug-python", + "type": "daprd" + }, + { + "appId": "pythonapp", + "label": "daprd-down-python", + "type": "daprd-down" + } + ] +} \ No newline at end of file diff --git a/dapr/quickstarts/tutorials/hello-world/README.md b/dapr/quickstarts/tutorials/hello-world/README.md new file mode 100644 index 0000000..7a12bb2 --- /dev/null +++ b/dapr/quickstarts/tutorials/hello-world/README.md @@ -0,0 +1,446 @@ +# Hello World + +This tutorial will demonstrate how to get Dapr running locally on your machine. You'll be deploying a Node.js app that subscribes to order messages and persists them. The following architecture diagram illustrates the components that make up the first part sample: + +![Architecture Diagram](./img/Architecture_Diagram.png) + +Later on, you'll deploy a Python app to act as the publisher. The architecture diagram below shows the addition of the new component: + +![Architecture Diagram Final](./img/Architecture_Diagram_B.png) + +## Prerequisites +This quickstart requires you to have the following installed on your machine: +- [Docker](https://docs.docker.com/) +- [Node.js version 14 or greater](https://nodejs.org/en/) +- [Python 3.x](https://www.python.org/downloads/): Note: When running this quickstart on Windows, it best to install Python from python.org rather than from the Windows store. +- [Postman](https://www.getpostman.com/) [Optional] + +## Step 1 - Setup Dapr + +Follow [instructions](https://docs.dapr.io/getting-started/install-dapr-cli/) to download and install the Dapr CLI and initialize Dapr. + +## Step 2 - Understand the code + +Now that Dapr is set up locally, clone the repo, then navigate to the Node.js version of the Hello World quickstart: + +```sh +git clone [-b ] https://github.com/dapr/quickstarts.git +cd quickstarts/tutorials/hello-world/node +``` + +> **Note**: See https://github.com/dapr/quickstarts#supported-dapr-runtime-version for supported tags. Use `git clone https://github.com/dapr/quickstarts.git` when using the edge version of dapr runtime. + + +In the `app.js` you'll find a simple `express` application, which exposes a few routes and handlers. First, take a look at the top of the file: + +```js +const daprPort = process.env.DAPR_HTTP_PORT || 3500; +const stateStoreName = `statestore`; +const stateUrl = `http://localhost:${daprPort}/v1.0/state/${stateStoreName}`; +``` + +Dapr CLI creates an environment variable for the Dapr port, which defaults to 3500. You'll be using this in step 3 when sending POST messages to the system. The `stateStoreName` is the name given to the state store. You'll come back to that later on to see how that name is configured. + +Next, take a look at the ```neworder``` handler: + +```js +app.post('/neworder', async (req, res) => { + const data = req.body.data; + const orderId = data.orderId; + console.log("Got a new order! Order ID: " + orderId); + + const state = [{ + key: "order", + value: data + }]; + + try { + const response = await fetch(stateUrl, { + method: "POST", + body: JSON.stringify(state), + headers: { + "Content-Type": "application/json" + } + }) + if (!response.ok) { + throw "Failed to persist state."; + } + console.log("Successfully persisted state."); + res.status(200).send(); + } catch (error) { + console.log(error); + res.status(500).send({message: error}); + } +}); +``` + +Here the app is exposing an endpoint that will receive and handle `neworder` messages. It first logs the incoming message, and then persist the order ID to the Redis store by posting a state array to the `/state/` endpoint. + +Alternatively, you could have persisted the state by simply returning it with the response object: + +```js +res.json({ + state: [{ + key: "order", + value: order + }] + }) +``` + +This approach, however, doesn't allow you to verify if the message successfully persisted. + +The app also exposes a GET endpoint, `/order`: + +```js +app.get('/order', async (_req, res) => { + try { + const response = await fetch(`${stateUrl}/order`) + if (!response.ok) { + throw "Could not get state."; + } + const orders = await response.text(); + res.send(orders); + } + catch (error) { + console.log(error); + res.status(500).send({message: error}); + } +}); +``` + +This calls out to the Redis cache to retrieve the latest value of the "order" key, which effectively allows the Node.js app to be _stateless_. + +## Step 3 - Run the Node.js app with Dapr + +Open a new terminal and navigate to the `./hello-world/node` directory and follow the steps below: + + + +1. Install dependencies: + + ```bash + npm install + ``` + + + +This will install `express` and `body-parser`, dependencies that are shown in the `package.json`. + + + +2. Run Node.js app with Dapr: + ```bash + dapr run --app-id nodeapp --app-port 3000 --dapr-http-port 3500 node app.js + ``` + + + +The command should output text that looks like the following, along with logs: + +``` +Starting Dapr with id nodeapp. HTTP Port: 3500. gRPC Port: 9165 +You're up and running! Both Dapr and your app logs will appear here. +... +``` +> **Note**: the `--app-port` (the port the app runs on) is configurable. The Node app happens to run on port 3000, but you could configure it to run on any other port. Also note that the Dapr `--app-port` parameter is optional, and if not supplied, a random available port is used. + +The `dapr run` command looks for the default components directory which for Linux/MacOS is `$HOME/.dapr/components` and for Windows is `%USERPROFILE%\.dapr\components` which holds yaml definition files for components Dapr will be using at runtime. When running locally, the yaml files which provide default definitions for a local development environment are placed in this default components directory. Review the `statestore.yaml` file in the `components` directory: + +```yml +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: statestore +spec: + type: state.redis + version: v1 +... +``` + +You can see the yaml file defined the state store to be Redis and is naming it `statestore`. This is the name which was used in `app.js` to make the call to the state store in the application: + +```js +const stateStoreName = `statestore`; +const stateUrl = `http://localhost:${daprPort}/v1.0/state/${stateStoreName}`; +``` + +While in this tutorial the default yaml files were used, usually a developer would modify them or create custom yaml definitions depending on the application and scenario. + +> **Optional**: Now it would be a good time to get acquainted with the [Dapr dashboard](https://docs.dapr.io/reference/cli/dapr-dashboard/). Which is a convenient interface to check status and information of applications running on Dapr. The following command will make it available on http://localhost:9999/. + +```bash +dapr dashboard -p 9999 +``` + +## Step 4 - Post messages to the service + +Now that Dapr and the Node.js app are running, you can send POST messages against it, using different tools. **Note**: here the POST message is sent to port 3500 - if you used a different port, be sure to update your URL accordingly. + +First, POST the message by using Dapr cli in a new terminal: + + + +```bash +dapr invoke --app-id nodeapp --method neworder --data-file sample.json +``` + + + +Alternatively, using `curl`: + + + +```bash +curl -XPOST -d @sample.json -H Content-Type:application/json http://localhost:3500/v1.0/invoke/nodeapp/method/neworder +``` + + + +Or, using the Visual Studio Code [Rest Client Plugin](https://marketplace.visualstudio.com/items?itemName=humao.rest-client) + +[sample.http](sample.http) +```http +POST http://localhost:3500/v1.0/invoke/nodeapp/method/neworder + +{ + "data": { + "orderId": "42" + } +} +``` + +Last but not least, you can use the Postman GUI. + +Open Postman and create a POST request against `http://localhost:3500/v1.0/invoke/nodeapp/method/neworder` +![Postman Screenshot](./img/postman1.jpg) +In your terminal, you should see logs indicating that the message was received and state was updated: +```bash +== APP == Got a new order! Order ID: 42 +== APP == Successfully persisted state. +``` + +## Step 5 - Confirm successful persistence + +Now, to verify the order was successfully persisted to the state store, create a GET request against: `http://localhost:3500/v1.0/invoke/nodeapp/method/order`. **Note**: Again, be sure to reflect the right port if you chose a port other than 3500. + + + +```bash +curl http://localhost:3500/v1.0/invoke/nodeapp/method/order +``` + + + +or use Dapr CLI + + + +```bash +dapr invoke --app-id nodeapp --method order --verb GET +``` + + + +or use the Visual Studio Code [Rest Client Plugin](https://marketplace.visualstudio.com/items?itemName=humao.rest-client) + +[sample.http](sample.http) +```http +GET http://localhost:3500/v1.0/invoke/nodeapp/method/order +``` + +or use the Postman GUI + +![Postman Screenshot 2](./img/postman2.jpg) + +This invokes the `/order` route, which calls out to the Redis store for the latest data. Observe the expected result! + +## Step 6 - Run the Python app with Dapr + +Take a look at the Python App in the `./hello-world/python` directory to see how another application can invoke the Node App via Dapr without being aware of the destination's hostname or port. In the `app.py` file you can find the endpoint definition to call the Node App via Dapr. + +```python +dapr_port = os.getenv("DAPR_HTTP_PORT", 3500) +dapr_url = "http://localhost:{}/v1.0/invoke/nodeapp/method/neworder".format(dapr_port) +``` +It is important to notice the Node App's name (`nodeapp`) in the URL, it will allow Dapr to redirect the request to the right API endpoint. This name needs to match the name used to run the Node App earlier in this exercise. + +The code block below shows how the Python App will incrementally post a new orderId every second, or print an exception if the post call fails. + +```python +n = 0 +while True: + n += 1 + message = {"data": {"orderId": n}} + + try: + response = requests.post(dapr_url, json=message) + except Exception as e: + print(e) + + time.sleep(1) +``` + +Now open a **new** terminal and go to the `./hello-world/python` directory. + + + +1. Install dependencies: + + ```bash + pip3 install requests + ``` + + + + + +2. Start the Python App with Dapr: + + ```bash + dapr run --app-id pythonapp python3 app.py + ``` + + + +3. If all went well, the **other** terminal, running the Node App, should log entries like these: + + ``` + Got a new order! Order ID: 1 + Successfully persisted state + Got a new order! Order ID: 2 + Successfully persisted state + Got a new order! Order ID: 3 + Successfully persisted state + ``` + +> **Known Issue**: If you are running python3 on Windows from the Microsoft Store, and you get the following error message: + + exec: "python3": executable file not found in %!P(MISSING)ATH%!(NOVERB) + +> This is due to golang being unable to properly execute Microsoft Store aliases. You can use the following command instead of the above: + + dapr run --app-id pythonapp cmd /c "python3 app.py" + +> For more info please see [this](https://github.com/dapr/quickstarts/issues/240) issue. + +4. Now, perform a GET request a few times and see how the orderId changes every second (enter it into the web browser, use Postman, or curl): + + ```http + GET http://localhost:3500/v1.0/invoke/nodeapp/method/order + ``` + ```json + { + "orderId": 3 + } + ``` + +> **Note**: It is not required to run `dapr init` in the **second** terminal because dapr was already setup on your local machine initially, running this command again would fail. + +## Step 7 - Cleanup + +To stop your services from running, simply stop the "dapr run" process. Alternatively, you can spin down each of your services with the Dapr CLI "stop" command. For example, to spin down both services, run these commands in a new terminal: + + + +```bash +dapr stop --app-id nodeapp +``` + +```bash +dapr stop --app-id pythonapp +``` + + + +To see that services have stopped running, run `dapr list`, noting that your services no longer appears! + +## [Optional Steps] VS Code Debugging + +If you are using Visual Studio Code, you can debug this application using the preconfigured launch.json and task.json files in the .vscode folder. +The .vscode folder has already been modified in the project to allow users to launch a compound configuration called "Node/Python Dapr" which will run both applications and allow you to debug in VS Code. + +For more information on how to configure the files visit [How-To: Debug multiple Dapr applications](https://docs.dapr.io/developing-applications/ides/vscode/vscode-how-to-debug-multiple-dapr-apps/) + +**Note**: Dapr offers a preview [Dapr Visual Studio Code extension](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-dapr) for local development which enables users a variety of features related to better managing their Dapr applications and debugging of your Dapr applications for all supported Dapr languages which are .NET, Go, PHP, Python and Java. + +## Next steps + +Now that you've gotten Dapr running locally on your machine, consider these next steps: +- Explore additional quickstarts such as [pub-sub](../pub-sub), [bindings](../bindings) or the [distributed calculator app](../distributed-calculator). +- Run this hello world application in Kubernetes via the [Hello Kubernetes](../hello-kubernetes) quickstart. +- Learn more about Dapr in the [Dapr overview](https://docs.dapr.io/concepts/overview/) documentation. +- Explore [Dapr concepts](https://docs.dapr.io/concepts/) such as building blocks and components in the Dapr documentation. diff --git a/dapr/quickstarts/tutorials/hello-world/img/Architecture_Diagram.png b/dapr/quickstarts/tutorials/hello-world/img/Architecture_Diagram.png new file mode 100644 index 0000000..ff85930 Binary files /dev/null and b/dapr/quickstarts/tutorials/hello-world/img/Architecture_Diagram.png differ diff --git a/dapr/quickstarts/tutorials/hello-world/img/Architecture_Diagram_B.png b/dapr/quickstarts/tutorials/hello-world/img/Architecture_Diagram_B.png new file mode 100644 index 0000000..a38818c Binary files /dev/null and b/dapr/quickstarts/tutorials/hello-world/img/Architecture_Diagram_B.png differ diff --git a/dapr/quickstarts/tutorials/hello-world/img/postman1.jpg b/dapr/quickstarts/tutorials/hello-world/img/postman1.jpg new file mode 100644 index 0000000..32d614e Binary files /dev/null and b/dapr/quickstarts/tutorials/hello-world/img/postman1.jpg differ diff --git a/dapr/quickstarts/tutorials/hello-world/img/postman2.jpg b/dapr/quickstarts/tutorials/hello-world/img/postman2.jpg new file mode 100644 index 0000000..d5f3252 Binary files /dev/null and b/dapr/quickstarts/tutorials/hello-world/img/postman2.jpg differ diff --git a/dapr/quickstarts/tutorials/hello-world/makefile b/dapr/quickstarts/tutorials/hello-world/makefile new file mode 100644 index 0000000..4510170 --- /dev/null +++ b/dapr/quickstarts/tutorials/hello-world/makefile @@ -0,0 +1,2 @@ + +include ../validate.mk diff --git a/dapr/quickstarts/tutorials/hello-world/node/app.js b/dapr/quickstarts/tutorials/hello-world/node/app.js new file mode 100644 index 0000000..b18da44 --- /dev/null +++ b/dapr/quickstarts/tutorials/hello-world/node/app.js @@ -0,0 +1,69 @@ +// +// Copyright 2021 The Dapr Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +const express = require('express'); +require('isomorphic-fetch'); + +const app = express(); +app.use(express.json()); + +const daprPort = process.env.DAPR_HTTP_PORT || 3500; +const stateStoreName = `statestore`; +const stateUrl = `http://localhost:${daprPort}/v1.0/state/${stateStoreName}`; +const port = process.env.APP_PORT ?? '3000'; + +app.get('/order', async (_req, res) => { + try { + const response = await fetch(`${stateUrl}/order`); + if (!response.ok) { + throw "Could not get state."; + } + const orders = await response.text(); + res.send(orders); + } + catch (error) { + console.log(error); + res.status(500).send({message: error}); + } +}); + +app.post('/neworder', async (req, res) => { + const data = req.body.data; + const orderId = data.orderId; + console.log("Got a new order! Order ID: " + orderId); + + const state = [{ + key: "order", + value: data + }]; + + try { + const response = await fetch(stateUrl, { + method: "POST", + body: JSON.stringify(state), + headers: { + "Content-Type": "application/json" + } + }); + if (!response.ok) { + throw "Failed to persist state."; + } + console.log("Successfully persisted state."); + res.status(200).send(); + } catch (error) { + console.log(error); + res.status(500).send({message: error}); + } +}); + +app.listen(port, () => console.log(`Node App listening on port ${port}!`)); diff --git a/dapr/quickstarts/tutorials/hello-world/node/package.json b/dapr/quickstarts/tutorials/hello-world/node/package.json new file mode 100644 index 0000000..c996da6 --- /dev/null +++ b/dapr/quickstarts/tutorials/hello-world/node/package.json @@ -0,0 +1,17 @@ +{ + "name": "node_server", + "version": "1.0.0", + "private": true, + "description": "", + "main": "app.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "body-parser": "^1.18.3", + "express": "^4.16.4", + "isomorphic-fetch": "^3.0.0" + } +} diff --git a/dapr/quickstarts/tutorials/hello-world/node/sample.http b/dapr/quickstarts/tutorials/hello-world/node/sample.http new file mode 100644 index 0000000..5ddc1de --- /dev/null +++ b/dapr/quickstarts/tutorials/hello-world/node/sample.http @@ -0,0 +1,7 @@ +POST http://localhost:3500/v1.0/invoke/nodeapp/method/neworder + +{ "data": {"orderId":"42"} } + +### + +GET http://localhost:3500/v1.0/invoke/nodeapp/method/order \ No newline at end of file diff --git a/dapr/quickstarts/tutorials/hello-world/node/sample.json b/dapr/quickstarts/tutorials/hello-world/node/sample.json new file mode 100644 index 0000000..dbad3ab --- /dev/null +++ b/dapr/quickstarts/tutorials/hello-world/node/sample.json @@ -0,0 +1 @@ +{"data":{"orderId":"42"}} diff --git a/dapr/quickstarts/tutorials/hello-world/python/app.py b/dapr/quickstarts/tutorials/hello-world/python/app.py new file mode 100755 index 0000000..e02f1dd --- /dev/null +++ b/dapr/quickstarts/tutorials/hello-world/python/app.py @@ -0,0 +1,34 @@ +# +# Copyright 2021 The Dapr Authors +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import os +import requests +import time + +dapr_port = os.getenv("DAPR_HTTP_PORT", 3500) +dapr_url = "http://localhost:{}/neworder".format(dapr_port) + +n = 0 +while True: + n += 1 + message = {"data": {"orderId": n}} + + try: + response = requests.post(dapr_url, json=message, timeout=5, headers = {"dapr-app-id": "nodeapp"}) + if not response.ok: + print("HTTP %d => %s" % (response.status_code, + response.content.decode("utf-8")), flush=True) + except Exception as e: + print(e, flush=True) + + time.sleep(1) diff --git a/dapr/quickstarts/tutorials/hello-world/python/requirements.txt b/dapr/quickstarts/tutorials/hello-world/python/requirements.txt new file mode 100644 index 0000000..f229360 --- /dev/null +++ b/dapr/quickstarts/tutorials/hello-world/python/requirements.txt @@ -0,0 +1 @@ +requests diff --git a/dapr/quickstarts/tutorials/hello-world/python/sample.json b/dapr/quickstarts/tutorials/hello-world/python/sample.json new file mode 100644 index 0000000..dbad3ab --- /dev/null +++ b/dapr/quickstarts/tutorials/hello-world/python/sample.json @@ -0,0 +1 @@ +{"data":{"orderId":"42"}} diff --git a/dapr/quickstarts/tutorials/makefile b/dapr/quickstarts/tutorials/makefile new file mode 100644 index 0000000..c79fd76 --- /dev/null +++ b/dapr/quickstarts/tutorials/makefile @@ -0,0 +1,2 @@ + +include validate.mk \ No newline at end of file diff --git a/dapr/quickstarts/tutorials/observability/README.md b/dapr/quickstarts/tutorials/observability/README.md new file mode 100644 index 0000000..9c43dda --- /dev/null +++ b/dapr/quickstarts/tutorials/observability/README.md @@ -0,0 +1,574 @@ +# Observability with Dapr + +This quickstart explores the [observability](https://docs.dapr.io/concepts/observability-concept/) capabilities of Dapr. Observability includes metric collection, tracing, logging and health checks. In this quickstart you'll be enabling [distributed tracing](https://docs.dapr.io/developing-applications/building-blocks/observability/tracing-overview/) on an application without changing any application code or creating a dependency on any specific tracing system. Since Dapr uses [OpenCensus](https://opencensus.io/), a variety of observability tools can be used to view and capture the traces. + +In this quickstart you will: + +- Deploy [Zipkin](https://zipkin.io/) and configure it as a tracing provider for Dapr in self hosted mode and in Kubernetes. +- Configure an application for tracing and then deploy it. +- Troubleshoot a performance issue. + +## Configure self hosted mode +For self hosted mode, first run `dapr init`. When you run `dapr init`: + +1. The following YAML file is created by default in `$HOME/.dapr/config.yaml` (on Linux/Mac) or `%USERPROFILE%\.dapr\config.yaml` (on Windows) and it is referenced by default on `dapr run` calls unless otherwise overridden: + +* config.yaml + +```yaml +apiVersion: dapr.io/v1alpha1 +kind: Configuration +metadata: + name: daprConfig + namespace: default +spec: + tracing: + samplingRate: "1" + zipkin: + endpointAddress: "http://localhost:9411/api/v2/spans" +``` + +2. The [openzipkin/zipkin](https://hub.docker.com/r/openzipkin/zipkin/) docker container is launched. + +3. The applications launched with `dapr run` will by default reference the config file in `$HOME/.dapr/config.yaml` or `%USERPROFILE%\.dapr\config.yaml` and can be overridden with the Dapr CLI using the `--config` param. For example, the following command will launch the hello-world quickstart app using the default config.yaml: + +4. Clone this repo using `git clone [-b ] https://github.com/dapr/quickstarts.git` and go to the repo's directory via `cd quickstarts/tutorials/observability`. + + + +```bash +cd ../hello-world/node && npm install && dapr run --app-id hello-tracing --app-port 3000 node app.js && cd ../../observability +``` + + + +5. Once the app is running, you can make a request, which will populate at least one trace: + + + + +```bash +dapr invoke --app-id hello-tracing --method neworder --data-file sample.json +``` + + + + + + + + + +### Viewing Traces +Tracing is set up out of the box when running `dapr init`. To +view traces, in your browser go to http://localhost:9411 and you will +see the Zipkin UI. + +### Zipkin API + +Zipkin also has an API available. See [Zipkin API](https://zipkin.io/zipkin-api/) for more details. + +To see traces collected through the API: + + + + +```bash +curl -s "http://localhost:9411/api/v2/traces?spanName=calllocal%2Fhello-tracing%2Fneworder" -H "accept:application/json" -o output.json && python3 -m json.tool output.json +``` + + +You should see output like the following: + +``` +[ + [ + { + "traceId": "4c480d57b0e6d96b7150b46d027c5904", + "id": "90d58917274e180a", + "kind": "CLIENT", + "name": "calllocal/hello-tracing/neworder", + "timestamp": 1613170216016911, + "duration": 93671, + "localEndpoint": { + "serviceName": "hello-tracing", + "ipv4": "127.0.0.1" + }, + "tags": { + "dapr.api": "POST /v1.0/invoke/hello-tracing/method/neworder", + "dapr.protocol": "http", + "dapr.status_code": "200", + "net.peer.name": "hello-tracing", + "opencensus.status_description": "OK", + "rpc.service": "ServiceInvocation" + } + } + ] +] +``` + +### Cleanup + + + +```bash +dapr stop --app-id hello-tracing +``` + + + +## Configure Kubernetes +### Prerequisites + +This quickstart builds on the [distributed calculator](../distributed-calculator/README.md) quickstart and requires Dapr to be installed on a Kubernetes cluster along with a state store. It is suggested to go through the distributed calculator quickstart before this one. If you have not done this then: + +1. Clone this repo using `git clone [-b ] https://github.com/dapr/quickstarts.git` and go to the directory via `cd quickstarts/tutorials/obervability`. +2. [Install Dapr on Kubernetes](https://docs.dapr.io/operations/hosting/kubernetes/kubernetes-deploy/). +3. [Configure Redis](https://docs.dapr.io/getting-started/tutorials/configure-state-pubsub/#step-1-create-a-redis-store) as a state store for Dapr. +4. Review the host and password for Redis state store Component in `../distributed-calculator/deploy/redis.yaml`. + +> **Note**: See https://github.com/dapr/quickstarts#supported-dapr-runtime-version for supported tags. Use `git clone https://github.com/dapr/quickstarts.git` when using the edge version of dapr runtime. +## Configure Dapr tracing in the cluster + +Review the Dapr configuration file `./deploy/appconfig.yaml` below: + +```yaml +apiVersion: dapr.io/v1alpha1 +kind: Configuration +metadata: + name: appconfig +spec: + tracing: + samplingRate: "1" + zipkin: + endpointAddress: "http://zipkin.default.svc.cluster.local:9411/api/v2/spans" +``` + +* `samplingRate` is used to enable or disable the tracing. To disable the sampling rate , +set `samplingRate : "0"` in the configuration. The valid range of samplingRate is between 0 and 1 inclusive. The sampling rate determines whether a trace span should be sampled or not based on value. `samplingRate : "1"` will always sample the traces. By default, the sampling rate is 1 in 10,000. +* `zipkin.endpointAddress` is used to specify the trace backend to receive trace using the Zipkin format. Any backend that understands the Zipkin trace format, like Zipkin, Jaeger, New Relic, etc can be used. In this sample, we use the address of the Zipkin server that we will deploy in the next step. + +This configuration file enables Dapr tracing. Deploy the configuration by running: + + + +```bash +kubectl apply -f ./deploy/appconfig.yaml +``` + +You can see that now a new Dapr configuration which enables tracing has been added. Run the command: + +```bash +dapr configurations --kubernetes +``` + + + +You should see output that looks like this: + +```bash + NAME TRACING-ENABLED METRICS-ENABLED AGE CREATED + appconfig true true 1h 2020-12-10 22:01.59 +``` + +You can see that `appconfig` has `TRACING-ENABLED` set to `true`. + +### Deploy Zipkin to the cluster and set it as the tracing provider + +In this quickstart Zipkin is used for tracing. Examine [*./deploy/zipkin.yaml*](./deploy/zipkin.yaml) and see how it includes three sections: + +1. A **Deployment** for Zipkin using the *openzipkin/zipkin* docker image. +2. A **Service** which will expose Zipkin internally as a ClusterIP in Kubernetes. + +Deploy Zipkin to your cluster by running: + + + +```bash +kubectl apply -f ./deploy/zipkin.yaml +``` + + + +Now that Zipkin is deployed, you can access the Zipkin UI by creating a tunnel to the internal Zipkin service you just created by running (with port 19411 chosen to avoid conflicts with Dapr CLI running Zipkin in self-hosted mode): + +```bash +kubectl port-forward svc/zipkin 19411:9411 +``` + +On your browser go to [http://localhost:19411](http://localhost:19411). You should be able to see the Zipkin dashboard. + + +### Instrument the application for tracing and deploy it + +To instrument a service for tracing with Dapr, no code changes are required, Dapr handles all of the tracing using the Dapr side-car. All that is needed is to add the Dapr annotation for the configuration you deployed earlier (which enables tracing) in the application deployment yaml along with the other Dapr annotations. The configuration annotation looks like this: + +```yaml +... +annotations: +... + dapr.io/config: "appconfig" +... + ``` + +For this quickstart, a configuration has already been enabled for every service in the distributed calculator app. You can find the annotation in each one of the calculator yaml files. For example review the yaml file for the calculator front end service [here](https://github.com/dapr/quickstarts/blob/master/tutorials/distributed-calculator/deploy/react-calculator.yaml#L36). + +Note you did not introduce any dependency on Zipkin into the calculator app code or deployment yaml files. The Zipkin Dapr component is configured to read tracing events and write these to a tracing backend. + +Now deploy the distributed calculator application to your cluster: + + + +```bash +kubectl apply -f ../distributed-calculator/deploy +``` + + + +Kubernetes deployments are asyncronous. This means you'll need to wait for the deployment to complete before moving on to the next steps. You can do so with the following commands: + + + +```bash +kubectl rollout status deploy/addapp +``` + +```bash +kubectl rollout status deploy/subtractapp +``` + +```bash +kubectl rollout status deploy/divideapp +``` + +```bash +kubectl rollout status deploy/multiplyapp +``` + +```bash +kubectl rollout status deploy/calculator-front-end +``` + + +You can view the status of the running pods with: + +```bash +kubectl get pods +``` + + + +Then, open the distributed calculator UI. + +If this is the first time trying the distributed calculator, find more detailed instructions in the [distributed-calculator](https://github.com/dapr/quickstarts/tree/master/tutorials/distributed-calculator) tutorial. + +> **Note:** If the distributed calculator is already running on your cluster you will need to restart it for the tracing to take effect. You can do so by running: + +> `kubectl rollout restart deployment addapp calculator-front-end divideapp multiplyapp subtractapp` + + + +**Optional:** if you don't have easy public browser access, you can always use port forwarding + +```bash +kubectl port-forward service/calculator-front-end 8000:80 +``` + + + +### Discover and troubleshoot a performance issue using Zipkin + +To show how observability can help discover and troubleshoot issues on a distributed application, you'll update one of the services in the calculator app. This updated version simulates a performance degradation in the multiply operation of the calculator that you can then investigate using the traces emitted by the Dapr sidecar. Run the following to apply a new version of the python-multiplier service: + + + +```bash +kubectl apply -f ./deploy/python-multiplier.yaml +``` + + + +As above, you can wait for the asyncronous Kubernetes deployment with the following: + + + +```bash +kubectl rollout status deploy/multiplyapp +``` + + + + +Now go to the calculator UI and perform several calculations. Make sure to use all operands. For example, do the following: + +`9 + 3 * 2 / 4 - 1 =` + +**Optional:** You can also use the following curl commands to execute all operations: + + + + +```bash +curl -s http://localhost:8000/calculate/add -H Content-Type:application/json --data @operands.json +``` + +```bash +curl -s http://localhost:8000/calculate/subtract -H Content-Type:application/json --data @operands.json +``` + +```bash +curl -s http://localhost:8000/calculate/divide -H Content-Type:application/json --data @operands.json +``` + +```bash +curl -s http://localhost:8000/calculate/multiply -H Content-Type:application/json --data @operands.json +``` + +```bash +curl -s http://localhost:8000/persist -H Content-Type:application/json --data @persist.json +``` + +```bash +curl -s http://localhost:8000/state +``` + + + + +Now go to the Zipkin dashboard by running. (Note: if you are running Dapr locally, be sure to use a different local port for Zipkin): + + + +```bash +kubectl port-forward svc/zipkin 19411:9411 +``` + + + + + + + + + + + +And browsing to [http://localhost:19411](http://localhost:19411). Click the search button to view tracing coming from the application: + + + +![Zipkin](./img/zipkin-1.png) + +Dapr adds a HTTP/gRPC middleware to the Dapr sidecar. The middleware intercepts all Dapr and application traffic and automatically injects correlation IDs to trace events. You can see a lot of transactions are being captured including the regular health checks done by Kubernetes: + +![Zipkin](./img/zipkin-2.png) + +Now look for any performance issues by filtering on any requests that have take too long. You can use `minDuration` criteria to query for long requests only: + +![Zipkin](./img/zipkin-3.png) + +You can quickly see that the multiply method invocation is unusually slow (takes over 1 second). Since the problem may be either at the calculator-frontend service or the python-multiplier service you can dig further by clicking on the entry: + +![Zipkin](./img/zipkin-4.png) + +Now you can see which specific call was delayed via the `data` field (here it's the 12 * 2 operation) and confirm that it is the multiplier service which you updated that is causing the slowdown (You can find the code for the slow multiplier under the python directory). + +### Zipkin API + +As before, you can also access traces through the Zipkin API. The following will give you the same traces as the UI search above: + + + +```bash +curl -s http://localhost:19411/api/v2/traces?minDuration=250000 -H accept:application/json -o output.json && python3 -m json.tool output.json +``` + + + +You should get output like this: +``` +[ + [ + { + "duration": 1009084, + "id": "ff0bf110ca88f770", + "kind": "SERVER", + "localEndpoint": { + "ipv4": "10.244.4.225", + "serviceName": "multiplyapp" + }, + "name": "calllocal/multiplyapp/multiply", + "parentId": "733a1812e839dcfb", + "tags": { + "dapr.api": "/dapr.proto.internals.v1.ServiceInvocation/CallLocal", + "dapr.invoke_method": "multiply", + "dapr.protocol": "grpc", + "rpc.service": "ServiceInvocation" + }, + "timestamp": 1613177766316506, + "traceId": "926f1a5a6c63ae6f5167c29b3ddf4271" + }, +... +``` + + +### Clean up + + + +1. To remove the distributed calculator application from your cluster run: + +```bash +kubectl delete -f ../distributed-calculator/deploy +``` + +2. To remove the Zipkin installation and tracing configuration run: + +```bash +kubectl delete -f deploy/zipkin.yaml +``` + + + +## Additional Resources + +- Learn more about [observability](https://docs.dapr.io/concepts/observability-concept/). +- Learn more on how Dapr does [distributed tracing](https://docs.dapr.io/developing-applications/building-blocks/observability/tracing-overview/). + +## Next steps + +- Explore additional [quickstarts](../../README.md#quickstarts) + +## Troubleshooting + +If you see an error with the following message: +```txt +time="2021-02-13T00:39:00.48769561Z" level=fatal msg="process component zipkin error: incorrect type exporters.zipkin" app_id=addapp instance=addapp-59f447d8b6-w48jt scope=dapr.runtime type=log ver=1.0.0-rc.4 +``` + +It means there is an old exporter Component in your Kubernetes cluster. First, list Components: + +```bash +kubectl get components +``` + +```txt +NAME AGE +zipkin 71d +statestore 43m +``` + +Then, identify which Component is the exporter (usually named `zipkin`) and delete it: +```bash +kubectl delete component zipkin +``` diff --git a/dapr/quickstarts/tutorials/observability/deploy/appconfig.yaml b/dapr/quickstarts/tutorials/observability/deploy/appconfig.yaml new file mode 100644 index 0000000..a06140b --- /dev/null +++ b/dapr/quickstarts/tutorials/observability/deploy/appconfig.yaml @@ -0,0 +1,9 @@ +apiVersion: dapr.io/v1alpha1 +kind: Configuration +metadata: + name: appconfig +spec: + tracing: + samplingRate: "1" + zipkin: + endpointAddress: "http://zipkin.default.svc.cluster.local:9411/api/v2/spans" diff --git a/dapr/quickstarts/tutorials/observability/deploy/python-multiplier.yaml b/dapr/quickstarts/tutorials/observability/deploy/python-multiplier.yaml new file mode 100644 index 0000000..93455fe --- /dev/null +++ b/dapr/quickstarts/tutorials/observability/deploy/python-multiplier.yaml @@ -0,0 +1,30 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: multiplyapp + labels: + app: multiply +spec: + replicas: 1 + selector: + matchLabels: + app: multiply + template: + metadata: + labels: + app: multiply + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "multiplyapp" + dapr.io/app-port: "5001" + dapr.io/config: "appconfig" + spec: + containers: + - name: multiply + image: ghcr.io/dapr/samples/distributed-calculator-slow-python:latest + env: + - name: APP_PORT + value: "5001" + ports: + - containerPort: 5001 + imagePullPolicy: Always diff --git a/dapr/quickstarts/tutorials/observability/deploy/zipkin.yaml b/dapr/quickstarts/tutorials/observability/deploy/zipkin.yaml new file mode 100644 index 0000000..8b2edab --- /dev/null +++ b/dapr/quickstarts/tutorials/observability/deploy/zipkin.yaml @@ -0,0 +1,38 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: zipkin + labels: + app: zipkin +spec: + replicas: 1 + selector: + matchLabels: + app: zipkin + template: + metadata: + labels: + app: zipkin + spec: + containers: + - name: zipkin + image: openzipkin/zipkin + ports: + - containerPort: 9411 + +--- + +kind: Service +apiVersion: v1 +metadata: + name: zipkin + labels: + app: zipkin +spec: + selector: + app: zipkin + ports: + - protocol: TCP + port: 9411 + targetPort: 9411 + type: ClusterIP diff --git a/dapr/quickstarts/tutorials/observability/img/zipkin-1.png b/dapr/quickstarts/tutorials/observability/img/zipkin-1.png new file mode 100644 index 0000000..8514968 Binary files /dev/null and b/dapr/quickstarts/tutorials/observability/img/zipkin-1.png differ diff --git a/dapr/quickstarts/tutorials/observability/img/zipkin-2.png b/dapr/quickstarts/tutorials/observability/img/zipkin-2.png new file mode 100644 index 0000000..d1316ad Binary files /dev/null and b/dapr/quickstarts/tutorials/observability/img/zipkin-2.png differ diff --git a/dapr/quickstarts/tutorials/observability/img/zipkin-3.png b/dapr/quickstarts/tutorials/observability/img/zipkin-3.png new file mode 100644 index 0000000..64008c9 Binary files /dev/null and b/dapr/quickstarts/tutorials/observability/img/zipkin-3.png differ diff --git a/dapr/quickstarts/tutorials/observability/img/zipkin-4.png b/dapr/quickstarts/tutorials/observability/img/zipkin-4.png new file mode 100644 index 0000000..21c5867 Binary files /dev/null and b/dapr/quickstarts/tutorials/observability/img/zipkin-4.png differ diff --git a/dapr/quickstarts/tutorials/observability/makefile b/dapr/quickstarts/tutorials/observability/makefile new file mode 100644 index 0000000..dd6f2fc --- /dev/null +++ b/dapr/quickstarts/tutorials/observability/makefile @@ -0,0 +1,5 @@ +DOCKER_IMAGE_PREFIX ?=distributed-calculator-slow- +APPS ?=python + +include ../docker.mk +include ../validate.mk diff --git a/dapr/quickstarts/tutorials/observability/operands.json b/dapr/quickstarts/tutorials/observability/operands.json new file mode 100644 index 0000000..7ffce44 --- /dev/null +++ b/dapr/quickstarts/tutorials/observability/operands.json @@ -0,0 +1 @@ +{"operandOne":"52","operandTwo":"34"} diff --git a/dapr/quickstarts/tutorials/observability/output.json b/dapr/quickstarts/tutorials/observability/output.json new file mode 100644 index 0000000..9188f60 --- /dev/null +++ b/dapr/quickstarts/tutorials/observability/output.json @@ -0,0 +1 @@ +[[{"traceId":"24fd86930de0e3056711a15d2692e17c","id":"ddafc2c3377542aa","kind":"CLIENT","name":"calllocal/multiplyapp/multiply","timestamp":1645221885721912,"duration":1063290,"localEndpoint":{"serviceName":"calculator-front-end","ipv4":"10.244.1.10"},"tags":{"dapr.api":"POST /v1.0/invoke/multiplyapp/method/multiply","dapr.protocol":"http","dapr.status_code":"200","net.peer.name":"multiplyapp","opencensus.status_description":"OK","rpc.service":"ServiceInvocation"}},{"traceId":"24fd86930de0e3056711a15d2692e17c","parentId":"ddafc2c3377542aa","id":"6d742000f2f33f15","kind":"SERVER","name":"calllocal/multiplyapp/multiply","timestamp":1645221885780656,"duration":1003261,"localEndpoint":{"serviceName":"multiplyapp","ipv4":"10.244.2.14"},"tags":{"dapr.api":"/dapr.proto.internals.v1.ServiceInvocation/CallLocal","dapr.invoke_method":"multiply","dapr.protocol":"grpc","rpc.service":"ServiceInvocation"}}]] \ No newline at end of file diff --git a/dapr/quickstarts/tutorials/observability/persist.json b/dapr/quickstarts/tutorials/observability/persist.json new file mode 100644 index 0000000..9f1a83b --- /dev/null +++ b/dapr/quickstarts/tutorials/observability/persist.json @@ -0,0 +1 @@ +[{"key":"calculatorState","value":{"total":"54","next":null,"operation":null}}] diff --git a/dapr/quickstarts/tutorials/observability/python/Dockerfile b/dapr/quickstarts/tutorials/observability/python/Dockerfile new file mode 100644 index 0000000..fa0d808 --- /dev/null +++ b/dapr/quickstarts/tutorials/observability/python/Dockerfile @@ -0,0 +1,7 @@ +FROM python:3.7-alpine +COPY . /app +WORKDIR /app +RUN pip install flask flask_cors +ENTRYPOINT ["python"] +EXPOSE 5001 +CMD ["app.py"] diff --git a/dapr/quickstarts/tutorials/observability/python/app.py b/dapr/quickstarts/tutorials/observability/python/app.py new file mode 100644 index 0000000..52bb8f1 --- /dev/null +++ b/dapr/quickstarts/tutorials/observability/python/app.py @@ -0,0 +1,38 @@ +# +# Copyright 2021 The Dapr Authors +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import flask +from flask import request, jsonify +from flask_cors import CORS +import math +import sys +import time +import os + +port = os.getenv("APP_PORT","5001") + +app = flask.Flask(__name__) +CORS(app) + +@app.route('/multiply', methods=['POST']) +def multiply(): + ###################################################### + # Adding this call to sleep() to slow down the multipier + time.sleep(1) + ###################################################### + content = request.json + [operand_one, operand_two] = [float(content['operandOne']), float(content['operandTwo'])] + print(f"Calculating {operand_one} * {operand_two}", flush=True) + return jsonify(math.ceil(operand_one * operand_two * 100000)/100000) + +app.run(host="0.0.0.0",port=port) diff --git a/dapr/quickstarts/tutorials/observability/sample.json b/dapr/quickstarts/tutorials/observability/sample.json new file mode 100644 index 0000000..dbad3ab --- /dev/null +++ b/dapr/quickstarts/tutorials/observability/sample.json @@ -0,0 +1 @@ +{"data":{"orderId":"42"}} diff --git a/dapr/quickstarts/tutorials/pub-sub/README.md b/dapr/quickstarts/tutorials/pub-sub/README.md new file mode 100644 index 0000000..b31c172 --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/README.md @@ -0,0 +1,727 @@ +# Dapr Pub-Sub + +In this quickstart, you'll create a publisher microservice and two subscriber microservices to demonstrate how Dapr enables a publish-subcribe pattern. The publisher will generate messages of a specific topic, while subscribers will listen for messages of specific topics. See [Why Pub-Sub](#why-pub-sub) to understand when this pattern might be a good choice for your software architecture. + +Visit [this](https://docs.dapr.io/developing-applications/building-blocks/pubsub/) link for more information about Dapr and Pub-Sub. + +This quickstart includes one publisher: + +- React front-end message generator + +And three subscribers: + +- Node.js subscriber +- Python subscriber +- C# subscriber + +Dapr uses pluggable message buses to enable pub-sub, and delivers messages to subscribers in a [Cloud Events](https://github.com/cloudevents/spec) compliant message envelope. in this case you'll use Redis Streams (enabled in Redis versions => 5). The following architecture diagram illustrates how components interconnect locally: + +![Architecture Diagram](./img/Local_Architecture_Diagram.png) + +Dapr allows you to deploy the same microservices from your local machines to the cloud. Correspondingly, this quickstart has instructions for deploying this project [locally](#Run-Locally) or in [Kubernetes](#Run-in-Kubernetes). + +## Prerequisites + +### Prerequisites to run locally + +- [Dapr CLI with Dapr initialized](https://docs.dapr.io/getting-started/install-dapr-cli/) +- [Node.js version 14 or greater](https://nodejs.org/en/) and/or [Python 3.4 or greater](https://www.python.org/) and/or [Asp.Net Core 6](https://dotnet.microsoft.com/download/dotnet/6.0): You can run this quickstart with one or both or all microservices + +### Prerequisites to Run in Kubernetes + +- [Dapr enabled Kubernetes cluster](https://docs.dapr.io/operations/hosting/kubernetes/kubernetes-deploy/) + +## Run locally + +In order to run the pub/sub quickstart locally, each of the microservices need to run with Dapr. Start by running message subscribers. + +> **Note**: These instructions deploy a Node subscriber and a Python subscriber, but if you don't have either Node or Python, feel free to run just one. + +### Clone the quickstarts repository +Clone this quickstarts repository to your local machine: +```bash +git clone [-b ] https://github.com/dapr/quickstarts.git +``` +> **Note**: See https://github.com/dapr/quickstarts#supported-dapr-runtime-version for supported tags. Use `git clone https://github.com/dapr/quickstarts.git` when using the edge version of dapr runtime. + +### Run Node message subscriber with Dapr + +1. Navigate to Node subscriber directory in your CLI: + +```bash +cd node-subscriber +``` + + + +2. Install dependencies: + +```bash +npm install +``` + + + +3. Run the Node subscriber app with Dapr: + + + +```bash +dapr run --app-id node-subscriber --app-port 3000 node app.js +``` + + + +`app-id` which can be any unique identifier for the microservice. `app-port`, is the port that the Node application is running on. Finally, the command to run the app `node app.js` is passed last. + +### Run Python message subscriber with Dapr + +1. Open a new CLI window and navigate to Python subscriber directory in your CLI: + +```bash +cd python-subscriber +``` + +2. Install dependencies: + + + +```bash +pip3 install -r requirements.txt +``` +or + + + +```bash +python -m pip install -r requirements.txt +``` + + +3. Run the Python subscriber app with Dapr: + + + +```bash +dapr run --app-id python-subscriber --app-port 5001 python3 app.py +``` + + + +### Run C# message subscriber with Dapr + +1. Open a new CLI window and navigate to C# subscriber directory in your CLI: + +```bash +cd csharp-subscriber +``` + +2. Build Asp.Net Core app: + + + +```bash +dotnet build +``` + + + +3. Run the C# subscriber app with Dapr: + + + +```bash +dapr run --app-id csharp-subscriber --app-port 5009 dotnet run csharp-subscriber.csproj +``` + + + +### Run the React front end with Dapr + +Now, run the React front end with Dapr. The front end will publish different kinds of messages that subscribers will pick up. + +1. Open a new CLI window and navigate to the react-form directory: + +```bash +cd react-form +``` + +2. Run the React front end app with Dapr: + + + +```bash +npm run buildclient +``` + +```bash +npm install +``` + + + + + +```bash +dapr run --app-id react-form --app-port 8080 npm run start +``` + + + +This may take a minute, as it downloads dependencies and creates an optimized production build. You'll know that it's done when you see `== APP == Listening on port 8080!` and several Dapr logs. + +3. Open the browser and navigate to "http://localhost:8080/". You should see a form with a dropdown for message type and message text: + +![Form Screenshot](./img/Form_Screenshot.JPG) + + + + + + + +4. Pick a topic, enter some text and fire off a message! Observe the logs coming through your respective Dapr. Note that the Node.js subscriber receives messages of type "A" and "B", while the Python subscriber receives messages of type "A" and "C". Note that logs are showing up in the console window where you ran each one: + +```bash +== APP == Listening on port 8080! +``` + +### Use the CLI to publish messages to subscribers + +The Dapr CLI provides a mechanism to publish messages for testing purposes. + +1. Use Dapr CLI to publish a message: + + + +```bash +dapr publish --publish-app-id react-form --pubsub pubsub --topic A --data-file message_a.json +``` + + + +2. **Optional**: Try publishing a message of topic B. You'll notice that only the Node app will receive this message. The same is true for topic 'C' and the python app. + + + +> **Note:** If you are running in an environment without easy access to a web browser, the following curl commands will simulate a browser request to the node server. + +```bash +curl -s http://localhost:8080/publish -H Content-Type:application/json --data @message_b.json +``` + +```bash +curl -s http://localhost:8080/publish -H Content-Type:application/json --data @message_c.json +``` + + + +3. Cleanup + + + +```bash +dapr stop --app-id node-subscriber +``` + +```bash +dapr stop --app-id python-subscriber +``` + +```bash +dapr stop --app-id csharp-subscriber +``` + +```bash +dapr stop --app-id react-form +``` + + + +4. If you want to deploy this same application to Kubernetes, move onto the next step. Otherwise, skip ahead to the [How it Works](#How-it-Works) section to understand the code! + + +## Run in Kubernetes + +To run the same code in Kubernetes, first set up a Redis store and then deploy the microservices. You'll be using the same microservices, but ultimately the architecture is a bit different: + +![Architecture Diagram](./img/K8s_Architecture_Diagram.png) + +### Set up a Redis store + +Dapr uses pluggable message buses to enable pub-sub, in this case Redis Streams (enabled in Redis version 5 and above) is used. You'll install Redis into the cluster using helm, but keep in mind that you could use whichever Redis host you like, as long as the version is greater than 5. + +1. Follow [these steps](https://docs.dapr.io/getting-started/tutorials/configure-state-pubsub/) to create a Redis store using Helm. + > **Note**: Currently the version of Redis supported by Azure Redis Cache is less than 5, so using Azure Redis Cache will not work. +2. Once your store is created, add the keys to the `redis.yaml` file in the `deploy` directory. Don't worry about applying the `redis.yaml`, as it will be covered in the next step. + > **Note:** the `redis.yaml` file provided in this quickstart takes plain text secrets. In a production-grade application, follow [secret management](https://docs.dapr.io/developing-applications/building-blocks/secrets/) instructions to securely manage your secrets. + +### Deploy assets + +Now that the Redis store is set up, you can deploy the assets. + +1. In your CLI window, navigate to the deploy directory +2. To deploy the publisher and three subscriber microservices, as well as the redis configuration you set up in the last step, run: + + + +```bash +kubectl apply -f . +``` + +Kubernetes deployments are asyncronous. This means you'll need to wait for the deployment to complete before moving on to the next steps. You can do so with the following command: + +```bash +kubectl rollout status deploy/node-subscriber +``` + +```bash +kubectl rollout status deploy/python-subscriber +``` + +```bash +kubectl rollout status deploy/csharp-subscriber +``` + +```bash +kubectl rollout status deploy/react-form +``` + + + + +3. To see each pod being provisioned run: + +```bash +kubectl get pods +``` + +4. To get the external IP exposed by the `react-form` microservice, run + +```bash +kubectl get svc -w +``` + +This may take a few minutes. + +> **Note:** Minikube users cannot see the external IP. Instead, you can use `minikube service [service_name]` to access loadbalancer without external IP. + +### Use the app + +1. Access the web form. + +There are several different ways to access a Kubernetes service depending on which platform you are using. Port forwarding is one consistent way to access a service, whether it is hosted locally or on a cloud Kubernetes provider like AKS. + + + +```bash +kubectl port-forward service/react-form 8000:80 +``` + + + +This will make your service available on http://localhost:8000 + +> **Optional**: If you are using a public cloud provider, you can substitue your EXTERNAL-IP address instead of port forwarding. You can find it with: + +```bash +kubectl get svc react-form +``` + +2. Create and submit messages of different types. + +Open a web broswer and navigate to http://localhost:8000 and you see the same form as with the locally hosted example above. + + + +> **Note:** If you are running in an environment without easy access to a web browser, the following curl commands will simulate a browser request to the node server. + + +```bash +curl -s http://localhost:8000/publish -H Content-Type:application/json --data @message_a.json +``` + +```bash +curl -s http://localhost:8000/publish -H Content-Type:application/json --data @message_b.json +``` + +```bash +curl -s http://localhost:8000/publish -H Content-Type:application/json --data @message_c.json +``` + + + +3. To see the logs generated from your subscribers: + + + +```bash +kubectl logs --selector app=node-subscriber -c node-subscriber +``` + + + + + +```bash +kubectl logs --selector app=python-subscriber -c python-subscriber +``` + + + + + +```bash +kubectl logs --selector app=csharp-subscriber -c csharp-subscriber +``` + + + +4. Note that the Node.js subscriber receives messages of type "A" and "B", while the Python subscriber receives messages of type "A" and "C" and the C# subscriber receives messages of type "A" and "B" and "C". + +### Cleanup + +Once you're done, you can spin down your Kubernetes resources by navigating to the `./deploy` directory and running: + + + +```bash +kubectl delete -f . +``` + + + +This will spin down each resource defined by the .yaml files in the `deploy` directory, including the state component. + +## How it works + +Now that you've run the quickstart locally and/or in Kubernetes, let's unpack how this all works. the app is broken up into two subscribers and one publisher: + +### Node message subscriber + +Navigate to the `node-subscriber` directory and open `app.js`, the code for the Node.js subscriber. Here three API endpoints are exposed using `express`. The first is a GET endpoint: + +```js +app.get('/dapr/subscribe', (_req, res) => { + res.json([ + { + pubsubname: "pubsub", + topic: "A", + route: "A" + }, + { + pubsubname: "pubsub", + topic: "B", + route: "B" + } + ]); +}); +``` + +This tells Dapr what topics in which pubsub component to subscribe to. When deployed (locally or in Kubernetes), Dapr will call out to the service to determine if it's subscribing to anything. The other two endpoints are POST endpoints: + +```js +app.post('/A', (req, res) => { + console.log("A: ", req.body.data.message); + res.sendStatus(200); +}); + +app.post('/B', (req, res) => { + console.log("B: ", req.body.data.message); + res.sendStatus(200); +}); +``` + +These handle messages of each topic type coming through. Note that this simply logs the message. In a more complex application this is where you would include topic-specific handlers. + +### Python message subscriber + +Navigate to the `python-subscriber` directory and open `app.py`, the code for the Python subscriber. As with the Node.js subscriber, we're exposing three API endpoints, this time using `flask`. The first is a GET endpoint: + +```python +@app.route('/dapr/subscribe', methods=['GET']) +def subscribe(): + subscriptions = [{'pubsubname': 'pubsub', 'topic': 'A', 'route': 'A'}, {'pubsubname': 'pubsub', 'topic': 'C', 'route': 'C'}] + return jsonify(subscriptions) +``` +Again, this is how you tell Dapr what topics in which pubsub component to subscribe to. In this case, subscribing to topics "A" and "C" of pubsub component named 'pubsub'. Messages of those topics are handled with the other two routes: + +```python +@app.route('/A', methods=['POST']) +def a_subscriber(): + print(f'A: {request.json}', flush=True) + print('Received message "{}" on topic "{}"'.format(request.json['data']['message'], request.json['topic']), flush=True) + return json.dumps({'success':True}), 200, {'ContentType':'application/json'} + +@app.route('/C', methods=['POST']) +def c_subscriber(): + print(f'C: {request.json}', flush=True) + print('Received message "{}" on topic "{}"'.format(request.json['data']['message'], request.json['topic']), flush=True) + return json.dumps({'success':True}), 200, {'ContentType':'application/json'} +``` + +Note: if `flush=True` is not set, logs will not appear when running `kubectl get logs...`. This is a product of Python's output buffering. + +### C# message subscriber + +Navigate to the `csharp-subscriber` directory and open `Program.cs`, the code for the C# subscriber. We're exposing three API endpoints, this time using `Asp.Net Core 6 Minimal API`. + +Again, this is how you tell Dapr what topics in which pubsub component to subscribe to. In this case, subscribing to topics "A" and "B" and "C" of pubsub component named 'pubsub'. Messages of those topics are handled with these three routes: + +```csharp +using Dapr; + +var builder = WebApplication.CreateBuilder(args); + +var app = builder.Build(); + +// Dapr configurations +app.UseCloudEvents(); + +app.MapSubscribeHandler(); + +app.MapPost("/A", [Topic("pubsub", "A")] (ILogger logger, MessageEvent item) => { + logger.LogInformation($"{item.MessageType}: {item.Message}"); + return Results.Ok(); +}); + +app.MapPost("/B", [Topic("pubsub", "B")] (ILogger logger, MessageEvent item) => { + logger.LogInformation($"{item.MessageType}: {item.Message}"); + return Results.Ok(); +}); + +app.MapPost("/C", [Topic("pubsub", "C")] (ILogger logger, Dictionary item) => { + logger.LogInformation($"{item["messageType"]}: {item["message"]}"); + return Results.Ok(); +}); + +app.Run(); + +internal record MessageEvent(string MessageType, string Message); +``` + +### React front end + +Our publisher is broken up into a client and a server: + +#### Client + +The client is a simple single page React application that was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). The relevant client code sits in `react-form/client/src/MessageForm.js` where a form is presented to the users. As users update the form, React state is updated with the latest aggregated JSON data. By default the data is set to: + +```js +{ + messageType: "A", + message: "" +}; +``` +Upon submission of the form, the aggregated JSON data is sent to the server: + +```js +fetch('/publish', { + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }, + method:"POST", + body: JSON.stringify(this.state), +}); +``` + +#### Server + +The server is a basic express application that exposes a POST endpoint: `/publish`. This takes the requests from the client and publishes them against Dapr. Express's built in JSON middleware function is used to parse the JSON out of the incoming requests: + +```js +app.use(express.json()); +``` + +This allows us to determine which topic to publish the message with. To publish messages against Dapr, the URL needs to look like: `http://localhost:/publish//`, so the `publish` endpoint builds a URL and posts the JSON against it. The POST request also needs to return a success code in the response upon successful completion. + +```js + const publishUrl = `${daprUrl}/publish/${pubsubName}/${req.body?.messageType}`; + await axios.post(publishUrl, req.body); + return res.sendStatus(200); +``` + +Note how the `daprUrl` determines what port Dapr live on: + +```js +const daprUrl = `http://localhost:${process.env.DAPR_HTTP_PORT || 3500}/v1.0`; +``` + +By default, Dapr live on 3500, but if we're running Dapr locally and set it to a different port (using the `--app-port` flag in the CLI `run` command), then that port will be injected into the application as an environment variable. + +The server also hosts the React application itself by forwarding default home page `/` route requests to the built client code: + +```js +app.get('/', function (_req, res) { + res.sendFile(path.join(__dirname, 'client/build', 'index.html')); +}); +``` + +## Why Pub-Sub? + +Developers use a pub-sub messaging pattern to achieve high scalability and loose coupling. + +### Scalability + +Pub-sub is generally used for large applications that need to be highly scalable. Pub-sub applications often scale better than traditional client-server applications. + +### Loose coupling + +Pub-sub allows us to completely decouple the components. Publishers need not be aware of any of their subscribers, nor must subscribers be aware of publishers. This allows developers to write leaner microservices that don't take an immediate dependency on each other. + +## Related links: + +- How to [setup a Dapr pub/sub](https://docs.dapr.io/developing-applications/building-blocks/pubsub/) +- How to [use Pub/Sub to publish a message to a topic](https://docs.dapr.io/developing-applications/building-blocks/pubsub/howto-publish-subscribe/) + +## Next steps: + +- Explore additional [quickstarts](../README.md#quickstarts). diff --git a/dapr/quickstarts/tutorials/pub-sub/csharp-subscriber/.dockerignore b/dapr/quickstarts/tutorials/pub-sub/csharp-subscriber/.dockerignore new file mode 100644 index 0000000..95520b4 --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/csharp-subscriber/.dockerignore @@ -0,0 +1,24 @@ +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/bin +**/charts +**/docker-compose* +**/compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +README.md diff --git a/dapr/quickstarts/tutorials/pub-sub/csharp-subscriber/.gitignore b/dapr/quickstarts/tutorials/pub-sub/csharp-subscriber/.gitignore new file mode 100644 index 0000000..976661b --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/csharp-subscriber/.gitignore @@ -0,0 +1,388 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Nuget personal access tokens and Credentials +# nuget.config + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +.idea/ +*.sln.iml \ No newline at end of file diff --git a/dapr/quickstarts/tutorials/pub-sub/csharp-subscriber/Dockerfile b/dapr/quickstarts/tutorials/pub-sub/csharp-subscriber/Dockerfile new file mode 100644 index 0000000..744df6c --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/csharp-subscriber/Dockerfile @@ -0,0 +1,7 @@ +# Note: we cannot do a staged dotnet docker build here for arm/arm64. + +# Build runtime image +FROM mcr.microsoft.com/dotnet/aspnet:6.0 +WORKDIR /app +COPY /out . +ENTRYPOINT ["dotnet", "csharp-subscriber.dll"] \ No newline at end of file diff --git a/dapr/quickstarts/tutorials/pub-sub/csharp-subscriber/Program.cs b/dapr/quickstarts/tutorials/pub-sub/csharp-subscriber/Program.cs new file mode 100644 index 0000000..891c460 --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/csharp-subscriber/Program.cs @@ -0,0 +1,29 @@ +using Dapr; + +var builder = WebApplication.CreateBuilder(args); + +var app = builder.Build(); + +// Dapr configurations +app.UseCloudEvents(); + +app.MapSubscribeHandler(); + +app.MapPost("/A", [Topic("pubsub", "A")] (ILogger logger, MessageEvent item) => { + Console.WriteLine($"{item.MessageType}: {item.Message}"); + return Results.Ok(); +}); + +app.MapPost("/B", [Topic("pubsub", "B")] (ILogger logger, MessageEvent item) => { + Console.WriteLine($"{item.MessageType}: {item.Message}"); + return Results.Ok(); +}); + +app.MapPost("/C", [Topic("pubsub", "C")] (ILogger logger, Dictionary item) => { + Console.WriteLine($"{item["messageType"]}: {item["message"]}"); + return Results.Ok(); +}); + +app.Run(); + +internal record MessageEvent(string MessageType, string Message); \ No newline at end of file diff --git a/dapr/quickstarts/tutorials/pub-sub/csharp-subscriber/Properties/launchSettings.json b/dapr/quickstarts/tutorials/pub-sub/csharp-subscriber/Properties/launchSettings.json new file mode 100644 index 0000000..5216706 --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/csharp-subscriber/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:55001", + "sslPort": 0 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "csharp_subscriber": { + "commandName": "Project", + "dotnetRunMessages": "true", + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5009", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/dapr/quickstarts/tutorials/pub-sub/csharp-subscriber/appsettings.Development.json b/dapr/quickstarts/tutorials/pub-sub/csharp-subscriber/appsettings.Development.json new file mode 100644 index 0000000..db9536f --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/csharp-subscriber/appsettings.Development.json @@ -0,0 +1,16 @@ +{ + "Kestrel": { + "EndPoints": { + "Http": { + "Url": "http://localhost:5009" + } + } + }, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/dapr/quickstarts/tutorials/pub-sub/csharp-subscriber/appsettings.json b/dapr/quickstarts/tutorials/pub-sub/csharp-subscriber/appsettings.json new file mode 100644 index 0000000..e6c2744 --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/csharp-subscriber/appsettings.json @@ -0,0 +1,17 @@ +{ + "Kestrel": { + "EndPoints": { + "Http": { + "Url": "http://localhost:5009" + } + } + }, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/dapr/quickstarts/tutorials/pub-sub/csharp-subscriber/csharp-subscriber.csproj b/dapr/quickstarts/tutorials/pub-sub/csharp-subscriber/csharp-subscriber.csproj new file mode 100644 index 0000000..7a455c3 --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/csharp-subscriber/csharp-subscriber.csproj @@ -0,0 +1,15 @@ + + + + net6.0 + enable + enable + Linux + + + + + + + + diff --git a/dapr/quickstarts/tutorials/pub-sub/deploy/csharp-subscriber.yaml b/dapr/quickstarts/tutorials/pub-sub/deploy/csharp-subscriber.yaml new file mode 100644 index 0000000..8daba74 --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/deploy/csharp-subscriber.yaml @@ -0,0 +1,26 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: csharp-subscriber + labels: + app: csharp-subscriber +spec: + replicas: 1 + selector: + matchLabels: + app: csharp-subscriber + template: + metadata: + labels: + app: csharp-subscriber + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "csharp-subscriber" + dapr.io/app-port: "5009" + spec: + containers: + - name: csharp-subscriber + image: ghcr.io/dapr/samples/pubsub-csharp-subscriber:latest + ports: + - containerPort: 5009 + imagePullPolicy: Always \ No newline at end of file diff --git a/dapr/quickstarts/tutorials/pub-sub/deploy/node-subscriber.yaml b/dapr/quickstarts/tutorials/pub-sub/deploy/node-subscriber.yaml new file mode 100644 index 0000000..6ad178e --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/deploy/node-subscriber.yaml @@ -0,0 +1,26 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: node-subscriber + labels: + app: node-subscriber +spec: + replicas: 1 + selector: + matchLabels: + app: node-subscriber + template: + metadata: + labels: + app: node-subscriber + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "node-subscriber" + dapr.io/app-port: "3000" + spec: + containers: + - name: node-subscriber + image: ghcr.io/dapr/samples/pubsub-node-subscriber:latest + ports: + - containerPort: 3000 + imagePullPolicy: Always \ No newline at end of file diff --git a/dapr/quickstarts/tutorials/pub-sub/deploy/python-subscriber.yaml b/dapr/quickstarts/tutorials/pub-sub/deploy/python-subscriber.yaml new file mode 100644 index 0000000..b07ef6a --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/deploy/python-subscriber.yaml @@ -0,0 +1,26 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: python-subscriber + labels: + app: python-subscriber +spec: + replicas: 1 + selector: + matchLabels: + app: python-subscriber + template: + metadata: + labels: + app: python-subscriber + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "python-subscriber" + dapr.io/app-port: "5001" + spec: + containers: + - name: python-subscriber + image: ghcr.io/dapr/samples/pubsub-python-subscriber:latest + ports: + - containerPort: 5001 + imagePullPolicy: Always diff --git a/dapr/quickstarts/tutorials/pub-sub/deploy/react-form.yaml b/dapr/quickstarts/tutorials/pub-sub/deploy/react-form.yaml new file mode 100644 index 0000000..66cc2eb --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/deploy/react-form.yaml @@ -0,0 +1,42 @@ +kind: Service +apiVersion: v1 +metadata: + name: react-form + labels: + app: react-form +spec: + selector: + app: react-form + ports: + - protocol: TCP + port: 80 + targetPort: 8080 + type: LoadBalancer + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: react-form + labels: + app: react-form +spec: + replicas: 1 + selector: + matchLabels: + app: react-form + template: + metadata: + labels: + app: react-form + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "react-form" + dapr.io/app-port: "8080" + spec: + containers: + - name: react-form + image: ghcr.io/dapr/samples/pubsub-react-form:latest + ports: + - containerPort: 8080 + imagePullPolicy: Always diff --git a/dapr/quickstarts/tutorials/pub-sub/deploy/redis.yaml b/dapr/quickstarts/tutorials/pub-sub/deploy/redis.yaml new file mode 100644 index 0000000..7fdc61f --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/deploy/redis.yaml @@ -0,0 +1,21 @@ +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: pubsub +spec: + type: pubsub.redis + version: v1 + metadata: + # These settings will work out of the box if you use `helm install + # bitnami/redis`. If you have your own setup, replace + # `redis-master:6379` with your own Redis master address, and the + # Redis password with your own Secret's name. For more information, + # see https://docs.dapr.io/operations/components/component-secrets . + - name: redisHost + value: redis-master:6379 + - name: redisPassword + secretKeyRef: + name: redis + key: redis-password +auth: + secretStore: kubernetes diff --git a/dapr/quickstarts/tutorials/pub-sub/img/Form_Screenshot.JPG b/dapr/quickstarts/tutorials/pub-sub/img/Form_Screenshot.JPG new file mode 100644 index 0000000..a753c4f Binary files /dev/null and b/dapr/quickstarts/tutorials/pub-sub/img/Form_Screenshot.JPG differ diff --git a/dapr/quickstarts/tutorials/pub-sub/img/K8s_Architecture_Diagram.png b/dapr/quickstarts/tutorials/pub-sub/img/K8s_Architecture_Diagram.png new file mode 100644 index 0000000..92c0703 Binary files /dev/null and b/dapr/quickstarts/tutorials/pub-sub/img/K8s_Architecture_Diagram.png differ diff --git a/dapr/quickstarts/tutorials/pub-sub/img/Local_Architecture_Diagram.png b/dapr/quickstarts/tutorials/pub-sub/img/Local_Architecture_Diagram.png new file mode 100644 index 0000000..72f3caf Binary files /dev/null and b/dapr/quickstarts/tutorials/pub-sub/img/Local_Architecture_Diagram.png differ diff --git a/dapr/quickstarts/tutorials/pub-sub/makefile b/dapr/quickstarts/tutorials/pub-sub/makefile new file mode 100644 index 0000000..9d9e849 --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/makefile @@ -0,0 +1,16 @@ +DOCKER_IMAGE_PREFIX ?=pubsub- +APPS ?=node-subscriber python-subscriber csharp-subscriber react-form + +include ../docker.mk +include ../validate.mk + +TARGET_DOTNET_PLATFORM = $(TARGET_ARCH) +ifeq ($(TARGET_DOTNET_PLATFORM),amd64) + TARGET_DOTNET_PLATFORM = x64 +endif + +build-csharp-subscriber-local: + cd csharp-subscriber && dotnet restore -r linux-$(TARGET_DOTNET_PLATFORM) + cd csharp-subscriber && dotnet publish -c Release -o out + +build-csharp-subscriber: build-csharp-subscriber-local diff --git a/dapr/quickstarts/tutorials/pub-sub/message_a.json b/dapr/quickstarts/tutorials/pub-sub/message_a.json new file mode 100644 index 0000000..e2a0c08 --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/message_a.json @@ -0,0 +1 @@ +{"messageType":"A","message":"Message on A"} diff --git a/dapr/quickstarts/tutorials/pub-sub/message_b.json b/dapr/quickstarts/tutorials/pub-sub/message_b.json new file mode 100644 index 0000000..ac56460 --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/message_b.json @@ -0,0 +1 @@ +{"messageType":"B","message":"Message on B"} diff --git a/dapr/quickstarts/tutorials/pub-sub/message_c.json b/dapr/quickstarts/tutorials/pub-sub/message_c.json new file mode 100644 index 0000000..90a0e16 --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/message_c.json @@ -0,0 +1 @@ +{"messageType":"C","message":"Message on C"} diff --git a/dapr/quickstarts/tutorials/pub-sub/node-subscriber/.gitignore b/dapr/quickstarts/tutorials/pub-sub/node-subscriber/.gitignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/node-subscriber/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/dapr/quickstarts/tutorials/pub-sub/node-subscriber/Dockerfile b/dapr/quickstarts/tutorials/pub-sub/node-subscriber/Dockerfile new file mode 100644 index 0000000..939caef --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/node-subscriber/Dockerfile @@ -0,0 +1,6 @@ +FROM node:17-alpine +WORKDIR /usr/src/app +COPY . . +RUN npm install +EXPOSE 3000 +CMD [ "node", "app.js" ] \ No newline at end of file diff --git a/dapr/quickstarts/tutorials/pub-sub/node-subscriber/app.js b/dapr/quickstarts/tutorials/pub-sub/node-subscriber/app.js new file mode 100644 index 0000000..79ee7f1 --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/node-subscriber/app.js @@ -0,0 +1,48 @@ +// +// Copyright 2021 The Dapr Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +const express = require('express'); +const bodyParser = require('body-parser'); + +const app = express(); +// Dapr publishes messages with the application/cloudevents+json content-type +app.use(bodyParser.json({ type: 'application/*+json' })); + +const port = 3000; + +app.get('/dapr/subscribe', (_req, res) => { + res.json([ + { + pubsubname: "pubsub", + topic: "A", + route: "A" + }, + { + pubsubname: "pubsub", + topic: "B", + route: "B" + } + ]); +}); + +app.post('/A', (req, res) => { + console.log("A: ", req.body.data.message); + res.sendStatus(200); +}); + +app.post('/B', (req, res) => { + console.log("B: ", req.body.data.message); + res.sendStatus(200); +}); + +app.listen(port, () => console.log(`Node App listening on port ${port}!`)); diff --git a/dapr/quickstarts/tutorials/pub-sub/node-subscriber/package.json b/dapr/quickstarts/tutorials/pub-sub/node-subscriber/package.json new file mode 100644 index 0000000..27ce726 --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/node-subscriber/package.json @@ -0,0 +1,15 @@ +{ + "name": "node-subscriber", + "version": "1.0.0", + "private": true, + "description": "A subscriber microservice in the Dapr pub-sub sample", + "main": "app.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Ryan Volum", + "license": "ISC", + "dependencies": { + "express": "^4.17.1" + } +} diff --git a/dapr/quickstarts/tutorials/pub-sub/python-subscriber/.gitignore b/dapr/quickstarts/tutorials/pub-sub/python-subscriber/.gitignore new file mode 100644 index 0000000..8fa5b33 --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/python-subscriber/.gitignore @@ -0,0 +1 @@ +env \ No newline at end of file diff --git a/dapr/quickstarts/tutorials/pub-sub/python-subscriber/Dockerfile b/dapr/quickstarts/tutorials/pub-sub/python-subscriber/Dockerfile new file mode 100644 index 0000000..f45330c --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/python-subscriber/Dockerfile @@ -0,0 +1,6 @@ +FROM python:3.7-alpine +COPY . /app +WORKDIR /app +RUN pip install flask flask_cors +EXPOSE 5001 +CMD ["python", "app.py"] diff --git a/dapr/quickstarts/tutorials/pub-sub/python-subscriber/app.py b/dapr/quickstarts/tutorials/pub-sub/python-subscriber/app.py new file mode 100644 index 0000000..eb525e2 --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/python-subscriber/app.py @@ -0,0 +1,40 @@ +# +# Copyright 2021 The Dapr Authors +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import flask +from flask import request, jsonify +from flask_cors import CORS +import json +import sys + +app = flask.Flask(__name__) +CORS(app) + +@app.route('/dapr/subscribe', methods=['GET']) +def subscribe(): + subscriptions = [{'pubsubname': 'pubsub', 'topic': 'A', 'route': 'A'}, {'pubsubname': 'pubsub', 'topic': 'C', 'route': 'C'}] + return jsonify(subscriptions) + +@app.route('/A', methods=['POST']) +def a_subscriber(): + print(f'A: {request.json}', flush=True) + print('Received message "{}" on topic "{}"'.format(request.json['data']['message'], request.json['topic']), flush=True) + return json.dumps({'success':True}), 200, {'ContentType':'application/json'} + +@app.route('/C', methods=['POST']) +def c_subscriber(): + print(f'C: {request.json}', flush=True) + print('Received message "{}" on topic "{}"'.format(request.json['data']['message'], request.json['topic']), flush=True) + return json.dumps({'success':True}), 200, {'ContentType':'application/json'} + +app.run(port=5001) diff --git a/dapr/quickstarts/tutorials/pub-sub/python-subscriber/requirements.txt b/dapr/quickstarts/tutorials/pub-sub/python-subscriber/requirements.txt new file mode 100644 index 0000000..1580db5 --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/python-subscriber/requirements.txt @@ -0,0 +1,8 @@ +Click==7.0 +Flask==1.1.1 +Flask-Cors==3.0.9 +itsdangerous==1.1.0 +Jinja2==2.11.3 +MarkupSafe==1.1.1 +six==1.12.0 +Werkzeug==0.15.6 diff --git a/dapr/quickstarts/tutorials/pub-sub/react-form/.dockerignore b/dapr/quickstarts/tutorials/pub-sub/react-form/.dockerignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/react-form/.dockerignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/dapr/quickstarts/tutorials/pub-sub/react-form/.gitignore b/dapr/quickstarts/tutorials/pub-sub/react-form/.gitignore new file mode 100644 index 0000000..4d29575 --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/react-form/.gitignore @@ -0,0 +1,23 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# production +/build + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/dapr/quickstarts/tutorials/pub-sub/react-form/Dockerfile b/dapr/quickstarts/tutorials/pub-sub/react-form/Dockerfile new file mode 100644 index 0000000..8d90922 --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/react-form/Dockerfile @@ -0,0 +1,6 @@ +FROM node:17-alpine +WORKDIR /usr/src/app +COPY . . +RUN npm run build +EXPOSE 8080 +CMD [ "npm", "run", "start" ] diff --git a/dapr/quickstarts/tutorials/pub-sub/react-form/client/.gitignore b/dapr/quickstarts/tutorials/pub-sub/react-form/client/.gitignore new file mode 100644 index 0000000..4d29575 --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/react-form/client/.gitignore @@ -0,0 +1,23 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# production +/build + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/dapr/quickstarts/tutorials/pub-sub/react-form/client/README.md b/dapr/quickstarts/tutorials/pub-sub/react-form/client/README.md new file mode 100644 index 0000000..9d9614c --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/react-form/client/README.md @@ -0,0 +1,68 @@ +This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). + +## Available Scripts + +In the project directory, you can run: + +### `npm start` + +Runs the app in the development mode.
+Open [http://localhost:3000](http://localhost:3000) to view it in the browser. + +The page will reload if you make edits.
+You will also see any lint errors in the console. + +### `npm test` + +Launches the test runner in the interactive watch mode.
+See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. + +### `npm run build` + +Builds the app for production to the `build` folder.
+It correctly bundles React in production mode and optimizes the build for the best performance. + +The build is minified and the filenames include the hashes.
+Your app is ready to be deployed! + +See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. + +### `npm run eject` + +**Note: this is a one-way operation. Once you `eject`, you can’t go back!** + +If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. + +Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. + +You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. + +## Learn More + +You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). + +To learn React, check out the [React documentation](https://reactjs.org/). + +### Code Splitting + +This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting + +### Analyzing the Bundle Size + +This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size + +### Making a Progressive Web App + +This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app + +### Advanced Configuration + +This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration + +### Deployment + +This section has moved here: https://facebook.github.io/create-react-app/docs/deployment + +### `npm run build` fails to minify + +This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify diff --git a/dapr/quickstarts/tutorials/pub-sub/react-form/client/package.json b/dapr/quickstarts/tutorials/pub-sub/react-form/client/package.json new file mode 100644 index 0000000..2b6f6a7 --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/react-form/client/package.json @@ -0,0 +1,33 @@ +{ + "name": "client", + "version": "0.1.0", + "private": true, + "dependencies": { + "bootstrap": "^4.3.1", + "react": "^16.9.0", + "react-dom": "^16.9.0", + "react-scripts": "^5.0.0" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject" + }, + "proxy": "http://localhost:8080/", + "eslintConfig": { + "extends": "react-app" + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + } +} diff --git a/dapr/quickstarts/tutorials/pub-sub/react-form/client/public/favicon.ico b/dapr/quickstarts/tutorials/pub-sub/react-form/client/public/favicon.ico new file mode 100644 index 0000000..a11777c Binary files /dev/null and b/dapr/quickstarts/tutorials/pub-sub/react-form/client/public/favicon.ico differ diff --git a/dapr/quickstarts/tutorials/pub-sub/react-form/client/public/index.html b/dapr/quickstarts/tutorials/pub-sub/react-form/client/public/index.html new file mode 100644 index 0000000..4e01a5e --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/react-form/client/public/index.html @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + React App + + + +
+ + diff --git a/dapr/quickstarts/tutorials/pub-sub/react-form/client/public/logo192.png b/dapr/quickstarts/tutorials/pub-sub/react-form/client/public/logo192.png new file mode 100644 index 0000000..fc44b0a Binary files /dev/null and b/dapr/quickstarts/tutorials/pub-sub/react-form/client/public/logo192.png differ diff --git a/dapr/quickstarts/tutorials/pub-sub/react-form/client/public/logo512.png b/dapr/quickstarts/tutorials/pub-sub/react-form/client/public/logo512.png new file mode 100644 index 0000000..a4e47a6 Binary files /dev/null and b/dapr/quickstarts/tutorials/pub-sub/react-form/client/public/logo512.png differ diff --git a/dapr/quickstarts/tutorials/pub-sub/react-form/client/public/manifest.json b/dapr/quickstarts/tutorials/pub-sub/react-form/client/public/manifest.json new file mode 100644 index 0000000..e4c94e6 --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/react-form/client/public/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/dapr/quickstarts/tutorials/pub-sub/react-form/client/src/App.css b/dapr/quickstarts/tutorials/pub-sub/react-form/client/src/App.css new file mode 100644 index 0000000..b41d297 --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/react-form/client/src/App.css @@ -0,0 +1,33 @@ +.App { + text-align: center; +} + +.App-logo { + animation: App-logo-spin infinite 20s linear; + height: 40vmin; + pointer-events: none; +} + +.App-header { + background-color: #282c34; + min-height: 100vh; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + font-size: calc(10px + 2vmin); + color: white; +} + +.App-link { + color: #61dafb; +} + +@keyframes App-logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} diff --git a/dapr/quickstarts/tutorials/pub-sub/react-form/client/src/App.js b/dapr/quickstarts/tutorials/pub-sub/react-form/client/src/App.js new file mode 100644 index 0000000..2a4d64a --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/react-form/client/src/App.js @@ -0,0 +1,21 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// ------------------------------------------------------------ + +import React from 'react'; +import { MessageForm } from './MessageForm'; +import { Nav } from './Nav'; + +import './App.css'; + +function App() { + return ( +
+
+ ); +} + +export default App; diff --git a/dapr/quickstarts/tutorials/pub-sub/react-form/client/src/App.test.js b/dapr/quickstarts/tutorials/pub-sub/react-form/client/src/App.test.js new file mode 100644 index 0000000..44a98fb --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/react-form/client/src/App.test.js @@ -0,0 +1,22 @@ +// +// Copyright 2021 The Dapr Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import React from 'react'; +import ReactDOM from 'react-dom'; +import App from './App'; + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); diff --git a/dapr/quickstarts/tutorials/pub-sub/react-form/client/src/MessageForm.js b/dapr/quickstarts/tutorials/pub-sub/react-form/client/src/MessageForm.js new file mode 100644 index 0000000..698b7d8 --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/react-form/client/src/MessageForm.js @@ -0,0 +1,75 @@ +// +// Copyright 2021 The Dapr Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import React from 'react'; + +export class MessageForm extends React.Component { + constructor(props) { + super(props); + + this.state = this.getInitialState(); + } + + handleInputChange = (event) => { + const target = event.target; + const value = target.value; + const name = target.name; + + console.log(`Setting ${name} to ${value}`) + this.setState({ + [name]: value + }); + } + + handleSubmit = (event) => { + fetch('/publish', { + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }, + method:"POST", + body: JSON.stringify(this.state), + }); + event.preventDefault(); + this.setState(this.getInitialState()); + } + + getInitialState = () => { + return { + messageType: "A", + message: "" + }; + } + + render() { + return ( +
+
+
+ + +
+
+ + +
+ +
+
+ ); + } + } \ No newline at end of file diff --git a/dapr/quickstarts/tutorials/pub-sub/react-form/client/src/Nav.js b/dapr/quickstarts/tutorials/pub-sub/react-form/client/src/Nav.js new file mode 100644 index 0000000..9c03657 --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/react-form/client/src/Nav.js @@ -0,0 +1,14 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// ------------------------------------------------------------ + +import React from 'react'; + +export function Nav(){ + return( + + ) +} \ No newline at end of file diff --git a/dapr/quickstarts/tutorials/pub-sub/react-form/client/src/index.css b/dapr/quickstarts/tutorials/pub-sub/react-form/client/src/index.css new file mode 100644 index 0000000..cd657c6 --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/react-form/client/src/index.css @@ -0,0 +1,17 @@ +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", + "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +code { + font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", + monospace; +} +/* +form { + padding-top: 20px +} */ diff --git a/dapr/quickstarts/tutorials/pub-sub/react-form/client/src/index.js b/dapr/quickstarts/tutorials/pub-sub/react-form/client/src/index.js new file mode 100644 index 0000000..cb24d90 --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/react-form/client/src/index.js @@ -0,0 +1,25 @@ +// +// Copyright 2021 The Dapr Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import React from 'react'; +import ReactDOM from 'react-dom'; +import './index.css'; +import App from './App'; +import * as serviceWorker from './serviceWorker'; + +ReactDOM.render(, document.getElementById('root')); + +// If you want your app to work offline and load faster, you can change +// unregister() to register() below. Note this comes with some pitfalls. +// Learn more about service workers: https://bit.ly/CRA-PWA +serviceWorker.unregister(); diff --git a/dapr/quickstarts/tutorials/pub-sub/react-form/client/src/logo.svg b/dapr/quickstarts/tutorials/pub-sub/react-form/client/src/logo.svg new file mode 100644 index 0000000..6b60c10 --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/react-form/client/src/logo.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/dapr/quickstarts/tutorials/pub-sub/react-form/client/src/serviceWorker.js b/dapr/quickstarts/tutorials/pub-sub/react-form/client/src/serviceWorker.js new file mode 100644 index 0000000..232ea2e --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/react-form/client/src/serviceWorker.js @@ -0,0 +1,148 @@ +// +// Copyright 2021 The Dapr Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// This optional code is used to register a service worker. +// register() is not called by default. + +// This lets the app load faster on subsequent visits in production, and gives +// it offline capabilities. However, it also means that developers (and users) +// will only see deployed updates on subsequent visits to a page, after all the +// existing tabs open on the page have been closed, since previously cached +// resources are updated in the background. + +// To learn more about the benefits of this model and instructions on how to +// opt-in, read https://bit.ly/CRA-PWA + +const isLocalhost = Boolean( + window.location.hostname === 'localhost' || + // [::1] is the IPv6 localhost address. + window.location.hostname === '[::1]' || + // 127.0.0.1/8 is considered localhost for IPv4. + window.location.hostname.match( + /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ + ) +); + +export function register(config) { + if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { + // The URL constructor is available in all browsers that support SW. + const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); + if (publicUrl.origin !== window.location.origin) { + // Our service worker won't work if PUBLIC_URL is on a different origin + // from what our page is served on. This might happen if a CDN is used to + // serve assets; see https://github.com/facebook/create-react-app/issues/2374 + return; + } + + window.addEventListener('load', () => { + const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; + + if (isLocalhost) { + // This is running on localhost. Let's check if a service worker still exists or not. + checkValidServiceWorker(swUrl, config); + + // Add some additional logging to localhost, pointing developers to the + // service worker/PWA documentation. + navigator.serviceWorker.ready.then(() => { + console.log( + 'This web app is being served cache-first by a service ' + + 'worker. To learn more, visit https://bit.ly/CRA-PWA' + ); + }); + } else { + // Is not localhost. Just register service worker + registerValidSW(swUrl, config); + } + }); + } +} + +function registerValidSW(swUrl, config) { + navigator.serviceWorker + .register(swUrl) + .then(registration => { + registration.onupdatefound = () => { + const installingWorker = registration.installing; + if (installingWorker == null) { + return; + } + installingWorker.onstatechange = () => { + if (installingWorker.state === 'installed') { + if (navigator.serviceWorker.controller) { + // At this point, the updated precached content has been fetched, + // but the previous service worker will still serve the older + // content until all client tabs are closed. + console.log( + 'New content is available and will be used when all ' + + 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' + ); + + // Execute callback + if (config && config.onUpdate) { + config.onUpdate(registration); + } + } else { + // At this point, everything has been precached. + // It's the perfect time to display a + // "Content is cached for offline use." message. + console.log('Content is cached for offline use.'); + + // Execute callback + if (config && config.onSuccess) { + config.onSuccess(registration); + } + } + } + }; + }; + }) + .catch(error => { + console.error('Error during service worker registration:', error); + }); +} + +function checkValidServiceWorker(swUrl, config) { + // Check if the service worker can be found. If it can't reload the page. + fetch(swUrl) + .then(response => { + // Ensure service worker exists, and that we really are getting a JS file. + const contentType = response.headers.get('content-type'); + if ( + response.status === 404 || + (contentType != null && contentType.indexOf('javascript') === -1) + ) { + // No service worker found. Probably a different app. Reload the page. + navigator.serviceWorker.ready.then(registration => { + registration.unregister().then(() => { + window.location.reload(); + }); + }); + } else { + // Service worker found. Proceed as normal. + registerValidSW(swUrl, config); + } + }) + .catch(() => { + console.log( + 'No internet connection found. App is running in offline mode.' + ); + }); +} + +export function unregister() { + if ('serviceWorker' in navigator) { + navigator.serviceWorker.ready.then(registration => { + registration.unregister(); + }); + } +} diff --git a/dapr/quickstarts/tutorials/pub-sub/react-form/client/yarn.lock b/dapr/quickstarts/tutorials/pub-sub/react-form/client/yarn.lock new file mode 100644 index 0000000..21dc81d --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/react-form/client/yarn.lock @@ -0,0 +1,8687 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@apideck/better-ajv-errors@^0.3.1": + "integrity" "sha512-JdEazx7qiVqTBzzBl5rolRwl5cmhihjfIcpqRzIZjtT6b18liVmDn/VlWpqW4C/qP2hrFFMLRV1wlex8ZVBPTg==" + "resolved" "https://registry.npmjs.org/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.2.tgz" + "version" "0.3.2" + dependencies: + "json-schema" "^0.4.0" + "jsonpointer" "^5.0.0" + "leven" "^3.1.0" + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.0", "@babel/code-frame@^7.16.7", "@babel/code-frame@^7.8.3": + "integrity" "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==" + "resolved" "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/highlight" "^7.16.7" + +"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.16.4", "@babel/compat-data@^7.16.8": + "integrity" "sha512-m7OkX0IdKLKPpBlJtF561YJal5y/jyI5fNfWbPxh2D/nbzzGI4qRyrD8xO2jB24u7l+5I2a43scCG2IrfjC50Q==" + "resolved" "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.8.tgz" + "version" "7.16.8" + +"@babel/core@^7.0.0", "@babel/core@^7.0.0-0", "@babel/core@^7.1.0", "@babel/core@^7.11.1", "@babel/core@^7.12.0", "@babel/core@^7.12.3", "@babel/core@^7.13.0", "@babel/core@^7.16.0", "@babel/core@^7.4.0-0", "@babel/core@^7.7.2", "@babel/core@^7.8.0", "@babel/core@>=7.11.0": + "integrity" "sha512-aeLaqcqThRNZYmbMqtulsetOQZ/5gbR/dWruUCJcpas4Qoyy+QeagfDsPdMrqwsPRDNxJvBlRiZxxX7THO7qtA==" + "resolved" "https://registry.npmjs.org/@babel/core/-/core-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/code-frame" "^7.16.7" + "@babel/generator" "^7.16.7" + "@babel/helper-compilation-targets" "^7.16.7" + "@babel/helper-module-transforms" "^7.16.7" + "@babel/helpers" "^7.16.7" + "@babel/parser" "^7.16.7" + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.16.7" + "@babel/types" "^7.16.7" + "convert-source-map" "^1.7.0" + "debug" "^4.1.0" + "gensync" "^1.0.0-beta.2" + "json5" "^2.1.2" + "semver" "^6.3.0" + "source-map" "^0.5.0" + +"@babel/eslint-parser@^7.16.3": + "integrity" "sha512-mUqYa46lgWqHKQ33Q6LNCGp/wPR3eqOYTUixHFsfrSQqRxH0+WOzca75iEjFr5RDGH1dDz622LaHhLOzOuQRUA==" + "resolved" "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.16.5.tgz" + "version" "7.16.5" + dependencies: + "eslint-scope" "^5.1.1" + "eslint-visitor-keys" "^2.1.0" + "semver" "^6.3.0" + +"@babel/generator@^7.16.7", "@babel/generator@^7.16.8", "@babel/generator@^7.7.2": + "integrity" "sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw==" + "resolved" "https://registry.npmjs.org/@babel/generator/-/generator-7.16.8.tgz" + "version" "7.16.8" + dependencies: + "@babel/types" "^7.16.8" + "jsesc" "^2.5.1" + "source-map" "^0.5.0" + +"@babel/helper-annotate-as-pure@^7.16.7": + "integrity" "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==" + "resolved" "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/types" "^7.16.7" + +"@babel/helper-builder-binary-assignment-operator-visitor@^7.16.7": + "integrity" "sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA==" + "resolved" "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-explode-assignable-expression" "^7.16.7" + "@babel/types" "^7.16.7" + +"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.16.7": + "integrity" "sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA==" + "resolved" "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/compat-data" "^7.16.4" + "@babel/helper-validator-option" "^7.16.7" + "browserslist" "^4.17.5" + "semver" "^6.3.0" + +"@babel/helper-create-class-features-plugin@^7.16.7": + "integrity" "sha512-kIFozAvVfK05DM4EVQYKK+zteWvY85BFdGBRQBytRyY3y+6PX0DkDOn/CZ3lEuczCfrCxEzwt0YtP/87YPTWSw==" + "resolved" "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-annotate-as-pure" "^7.16.7" + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-function-name" "^7.16.7" + "@babel/helper-member-expression-to-functions" "^7.16.7" + "@babel/helper-optimise-call-expression" "^7.16.7" + "@babel/helper-replace-supers" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" + +"@babel/helper-create-regexp-features-plugin@^7.16.7": + "integrity" "sha512-fk5A6ymfp+O5+p2yCkXAu5Kyj6v0xh0RBeNcAkYUMDvvAAoxvSKXn+Jb37t/yWFiQVDFK1ELpUTD8/aLhCPu+g==" + "resolved" "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-annotate-as-pure" "^7.16.7" + "regexpu-core" "^4.7.1" + +"@babel/helper-define-polyfill-provider@^0.3.0": + "integrity" "sha512-7hfT8lUljl/tM3h+izTX/pO3W3frz2ok6Pk+gzys8iJqDfZrZy2pXjRTZAvG2YmfHun1X4q8/UZRLatMfqc5Tg==" + "resolved" "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.0.tgz" + "version" "0.3.0" + dependencies: + "@babel/helper-compilation-targets" "^7.13.0" + "@babel/helper-module-imports" "^7.12.13" + "@babel/helper-plugin-utils" "^7.13.0" + "@babel/traverse" "^7.13.0" + "debug" "^4.1.1" + "lodash.debounce" "^4.0.8" + "resolve" "^1.14.2" + "semver" "^6.1.2" + +"@babel/helper-environment-visitor@^7.16.7": + "integrity" "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==" + "resolved" "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/types" "^7.16.7" + +"@babel/helper-explode-assignable-expression@^7.16.7": + "integrity" "sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ==" + "resolved" "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/types" "^7.16.7" + +"@babel/helper-function-name@^7.16.7": + "integrity" "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==" + "resolved" "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-get-function-arity" "^7.16.7" + "@babel/template" "^7.16.7" + "@babel/types" "^7.16.7" + +"@babel/helper-get-function-arity@^7.16.7": + "integrity" "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==" + "resolved" "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/types" "^7.16.7" + +"@babel/helper-hoist-variables@^7.16.7": + "integrity" "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==" + "resolved" "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/types" "^7.16.7" + +"@babel/helper-member-expression-to-functions@^7.16.7": + "integrity" "sha512-VtJ/65tYiU/6AbMTDwyoXGPKHgTsfRarivm+YbB5uAzKUyuPjgZSgAFeG87FCigc7KNHu2Pegh1XIT3lXjvz3Q==" + "resolved" "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/types" "^7.16.7" + +"@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.16.7": + "integrity" "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==" + "resolved" "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/types" "^7.16.7" + +"@babel/helper-module-transforms@^7.16.7": + "integrity" "sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng==" + "resolved" "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-module-imports" "^7.16.7" + "@babel/helper-simple-access" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" + "@babel/helper-validator-identifier" "^7.16.7" + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.16.7" + "@babel/types" "^7.16.7" + +"@babel/helper-optimise-call-expression@^7.16.7": + "integrity" "sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==" + "resolved" "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/types" "^7.16.7" + +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": + "integrity" "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==" + "resolved" "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz" + "version" "7.16.7" + +"@babel/helper-remap-async-to-generator@^7.16.8": + "integrity" "sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw==" + "resolved" "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz" + "version" "7.16.8" + dependencies: + "@babel/helper-annotate-as-pure" "^7.16.7" + "@babel/helper-wrap-function" "^7.16.8" + "@babel/types" "^7.16.8" + +"@babel/helper-replace-supers@^7.16.7": + "integrity" "sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw==" + "resolved" "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-member-expression-to-functions" "^7.16.7" + "@babel/helper-optimise-call-expression" "^7.16.7" + "@babel/traverse" "^7.16.7" + "@babel/types" "^7.16.7" + +"@babel/helper-simple-access@^7.16.7": + "integrity" "sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g==" + "resolved" "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/types" "^7.16.7" + +"@babel/helper-skip-transparent-expression-wrappers@^7.16.0": + "integrity" "sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==" + "resolved" "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz" + "version" "7.16.0" + dependencies: + "@babel/types" "^7.16.0" + +"@babel/helper-split-export-declaration@^7.16.7": + "integrity" "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==" + "resolved" "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/types" "^7.16.7" + +"@babel/helper-validator-identifier@^7.16.7": + "integrity" "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==" + "resolved" "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz" + "version" "7.16.7" + +"@babel/helper-validator-option@^7.16.7": + "integrity" "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==" + "resolved" "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz" + "version" "7.16.7" + +"@babel/helper-wrap-function@^7.16.8": + "integrity" "sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw==" + "resolved" "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz" + "version" "7.16.8" + dependencies: + "@babel/helper-function-name" "^7.16.7" + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.16.8" + "@babel/types" "^7.16.8" + +"@babel/helpers@^7.16.7": + "integrity" "sha512-9ZDoqtfY7AuEOt3cxchfii6C7GDyyMBffktR5B2jvWv8u2+efwvpnVKXMWzNehqy68tKgAfSwfdw/lWpthS2bw==" + "resolved" "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.16.7" + "@babel/types" "^7.16.7" + +"@babel/highlight@^7.16.7": + "integrity" "sha512-aKpPMfLvGO3Q97V0qhw/V2SWNWlwfJknuwAunU7wZLSfrM4xTBvg7E5opUVi1kJTBKihE38CPg4nBiqX83PWYw==" + "resolved" "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-validator-identifier" "^7.16.7" + "chalk" "^2.0.0" + "js-tokens" "^4.0.0" + +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.16.7", "@babel/parser@^7.16.8": + "integrity" "sha512-i7jDUfrVBWc+7OKcBzEe5n7fbv3i2fWtxKzzCvOjnzSxMfWMigAhtfJ7qzZNGFNMsCCd67+uz553dYKWXPvCKw==" + "resolved" "https://registry.npmjs.org/@babel/parser/-/parser-7.16.8.tgz" + "version" "7.16.8" + +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.16.7": + "integrity" "sha512-anv/DObl7waiGEnC24O9zqL0pSuI9hljihqiDuFHC8d7/bjr/4RLGPWuc8rYOff/QPzbEPSkzG8wGG9aDuhHRg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.16.7": + "integrity" "sha512-di8vUHRdf+4aJ7ltXhaDbPoszdkh59AQtJM5soLsuHpQJdFQZOA4uGj0V2u/CZ8bJ/u8ULDL5yq6FO/bCXnKHw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" + "@babel/plugin-proposal-optional-chaining" "^7.16.7" + +"@babel/plugin-proposal-async-generator-functions@^7.16.8": + "integrity" "sha512-71YHIvMuiuqWJQkebWJtdhQTfd4Q4mF76q2IX37uZPkG9+olBxsX+rH1vkhFto4UeJZ9dPY2s+mDvhDm1u2BGQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.16.8.tgz" + "version" "7.16.8" + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-remap-async-to-generator" "^7.16.8" + "@babel/plugin-syntax-async-generators" "^7.8.4" + +"@babel/plugin-proposal-class-properties@^7.16.0", "@babel/plugin-proposal-class-properties@^7.16.7": + "integrity" "sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-create-class-features-plugin" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-proposal-class-static-block@^7.16.7": + "integrity" "sha512-dgqJJrcZoG/4CkMopzhPJjGxsIe9A8RlkQLnL/Vhhx8AA9ZuaRwGSlscSh42hazc7WSrya/IK7mTeoF0DP9tEw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-create-class-features-plugin" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + +"@babel/plugin-proposal-decorators@^7.16.4": + "integrity" "sha512-DoEpnuXK14XV9btI1k8tzNGCutMclpj4yru8aXKoHlVmbO1s+2A+g2+h4JhcjrxkFJqzbymnLG6j/niOf3iFXQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-create-class-features-plugin" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-decorators" "^7.16.7" + +"@babel/plugin-proposal-dynamic-import@^7.16.7": + "integrity" "sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + +"@babel/plugin-proposal-export-namespace-from@^7.16.7": + "integrity" "sha512-ZxdtqDXLRGBL64ocZcs7ovt71L3jhC1RGSyR996svrCi3PYqHNkb3SwPJCs8RIzD86s+WPpt2S73+EHCGO+NUA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + +"@babel/plugin-proposal-json-strings@^7.16.7": + "integrity" "sha512-lNZ3EEggsGY78JavgbHsK9u5P3pQaW7k4axlgFLYkMd7UBsiNahCITShLjNQschPyjtO6dADrL24757IdhBrsQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-json-strings" "^7.8.3" + +"@babel/plugin-proposal-logical-assignment-operators@^7.16.7": + "integrity" "sha512-K3XzyZJGQCr00+EtYtrDjmwX7o7PLK6U9bi1nCwkQioRFVUv6dJoxbQjtWVtP+bCPy82bONBKG8NPyQ4+i6yjg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + +"@babel/plugin-proposal-nullish-coalescing-operator@^7.16.0", "@babel/plugin-proposal-nullish-coalescing-operator@^7.16.7": + "integrity" "sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + +"@babel/plugin-proposal-numeric-separator@^7.16.0", "@babel/plugin-proposal-numeric-separator@^7.16.7": + "integrity" "sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + +"@babel/plugin-proposal-object-rest-spread@^7.16.7": + "integrity" "sha512-3O0Y4+dw94HA86qSg9IHfyPktgR7q3gpNVAeiKQd+8jBKFaU5NQS1Yatgo4wY+UFNuLjvxcSmzcsHqrhgTyBUA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/compat-data" "^7.16.4" + "@babel/helper-compilation-targets" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-transform-parameters" "^7.16.7" + +"@babel/plugin-proposal-optional-catch-binding@^7.16.7": + "integrity" "sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + +"@babel/plugin-proposal-optional-chaining@^7.16.0", "@babel/plugin-proposal-optional-chaining@^7.16.7": + "integrity" "sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + +"@babel/plugin-proposal-private-methods@^7.16.0", "@babel/plugin-proposal-private-methods@^7.16.7": + "integrity" "sha512-7twV3pzhrRxSwHeIvFE6coPgvo+exNDOiGUMg39o2LiLo1Y+4aKpfkcLGcg1UHonzorCt7SNXnoMyCnnIOA8Sw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-create-class-features-plugin" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-proposal-private-property-in-object@^7.16.7": + "integrity" "sha512-rMQkjcOFbm+ufe3bTZLyOfsOUOxyvLXZJCTARhJr+8UMSoZmqTe1K1BgkFcrW37rAchWg57yI69ORxiWvUINuQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-annotate-as-pure" "^7.16.7" + "@babel/helper-create-class-features-plugin" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + +"@babel/plugin-proposal-unicode-property-regex@^7.16.7", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": + "integrity" "sha512-QRK0YI/40VLhNVGIjRNAAQkEHws0cswSdFFjpFyt943YmJIU1da9uW63Iu6NFV6CxTZW5eTDCrwZUstBWgp/Rg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-syntax-async-generators@^7.8.4": + "integrity" "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz" + "version" "7.8.4" + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-bigint@^7.8.3": + "integrity" "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz" + "version" "7.8.3" + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-class-properties@^7.12.13", "@babel/plugin-syntax-class-properties@^7.8.3": + "integrity" "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz" + "version" "7.12.13" + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-syntax-class-static-block@^7.14.5": + "integrity" "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz" + "version" "7.14.5" + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-decorators@^7.16.7": + "integrity" "sha512-vQ+PxL+srA7g6Rx6I1e15m55gftknl2X8GCUW1JTlkTaXZLJOS0UcaY0eK9jYT7IYf4awn6qwyghVHLDz1WyMw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-syntax-dynamic-import@^7.8.3": + "integrity" "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz" + "version" "7.8.3" + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-export-namespace-from@^7.8.3": + "integrity" "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz" + "version" "7.8.3" + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-syntax-flow@^7.14.5", "@babel/plugin-syntax-flow@^7.16.7": + "integrity" "sha512-UDo3YGQO0jH6ytzVwgSLv9i/CzMcUjbKenL67dTrAZPPv6GFAtDhe6jqnvmoKzC/7htNTohhos+onPtDMqJwaQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-syntax-import-meta@^7.8.3": + "integrity" "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz" + "version" "7.10.4" + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-json-strings@^7.8.3": + "integrity" "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz" + "version" "7.8.3" + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-jsx@^7.16.7": + "integrity" "sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-syntax-logical-assignment-operators@^7.10.4", "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": + "integrity" "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz" + "version" "7.10.4" + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": + "integrity" "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz" + "version" "7.8.3" + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-numeric-separator@^7.10.4", "@babel/plugin-syntax-numeric-separator@^7.8.3": + "integrity" "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz" + "version" "7.10.4" + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-object-rest-spread@^7.8.3": + "integrity" "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz" + "version" "7.8.3" + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-catch-binding@^7.8.3": + "integrity" "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz" + "version" "7.8.3" + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-chaining@^7.8.3": + "integrity" "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz" + "version" "7.8.3" + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-private-property-in-object@^7.14.5": + "integrity" "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz" + "version" "7.14.5" + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-top-level-await@^7.14.5", "@babel/plugin-syntax-top-level-await@^7.8.3": + "integrity" "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz" + "version" "7.14.5" + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-typescript@^7.16.7", "@babel/plugin-syntax-typescript@^7.7.2": + "integrity" "sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-arrow-functions@^7.16.7": + "integrity" "sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-async-to-generator@^7.16.8": + "integrity" "sha512-MtmUmTJQHCnyJVrScNzNlofQJ3dLFuobYn3mwOTKHnSCMtbNsqvF71GQmJfFjdrXSsAA7iysFmYWw4bXZ20hOg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.16.8.tgz" + "version" "7.16.8" + dependencies: + "@babel/helper-module-imports" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-remap-async-to-generator" "^7.16.8" + +"@babel/plugin-transform-block-scoped-functions@^7.16.7": + "integrity" "sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-block-scoping@^7.16.7": + "integrity" "sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-classes@^7.16.7": + "integrity" "sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-annotate-as-pure" "^7.16.7" + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-function-name" "^7.16.7" + "@babel/helper-optimise-call-expression" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-replace-supers" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" + "globals" "^11.1.0" + +"@babel/plugin-transform-computed-properties@^7.16.7": + "integrity" "sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-destructuring@^7.16.7": + "integrity" "sha512-VqAwhTHBnu5xBVDCvrvqJbtLUa++qZaWC0Fgr2mqokBlulZARGyIvZDoqbPlPaKImQ9dKAcCzbv+ul//uqu70A==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-dotall-regex@^7.16.7", "@babel/plugin-transform-dotall-regex@^7.4.4": + "integrity" "sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-duplicate-keys@^7.16.7": + "integrity" "sha512-03DvpbRfvWIXyK0/6QiR1KMTWeT6OcQ7tbhjrXyFS02kjuX/mu5Bvnh5SDSWHxyawit2g5aWhKwI86EE7GUnTw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-exponentiation-operator@^7.16.7": + "integrity" "sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-flow-strip-types@^7.16.0": + "integrity" "sha512-mzmCq3cNsDpZZu9FADYYyfZJIOrSONmHcop2XEKPdBNMa4PDC4eEvcOvzZaCNcjKu72v0XQlA5y1g58aLRXdYg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-flow" "^7.16.7" + +"@babel/plugin-transform-for-of@^7.16.7": + "integrity" "sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-function-name@^7.16.7": + "integrity" "sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-compilation-targets" "^7.16.7" + "@babel/helper-function-name" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-literals@^7.16.7": + "integrity" "sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-member-expression-literals@^7.16.7": + "integrity" "sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-modules-amd@^7.16.7": + "integrity" "sha512-KaaEtgBL7FKYwjJ/teH63oAmE3lP34N3kshz8mm4VMAw7U3PxjVwwUmxEFksbgsNUaO3wId9R2AVQYSEGRa2+g==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-module-transforms" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "babel-plugin-dynamic-import-node" "^2.3.3" + +"@babel/plugin-transform-modules-commonjs@^7.16.8": + "integrity" "sha512-oflKPvsLT2+uKQopesJt3ApiaIS2HW+hzHFcwRNtyDGieAeC/dIHZX8buJQ2J2X1rxGPy4eRcUijm3qcSPjYcA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.8.tgz" + "version" "7.16.8" + dependencies: + "@babel/helper-module-transforms" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-simple-access" "^7.16.7" + "babel-plugin-dynamic-import-node" "^2.3.3" + +"@babel/plugin-transform-modules-systemjs@^7.16.7": + "integrity" "sha512-DuK5E3k+QQmnOqBR9UkusByy5WZWGRxfzV529s9nPra1GE7olmxfqO2FHobEOYSPIjPBTr4p66YDcjQnt8cBmw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-hoist-variables" "^7.16.7" + "@babel/helper-module-transforms" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-validator-identifier" "^7.16.7" + "babel-plugin-dynamic-import-node" "^2.3.3" + +"@babel/plugin-transform-modules-umd@^7.16.7": + "integrity" "sha512-EMh7uolsC8O4xhudF2F6wedbSHm1HHZ0C6aJ7K67zcDNidMzVcxWdGr+htW9n21klm+bOn+Rx4CBsAntZd3rEQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-module-transforms" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-named-capturing-groups-regex@^7.16.8": + "integrity" "sha512-j3Jw+n5PvpmhRR+mrgIh04puSANCk/T/UA3m3P1MjJkhlK906+ApHhDIqBQDdOgL/r1UYpz4GNclTXxyZrYGSw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.8.tgz" + "version" "7.16.8" + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.16.7" + +"@babel/plugin-transform-new-target@^7.16.7": + "integrity" "sha512-xiLDzWNMfKoGOpc6t3U+etCE2yRnn3SM09BXqWPIZOBpL2gvVrBWUKnsJx0K/ADi5F5YC5f8APFfWrz25TdlGg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-object-super@^7.16.7": + "integrity" "sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-replace-supers" "^7.16.7" + +"@babel/plugin-transform-parameters@^7.16.7": + "integrity" "sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-property-literals@^7.16.7": + "integrity" "sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-react-constant-elements@^7.12.1": + "integrity" "sha512-lF+cfsyTgwWkcw715J88JhMYJ5GpysYNLhLP1PkvkhTRN7B3e74R/1KsDxFxhRpSn0UUD3IWM4GvdBR2PEbbQQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-react-display-name@^7.16.0", "@babel/plugin-transform-react-display-name@^7.16.7": + "integrity" "sha512-qgIg8BcZgd0G/Cz916D5+9kqX0c7nPZyXaP8R2tLNN5tkyIZdG5fEwBrxwplzSnjC1jvQmyMNVwUCZPcbGY7Pg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-react-jsx-development@^7.16.7": + "integrity" "sha512-RMvQWvpla+xy6MlBpPlrKZCMRs2AGiHOGHY3xRwl0pEeim348dDyxeH4xBsMPbIMhujeq7ihE702eM2Ew0Wo+A==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/plugin-transform-react-jsx" "^7.16.7" + +"@babel/plugin-transform-react-jsx@^7.14.9", "@babel/plugin-transform-react-jsx@^7.16.7": + "integrity" "sha512-8D16ye66fxiE8m890w0BpPpngG9o9OVBBy0gH2E+2AR7qMR2ZpTYJEqLxAsoroenMId0p/wMW+Blc0meDgu0Ag==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-annotate-as-pure" "^7.16.7" + "@babel/helper-module-imports" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-jsx" "^7.16.7" + "@babel/types" "^7.16.7" + +"@babel/plugin-transform-react-pure-annotations@^7.16.7": + "integrity" "sha512-hs71ToC97k3QWxswh2ElzMFABXHvGiJ01IB1TbYQDGeWRKWz/MPUTh5jGExdHvosYKpnJW5Pm3S4+TA3FyX+GA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-annotate-as-pure" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-regenerator@^7.16.7": + "integrity" "sha512-mF7jOgGYCkSJagJ6XCujSQg+6xC1M77/03K2oBmVJWoFGNUtnVJO4WHKJk3dnPC8HCcj4xBQP1Egm8DWh3Pb3Q==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "regenerator-transform" "^0.14.2" + +"@babel/plugin-transform-reserved-words@^7.16.7": + "integrity" "sha512-KQzzDnZ9hWQBjwi5lpY5v9shmm6IVG0U9pB18zvMu2i4H90xpT4gmqwPYsn8rObiadYe2M0gmgsiOIF5A/2rtg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-runtime@^7.16.4": + "integrity" "sha512-6Kg2XHPFnIarNweZxmzbgYnnWsXxkx9WQUVk2sksBRL80lBC1RAQV3wQagWxdCHiYHqPN+oenwNIuttlYgIbQQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.16.8.tgz" + "version" "7.16.8" + dependencies: + "@babel/helper-module-imports" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "babel-plugin-polyfill-corejs2" "^0.3.0" + "babel-plugin-polyfill-corejs3" "^0.5.0" + "babel-plugin-polyfill-regenerator" "^0.3.0" + "semver" "^6.3.0" + +"@babel/plugin-transform-shorthand-properties@^7.16.7": + "integrity" "sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-spread@^7.16.7": + "integrity" "sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" + +"@babel/plugin-transform-sticky-regex@^7.16.7": + "integrity" "sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-template-literals@^7.16.7": + "integrity" "sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-typeof-symbol@^7.16.7": + "integrity" "sha512-p2rOixCKRJzpg9JB4gjnG4gjWkWa89ZoYUnl9snJ1cWIcTH/hvxZqfO+WjG6T8DRBpctEol5jw1O5rA8gkCokQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-typescript@^7.16.7": + "integrity" "sha512-bHdQ9k7YpBDO2d0NVfkj51DpQcvwIzIusJ7mEUaMlbZq3Kt/U47j24inXZHQ5MDiYpCs+oZiwnXyKedE8+q7AQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.16.8.tgz" + "version" "7.16.8" + dependencies: + "@babel/helper-create-class-features-plugin" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-typescript" "^7.16.7" + +"@babel/plugin-transform-unicode-escapes@^7.16.7": + "integrity" "sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-unicode-regex@^7.16.7": + "integrity" "sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/preset-env@^7.11.0", "@babel/preset-env@^7.12.1", "@babel/preset-env@^7.16.4": + "integrity" "sha512-9rNKgVCdwHb3z1IlbMyft6yIXIeP3xz6vWvGaLHrJThuEIqWfHb0DNBH9VuTgnDfdbUDhkmkvMZS/YMCtP7Elg==" + "resolved" "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.16.8.tgz" + "version" "7.16.8" + dependencies: + "@babel/compat-data" "^7.16.8" + "@babel/helper-compilation-targets" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-validator-option" "^7.16.7" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.16.7" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.16.7" + "@babel/plugin-proposal-async-generator-functions" "^7.16.8" + "@babel/plugin-proposal-class-properties" "^7.16.7" + "@babel/plugin-proposal-class-static-block" "^7.16.7" + "@babel/plugin-proposal-dynamic-import" "^7.16.7" + "@babel/plugin-proposal-export-namespace-from" "^7.16.7" + "@babel/plugin-proposal-json-strings" "^7.16.7" + "@babel/plugin-proposal-logical-assignment-operators" "^7.16.7" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.16.7" + "@babel/plugin-proposal-numeric-separator" "^7.16.7" + "@babel/plugin-proposal-object-rest-spread" "^7.16.7" + "@babel/plugin-proposal-optional-catch-binding" "^7.16.7" + "@babel/plugin-proposal-optional-chaining" "^7.16.7" + "@babel/plugin-proposal-private-methods" "^7.16.7" + "@babel/plugin-proposal-private-property-in-object" "^7.16.7" + "@babel/plugin-proposal-unicode-property-regex" "^7.16.7" + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-class-properties" "^7.12.13" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + "@babel/plugin-syntax-top-level-await" "^7.14.5" + "@babel/plugin-transform-arrow-functions" "^7.16.7" + "@babel/plugin-transform-async-to-generator" "^7.16.8" + "@babel/plugin-transform-block-scoped-functions" "^7.16.7" + "@babel/plugin-transform-block-scoping" "^7.16.7" + "@babel/plugin-transform-classes" "^7.16.7" + "@babel/plugin-transform-computed-properties" "^7.16.7" + "@babel/plugin-transform-destructuring" "^7.16.7" + "@babel/plugin-transform-dotall-regex" "^7.16.7" + "@babel/plugin-transform-duplicate-keys" "^7.16.7" + "@babel/plugin-transform-exponentiation-operator" "^7.16.7" + "@babel/plugin-transform-for-of" "^7.16.7" + "@babel/plugin-transform-function-name" "^7.16.7" + "@babel/plugin-transform-literals" "^7.16.7" + "@babel/plugin-transform-member-expression-literals" "^7.16.7" + "@babel/plugin-transform-modules-amd" "^7.16.7" + "@babel/plugin-transform-modules-commonjs" "^7.16.8" + "@babel/plugin-transform-modules-systemjs" "^7.16.7" + "@babel/plugin-transform-modules-umd" "^7.16.7" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.16.8" + "@babel/plugin-transform-new-target" "^7.16.7" + "@babel/plugin-transform-object-super" "^7.16.7" + "@babel/plugin-transform-parameters" "^7.16.7" + "@babel/plugin-transform-property-literals" "^7.16.7" + "@babel/plugin-transform-regenerator" "^7.16.7" + "@babel/plugin-transform-reserved-words" "^7.16.7" + "@babel/plugin-transform-shorthand-properties" "^7.16.7" + "@babel/plugin-transform-spread" "^7.16.7" + "@babel/plugin-transform-sticky-regex" "^7.16.7" + "@babel/plugin-transform-template-literals" "^7.16.7" + "@babel/plugin-transform-typeof-symbol" "^7.16.7" + "@babel/plugin-transform-unicode-escapes" "^7.16.7" + "@babel/plugin-transform-unicode-regex" "^7.16.7" + "@babel/preset-modules" "^0.1.5" + "@babel/types" "^7.16.8" + "babel-plugin-polyfill-corejs2" "^0.3.0" + "babel-plugin-polyfill-corejs3" "^0.5.0" + "babel-plugin-polyfill-regenerator" "^0.3.0" + "core-js-compat" "^3.20.2" + "semver" "^6.3.0" + +"@babel/preset-modules@^0.1.5": + "integrity" "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==" + "resolved" "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz" + "version" "0.1.5" + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" + "@babel/plugin-transform-dotall-regex" "^7.4.4" + "@babel/types" "^7.4.4" + "esutils" "^2.0.2" + +"@babel/preset-react@^7.12.5", "@babel/preset-react@^7.16.0": + "integrity" "sha512-fWpyI8UM/HE6DfPBzD8LnhQ/OcH8AgTaqcqP2nGOXEUV+VKBR5JRN9hCk9ai+zQQ57vtm9oWeXguBCPNUjytgA==" + "resolved" "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-validator-option" "^7.16.7" + "@babel/plugin-transform-react-display-name" "^7.16.7" + "@babel/plugin-transform-react-jsx" "^7.16.7" + "@babel/plugin-transform-react-jsx-development" "^7.16.7" + "@babel/plugin-transform-react-pure-annotations" "^7.16.7" + +"@babel/preset-typescript@^7.16.0": + "integrity" "sha512-WbVEmgXdIyvzB77AQjGBEyYPZx+8tTsO50XtfozQrkW8QB2rLJpH2lgx0TRw5EJrBxOZQ+wCcyPVQvS8tjEHpQ==" + "resolved" "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-validator-option" "^7.16.7" + "@babel/plugin-transform-typescript" "^7.16.7" + +"@babel/runtime-corejs3@^7.10.2": + "integrity" "sha512-3fKhuICS1lMz0plI5ktOE/yEtBRMVxplzRkdn6mJQ197XiY0JnrzYV0+Mxozq3JZ8SBV9Ecurmw1XsGbwOf+Sg==" + "resolved" "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.16.8.tgz" + "version" "7.16.8" + dependencies: + "core-js-pure" "^3.20.2" + "regenerator-runtime" "^0.13.4" + +"@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.8.4": + "integrity" "sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ==" + "resolved" "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "regenerator-runtime" "^0.13.4" + +"@babel/template@^7.16.7", "@babel/template@^7.3.3": + "integrity" "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==" + "resolved" "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz" + "version" "7.16.7" + dependencies: + "@babel/code-frame" "^7.16.7" + "@babel/parser" "^7.16.7" + "@babel/types" "^7.16.7" + +"@babel/traverse@^7.13.0", "@babel/traverse@^7.16.7", "@babel/traverse@^7.16.8", "@babel/traverse@^7.7.2": + "integrity" "sha512-xe+H7JlvKsDQwXRsBhSnq1/+9c+LlQcCK3Tn/l5sbx02HYns/cn7ibp9+RV1sIUqu7hKg91NWsgHurO9dowITQ==" + "resolved" "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.8.tgz" + "version" "7.16.8" + dependencies: + "@babel/code-frame" "^7.16.7" + "@babel/generator" "^7.16.8" + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-function-name" "^7.16.7" + "@babel/helper-hoist-variables" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" + "@babel/parser" "^7.16.8" + "@babel/types" "^7.16.8" + "debug" "^4.1.0" + "globals" "^11.1.0" + +"@babel/types@^7.0.0", "@babel/types@^7.12.6", "@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.16.8", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": + "integrity" "sha512-smN2DQc5s4M7fntyjGtyIPbRJv6wW4rU/94fmYJ7PKQuZkC0qGMHXJbg6sNGt12JmVr4k5YaptI/XtiLJBnmIg==" + "resolved" "https://registry.npmjs.org/@babel/types/-/types-7.16.8.tgz" + "version" "7.16.8" + dependencies: + "@babel/helper-validator-identifier" "^7.16.7" + "to-fast-properties" "^2.0.0" + +"@bcoe/v8-coverage@^0.2.3": + "integrity" "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==" + "resolved" "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz" + "version" "0.2.3" + +"@csstools/normalize.css@*": + "integrity" "sha512-M0qqxAcwCsIVfpFQSlGN5XjXWu8l5JDZN+fPt1LeW5SZexQTgnaEvgXAY+CeygRw0EeppWHi12JxESWiWrB0Sg==" + "resolved" "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-12.0.0.tgz" + "version" "12.0.0" + +"@eslint/eslintrc@^1.0.5": + "integrity" "sha512-BLxsnmK3KyPunz5wmCCpqy0YelEoxxGmH73Is+Z74oOTMtExcjkr3dDR6quwrjh1YspA8DH9gnX1o069KiS9AQ==" + "resolved" "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.5.tgz" + "version" "1.0.5" + dependencies: + "ajv" "^6.12.4" + "debug" "^4.3.2" + "espree" "^9.2.0" + "globals" "^13.9.0" + "ignore" "^4.0.6" + "import-fresh" "^3.2.1" + "js-yaml" "^4.1.0" + "minimatch" "^3.0.4" + "strip-json-comments" "^3.1.1" + +"@humanwhocodes/config-array@^0.9.2": + "integrity" "sha512-UXOuFCGcwciWckOpmfKDq/GyhlTf9pN/BzG//x8p8zTOFEcGuA68ANXheFS0AGvy3qgZqLBUkMs7hqzqCKOVwA==" + "resolved" "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.2.tgz" + "version" "0.9.2" + dependencies: + "@humanwhocodes/object-schema" "^1.2.1" + "debug" "^4.1.1" + "minimatch" "^3.0.4" + +"@humanwhocodes/object-schema@^1.2.1": + "integrity" "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==" + "resolved" "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz" + "version" "1.2.1" + +"@istanbuljs/load-nyc-config@^1.0.0": + "integrity" "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==" + "resolved" "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz" + "version" "1.1.0" + dependencies: + "camelcase" "^5.3.1" + "find-up" "^4.1.0" + "get-package-type" "^0.1.0" + "js-yaml" "^3.13.1" + "resolve-from" "^5.0.0" + +"@istanbuljs/schema@^0.1.2": + "integrity" "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==" + "resolved" "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz" + "version" "0.1.3" + +"@jest/console@^27.4.6": + "integrity" "sha512-jauXyacQD33n47A44KrlOVeiXHEXDqapSdfb9kTekOchH/Pd18kBIO1+xxJQRLuG+LUuljFCwTG92ra4NW7SpA==" + "resolved" "https://registry.npmjs.org/@jest/console/-/console-27.4.6.tgz" + "version" "27.4.6" + dependencies: + "@jest/types" "^27.4.2" + "@types/node" "*" + "chalk" "^4.0.0" + "jest-message-util" "^27.4.6" + "jest-util" "^27.4.2" + "slash" "^3.0.0" + +"@jest/core@^27.4.7": + "integrity" "sha512-n181PurSJkVMS+kClIFSX/LLvw9ExSb+4IMtD6YnfxZVerw9ANYtW0bPrm0MJu2pfe9SY9FJ9FtQ+MdZkrZwjg==" + "resolved" "https://registry.npmjs.org/@jest/core/-/core-27.4.7.tgz" + "version" "27.4.7" + dependencies: + "@jest/console" "^27.4.6" + "@jest/reporters" "^27.4.6" + "@jest/test-result" "^27.4.6" + "@jest/transform" "^27.4.6" + "@jest/types" "^27.4.2" + "@types/node" "*" + "ansi-escapes" "^4.2.1" + "chalk" "^4.0.0" + "emittery" "^0.8.1" + "exit" "^0.1.2" + "graceful-fs" "^4.2.4" + "jest-changed-files" "^27.4.2" + "jest-config" "^27.4.7" + "jest-haste-map" "^27.4.6" + "jest-message-util" "^27.4.6" + "jest-regex-util" "^27.4.0" + "jest-resolve" "^27.4.6" + "jest-resolve-dependencies" "^27.4.6" + "jest-runner" "^27.4.6" + "jest-runtime" "^27.4.6" + "jest-snapshot" "^27.4.6" + "jest-util" "^27.4.2" + "jest-validate" "^27.4.6" + "jest-watcher" "^27.4.6" + "micromatch" "^4.0.4" + "rimraf" "^3.0.0" + "slash" "^3.0.0" + "strip-ansi" "^6.0.0" + +"@jest/environment@^27.4.6": + "integrity" "sha512-E6t+RXPfATEEGVidr84WngLNWZ8ffCPky8RqqRK6u1Bn0LK92INe0MDttyPl/JOzaq92BmDzOeuqk09TvM22Sg==" + "resolved" "https://registry.npmjs.org/@jest/environment/-/environment-27.4.6.tgz" + "version" "27.4.6" + dependencies: + "@jest/fake-timers" "^27.4.6" + "@jest/types" "^27.4.2" + "@types/node" "*" + "jest-mock" "^27.4.6" + +"@jest/fake-timers@^27.4.6": + "integrity" "sha512-mfaethuYF8scV8ntPpiVGIHQgS0XIALbpY2jt2l7wb/bvq4Q5pDLk4EP4D7SAvYT1QrPOPVZAtbdGAOOyIgs7A==" + "resolved" "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.4.6.tgz" + "version" "27.4.6" + dependencies: + "@jest/types" "^27.4.2" + "@sinonjs/fake-timers" "^8.0.1" + "@types/node" "*" + "jest-message-util" "^27.4.6" + "jest-mock" "^27.4.6" + "jest-util" "^27.4.2" + +"@jest/globals@^27.4.6": + "integrity" "sha512-kAiwMGZ7UxrgPzu8Yv9uvWmXXxsy0GciNejlHvfPIfWkSxChzv6bgTS3YqBkGuHcis+ouMFI2696n2t+XYIeFw==" + "resolved" "https://registry.npmjs.org/@jest/globals/-/globals-27.4.6.tgz" + "version" "27.4.6" + dependencies: + "@jest/environment" "^27.4.6" + "@jest/types" "^27.4.2" + "expect" "^27.4.6" + +"@jest/reporters@^27.4.6": + "integrity" "sha512-+Zo9gV81R14+PSq4wzee4GC2mhAN9i9a7qgJWL90Gpx7fHYkWpTBvwWNZUXvJByYR9tAVBdc8VxDWqfJyIUrIQ==" + "resolved" "https://registry.npmjs.org/@jest/reporters/-/reporters-27.4.6.tgz" + "version" "27.4.6" + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@jest/console" "^27.4.6" + "@jest/test-result" "^27.4.6" + "@jest/transform" "^27.4.6" + "@jest/types" "^27.4.2" + "@types/node" "*" + "chalk" "^4.0.0" + "collect-v8-coverage" "^1.0.0" + "exit" "^0.1.2" + "glob" "^7.1.2" + "graceful-fs" "^4.2.4" + "istanbul-lib-coverage" "^3.0.0" + "istanbul-lib-instrument" "^5.1.0" + "istanbul-lib-report" "^3.0.0" + "istanbul-lib-source-maps" "^4.0.0" + "istanbul-reports" "^3.1.3" + "jest-haste-map" "^27.4.6" + "jest-resolve" "^27.4.6" + "jest-util" "^27.4.2" + "jest-worker" "^27.4.6" + "slash" "^3.0.0" + "source-map" "^0.6.0" + "string-length" "^4.0.1" + "terminal-link" "^2.0.0" + "v8-to-istanbul" "^8.1.0" + +"@jest/source-map@^27.4.0": + "integrity" "sha512-Ntjx9jzP26Bvhbm93z/AKcPRj/9wrkI88/gK60glXDx1q+IeI0rf7Lw2c89Ch6ofonB0On/iRDreQuQ6te9pgQ==" + "resolved" "https://registry.npmjs.org/@jest/source-map/-/source-map-27.4.0.tgz" + "version" "27.4.0" + dependencies: + "callsites" "^3.0.0" + "graceful-fs" "^4.2.4" + "source-map" "^0.6.0" + +"@jest/test-result@^27.4.6": + "integrity" "sha512-fi9IGj3fkOrlMmhQqa/t9xum8jaJOOAi/lZlm6JXSc55rJMXKHxNDN1oCP39B0/DhNOa2OMupF9BcKZnNtXMOQ==" + "resolved" "https://registry.npmjs.org/@jest/test-result/-/test-result-27.4.6.tgz" + "version" "27.4.6" + dependencies: + "@jest/console" "^27.4.6" + "@jest/types" "^27.4.2" + "@types/istanbul-lib-coverage" "^2.0.0" + "collect-v8-coverage" "^1.0.0" + +"@jest/test-sequencer@^27.4.6": + "integrity" "sha512-3GL+nsf6E1PsyNsJuvPyIz+DwFuCtBdtvPpm/LMXVkBJbdFvQYCDpccYT56qq5BGniXWlE81n2qk1sdXfZebnw==" + "resolved" "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.4.6.tgz" + "version" "27.4.6" + dependencies: + "@jest/test-result" "^27.4.6" + "graceful-fs" "^4.2.4" + "jest-haste-map" "^27.4.6" + "jest-runtime" "^27.4.6" + +"@jest/transform@^27.4.6": + "integrity" "sha512-9MsufmJC8t5JTpWEQJ0OcOOAXaH5ioaIX6uHVBLBMoCZPfKKQF+EqP8kACAvCZ0Y1h2Zr3uOccg8re+Dr5jxyw==" + "resolved" "https://registry.npmjs.org/@jest/transform/-/transform-27.4.6.tgz" + "version" "27.4.6" + dependencies: + "@babel/core" "^7.1.0" + "@jest/types" "^27.4.2" + "babel-plugin-istanbul" "^6.1.1" + "chalk" "^4.0.0" + "convert-source-map" "^1.4.0" + "fast-json-stable-stringify" "^2.0.0" + "graceful-fs" "^4.2.4" + "jest-haste-map" "^27.4.6" + "jest-regex-util" "^27.4.0" + "jest-util" "^27.4.2" + "micromatch" "^4.0.4" + "pirates" "^4.0.4" + "slash" "^3.0.0" + "source-map" "^0.6.1" + "write-file-atomic" "^3.0.0" + +"@jest/types@^27.4.2": + "integrity" "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==" + "resolved" "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz" + "version" "27.4.2" + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^16.0.0" + "chalk" "^4.0.0" + +"@nodelib/fs.scandir@2.1.5": + "integrity" "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==" + "resolved" "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" + "version" "2.1.5" + dependencies: + "@nodelib/fs.stat" "2.0.5" + "run-parallel" "^1.1.9" + +"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5": + "integrity" "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" + "resolved" "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" + "version" "2.0.5" + +"@nodelib/fs.walk@^1.2.3": + "integrity" "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==" + "resolved" "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz" + "version" "1.2.8" + dependencies: + "@nodelib/fs.scandir" "2.1.5" + "fastq" "^1.6.0" + +"@pmmmwh/react-refresh-webpack-plugin@^0.5.3": + "integrity" "sha512-zZbZeHQDnoTlt2AF+diQT0wsSXpvWiaIOZwBRdltNFhG1+I3ozyaw7U/nBiUwyJ0D+zwdXp0E3bWOl38Ag2BMw==" + "resolved" "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.4.tgz" + "version" "0.5.4" + dependencies: + "ansi-html-community" "^0.0.8" + "common-path-prefix" "^3.0.0" + "core-js-pure" "^3.8.1" + "error-stack-parser" "^2.0.6" + "find-up" "^5.0.0" + "html-entities" "^2.1.0" + "loader-utils" "^2.0.0" + "schema-utils" "^3.0.0" + "source-map" "^0.7.3" + +"@rollup/plugin-babel@^5.2.0": + "integrity" "sha512-9uIC8HZOnVLrLHxayq/PTzw+uS25E14KPUBh5ktF+18Mjo5yK0ToMMx6epY0uEgkjwJw0aBW4x2horYXh8juWw==" + "resolved" "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.0.tgz" + "version" "5.3.0" + dependencies: + "@babel/helper-module-imports" "^7.10.4" + "@rollup/pluginutils" "^3.1.0" + +"@rollup/plugin-node-resolve@^11.2.1": + "integrity" "sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==" + "resolved" "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz" + "version" "11.2.1" + dependencies: + "@rollup/pluginutils" "^3.1.0" + "@types/resolve" "1.17.1" + "builtin-modules" "^3.1.0" + "deepmerge" "^4.2.2" + "is-module" "^1.0.0" + "resolve" "^1.19.0" + +"@rollup/plugin-replace@^2.4.1": + "integrity" "sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==" + "resolved" "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.4.2.tgz" + "version" "2.4.2" + dependencies: + "@rollup/pluginutils" "^3.1.0" + "magic-string" "^0.25.7" + +"@rollup/pluginutils@^3.1.0": + "integrity" "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==" + "resolved" "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz" + "version" "3.1.0" + dependencies: + "@types/estree" "0.0.39" + "estree-walker" "^1.0.1" + "picomatch" "^2.2.2" + +"@rushstack/eslint-patch@^1.1.0": + "integrity" "sha512-JLo+Y592QzIE+q7Dl2pMUtt4q8SKYI5jDrZxrozEQxnGVOyYE+GWK9eLkwTaeN9DDctlaRAQ3TBmzZ1qdLE30A==" + "resolved" "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.1.0.tgz" + "version" "1.1.0" + +"@sinonjs/commons@^1.7.0": + "integrity" "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==" + "resolved" "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz" + "version" "1.8.3" + dependencies: + "type-detect" "4.0.8" + +"@sinonjs/fake-timers@^8.0.1": + "integrity" "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==" + "resolved" "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz" + "version" "8.1.0" + dependencies: + "@sinonjs/commons" "^1.7.0" + +"@surma/rollup-plugin-off-main-thread@^2.2.3": + "integrity" "sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==" + "resolved" "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz" + "version" "2.2.3" + dependencies: + "ejs" "^3.1.6" + "json5" "^2.2.0" + "magic-string" "^0.25.0" + "string.prototype.matchall" "^4.0.6" + +"@svgr/babel-plugin-add-jsx-attribute@^5.4.0": + "integrity" "sha512-ZFf2gs/8/6B8PnSofI0inYXr2SDNTDScPXhN7k5EqD4aZ3gi6u+rbmZHVB8IM3wDyx8ntKACZbtXSm7oZGRqVg==" + "resolved" "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz" + "version" "5.4.0" + +"@svgr/babel-plugin-remove-jsx-attribute@^5.4.0": + "integrity" "sha512-yaS4o2PgUtwLFGTKbsiAy6D0o3ugcUhWK0Z45umJ66EPWunAz9fuFw2gJuje6wqQvQWOTJvIahUwndOXb7QCPg==" + "resolved" "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-5.4.0.tgz" + "version" "5.4.0" + +"@svgr/babel-plugin-remove-jsx-empty-expression@^5.0.1": + "integrity" "sha512-LA72+88A11ND/yFIMzyuLRSMJ+tRKeYKeQ+mR3DcAZ5I4h5CPWN9AHyUzJbWSYp/u2u0xhmgOe0+E41+GjEueA==" + "resolved" "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-5.0.1.tgz" + "version" "5.0.1" + +"@svgr/babel-plugin-replace-jsx-attribute-value@^5.0.1": + "integrity" "sha512-PoiE6ZD2Eiy5mK+fjHqwGOS+IXX0wq/YDtNyIgOrc6ejFnxN4b13pRpiIPbtPwHEc+NT2KCjteAcq33/F1Y9KQ==" + "resolved" "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-5.0.1.tgz" + "version" "5.0.1" + +"@svgr/babel-plugin-svg-dynamic-title@^5.4.0": + "integrity" "sha512-zSOZH8PdZOpuG1ZVx/cLVePB2ibo3WPpqo7gFIjLV9a0QsuQAzJiwwqmuEdTaW2pegyBE17Uu15mOgOcgabQZg==" + "resolved" "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-5.4.0.tgz" + "version" "5.4.0" + +"@svgr/babel-plugin-svg-em-dimensions@^5.4.0": + "integrity" "sha512-cPzDbDA5oT/sPXDCUYoVXEmm3VIoAWAPT6mSPTJNbQaBNUuEKVKyGH93oDY4e42PYHRW67N5alJx/eEol20abw==" + "resolved" "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-5.4.0.tgz" + "version" "5.4.0" + +"@svgr/babel-plugin-transform-react-native-svg@^5.4.0": + "integrity" "sha512-3eYP/SaopZ41GHwXma7Rmxcv9uRslRDTY1estspeB1w1ueZWd/tPlMfEOoccYpEMZU3jD4OU7YitnXcF5hLW2Q==" + "resolved" "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-5.4.0.tgz" + "version" "5.4.0" + +"@svgr/babel-plugin-transform-svg-component@^5.5.0": + "integrity" "sha512-q4jSH1UUvbrsOtlo/tKcgSeiCHRSBdXoIoqX1pgcKK/aU3JD27wmMKwGtpB8qRYUYoyXvfGxUVKchLuR5pB3rQ==" + "resolved" "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-5.5.0.tgz" + "version" "5.5.0" + +"@svgr/babel-preset@^5.5.0": + "integrity" "sha512-4FiXBjvQ+z2j7yASeGPEi8VD/5rrGQk4Xrq3EdJmoZgz/tpqChpo5hgXDvmEauwtvOc52q8ghhZK4Oy7qph4ig==" + "resolved" "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-5.5.0.tgz" + "version" "5.5.0" + dependencies: + "@svgr/babel-plugin-add-jsx-attribute" "^5.4.0" + "@svgr/babel-plugin-remove-jsx-attribute" "^5.4.0" + "@svgr/babel-plugin-remove-jsx-empty-expression" "^5.0.1" + "@svgr/babel-plugin-replace-jsx-attribute-value" "^5.0.1" + "@svgr/babel-plugin-svg-dynamic-title" "^5.4.0" + "@svgr/babel-plugin-svg-em-dimensions" "^5.4.0" + "@svgr/babel-plugin-transform-react-native-svg" "^5.4.0" + "@svgr/babel-plugin-transform-svg-component" "^5.5.0" + +"@svgr/core@^5.5.0": + "integrity" "sha512-q52VOcsJPvV3jO1wkPtzTuKlvX7Y3xIcWRpCMtBF3MrteZJtBfQw/+u0B1BHy5ColpQc1/YVTrPEtSYIMNZlrQ==" + "resolved" "https://registry.npmjs.org/@svgr/core/-/core-5.5.0.tgz" + "version" "5.5.0" + dependencies: + "@svgr/plugin-jsx" "^5.5.0" + "camelcase" "^6.2.0" + "cosmiconfig" "^7.0.0" + +"@svgr/hast-util-to-babel-ast@^5.5.0": + "integrity" "sha512-cAaR/CAiZRB8GP32N+1jocovUtvlj0+e65TB50/6Lcime+EA49m/8l+P2ko+XPJ4dw3xaPS3jOL4F2X4KWxoeQ==" + "resolved" "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-5.5.0.tgz" + "version" "5.5.0" + dependencies: + "@babel/types" "^7.12.6" + +"@svgr/plugin-jsx@^5.5.0": + "integrity" "sha512-V/wVh33j12hGh05IDg8GpIUXbjAPnTdPTKuP4VNLggnwaHMPNQNae2pRnyTAILWCQdz5GyMqtO488g7CKM8CBA==" + "resolved" "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-5.5.0.tgz" + "version" "5.5.0" + dependencies: + "@babel/core" "^7.12.3" + "@svgr/babel-preset" "^5.5.0" + "@svgr/hast-util-to-babel-ast" "^5.5.0" + "svg-parser" "^2.0.2" + +"@svgr/plugin-svgo@^5.5.0": + "integrity" "sha512-r5swKk46GuQl4RrVejVwpeeJaydoxkdwkM1mBKOgJLBUJPGaLci6ylg/IjhrRsREKDkr4kbMWdgOtbXEh0fyLQ==" + "resolved" "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-5.5.0.tgz" + "version" "5.5.0" + dependencies: + "cosmiconfig" "^7.0.0" + "deepmerge" "^4.2.2" + "svgo" "^1.2.2" + +"@svgr/webpack@^5.5.0": + "integrity" "sha512-DOBOK255wfQxguUta2INKkzPj6AIS6iafZYiYmHn6W3pHlycSRRlvWKCfLDG10fXfLWqE3DJHgRUOyJYmARa7g==" + "resolved" "https://registry.npmjs.org/@svgr/webpack/-/webpack-5.5.0.tgz" + "version" "5.5.0" + dependencies: + "@babel/core" "^7.12.3" + "@babel/plugin-transform-react-constant-elements" "^7.12.1" + "@babel/preset-env" "^7.12.1" + "@babel/preset-react" "^7.12.5" + "@svgr/core" "^5.5.0" + "@svgr/plugin-jsx" "^5.5.0" + "@svgr/plugin-svgo" "^5.5.0" + "loader-utils" "^2.0.0" + +"@tootallnate/once@1": + "integrity" "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==" + "resolved" "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz" + "version" "1.1.2" + +"@trysound/sax@0.2.0": + "integrity" "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==" + "resolved" "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz" + "version" "0.2.0" + +"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14", "@types/babel__core@^7.1.9": + "integrity" "sha512-S7unDjm/C7z2A2R9NzfKCK1I+BAALDtxEmsJBwlB3EzNfb929ykjL++1CK9LO++EIp2fQrC8O+BwjKvz6UeDyQ==" + "resolved" "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.18.tgz" + "version" "7.1.18" + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + +"@types/babel__generator@*": + "integrity" "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==" + "resolved" "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz" + "version" "7.6.4" + dependencies: + "@babel/types" "^7.0.0" + +"@types/babel__template@*": + "integrity" "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==" + "resolved" "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz" + "version" "7.4.1" + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": + "integrity" "sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA==" + "resolved" "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.14.2.tgz" + "version" "7.14.2" + dependencies: + "@babel/types" "^7.3.0" + +"@types/body-parser@*": + "integrity" "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==" + "resolved" "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz" + "version" "1.19.2" + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/bonjour@^3.5.9": + "integrity" "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==" + "resolved" "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz" + "version" "3.5.10" + dependencies: + "@types/node" "*" + +"@types/connect-history-api-fallback@^1.3.5": + "integrity" "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==" + "resolved" "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz" + "version" "1.3.5" + dependencies: + "@types/express-serve-static-core" "*" + "@types/node" "*" + +"@types/connect@*": + "integrity" "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==" + "resolved" "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz" + "version" "3.4.35" + dependencies: + "@types/node" "*" + +"@types/eslint-scope@^3.7.0": + "integrity" "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==" + "resolved" "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz" + "version" "3.7.3" + dependencies: + "@types/eslint" "*" + "@types/estree" "*" + +"@types/eslint@*", "@types/eslint@^7.28.2": + "integrity" "sha512-VNcvioYDH8/FxaeTKkM4/TiTwt6pBV9E3OfGmvaw8tPl0rrHCJ4Ll15HRT+pMiFAf/MLQvAzC+6RzUMEL9Ceng==" + "resolved" "https://registry.npmjs.org/@types/eslint/-/eslint-7.29.0.tgz" + "version" "7.29.0" + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*", "@types/estree@^0.0.50": + "integrity" "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==" + "resolved" "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz" + "version" "0.0.50" + +"@types/estree@0.0.39": + "integrity" "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==" + "resolved" "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz" + "version" "0.0.39" + +"@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.18": + "integrity" "sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==" + "resolved" "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz" + "version" "4.17.28" + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + +"@types/express@*": + "integrity" "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==" + "resolved" "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz" + "version" "4.17.13" + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.18" + "@types/qs" "*" + "@types/serve-static" "*" + +"@types/graceful-fs@^4.1.2": + "integrity" "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==" + "resolved" "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz" + "version" "4.1.5" + dependencies: + "@types/node" "*" + +"@types/html-minifier-terser@^6.0.0": + "integrity" "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==" + "resolved" "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz" + "version" "6.1.0" + +"@types/http-proxy@^1.17.5": + "integrity" "sha512-5kPLG5BKpWYkw/LVOGWpiq3nEVqxiN32rTgI53Sk12/xHFQ2rG3ehI9IO+O3W2QoKeyB92dJkoka8SUm6BX1pA==" + "resolved" "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.8.tgz" + "version" "1.17.8" + dependencies: + "@types/node" "*" + +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": + "integrity" "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==" + "resolved" "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz" + "version" "2.0.4" + +"@types/istanbul-lib-report@*": + "integrity" "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==" + "resolved" "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz" + "version" "3.0.0" + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^3.0.0": + "integrity" "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==" + "resolved" "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz" + "version" "3.0.1" + dependencies: + "@types/istanbul-lib-report" "*" + +"@types/json-schema@*", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": + "integrity" "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==" + "resolved" "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz" + "version" "7.0.9" + +"@types/json5@^0.0.29": + "integrity" "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=" + "resolved" "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz" + "version" "0.0.29" + +"@types/mime@^1": + "integrity" "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" + "resolved" "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz" + "version" "1.3.2" + +"@types/node@*": + "integrity" "sha512-YofkM6fGv4gDJq78g4j0mMuGMkZVxZDgtU0JRdx6FgiJDG+0fY0GKVolOV8WqVmEhLCXkQRjwDdKyPxJp/uucg==" + "resolved" "https://registry.npmjs.org/@types/node/-/node-17.0.8.tgz" + "version" "17.0.8" + +"@types/parse-json@^4.0.0": + "integrity" "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" + "resolved" "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz" + "version" "4.0.0" + +"@types/prettier@^2.1.5": + "integrity" "sha512-QzSuZMBuG5u8HqYz01qtMdg/Jfctlnvj1z/lYnIDXs/golxw0fxtRAHd9KrzjR7Yxz1qVeI00o0kiO3PmVdJ9w==" + "resolved" "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.3.tgz" + "version" "2.4.3" + +"@types/q@^1.5.1": + "integrity" "sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ==" + "resolved" "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz" + "version" "1.5.5" + +"@types/qs@*": + "integrity" "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" + "resolved" "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz" + "version" "6.9.7" + +"@types/range-parser@*": + "integrity" "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" + "resolved" "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz" + "version" "1.2.4" + +"@types/resolve@1.17.1": + "integrity" "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==" + "resolved" "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz" + "version" "1.17.1" + dependencies: + "@types/node" "*" + +"@types/retry@^0.12.0": + "integrity" "sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==" + "resolved" "https://registry.npmjs.org/@types/retry/-/retry-0.12.1.tgz" + "version" "0.12.1" + +"@types/serve-index@^1.9.1": + "integrity" "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==" + "resolved" "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz" + "version" "1.9.1" + dependencies: + "@types/express" "*" + +"@types/serve-static@*": + "integrity" "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==" + "resolved" "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz" + "version" "1.13.10" + dependencies: + "@types/mime" "^1" + "@types/node" "*" + +"@types/sockjs@^0.3.33": + "integrity" "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==" + "resolved" "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz" + "version" "0.3.33" + dependencies: + "@types/node" "*" + +"@types/stack-utils@^2.0.0": + "integrity" "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==" + "resolved" "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz" + "version" "2.0.1" + +"@types/trusted-types@^2.0.2": + "integrity" "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==" + "resolved" "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz" + "version" "2.0.2" + +"@types/ws@^8.2.2": + "integrity" "sha512-NOn5eIcgWLOo6qW8AcuLZ7G8PycXu0xTxxkS6Q18VWFxgPUSOwV0pBj2a/4viNZVu25i7RIB7GttdkAIUUXOOg==" + "resolved" "https://registry.npmjs.org/@types/ws/-/ws-8.2.2.tgz" + "version" "8.2.2" + dependencies: + "@types/node" "*" + +"@types/yargs-parser@*": + "integrity" "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==" + "resolved" "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz" + "version" "20.2.1" + +"@types/yargs@^16.0.0": + "integrity" "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==" + "resolved" "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz" + "version" "16.0.4" + dependencies: + "@types/yargs-parser" "*" + +"@typescript-eslint/eslint-plugin@^4.0.0 || ^5.0.0", "@typescript-eslint/eslint-plugin@^5.5.0": + "integrity" "sha512-Xv9tkFlyD4MQGpJgTo6wqDqGvHIRmRgah/2Sjz1PUnJTawjHWIwBivUE9x0QtU2WVii9baYgavo/bHjrZJkqTw==" + "resolved" "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.9.1.tgz" + "version" "5.9.1" + dependencies: + "@typescript-eslint/experimental-utils" "5.9.1" + "@typescript-eslint/scope-manager" "5.9.1" + "@typescript-eslint/type-utils" "5.9.1" + "debug" "^4.3.2" + "functional-red-black-tree" "^1.0.1" + "ignore" "^5.1.8" + "regexpp" "^3.2.0" + "semver" "^7.3.5" + "tsutils" "^3.21.0" + +"@typescript-eslint/experimental-utils@^5.0.0", "@typescript-eslint/experimental-utils@^5.9.0", "@typescript-eslint/experimental-utils@5.9.1": + "integrity" "sha512-cb1Njyss0mLL9kLXgS/eEY53SZQ9sT519wpX3i+U457l2UXRDuo87hgKfgRazmu9/tQb0x2sr3Y0yrU+Zz0y+w==" + "resolved" "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.9.1.tgz" + "version" "5.9.1" + dependencies: + "@types/json-schema" "^7.0.9" + "@typescript-eslint/scope-manager" "5.9.1" + "@typescript-eslint/types" "5.9.1" + "@typescript-eslint/typescript-estree" "5.9.1" + "eslint-scope" "^5.1.1" + "eslint-utils" "^3.0.0" + +"@typescript-eslint/parser@^5.0.0", "@typescript-eslint/parser@^5.5.0": + "integrity" "sha512-PLYO0AmwD6s6n0ZQB5kqPgfvh73p0+VqopQQLuNfi7Lm0EpfKyDalchpVwkE+81k5HeiRrTV/9w1aNHzjD7C4g==" + "resolved" "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.9.1.tgz" + "version" "5.9.1" + dependencies: + "@typescript-eslint/scope-manager" "5.9.1" + "@typescript-eslint/types" "5.9.1" + "@typescript-eslint/typescript-estree" "5.9.1" + "debug" "^4.3.2" + +"@typescript-eslint/scope-manager@5.9.1": + "integrity" "sha512-8BwvWkho3B/UOtzRyW07ffJXPaLSUKFBjpq8aqsRvu6HdEuzCY57+ffT7QoV4QXJXWSU1+7g3wE4AlgImmQ9pQ==" + "resolved" "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.9.1.tgz" + "version" "5.9.1" + dependencies: + "@typescript-eslint/types" "5.9.1" + "@typescript-eslint/visitor-keys" "5.9.1" + +"@typescript-eslint/type-utils@5.9.1": + "integrity" "sha512-tRSpdBnPRssjlUh35rE9ug5HrUvaB9ntREy7gPXXKwmIx61TNN7+l5YKgi1hMKxo5NvqZCfYhA5FvyuJG6X6vg==" + "resolved" "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.9.1.tgz" + "version" "5.9.1" + dependencies: + "@typescript-eslint/experimental-utils" "5.9.1" + "debug" "^4.3.2" + "tsutils" "^3.21.0" + +"@typescript-eslint/types@5.9.1": + "integrity" "sha512-SsWegWudWpkZCwwYcKoDwuAjoZXnM1y2EbEerTHho19Hmm+bQ56QG4L4jrtCu0bI5STaRTvRTZmjprWlTw/5NQ==" + "resolved" "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.9.1.tgz" + "version" "5.9.1" + +"@typescript-eslint/typescript-estree@5.9.1": + "integrity" "sha512-gL1sP6A/KG0HwrahVXI9fZyeVTxEYV//6PmcOn1tD0rw8VhUWYeZeuWHwwhnewnvEMcHjhnJLOBhA9rK4vmb8A==" + "resolved" "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.9.1.tgz" + "version" "5.9.1" + dependencies: + "@typescript-eslint/types" "5.9.1" + "@typescript-eslint/visitor-keys" "5.9.1" + "debug" "^4.3.2" + "globby" "^11.0.4" + "is-glob" "^4.0.3" + "semver" "^7.3.5" + "tsutils" "^3.21.0" + +"@typescript-eslint/visitor-keys@5.9.1": + "integrity" "sha512-Xh37pNz9e9ryW4TVdwiFzmr4hloty8cFj8GTWMXh3Z8swGwyQWeCcNgF0hm6t09iZd6eiZmIf4zHedQVP6TVtg==" + "resolved" "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.9.1.tgz" + "version" "5.9.1" + dependencies: + "@typescript-eslint/types" "5.9.1" + "eslint-visitor-keys" "^3.0.0" + +"@webassemblyjs/ast@1.11.1": + "integrity" "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz" + "version" "1.11.1" + dependencies: + "@webassemblyjs/helper-numbers" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + +"@webassemblyjs/floating-point-hex-parser@1.11.1": + "integrity" "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz" + "version" "1.11.1" + +"@webassemblyjs/helper-api-error@1.11.1": + "integrity" "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz" + "version" "1.11.1" + +"@webassemblyjs/helper-buffer@1.11.1": + "integrity" "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz" + "version" "1.11.1" + +"@webassemblyjs/helper-numbers@1.11.1": + "integrity" "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz" + "version" "1.11.1" + dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.11.1" + "@webassemblyjs/helper-api-error" "1.11.1" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/helper-wasm-bytecode@1.11.1": + "integrity" "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz" + "version" "1.11.1" + +"@webassemblyjs/helper-wasm-section@1.11.1": + "integrity" "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz" + "version" "1.11.1" + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-buffer" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/wasm-gen" "1.11.1" + +"@webassemblyjs/ieee754@1.11.1": + "integrity" "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz" + "version" "1.11.1" + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.11.1": + "integrity" "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz" + "version" "1.11.1" + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/utf8@1.11.1": + "integrity" "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz" + "version" "1.11.1" + +"@webassemblyjs/wasm-edit@1.11.1": + "integrity" "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz" + "version" "1.11.1" + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-buffer" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/helper-wasm-section" "1.11.1" + "@webassemblyjs/wasm-gen" "1.11.1" + "@webassemblyjs/wasm-opt" "1.11.1" + "@webassemblyjs/wasm-parser" "1.11.1" + "@webassemblyjs/wast-printer" "1.11.1" + +"@webassemblyjs/wasm-gen@1.11.1": + "integrity" "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz" + "version" "1.11.1" + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/ieee754" "1.11.1" + "@webassemblyjs/leb128" "1.11.1" + "@webassemblyjs/utf8" "1.11.1" + +"@webassemblyjs/wasm-opt@1.11.1": + "integrity" "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz" + "version" "1.11.1" + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-buffer" "1.11.1" + "@webassemblyjs/wasm-gen" "1.11.1" + "@webassemblyjs/wasm-parser" "1.11.1" + +"@webassemblyjs/wasm-parser@1.11.1": + "integrity" "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz" + "version" "1.11.1" + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-api-error" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/ieee754" "1.11.1" + "@webassemblyjs/leb128" "1.11.1" + "@webassemblyjs/utf8" "1.11.1" + +"@webassemblyjs/wast-printer@1.11.1": + "integrity" "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz" + "version" "1.11.1" + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@xtuc/long" "4.2.2" + +"@xtuc/ieee754@^1.2.0": + "integrity" "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" + "resolved" "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz" + "version" "1.2.0" + +"@xtuc/long@4.2.2": + "integrity" "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" + "resolved" "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz" + "version" "4.2.2" + +"abab@^2.0.3", "abab@^2.0.5": + "integrity" "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==" + "resolved" "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz" + "version" "2.0.5" + +"accepts@~1.3.4", "accepts@~1.3.5", "accepts@~1.3.7": + "integrity" "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==" + "resolved" "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz" + "version" "1.3.7" + dependencies: + "mime-types" "~2.1.24" + "negotiator" "0.6.2" + +"acorn-globals@^6.0.0": + "integrity" "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==" + "resolved" "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz" + "version" "6.0.0" + dependencies: + "acorn" "^7.1.1" + "acorn-walk" "^7.1.1" + +"acorn-import-assertions@^1.7.6": + "integrity" "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==" + "resolved" "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz" + "version" "1.8.0" + +"acorn-jsx@^5.3.1": + "integrity" "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==" + "resolved" "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" + "version" "5.3.2" + +"acorn-node@^1.6.1": + "integrity" "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==" + "resolved" "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz" + "version" "1.8.2" + dependencies: + "acorn" "^7.0.0" + "acorn-walk" "^7.0.0" + "xtend" "^4.0.2" + +"acorn-walk@^7.0.0", "acorn-walk@^7.1.1": + "integrity" "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==" + "resolved" "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz" + "version" "7.2.0" + +"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", "acorn@^8", "acorn@^8.2.4", "acorn@^8.4.1", "acorn@^8.5.0", "acorn@^8.7.0": + "integrity" "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==" + "resolved" "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz" + "version" "8.7.0" + +"acorn@^7.0.0": + "integrity" "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==" + "resolved" "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz" + "version" "7.4.1" + +"acorn@^7.1.1": + "integrity" "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==" + "resolved" "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz" + "version" "7.4.1" + +"address@^1.0.1", "address@^1.1.2": + "integrity" "sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA==" + "resolved" "https://registry.npmjs.org/address/-/address-1.1.2.tgz" + "version" "1.1.2" + +"adjust-sourcemap-loader@^4.0.0": + "integrity" "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==" + "resolved" "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz" + "version" "4.0.0" + dependencies: + "loader-utils" "^2.0.0" + "regex-parser" "^2.2.11" + +"agent-base@6": + "integrity" "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==" + "resolved" "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz" + "version" "6.0.2" + dependencies: + "debug" "4" + +"aggregate-error@^3.0.0": + "integrity" "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==" + "resolved" "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz" + "version" "3.1.0" + dependencies: + "clean-stack" "^2.0.0" + "indent-string" "^4.0.0" + +"ajv-formats@^2.1.1": + "integrity" "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==" + "resolved" "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz" + "version" "2.1.1" + dependencies: + "ajv" "^8.0.0" + +"ajv-keywords@^3.4.1", "ajv-keywords@^3.5.2": + "integrity" "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" + "resolved" "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz" + "version" "3.5.2" + +"ajv-keywords@^5.0.0": + "integrity" "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==" + "resolved" "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz" + "version" "5.1.0" + dependencies: + "fast-deep-equal" "^3.1.3" + +"ajv@^6.10.0", "ajv@^6.12.2", "ajv@^6.12.4", "ajv@^6.12.5", "ajv@^6.9.1": + "integrity" "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==" + "resolved" "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" + "version" "6.12.6" + dependencies: + "fast-deep-equal" "^3.1.1" + "fast-json-stable-stringify" "^2.0.0" + "json-schema-traverse" "^0.4.1" + "uri-js" "^4.2.2" + +"ajv@^8.0.0": + "integrity" "sha512-x9VuX+R/jcFj1DHo/fCp99esgGDWiHENrKxaCENuCxpoMCmAt/COCGVDwA7kleEpEzJjDnvh3yGoOuLu0Dtllw==" + "resolved" "https://registry.npmjs.org/ajv/-/ajv-8.8.2.tgz" + "version" "8.8.2" + dependencies: + "fast-deep-equal" "^3.1.1" + "json-schema-traverse" "^1.0.0" + "require-from-string" "^2.0.2" + "uri-js" "^4.2.2" + +"ajv@^8.6.0", "ajv@>=8": + "integrity" "sha512-x9VuX+R/jcFj1DHo/fCp99esgGDWiHENrKxaCENuCxpoMCmAt/COCGVDwA7kleEpEzJjDnvh3yGoOuLu0Dtllw==" + "resolved" "https://registry.npmjs.org/ajv/-/ajv-8.8.2.tgz" + "version" "8.8.2" + dependencies: + "fast-deep-equal" "^3.1.1" + "json-schema-traverse" "^1.0.0" + "require-from-string" "^2.0.2" + "uri-js" "^4.2.2" + +"ajv@^8.8.0", "ajv@^8.8.2": + "integrity" "sha512-x9VuX+R/jcFj1DHo/fCp99esgGDWiHENrKxaCENuCxpoMCmAt/COCGVDwA7kleEpEzJjDnvh3yGoOuLu0Dtllw==" + "resolved" "https://registry.npmjs.org/ajv/-/ajv-8.8.2.tgz" + "version" "8.8.2" + dependencies: + "fast-deep-equal" "^3.1.1" + "json-schema-traverse" "^1.0.0" + "require-from-string" "^2.0.2" + "uri-js" "^4.2.2" + +"alphanum-sort@^1.0.2": + "integrity" "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=" + "resolved" "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz" + "version" "1.0.2" + +"ansi-colors@^4.1.1": + "integrity" "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==" + "resolved" "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz" + "version" "4.1.1" + +"ansi-escapes@^4.2.1", "ansi-escapes@^4.3.1": + "integrity" "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==" + "resolved" "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz" + "version" "4.3.2" + dependencies: + "type-fest" "^0.21.3" + +"ansi-html-community@^0.0.8": + "integrity" "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==" + "resolved" "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz" + "version" "0.0.8" + +"ansi-regex@^5.0.1": + "integrity" "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + "resolved" "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" + "version" "5.0.1" + +"ansi-regex@^6.0.1": + "integrity" "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==" + "resolved" "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz" + "version" "6.0.1" + +"ansi-styles@^3.2.1": + "integrity" "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==" + "resolved" "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz" + "version" "3.2.1" + dependencies: + "color-convert" "^1.9.0" + +"ansi-styles@^4.0.0": + "integrity" "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==" + "resolved" "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" + "version" "4.3.0" + dependencies: + "color-convert" "^2.0.1" + +"ansi-styles@^4.1.0": + "integrity" "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==" + "resolved" "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" + "version" "4.3.0" + dependencies: + "color-convert" "^2.0.1" + +"ansi-styles@^5.0.0": + "integrity" "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" + "resolved" "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz" + "version" "5.2.0" + +"anymatch@^3.0.3", "anymatch@~3.1.2": + "integrity" "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==" + "resolved" "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz" + "version" "3.1.2" + dependencies: + "normalize-path" "^3.0.0" + "picomatch" "^2.0.4" + +"arg@^5.0.1": + "integrity" "sha512-e0hDa9H2Z9AwFkk2qDlwhoMYE4eToKarchkQHovNdLTCYMHZHeRjI71crOh+dio4K6u1IcwubQqo79Ga4CyAQA==" + "resolved" "https://registry.npmjs.org/arg/-/arg-5.0.1.tgz" + "version" "5.0.1" + +"argparse@^1.0.7": + "integrity" "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==" + "resolved" "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz" + "version" "1.0.10" + dependencies: + "sprintf-js" "~1.0.2" + +"argparse@^2.0.1": + "integrity" "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + "resolved" "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" + "version" "2.0.1" + +"aria-query@^4.2.2": + "integrity" "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==" + "resolved" "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz" + "version" "4.2.2" + dependencies: + "@babel/runtime" "^7.10.2" + "@babel/runtime-corejs3" "^7.10.2" + +"array-flatten@^2.1.0": + "integrity" "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" + "resolved" "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz" + "version" "2.1.2" + +"array-flatten@1.1.1": + "integrity" "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + "resolved" "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz" + "version" "1.1.1" + +"array-includes@^3.1.3", "array-includes@^3.1.4": + "integrity" "sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==" + "resolved" "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz" + "version" "3.1.4" + dependencies: + "call-bind" "^1.0.2" + "define-properties" "^1.1.3" + "es-abstract" "^1.19.1" + "get-intrinsic" "^1.1.1" + "is-string" "^1.0.7" + +"array-union@^2.1.0": + "integrity" "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==" + "resolved" "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz" + "version" "2.1.0" + +"array.prototype.flat@^1.2.5": + "integrity" "sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg==" + "resolved" "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz" + "version" "1.2.5" + dependencies: + "call-bind" "^1.0.2" + "define-properties" "^1.1.3" + "es-abstract" "^1.19.0" + +"array.prototype.flatmap@^1.2.5": + "integrity" "sha512-08u6rVyi1Lj7oqWbS9nUxliETrtIROT4XGTA4D/LWGten6E3ocm7cy9SIrmNHOL5XVbVuckUp3X6Xyg8/zpvHA==" + "resolved" "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.5.tgz" + "version" "1.2.5" + dependencies: + "call-bind" "^1.0.0" + "define-properties" "^1.1.3" + "es-abstract" "^1.19.0" + +"asap@~2.0.6": + "integrity" "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + "resolved" "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz" + "version" "2.0.6" + +"ast-types-flow@^0.0.7": + "integrity" "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=" + "resolved" "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz" + "version" "0.0.7" + +"async@^2.6.2": + "integrity" "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==" + "resolved" "https://registry.npmjs.org/async/-/async-2.6.3.tgz" + "version" "2.6.3" + dependencies: + "lodash" "^4.17.14" + +"async@0.9.x": + "integrity" "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=" + "resolved" "https://registry.npmjs.org/async/-/async-0.9.2.tgz" + "version" "0.9.2" + +"asynckit@^0.4.0": + "integrity" "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + "resolved" "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" + "version" "0.4.0" + +"at-least-node@^1.0.0": + "integrity" "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" + "resolved" "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz" + "version" "1.0.0" + +"autoprefixer@^10.0.2", "autoprefixer@^10.4.2": + "integrity" "sha512-9fOPpHKuDW1w/0EKfRmVnxTDt8166MAnLI3mgZ1JCnhNtYWxcJ6Ud5CO/AVOZi/AvFa8DY9RTy3h3+tFBlrrdQ==" + "resolved" "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.2.tgz" + "version" "10.4.2" + dependencies: + "browserslist" "^4.19.1" + "caniuse-lite" "^1.0.30001297" + "fraction.js" "^4.1.2" + "normalize-range" "^0.1.2" + "picocolors" "^1.0.0" + "postcss-value-parser" "^4.2.0" + +"axe-core@^4.3.5": + "integrity" "sha512-WKTW1+xAzhMS5dJsxWkliixlO/PqC4VhmO9T4juNYcaTg9jzWiJsou6m5pxWYGfigWbwzJWeFY6z47a+4neRXA==" + "resolved" "https://registry.npmjs.org/axe-core/-/axe-core-4.3.5.tgz" + "version" "4.3.5" + +"axobject-query@^2.2.0": + "integrity" "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==" + "resolved" "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz" + "version" "2.2.0" + +"babel-jest@^27.4.2", "babel-jest@^27.4.6": + "integrity" "sha512-qZL0JT0HS1L+lOuH+xC2DVASR3nunZi/ozGhpgauJHgmI7f8rudxf6hUjEHympdQ/J64CdKmPkgfJ+A3U6QCrg==" + "resolved" "https://registry.npmjs.org/babel-jest/-/babel-jest-27.4.6.tgz" + "version" "27.4.6" + dependencies: + "@jest/transform" "^27.4.6" + "@jest/types" "^27.4.2" + "@types/babel__core" "^7.1.14" + "babel-plugin-istanbul" "^6.1.1" + "babel-preset-jest" "^27.4.0" + "chalk" "^4.0.0" + "graceful-fs" "^4.2.4" + "slash" "^3.0.0" + +"babel-loader@^8.2.3": + "integrity" "sha512-n4Zeta8NC3QAsuyiizu0GkmRcQ6clkV9WFUnUf1iXP//IeSKbWjofW3UHyZVwlOB4y039YQKefawyTn64Zwbuw==" + "resolved" "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.3.tgz" + "version" "8.2.3" + dependencies: + "find-cache-dir" "^3.3.1" + "loader-utils" "^1.4.0" + "make-dir" "^3.1.0" + "schema-utils" "^2.6.5" + +"babel-plugin-dynamic-import-node@^2.3.3": + "integrity" "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==" + "resolved" "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz" + "version" "2.3.3" + dependencies: + "object.assign" "^4.1.0" + +"babel-plugin-istanbul@^6.1.1": + "integrity" "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==" + "resolved" "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz" + "version" "6.1.1" + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@istanbuljs/load-nyc-config" "^1.0.0" + "@istanbuljs/schema" "^0.1.2" + "istanbul-lib-instrument" "^5.0.4" + "test-exclude" "^6.0.0" + +"babel-plugin-jest-hoist@^27.4.0": + "integrity" "sha512-Jcu7qS4OX5kTWBc45Hz7BMmgXuJqRnhatqpUhnzGC3OBYpOmf2tv6jFNwZpwM7wU7MUuv2r9IPS/ZlYOuburVw==" + "resolved" "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.4.0.tgz" + "version" "27.4.0" + dependencies: + "@babel/template" "^7.3.3" + "@babel/types" "^7.3.3" + "@types/babel__core" "^7.0.0" + "@types/babel__traverse" "^7.0.6" + +"babel-plugin-macros@^3.1.0": + "integrity" "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==" + "resolved" "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz" + "version" "3.1.0" + dependencies: + "@babel/runtime" "^7.12.5" + "cosmiconfig" "^7.0.0" + "resolve" "^1.19.0" + +"babel-plugin-named-asset-import@^0.3.8": + "integrity" "sha512-WXiAc++qo7XcJ1ZnTYGtLxmBCVbddAml3CEXgWaBzNzLNoxtQ8AiGEFDMOhot9XjTCQbvP5E77Fj9Gk924f00Q==" + "resolved" "https://registry.npmjs.org/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.8.tgz" + "version" "0.3.8" + +"babel-plugin-polyfill-corejs2@^0.3.0": + "integrity" "sha512-wMDoBJ6uG4u4PNFh72Ty6t3EgfA91puCuAwKIazbQlci+ENb/UU9A3xG5lutjUIiXCIn1CY5L15r9LimiJyrSA==" + "resolved" "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.0.tgz" + "version" "0.3.0" + dependencies: + "@babel/compat-data" "^7.13.11" + "@babel/helper-define-polyfill-provider" "^0.3.0" + "semver" "^6.1.1" + +"babel-plugin-polyfill-corejs3@^0.5.0": + "integrity" "sha512-Hcrgnmkf+4JTj73GbK3bBhlVPiLL47owUAnoJIf69Hakl3q+KfodbDXiZWGMM7iqCZTxCG3Z2VRfPNYES4rXqQ==" + "resolved" "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.0.tgz" + "version" "0.5.0" + dependencies: + "@babel/helper-define-polyfill-provider" "^0.3.0" + "core-js-compat" "^3.20.0" + +"babel-plugin-polyfill-regenerator@^0.3.0": + "integrity" "sha512-dhAPTDLGoMW5/84wkgwiLRwMnio2i1fUe53EuvtKMv0pn2p3S8OCoV1xAzfJPl0KOX7IB89s2ib85vbYiea3jg==" + "resolved" "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.0.tgz" + "version" "0.3.0" + dependencies: + "@babel/helper-define-polyfill-provider" "^0.3.0" + +"babel-plugin-transform-react-remove-prop-types@^0.4.24": + "integrity" "sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA==" + "resolved" "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz" + "version" "0.4.24" + +"babel-preset-current-node-syntax@^1.0.0": + "integrity" "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==" + "resolved" "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz" + "version" "1.0.1" + dependencies: + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-bigint" "^7.8.3" + "@babel/plugin-syntax-class-properties" "^7.8.3" + "@babel/plugin-syntax-import-meta" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.8.3" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-top-level-await" "^7.8.3" + +"babel-preset-jest@^27.4.0": + "integrity" "sha512-NK4jGYpnBvNxcGo7/ZpZJr51jCGT+3bwwpVIDY2oNfTxJJldRtB4VAcYdgp1loDE50ODuTu+yBjpMAswv5tlpg==" + "resolved" "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.4.0.tgz" + "version" "27.4.0" + dependencies: + "babel-plugin-jest-hoist" "^27.4.0" + "babel-preset-current-node-syntax" "^1.0.0" + +"babel-preset-react-app@^10.0.1": + "integrity" "sha512-b0D9IZ1WhhCWkrTXyFuIIgqGzSkRIH5D5AmB0bXbzYAB1OBAwHcUeyWW2LorutLWF5btNo/N7r/cIdmvvKJlYg==" + "resolved" "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-10.0.1.tgz" + "version" "10.0.1" + dependencies: + "@babel/core" "^7.16.0" + "@babel/plugin-proposal-class-properties" "^7.16.0" + "@babel/plugin-proposal-decorators" "^7.16.4" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.16.0" + "@babel/plugin-proposal-numeric-separator" "^7.16.0" + "@babel/plugin-proposal-optional-chaining" "^7.16.0" + "@babel/plugin-proposal-private-methods" "^7.16.0" + "@babel/plugin-transform-flow-strip-types" "^7.16.0" + "@babel/plugin-transform-react-display-name" "^7.16.0" + "@babel/plugin-transform-runtime" "^7.16.4" + "@babel/preset-env" "^7.16.4" + "@babel/preset-react" "^7.16.0" + "@babel/preset-typescript" "^7.16.0" + "@babel/runtime" "^7.16.3" + "babel-plugin-macros" "^3.1.0" + "babel-plugin-transform-react-remove-prop-types" "^0.4.24" + +"balanced-match@^1.0.0": + "integrity" "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + "resolved" "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" + "version" "1.0.2" + +"batch@0.6.1": + "integrity" "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=" + "resolved" "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz" + "version" "0.6.1" + +"bfj@^7.0.2": + "integrity" "sha512-+e/UqUzwmzJamNF50tBV6tZPTORow7gQ96iFow+8b562OdMpEK0BcJEq2OSPEDmAbSMBQ7PKZ87ubFkgxpYWgw==" + "resolved" "https://registry.npmjs.org/bfj/-/bfj-7.0.2.tgz" + "version" "7.0.2" + dependencies: + "bluebird" "^3.5.5" + "check-types" "^11.1.1" + "hoopy" "^0.1.4" + "tryer" "^1.0.1" + +"big.js@^5.2.2": + "integrity" "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" + "resolved" "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz" + "version" "5.2.2" + +"binary-extensions@^2.0.0": + "integrity" "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" + "resolved" "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" + "version" "2.2.0" + +"bluebird@^3.5.5": + "integrity" "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + "resolved" "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz" + "version" "3.7.2" + +"body-parser@1.19.1": + "integrity" "sha512-8ljfQi5eBk8EJfECMrgqNGWPEY5jWP+1IzkzkGdFFEwFQZZyaZ21UqdaHktgiMlH0xLHqIFtE/u2OYE5dOtViA==" + "resolved" "https://registry.npmjs.org/body-parser/-/body-parser-1.19.1.tgz" + "version" "1.19.1" + dependencies: + "bytes" "3.1.1" + "content-type" "~1.0.4" + "debug" "2.6.9" + "depd" "~1.1.2" + "http-errors" "1.8.1" + "iconv-lite" "0.4.24" + "on-finished" "~2.3.0" + "qs" "6.9.6" + "raw-body" "2.4.2" + "type-is" "~1.6.18" + +"bonjour@^3.5.0": + "integrity" "sha1-jokKGD2O6aI5OzhExpGkK897yfU=" + "resolved" "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz" + "version" "3.5.0" + dependencies: + "array-flatten" "^2.1.0" + "deep-equal" "^1.0.1" + "dns-equal" "^1.0.0" + "dns-txt" "^2.0.2" + "multicast-dns" "^6.0.1" + "multicast-dns-service-types" "^1.1.0" + +"boolbase@^1.0.0", "boolbase@~1.0.0": + "integrity" "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" + "resolved" "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz" + "version" "1.0.0" + +"bootstrap@^4.3.1": + "integrity" "sha512-rXqOmH1VilAt2DyPzluTi2blhk17bO7ef+zLLPlWvG494pDxcM234pJ8wTc/6R40UWizAIIMgxjvxZg5kmsbag==" + "resolved" "https://registry.npmjs.org/bootstrap/-/bootstrap-4.3.1.tgz" + "version" "4.3.1" + +"brace-expansion@^1.1.7": + "integrity" "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==" + "resolved" "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" + "version" "1.1.11" + dependencies: + "balanced-match" "^1.0.0" + "concat-map" "0.0.1" + +"braces@^3.0.1", "braces@~3.0.2": + "integrity" "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==" + "resolved" "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" + "version" "3.0.2" + dependencies: + "fill-range" "^7.0.1" + +"browser-process-hrtime@^1.0.0": + "integrity" "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==" + "resolved" "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz" + "version" "1.0.0" + +"browserslist@^4.0.0", "browserslist@^4.14.5", "browserslist@^4.16.0", "browserslist@^4.16.6", "browserslist@^4.17.5", "browserslist@^4.18.1", "browserslist@^4.19.1", "browserslist@>= 4", "browserslist@>=4": + "integrity" "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==" + "resolved" "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz" + "version" "4.19.1" + dependencies: + "caniuse-lite" "^1.0.30001286" + "electron-to-chromium" "^1.4.17" + "escalade" "^3.1.1" + "node-releases" "^2.0.1" + "picocolors" "^1.0.0" + +"bser@2.1.1": + "integrity" "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==" + "resolved" "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz" + "version" "2.1.1" + dependencies: + "node-int64" "^0.4.0" + +"buffer-from@^1.0.0": + "integrity" "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + "resolved" "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz" + "version" "1.1.2" + +"buffer-indexof@^1.0.0": + "integrity" "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==" + "resolved" "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz" + "version" "1.1.1" + +"builtin-modules@^3.1.0": + "integrity" "sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==" + "resolved" "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.2.0.tgz" + "version" "3.2.0" + +"bytes@3.0.0": + "integrity" "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + "resolved" "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz" + "version" "3.0.0" + +"bytes@3.1.1": + "integrity" "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==" + "resolved" "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz" + "version" "3.1.1" + +"call-bind@^1.0.0", "call-bind@^1.0.2": + "integrity" "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==" + "resolved" "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz" + "version" "1.0.2" + dependencies: + "function-bind" "^1.1.1" + "get-intrinsic" "^1.0.2" + +"callsites@^3.0.0": + "integrity" "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" + "resolved" "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" + "version" "3.1.0" + +"camel-case@^4.1.2": + "integrity" "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==" + "resolved" "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz" + "version" "4.1.2" + dependencies: + "pascal-case" "^3.1.2" + "tslib" "^2.0.3" + +"camelcase-css@^2.0.1": + "integrity" "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==" + "resolved" "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz" + "version" "2.0.1" + +"camelcase@^5.3.1": + "integrity" "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" + "resolved" "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz" + "version" "5.3.1" + +"camelcase@^6.2.0", "camelcase@^6.2.1": + "integrity" "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==" + "resolved" "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz" + "version" "6.3.0" + +"caniuse-api@^3.0.0": + "integrity" "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==" + "resolved" "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz" + "version" "3.0.0" + dependencies: + "browserslist" "^4.0.0" + "caniuse-lite" "^1.0.0" + "lodash.memoize" "^4.1.2" + "lodash.uniq" "^4.5.0" + +"caniuse-lite@^1.0.0", "caniuse-lite@^1.0.30001286", "caniuse-lite@^1.0.30001297", "caniuse-lite@^1.0.30001299": + "integrity" "sha512-iujN4+x7QzqA2NCSrS5VUy+4gLmRd4xv6vbBBsmfVqTx8bLAD8097euLqQgKxSVLvxjSDcvF1T/i9ocgnUFexw==" + "resolved" "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001299.tgz" + "version" "1.0.30001299" + +"case-sensitive-paths-webpack-plugin@^2.4.0": + "integrity" "sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==" + "resolved" "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz" + "version" "2.4.0" + +"chalk@^2.0.0", "chalk@^2.4.1", "chalk@^2.4.2": + "integrity" "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==" + "resolved" "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" + "version" "2.4.2" + dependencies: + "ansi-styles" "^3.2.1" + "escape-string-regexp" "^1.0.5" + "supports-color" "^5.3.0" + +"chalk@^4.0.0": + "integrity" "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==" + "resolved" "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" + "version" "4.1.2" + dependencies: + "ansi-styles" "^4.1.0" + "supports-color" "^7.1.0" + +"chalk@^4.1.0": + "integrity" "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==" + "resolved" "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" + "version" "4.1.2" + dependencies: + "ansi-styles" "^4.1.0" + "supports-color" "^7.1.0" + +"chalk@^4.1.2": + "integrity" "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==" + "resolved" "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" + "version" "4.1.2" + dependencies: + "ansi-styles" "^4.1.0" + "supports-color" "^7.1.0" + +"char-regex@^1.0.2": + "integrity" "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==" + "resolved" "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz" + "version" "1.0.2" + +"char-regex@^2.0.0": + "integrity" "sha512-oGu2QekBMXgyQNWPDRQ001bjvDnZe4/zBTz37TMbiKz1NbNiyiH5hRkobe7npRN6GfbGbxMYFck/vQ1r9c1VMA==" + "resolved" "https://registry.npmjs.org/char-regex/-/char-regex-2.0.0.tgz" + "version" "2.0.0" + +"check-types@^11.1.1": + "integrity" "sha512-tzWzvgePgLORb9/3a0YenggReLKAIb2owL03H2Xdoe5pKcUyWRSEQ8xfCar8t2SIAuEDwtmx2da1YB52YuHQMQ==" + "resolved" "https://registry.npmjs.org/check-types/-/check-types-11.1.2.tgz" + "version" "11.1.2" + +"chokidar@^3.4.2", "chokidar@^3.5.2": + "integrity" "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==" + "resolved" "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz" + "version" "3.5.2" + dependencies: + "anymatch" "~3.1.2" + "braces" "~3.0.2" + "glob-parent" "~5.1.2" + "is-binary-path" "~2.1.0" + "is-glob" "~4.0.1" + "normalize-path" "~3.0.0" + "readdirp" "~3.6.0" + optionalDependencies: + "fsevents" "~2.3.2" + +"chrome-trace-event@^1.0.2": + "integrity" "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==" + "resolved" "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz" + "version" "1.0.3" + +"ci-info@^3.2.0": + "integrity" "sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw==" + "resolved" "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz" + "version" "3.3.0" + +"cjs-module-lexer@^1.0.0": + "integrity" "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==" + "resolved" "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz" + "version" "1.2.2" + +"clean-css@^5.2.2": + "integrity" "sha512-/eR8ru5zyxKzpBLv9YZvMXgTSSQn7AdkMItMYynsFgGwTveCRVam9IUPFloE85B4vAIj05IuKmmEoV7/AQjT0w==" + "resolved" "https://registry.npmjs.org/clean-css/-/clean-css-5.2.2.tgz" + "version" "5.2.2" + dependencies: + "source-map" "~0.6.0" + +"clean-stack@^2.0.0": + "integrity" "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==" + "resolved" "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz" + "version" "2.2.0" + +"cliui@^7.0.2": + "integrity" "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==" + "resolved" "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz" + "version" "7.0.4" + dependencies: + "string-width" "^4.2.0" + "strip-ansi" "^6.0.0" + "wrap-ansi" "^7.0.0" + +"co@^4.6.0": + "integrity" "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" + "resolved" "https://registry.npmjs.org/co/-/co-4.6.0.tgz" + "version" "4.6.0" + +"coa@^2.0.2": + "integrity" "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==" + "resolved" "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz" + "version" "2.0.2" + dependencies: + "@types/q" "^1.5.1" + "chalk" "^2.4.1" + "q" "^1.1.2" + +"collect-v8-coverage@^1.0.0": + "integrity" "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==" + "resolved" "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz" + "version" "1.0.1" + +"color-convert@^1.9.0": + "integrity" "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==" + "resolved" "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" + "version" "1.9.3" + dependencies: + "color-name" "1.1.3" + +"color-convert@^2.0.1": + "integrity" "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==" + "resolved" "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" + "version" "2.0.1" + dependencies: + "color-name" "~1.1.4" + +"color-name@^1.1.4", "color-name@~1.1.4": + "integrity" "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "resolved" "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" + "version" "1.1.4" + +"color-name@1.1.3": + "integrity" "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + "resolved" "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" + "version" "1.1.3" + +"colord@^2.9.1": + "integrity" "sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ==" + "resolved" "https://registry.npmjs.org/colord/-/colord-2.9.2.tgz" + "version" "2.9.2" + +"colorette@^2.0.10": + "integrity" "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==" + "resolved" "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz" + "version" "2.0.16" + +"combined-stream@^1.0.8": + "integrity" "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==" + "resolved" "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" + "version" "1.0.8" + dependencies: + "delayed-stream" "~1.0.0" + +"commander@^2.20.0": + "integrity" "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + "resolved" "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz" + "version" "2.20.3" + +"commander@^7.2.0": + "integrity" "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" + "resolved" "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz" + "version" "7.2.0" + +"commander@^8.3.0": + "integrity" "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==" + "resolved" "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz" + "version" "8.3.0" + +"common-path-prefix@^3.0.0": + "integrity" "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==" + "resolved" "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz" + "version" "3.0.0" + +"common-tags@^1.8.0": + "integrity" "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==" + "resolved" "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz" + "version" "1.8.2" + +"commondir@^1.0.1": + "integrity" "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" + "resolved" "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz" + "version" "1.0.1" + +"compressible@~2.0.16": + "integrity" "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==" + "resolved" "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz" + "version" "2.0.18" + dependencies: + "mime-db" ">= 1.43.0 < 2" + +"compression@^1.7.4": + "integrity" "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==" + "resolved" "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz" + "version" "1.7.4" + dependencies: + "accepts" "~1.3.5" + "bytes" "3.0.0" + "compressible" "~2.0.16" + "debug" "2.6.9" + "on-headers" "~1.0.2" + "safe-buffer" "5.1.2" + "vary" "~1.1.2" + +"concat-map@0.0.1": + "integrity" "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "resolved" "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + "version" "0.0.1" + +"confusing-browser-globals@^1.0.11": + "integrity" "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==" + "resolved" "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz" + "version" "1.0.11" + +"connect-history-api-fallback@^1.6.0": + "integrity" "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==" + "resolved" "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz" + "version" "1.6.0" + +"content-disposition@0.5.4": + "integrity" "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==" + "resolved" "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz" + "version" "0.5.4" + dependencies: + "safe-buffer" "5.2.1" + +"content-type@~1.0.4": + "integrity" "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + "resolved" "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz" + "version" "1.0.4" + +"convert-source-map@^1.4.0", "convert-source-map@^1.6.0", "convert-source-map@^1.7.0": + "integrity" "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==" + "resolved" "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz" + "version" "1.8.0" + dependencies: + "safe-buffer" "~5.1.1" + +"cookie-signature@1.0.6": + "integrity" "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + "resolved" "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" + "version" "1.0.6" + +"cookie@0.4.1": + "integrity" "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" + "resolved" "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz" + "version" "0.4.1" + +"core-js-compat@^3.20.0", "core-js-compat@^3.20.2": + "integrity" "sha512-qZEzVQ+5Qh6cROaTPFLNS4lkvQ6mBzE3R6A6EEpssj7Zr2egMHgsy4XapdifqJDGC9CBiNv7s+ejI96rLNQFdg==" + "resolved" "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.20.2.tgz" + "version" "3.20.2" + dependencies: + "browserslist" "^4.19.1" + "semver" "7.0.0" + +"core-js-pure@^3.20.2", "core-js-pure@^3.8.1": + "integrity" "sha512-CmWHvSKn2vNL6p6StNp1EmMIfVY/pqn3JLAjfZQ8WZGPOlGoO92EkX9/Mk81i6GxvoPXjUqEQnpM3rJ5QxxIOg==" + "resolved" "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.20.2.tgz" + "version" "3.20.2" + +"core-js@^3.19.2": + "integrity" "sha512-nuqhq11DcOAbFBV4zCbKeGbKQsUDRqTX0oqx7AttUBuqe3h20ixsE039QHelbL6P4h+9kytVqyEtyZ6gsiwEYw==" + "resolved" "https://registry.npmjs.org/core-js/-/core-js-3.20.2.tgz" + "version" "3.20.2" + +"core-util-is@~1.0.0": + "integrity" "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + "resolved" "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz" + "version" "1.0.3" + +"cosmiconfig@^6.0.0": + "integrity" "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==" + "resolved" "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz" + "version" "6.0.0" + dependencies: + "@types/parse-json" "^4.0.0" + "import-fresh" "^3.1.0" + "parse-json" "^5.0.0" + "path-type" "^4.0.0" + "yaml" "^1.7.2" + +"cosmiconfig@^7.0.0", "cosmiconfig@^7.0.1": + "integrity" "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==" + "resolved" "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz" + "version" "7.0.1" + dependencies: + "@types/parse-json" "^4.0.0" + "import-fresh" "^3.2.1" + "parse-json" "^5.0.0" + "path-type" "^4.0.0" + "yaml" "^1.10.0" + +"cross-spawn@^7.0.2", "cross-spawn@^7.0.3": + "integrity" "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==" + "resolved" "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" + "version" "7.0.3" + dependencies: + "path-key" "^3.1.0" + "shebang-command" "^2.0.0" + "which" "^2.0.1" + +"crypto-random-string@^2.0.0": + "integrity" "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==" + "resolved" "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz" + "version" "2.0.0" + +"css-blank-pseudo@^3.0.2": + "integrity" "sha512-hOb1LFjRR+8ocA071xUSmg5VslJ8NGo/I2qpUpdeAYyBVCgupS5O8SEVo4SxEMYyFBNodBkzG3T1iqW9HCXxew==" + "resolved" "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.2.tgz" + "version" "3.0.2" + dependencies: + "postcss-selector-parser" "^6.0.8" + +"css-declaration-sorter@^6.0.3": + "integrity" "sha512-lpfkqS0fctcmZotJGhnxkIyJWvBXgpyi2wsFd4J8VB7wzyrT6Ch/3Q+FMNJpjK4gu1+GN5khOnpU2ZVKrLbhCw==" + "resolved" "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.1.4.tgz" + "version" "6.1.4" + dependencies: + "timsort" "^0.3.0" + +"css-has-pseudo@^3.0.3": + "integrity" "sha512-0gDYWEKaGacwxCqvQ3Ypg6wGdD1AztbMm5h1JsactG2hP2eiflj808QITmuWBpE7sjSEVrAlZhPTVd/nNMj/hQ==" + "resolved" "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-3.0.3.tgz" + "version" "3.0.3" + dependencies: + "postcss-selector-parser" "^6.0.8" + +"css-loader@^6.5.1": + "integrity" "sha512-gEy2w9AnJNnD9Kuo4XAP9VflW/ujKoS9c/syO+uWMlm5igc7LysKzPXaDoR2vroROkSwsTS2tGr1yGGEbZOYZQ==" + "resolved" "https://registry.npmjs.org/css-loader/-/css-loader-6.5.1.tgz" + "version" "6.5.1" + dependencies: + "icss-utils" "^5.1.0" + "postcss" "^8.2.15" + "postcss-modules-extract-imports" "^3.0.0" + "postcss-modules-local-by-default" "^4.0.0" + "postcss-modules-scope" "^3.0.0" + "postcss-modules-values" "^4.0.0" + "postcss-value-parser" "^4.1.0" + "semver" "^7.3.5" + +"css-minimizer-webpack-plugin@^3.2.0": + "integrity" "sha512-SHA7Hu/EiF0dOwdmV2+agvqYpG+ljlUa7Dvn1AVOmSH3N8KOERoaM9lGpstz9nGsoTjANGyUXdrxl/EwdMScRg==" + "resolved" "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.3.1.tgz" + "version" "3.3.1" + dependencies: + "cssnano" "^5.0.6" + "jest-worker" "^27.0.2" + "postcss" "^8.3.5" + "schema-utils" "^4.0.0" + "serialize-javascript" "^6.0.0" + "source-map" "^0.6.1" + +"css-prefers-color-scheme@^6.0.2": + "integrity" "sha512-gv0KQBEM+q/XdoKyznovq3KW7ocO7k+FhPP+hQR1MenJdu0uPGS6IZa9PzlbqBeS6XcZJNAoqoFxlAUW461CrA==" + "resolved" "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.2.tgz" + "version" "6.0.2" + +"css-select-base-adapter@^0.1.1": + "integrity" "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" + "resolved" "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz" + "version" "0.1.1" + +"css-select@^2.0.0": + "integrity" "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==" + "resolved" "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz" + "version" "2.1.0" + dependencies: + "boolbase" "^1.0.0" + "css-what" "^3.2.1" + "domutils" "^1.7.0" + "nth-check" "^1.0.2" + +"css-select@^4.1.3": + "integrity" "sha512-/aUslKhzkTNCQUB2qTX84lVmfia9NyjP3WpDGtj/WxhwBzWBYUV3DgUpurHTme8UTPcPlAD1DJ+b0nN/t50zDQ==" + "resolved" "https://registry.npmjs.org/css-select/-/css-select-4.2.1.tgz" + "version" "4.2.1" + dependencies: + "boolbase" "^1.0.0" + "css-what" "^5.1.0" + "domhandler" "^4.3.0" + "domutils" "^2.8.0" + "nth-check" "^2.0.1" + +"css-tree@^1.1.2": + "integrity" "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==" + "resolved" "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz" + "version" "1.1.3" + dependencies: + "mdn-data" "2.0.14" + "source-map" "^0.6.1" + +"css-tree@^1.1.3": + "integrity" "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==" + "resolved" "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz" + "version" "1.1.3" + dependencies: + "mdn-data" "2.0.14" + "source-map" "^0.6.1" + +"css-tree@1.0.0-alpha.37": + "integrity" "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==" + "resolved" "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz" + "version" "1.0.0-alpha.37" + dependencies: + "mdn-data" "2.0.4" + "source-map" "^0.6.1" + +"css-what@^3.2.1": + "integrity" "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==" + "resolved" "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz" + "version" "3.4.2" + +"css-what@^5.1.0": + "integrity" "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==" + "resolved" "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz" + "version" "5.1.0" + +"cssdb@^5.0.0": + "integrity" "sha512-/vqjXhv1x9eGkE/zO6o8ZOI7dgdZbLVLUGyVRbPgk6YipXbW87YzUCcO+Jrmi5bwJlAH6oD+MNeZyRgXea1GZw==" + "resolved" "https://registry.npmjs.org/cssdb/-/cssdb-5.1.0.tgz" + "version" "5.1.0" + +"cssesc@^3.0.0": + "integrity" "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" + "resolved" "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz" + "version" "3.0.0" + +"cssnano-preset-default@^5.1.10": + "integrity" "sha512-BcpSzUVygHMOnp9uG5rfPzTOCb0GAHQkqtUQx8j1oMNF9A1Q8hziOOhiM4bdICpmrBIU85BE64RD5XGYsVQZNA==" + "resolved" "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.1.10.tgz" + "version" "5.1.10" + dependencies: + "css-declaration-sorter" "^6.0.3" + "cssnano-utils" "^3.0.0" + "postcss-calc" "^8.2.0" + "postcss-colormin" "^5.2.3" + "postcss-convert-values" "^5.0.2" + "postcss-discard-comments" "^5.0.1" + "postcss-discard-duplicates" "^5.0.1" + "postcss-discard-empty" "^5.0.1" + "postcss-discard-overridden" "^5.0.2" + "postcss-merge-longhand" "^5.0.4" + "postcss-merge-rules" "^5.0.4" + "postcss-minify-font-values" "^5.0.2" + "postcss-minify-gradients" "^5.0.4" + "postcss-minify-params" "^5.0.3" + "postcss-minify-selectors" "^5.1.1" + "postcss-normalize-charset" "^5.0.1" + "postcss-normalize-display-values" "^5.0.2" + "postcss-normalize-positions" "^5.0.2" + "postcss-normalize-repeat-style" "^5.0.2" + "postcss-normalize-string" "^5.0.2" + "postcss-normalize-timing-functions" "^5.0.2" + "postcss-normalize-unicode" "^5.0.2" + "postcss-normalize-url" "^5.0.4" + "postcss-normalize-whitespace" "^5.0.2" + "postcss-ordered-values" "^5.0.3" + "postcss-reduce-initial" "^5.0.2" + "postcss-reduce-transforms" "^5.0.2" + "postcss-svgo" "^5.0.3" + "postcss-unique-selectors" "^5.0.2" + +"cssnano-utils@^3.0.0": + "integrity" "sha512-Pzs7/BZ6OgT+tXXuF12DKR8SmSbzUeVYCtMBbS8lI0uAm3mrYmkyqCXXPsQESI6kmLfEVBppbdVY/el3hg3nAA==" + "resolved" "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.0.0.tgz" + "version" "3.0.0" + +"cssnano@^5.0.6": + "integrity" "sha512-ppZsS7oPpi2sfiyV5+i+NbB/3GtQ+ab2Vs1azrZaXWujUSN4o+WdTxlCZIMcT9yLW3VO/5yX3vpyDaQ1nIn8CQ==" + "resolved" "https://registry.npmjs.org/cssnano/-/cssnano-5.0.15.tgz" + "version" "5.0.15" + dependencies: + "cssnano-preset-default" "^5.1.10" + "lilconfig" "^2.0.3" + "yaml" "^1.10.2" + +"csso@^4.0.2", "csso@^4.2.0": + "integrity" "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==" + "resolved" "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz" + "version" "4.2.0" + dependencies: + "css-tree" "^1.1.2" + +"cssom@^0.4.4": + "integrity" "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==" + "resolved" "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz" + "version" "0.4.4" + +"cssom@~0.3.6": + "integrity" "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" + "resolved" "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz" + "version" "0.3.8" + +"cssstyle@^2.3.0": + "integrity" "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==" + "resolved" "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz" + "version" "2.3.0" + dependencies: + "cssom" "~0.3.6" + +"damerau-levenshtein@^1.0.7": + "integrity" "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==" + "resolved" "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz" + "version" "1.0.8" + +"data-urls@^2.0.0": + "integrity" "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==" + "resolved" "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz" + "version" "2.0.0" + dependencies: + "abab" "^2.0.3" + "whatwg-mimetype" "^2.3.0" + "whatwg-url" "^8.0.0" + +"debug@^2.6.0": + "integrity" "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==" + "resolved" "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" + "version" "2.6.9" + dependencies: + "ms" "2.0.0" + +"debug@^2.6.9": + "integrity" "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==" + "resolved" "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" + "version" "2.6.9" + dependencies: + "ms" "2.0.0" + +"debug@^3.1.1": + "integrity" "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==" + "resolved" "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" + "version" "3.2.7" + dependencies: + "ms" "^2.1.1" + +"debug@^3.2.7": + "integrity" "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==" + "resolved" "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" + "version" "3.2.7" + dependencies: + "ms" "^2.1.1" + +"debug@^4.1.0", "debug@^4.1.1", "debug@^4.3.2", "debug@4": + "integrity" "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==" + "resolved" "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz" + "version" "4.3.3" + dependencies: + "ms" "2.1.2" + +"debug@2.6.9": + "integrity" "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==" + "resolved" "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" + "version" "2.6.9" + dependencies: + "ms" "2.0.0" + +"decimal.js@^10.2.1": + "integrity" "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==" + "resolved" "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz" + "version" "10.3.1" + +"dedent@^0.7.0": + "integrity" "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=" + "resolved" "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz" + "version" "0.7.0" + +"deep-equal@^1.0.1": + "integrity" "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==" + "resolved" "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz" + "version" "1.1.1" + dependencies: + "is-arguments" "^1.0.4" + "is-date-object" "^1.0.1" + "is-regex" "^1.0.4" + "object-is" "^1.0.1" + "object-keys" "^1.1.1" + "regexp.prototype.flags" "^1.2.0" + +"deep-is@^0.1.3", "deep-is@~0.1.3": + "integrity" "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" + "resolved" "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz" + "version" "0.1.4" + +"deepmerge@^4.2.2": + "integrity" "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" + "resolved" "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz" + "version" "4.2.2" + +"default-gateway@^6.0.3": + "integrity" "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==" + "resolved" "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz" + "version" "6.0.3" + dependencies: + "execa" "^5.0.0" + +"define-lazy-prop@^2.0.0": + "integrity" "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==" + "resolved" "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz" + "version" "2.0.0" + +"define-properties@^1.1.3": + "integrity" "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==" + "resolved" "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz" + "version" "1.1.3" + dependencies: + "object-keys" "^1.0.12" + +"defined@^1.0.0": + "integrity" "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=" + "resolved" "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz" + "version" "1.0.0" + +"del@^6.0.0": + "integrity" "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==" + "resolved" "https://registry.npmjs.org/del/-/del-6.0.0.tgz" + "version" "6.0.0" + dependencies: + "globby" "^11.0.1" + "graceful-fs" "^4.2.4" + "is-glob" "^4.0.1" + "is-path-cwd" "^2.2.0" + "is-path-inside" "^3.0.2" + "p-map" "^4.0.0" + "rimraf" "^3.0.2" + "slash" "^3.0.0" + +"delayed-stream@~1.0.0": + "integrity" "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + "resolved" "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" + "version" "1.0.0" + +"depd@~1.1.2": + "integrity" "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + "resolved" "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz" + "version" "1.1.2" + +"destroy@~1.0.4": + "integrity" "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + "resolved" "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz" + "version" "1.0.4" + +"detect-newline@^3.0.0": + "integrity" "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==" + "resolved" "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz" + "version" "3.1.0" + +"detect-node@^2.0.4": + "integrity" "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" + "resolved" "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz" + "version" "2.1.0" + +"detect-port-alt@^1.1.6": + "integrity" "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==" + "resolved" "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz" + "version" "1.1.6" + dependencies: + "address" "^1.0.1" + "debug" "^2.6.0" + +"detective@^5.2.0": + "integrity" "sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==" + "resolved" "https://registry.npmjs.org/detective/-/detective-5.2.0.tgz" + "version" "5.2.0" + dependencies: + "acorn-node" "^1.6.1" + "defined" "^1.0.0" + "minimist" "^1.1.1" + +"didyoumean@^1.2.2": + "integrity" "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" + "resolved" "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz" + "version" "1.2.2" + +"diff-sequences@^27.4.0": + "integrity" "sha512-YqiQzkrsmHMH5uuh8OdQFU9/ZpADnwzml8z0O5HvRNda+5UZsaX/xN+AAxfR2hWq1Y7HZnAzO9J5lJXOuDz2Ww==" + "resolved" "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.4.0.tgz" + "version" "27.4.0" + +"dir-glob@^3.0.1": + "integrity" "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==" + "resolved" "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz" + "version" "3.0.1" + dependencies: + "path-type" "^4.0.0" + +"dlv@^1.1.3": + "integrity" "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" + "resolved" "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz" + "version" "1.1.3" + +"dns-equal@^1.0.0": + "integrity" "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=" + "resolved" "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz" + "version" "1.0.0" + +"dns-packet@^1.3.1": + "integrity" "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==" + "resolved" "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz" + "version" "1.3.4" + dependencies: + "ip" "^1.1.0" + "safe-buffer" "^5.0.1" + +"dns-txt@^2.0.2": + "integrity" "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=" + "resolved" "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz" + "version" "2.0.2" + dependencies: + "buffer-indexof" "^1.0.0" + +"doctrine@^2.1.0": + "integrity" "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==" + "resolved" "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz" + "version" "2.1.0" + dependencies: + "esutils" "^2.0.2" + +"doctrine@^3.0.0": + "integrity" "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==" + "resolved" "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz" + "version" "3.0.0" + dependencies: + "esutils" "^2.0.2" + +"dom-converter@^0.2.0": + "integrity" "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==" + "resolved" "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz" + "version" "0.2.0" + dependencies: + "utila" "~0.4" + +"dom-serializer@^1.0.1": + "integrity" "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==" + "resolved" "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz" + "version" "1.3.2" + dependencies: + "domelementtype" "^2.0.1" + "domhandler" "^4.2.0" + "entities" "^2.0.0" + +"dom-serializer@0": + "integrity" "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==" + "resolved" "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz" + "version" "0.2.2" + dependencies: + "domelementtype" "^2.0.1" + "entities" "^2.0.0" + +"domelementtype@^2.0.1", "domelementtype@^2.2.0": + "integrity" "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==" + "resolved" "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz" + "version" "2.2.0" + +"domelementtype@1": + "integrity" "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + "resolved" "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz" + "version" "1.3.1" + +"domexception@^2.0.1": + "integrity" "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==" + "resolved" "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz" + "version" "2.0.1" + dependencies: + "webidl-conversions" "^5.0.0" + +"domhandler@^4.0.0", "domhandler@^4.2.0", "domhandler@^4.3.0": + "integrity" "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==" + "resolved" "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz" + "version" "4.3.0" + dependencies: + "domelementtype" "^2.2.0" + +"domutils@^1.7.0": + "integrity" "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==" + "resolved" "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz" + "version" "1.7.0" + dependencies: + "dom-serializer" "0" + "domelementtype" "1" + +"domutils@^2.5.2", "domutils@^2.8.0": + "integrity" "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==" + "resolved" "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz" + "version" "2.8.0" + dependencies: + "dom-serializer" "^1.0.1" + "domelementtype" "^2.2.0" + "domhandler" "^4.2.0" + +"dot-case@^3.0.4": + "integrity" "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==" + "resolved" "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz" + "version" "3.0.4" + dependencies: + "no-case" "^3.0.4" + "tslib" "^2.0.3" + +"dotenv-expand@^5.1.0": + "integrity" "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==" + "resolved" "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz" + "version" "5.1.0" + +"dotenv@^10.0.0": + "integrity" "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==" + "resolved" "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz" + "version" "10.0.0" + +"duplexer@^0.1.2": + "integrity" "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" + "resolved" "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz" + "version" "0.1.2" + +"ee-first@1.1.1": + "integrity" "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + "resolved" "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" + "version" "1.1.1" + +"ejs@^3.1.6": + "integrity" "sha512-9lt9Zse4hPucPkoP7FHDF0LQAlGyF9JVpnClFLFH3aSSbxmyoqINRpp/9wePWJTUl4KOQwRL72Iw3InHPDkoGw==" + "resolved" "https://registry.npmjs.org/ejs/-/ejs-3.1.6.tgz" + "version" "3.1.6" + dependencies: + "jake" "^10.6.1" + +"electron-to-chromium@^1.4.17": + "integrity" "sha512-tHGWiUUmY7GABK8+DNcr474cnZDTzD8x1736SlDosVH8+/vRJeqfaIBAEHFtMjddz/0T4rKKYsxEc8BwQRdBpw==" + "resolved" "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.44.tgz" + "version" "1.4.44" + +"emittery@^0.8.1": + "integrity" "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==" + "resolved" "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz" + "version" "0.8.1" + +"emoji-regex@^8.0.0": + "integrity" "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + "resolved" "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" + "version" "8.0.0" + +"emoji-regex@^9.2.2": + "integrity" "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + "resolved" "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz" + "version" "9.2.2" + +"emojis-list@^3.0.0": + "integrity" "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==" + "resolved" "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz" + "version" "3.0.0" + +"encodeurl@~1.0.2": + "integrity" "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + "resolved" "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz" + "version" "1.0.2" + +"enhanced-resolve@^5.8.3": + "integrity" "sha512-EGAbGvH7j7Xt2nc0E7D99La1OiEs8LnyimkRgwExpUMScN6O+3x9tIWs7PLQZVNx4YD+00skHXPXi1yQHpAmZA==" + "resolved" "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz" + "version" "5.8.3" + dependencies: + "graceful-fs" "^4.2.4" + "tapable" "^2.2.0" + +"enquirer@^2.3.5": + "integrity" "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==" + "resolved" "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz" + "version" "2.3.6" + dependencies: + "ansi-colors" "^4.1.1" + +"entities@^2.0.0": + "integrity" "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" + "resolved" "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz" + "version" "2.2.0" + +"error-ex@^1.3.1": + "integrity" "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==" + "resolved" "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz" + "version" "1.3.2" + dependencies: + "is-arrayish" "^0.2.1" + +"error-stack-parser@^2.0.6": + "integrity" "sha512-d51brTeqC+BHlwF0BhPtcYgF5nlzf9ZZ0ZIUQNZpc9ZB9qw5IJ2diTrBY9jlCJkTLITYPjmiX6OWCwH+fuyNgQ==" + "resolved" "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.0.6.tgz" + "version" "2.0.6" + dependencies: + "stackframe" "^1.1.1" + +"es-abstract@^1.17.2", "es-abstract@^1.19.0", "es-abstract@^1.19.1": + "integrity" "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==" + "resolved" "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz" + "version" "1.19.1" + dependencies: + "call-bind" "^1.0.2" + "es-to-primitive" "^1.2.1" + "function-bind" "^1.1.1" + "get-intrinsic" "^1.1.1" + "get-symbol-description" "^1.0.0" + "has" "^1.0.3" + "has-symbols" "^1.0.2" + "internal-slot" "^1.0.3" + "is-callable" "^1.2.4" + "is-negative-zero" "^2.0.1" + "is-regex" "^1.1.4" + "is-shared-array-buffer" "^1.0.1" + "is-string" "^1.0.7" + "is-weakref" "^1.0.1" + "object-inspect" "^1.11.0" + "object-keys" "^1.1.1" + "object.assign" "^4.1.2" + "string.prototype.trimend" "^1.0.4" + "string.prototype.trimstart" "^1.0.4" + "unbox-primitive" "^1.0.1" + +"es-module-lexer@^0.9.0": + "integrity" "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==" + "resolved" "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz" + "version" "0.9.3" + +"es-to-primitive@^1.2.1": + "integrity" "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==" + "resolved" "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz" + "version" "1.2.1" + dependencies: + "is-callable" "^1.1.4" + "is-date-object" "^1.0.1" + "is-symbol" "^1.0.2" + +"escalade@^3.1.1": + "integrity" "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + "resolved" "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz" + "version" "3.1.1" + +"escape-html@~1.0.3": + "integrity" "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + "resolved" "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz" + "version" "1.0.3" + +"escape-string-regexp@^1.0.5": + "integrity" "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + "resolved" "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" + "version" "1.0.5" + +"escape-string-regexp@^2.0.0": + "integrity" "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==" + "resolved" "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz" + "version" "2.0.0" + +"escape-string-regexp@^4.0.0": + "integrity" "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" + "resolved" "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" + "version" "4.0.0" + +"escodegen@^2.0.0": + "integrity" "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==" + "resolved" "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz" + "version" "2.0.0" + dependencies: + "esprima" "^4.0.1" + "estraverse" "^5.2.0" + "esutils" "^2.0.2" + "optionator" "^0.8.1" + optionalDependencies: + "source-map" "~0.6.1" + +"eslint-config-react-app@^7.0.0": + "integrity" "sha512-xyymoxtIt1EOsSaGag+/jmcywRuieQoA2JbPCjnw9HukFj9/97aGPoZVFioaotzk1K5Qt9sHO5EutZbkrAXS0g==" + "resolved" "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-7.0.0.tgz" + "version" "7.0.0" + dependencies: + "@babel/core" "^7.16.0" + "@babel/eslint-parser" "^7.16.3" + "@rushstack/eslint-patch" "^1.1.0" + "@typescript-eslint/eslint-plugin" "^5.5.0" + "@typescript-eslint/parser" "^5.5.0" + "babel-preset-react-app" "^10.0.1" + "confusing-browser-globals" "^1.0.11" + "eslint-plugin-flowtype" "^8.0.3" + "eslint-plugin-import" "^2.25.3" + "eslint-plugin-jest" "^25.3.0" + "eslint-plugin-jsx-a11y" "^6.5.1" + "eslint-plugin-react" "^7.27.1" + "eslint-plugin-react-hooks" "^4.3.0" + "eslint-plugin-testing-library" "^5.0.1" + +"eslint-import-resolver-node@^0.3.6": + "integrity" "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==" + "resolved" "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz" + "version" "0.3.6" + dependencies: + "debug" "^3.2.7" + "resolve" "^1.20.0" + +"eslint-module-utils@^2.7.2": + "integrity" "sha512-zquepFnWCY2ISMFwD/DqzaM++H+7PDzOpUvotJWm/y1BAFt5R4oeULgdrTejKqLkz7MA/tgstsUMNYc7wNdTrg==" + "resolved" "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.2.tgz" + "version" "2.7.2" + dependencies: + "debug" "^3.2.7" + "find-up" "^2.1.0" + +"eslint-plugin-flowtype@^8.0.3": + "integrity" "sha512-dX8l6qUL6O+fYPtpNRideCFSpmWOUVx5QcaGLVqe/vlDiBSe4vYljDWDETwnyFzpl7By/WVIu6rcrniCgH9BqQ==" + "resolved" "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-8.0.3.tgz" + "version" "8.0.3" + dependencies: + "lodash" "^4.17.21" + "string-natural-compare" "^3.0.1" + +"eslint-plugin-import@^2.25.3": + "integrity" "sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA==" + "resolved" "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz" + "version" "2.25.4" + dependencies: + "array-includes" "^3.1.4" + "array.prototype.flat" "^1.2.5" + "debug" "^2.6.9" + "doctrine" "^2.1.0" + "eslint-import-resolver-node" "^0.3.6" + "eslint-module-utils" "^2.7.2" + "has" "^1.0.3" + "is-core-module" "^2.8.0" + "is-glob" "^4.0.3" + "minimatch" "^3.0.4" + "object.values" "^1.1.5" + "resolve" "^1.20.0" + "tsconfig-paths" "^3.12.0" + +"eslint-plugin-jest@^25.3.0": + "integrity" "sha512-CCnwG71wvabmwq/qkz0HWIqBHQxw6pXB1uqt24dxqJ9WB34pVg49bL1sjXphlJHgTMWGhBjN1PicdyxDxrfP5A==" + "resolved" "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-25.3.4.tgz" + "version" "25.3.4" + dependencies: + "@typescript-eslint/experimental-utils" "^5.0.0" + +"eslint-plugin-jsx-a11y@^6.5.1": + "integrity" "sha512-sVCFKX9fllURnXT2JwLN5Qgo24Ug5NF6dxhkmxsMEUZhXRcGg+X3e1JbJ84YePQKBl5E0ZjAH5Q4rkdcGY99+g==" + "resolved" "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.5.1.tgz" + "version" "6.5.1" + dependencies: + "@babel/runtime" "^7.16.3" + "aria-query" "^4.2.2" + "array-includes" "^3.1.4" + "ast-types-flow" "^0.0.7" + "axe-core" "^4.3.5" + "axobject-query" "^2.2.0" + "damerau-levenshtein" "^1.0.7" + "emoji-regex" "^9.2.2" + "has" "^1.0.3" + "jsx-ast-utils" "^3.2.1" + "language-tags" "^1.0.5" + "minimatch" "^3.0.4" + +"eslint-plugin-react-hooks@^4.3.0": + "integrity" "sha512-XslZy0LnMn+84NEG9jSGR6eGqaZB3133L8xewQo3fQagbQuGt7a63gf+P1NGKZavEYEC3UXaWEAA/AqDkuN6xA==" + "resolved" "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.3.0.tgz" + "version" "4.3.0" + +"eslint-plugin-react@^7.27.1": + "integrity" "sha512-IOlFIRHzWfEQQKcAD4iyYDndHwTQiCMcJVJjxempf203jnNLUnW34AXLrV33+nEXoifJE2ZEGmcjKPL8957eSw==" + "resolved" "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.28.0.tgz" + "version" "7.28.0" + dependencies: + "array-includes" "^3.1.4" + "array.prototype.flatmap" "^1.2.5" + "doctrine" "^2.1.0" + "estraverse" "^5.3.0" + "jsx-ast-utils" "^2.4.1 || ^3.0.0" + "minimatch" "^3.0.4" + "object.entries" "^1.1.5" + "object.fromentries" "^2.0.5" + "object.hasown" "^1.1.0" + "object.values" "^1.1.5" + "prop-types" "^15.7.2" + "resolve" "^2.0.0-next.3" + "semver" "^6.3.0" + "string.prototype.matchall" "^4.0.6" + +"eslint-plugin-testing-library@^5.0.1": + "integrity" "sha512-tKZ9G+HnIOnYAhXeoBCiAT8LOdU3m1VquBTKsBW/5zAaB30vq7gC60DIayPfMJt8EZBlqPVzGqSN57sIFmTunQ==" + "resolved" "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.0.3.tgz" + "version" "5.0.3" + dependencies: + "@typescript-eslint/experimental-utils" "^5.9.0" + +"eslint-scope@^5.1.1": + "integrity" "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==" + "resolved" "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz" + "version" "5.1.1" + dependencies: + "esrecurse" "^4.3.0" + "estraverse" "^4.1.1" + +"eslint-scope@^7.1.0": + "integrity" "sha512-aWwkhnS0qAXqNOgKOK0dJ2nvzEbhEvpy8OlJ9kZ0FeZnA6zpjv1/Vei+puGFFX7zkPCkHHXb7IDX3A+7yPrRWg==" + "resolved" "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.0.tgz" + "version" "7.1.0" + dependencies: + "esrecurse" "^4.3.0" + "estraverse" "^5.2.0" + +"eslint-scope@5.1.1": + "integrity" "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==" + "resolved" "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz" + "version" "5.1.1" + dependencies: + "esrecurse" "^4.3.0" + "estraverse" "^4.1.1" + +"eslint-utils@^3.0.0": + "integrity" "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==" + "resolved" "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz" + "version" "3.0.0" + dependencies: + "eslint-visitor-keys" "^2.0.0" + +"eslint-visitor-keys@^2.0.0": + "integrity" "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==" + "resolved" "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz" + "version" "2.1.0" + +"eslint-visitor-keys@^2.1.0": + "integrity" "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==" + "resolved" "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz" + "version" "2.1.0" + +"eslint-visitor-keys@^3.0.0", "eslint-visitor-keys@^3.1.0": + "integrity" "sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA==" + "resolved" "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.1.0.tgz" + "version" "3.1.0" + +"eslint-webpack-plugin@^3.1.1": + "integrity" "sha512-xSucskTN9tOkfW7so4EaiFIkulWLXwCB/15H917lR6pTv0Zot6/fetFucmENRb7J5whVSFKIvwnrnsa78SG2yg==" + "resolved" "https://registry.npmjs.org/eslint-webpack-plugin/-/eslint-webpack-plugin-3.1.1.tgz" + "version" "3.1.1" + dependencies: + "@types/eslint" "^7.28.2" + "jest-worker" "^27.3.1" + "micromatch" "^4.0.4" + "normalize-path" "^3.0.0" + "schema-utils" "^3.1.1" + +"eslint@*", "eslint@^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8", "eslint@^3 || ^4 || ^5 || ^6 || ^7 || ^8", "eslint@^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0", "eslint@^6.0.0 || ^7.0.0 || ^8.0.0", "eslint@^7.0.0 || ^8.0.0", "eslint@^7.5.0 || ^8.0.0", "eslint@^8.0.0", "eslint@^8.1.0", "eslint@^8.3.0", "eslint@>= 6", "eslint@>=5": + "integrity" "sha512-UvxdOJ7mXFlw7iuHZA4jmzPaUqIw54mZrv+XPYKNbKdLR0et4rf60lIZUU9kiNtnzzMzGWxMV+tQ7uG7JG8DPw==" + "resolved" "https://registry.npmjs.org/eslint/-/eslint-8.6.0.tgz" + "version" "8.6.0" + dependencies: + "@eslint/eslintrc" "^1.0.5" + "@humanwhocodes/config-array" "^0.9.2" + "ajv" "^6.10.0" + "chalk" "^4.0.0" + "cross-spawn" "^7.0.2" + "debug" "^4.3.2" + "doctrine" "^3.0.0" + "enquirer" "^2.3.5" + "escape-string-regexp" "^4.0.0" + "eslint-scope" "^7.1.0" + "eslint-utils" "^3.0.0" + "eslint-visitor-keys" "^3.1.0" + "espree" "^9.3.0" + "esquery" "^1.4.0" + "esutils" "^2.0.2" + "fast-deep-equal" "^3.1.3" + "file-entry-cache" "^6.0.1" + "functional-red-black-tree" "^1.0.1" + "glob-parent" "^6.0.1" + "globals" "^13.6.0" + "ignore" "^4.0.6" + "import-fresh" "^3.0.0" + "imurmurhash" "^0.1.4" + "is-glob" "^4.0.0" + "js-yaml" "^4.1.0" + "json-stable-stringify-without-jsonify" "^1.0.1" + "levn" "^0.4.1" + "lodash.merge" "^4.6.2" + "minimatch" "^3.0.4" + "natural-compare" "^1.4.0" + "optionator" "^0.9.1" + "progress" "^2.0.0" + "regexpp" "^3.2.0" + "semver" "^7.2.1" + "strip-ansi" "^6.0.1" + "strip-json-comments" "^3.1.0" + "text-table" "^0.2.0" + "v8-compile-cache" "^2.0.3" + +"espree@^9.2.0", "espree@^9.3.0": + "integrity" "sha512-d/5nCsb0JcqsSEeQzFZ8DH1RmxPcglRWh24EFTlUEmCKoehXGdpsx0RkHDubqUI8LSAIKMQp4r9SzQ3n+sm4HQ==" + "resolved" "https://registry.npmjs.org/espree/-/espree-9.3.0.tgz" + "version" "9.3.0" + dependencies: + "acorn" "^8.7.0" + "acorn-jsx" "^5.3.1" + "eslint-visitor-keys" "^3.1.0" + +"esprima@^4.0.0", "esprima@^4.0.1": + "integrity" "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + "resolved" "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz" + "version" "4.0.1" + +"esquery@^1.4.0": + "integrity" "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==" + "resolved" "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz" + "version" "1.4.0" + dependencies: + "estraverse" "^5.1.0" + +"esrecurse@^4.3.0": + "integrity" "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==" + "resolved" "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz" + "version" "4.3.0" + dependencies: + "estraverse" "^5.2.0" + +"estraverse@^4.1.1": + "integrity" "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" + "resolved" "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz" + "version" "4.3.0" + +"estraverse@^5.1.0", "estraverse@^5.2.0", "estraverse@^5.3.0": + "integrity" "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" + "resolved" "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz" + "version" "5.3.0" + +"estree-walker@^1.0.1": + "integrity" "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==" + "resolved" "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz" + "version" "1.0.1" + +"esutils@^2.0.2": + "integrity" "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + "resolved" "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" + "version" "2.0.3" + +"etag@~1.8.1": + "integrity" "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + "resolved" "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz" + "version" "1.8.1" + +"eventemitter3@^4.0.0": + "integrity" "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + "resolved" "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz" + "version" "4.0.7" + +"events@^3.2.0": + "integrity" "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" + "resolved" "https://registry.npmjs.org/events/-/events-3.3.0.tgz" + "version" "3.3.0" + +"execa@^5.0.0": + "integrity" "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==" + "resolved" "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz" + "version" "5.1.1" + dependencies: + "cross-spawn" "^7.0.3" + "get-stream" "^6.0.0" + "human-signals" "^2.1.0" + "is-stream" "^2.0.0" + "merge-stream" "^2.0.0" + "npm-run-path" "^4.0.1" + "onetime" "^5.1.2" + "signal-exit" "^3.0.3" + "strip-final-newline" "^2.0.0" + +"exit@^0.1.2": + "integrity" "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=" + "resolved" "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" + "version" "0.1.2" + +"expect@^27.4.6": + "integrity" "sha512-1M/0kAALIaj5LaG66sFJTbRsWTADnylly82cu4bspI0nl+pgP4E6Bh/aqdHlTUjul06K7xQnnrAoqfxVU0+/ag==" + "resolved" "https://registry.npmjs.org/expect/-/expect-27.4.6.tgz" + "version" "27.4.6" + dependencies: + "@jest/types" "^27.4.2" + "jest-get-type" "^27.4.0" + "jest-matcher-utils" "^27.4.6" + "jest-message-util" "^27.4.6" + +"express@^4.17.1": + "integrity" "sha512-oxlxJxcQlYwqPWKVJJtvQiwHgosH/LrLSPA+H4UxpyvSS6jC5aH+5MoHFM+KABgTOt0APue4w66Ha8jCUo9QGg==" + "resolved" "https://registry.npmjs.org/express/-/express-4.17.2.tgz" + "version" "4.17.2" + dependencies: + "accepts" "~1.3.7" + "array-flatten" "1.1.1" + "body-parser" "1.19.1" + "content-disposition" "0.5.4" + "content-type" "~1.0.4" + "cookie" "0.4.1" + "cookie-signature" "1.0.6" + "debug" "2.6.9" + "depd" "~1.1.2" + "encodeurl" "~1.0.2" + "escape-html" "~1.0.3" + "etag" "~1.8.1" + "finalhandler" "~1.1.2" + "fresh" "0.5.2" + "merge-descriptors" "1.0.1" + "methods" "~1.1.2" + "on-finished" "~2.3.0" + "parseurl" "~1.3.3" + "path-to-regexp" "0.1.7" + "proxy-addr" "~2.0.7" + "qs" "6.9.6" + "range-parser" "~1.2.1" + "safe-buffer" "5.2.1" + "send" "0.17.2" + "serve-static" "1.14.2" + "setprototypeof" "1.2.0" + "statuses" "~1.5.0" + "type-is" "~1.6.18" + "utils-merge" "1.0.1" + "vary" "~1.1.2" + +"fast-deep-equal@^3.1.1", "fast-deep-equal@^3.1.3": + "integrity" "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + "resolved" "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" + "version" "3.1.3" + +"fast-glob@^3.2.7", "fast-glob@^3.2.9": + "integrity" "sha512-s9nFhFnvR63wls6/kM88kQqDhMu0AfdjqouE2l5GVQPbqLgyFjjU5ry/r2yKsJxpb9Py1EYNqieFrmMaX4v++A==" + "resolved" "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.10.tgz" + "version" "3.2.10" + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + "glob-parent" "^5.1.2" + "merge2" "^1.3.0" + "micromatch" "^4.0.4" + +"fast-json-stable-stringify@^2.0.0", "fast-json-stable-stringify@^2.1.0": + "integrity" "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + "resolved" "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" + "version" "2.1.0" + +"fast-levenshtein@^2.0.6", "fast-levenshtein@~2.0.6": + "integrity" "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" + "resolved" "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" + "version" "2.0.6" + +"fastq@^1.6.0": + "integrity" "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==" + "resolved" "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz" + "version" "1.13.0" + dependencies: + "reusify" "^1.0.4" + +"faye-websocket@^0.11.3": + "integrity" "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==" + "resolved" "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz" + "version" "0.11.4" + dependencies: + "websocket-driver" ">=0.5.1" + +"fb-watchman@^2.0.0": + "integrity" "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==" + "resolved" "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz" + "version" "2.0.1" + dependencies: + "bser" "2.1.1" + +"file-entry-cache@^6.0.1": + "integrity" "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==" + "resolved" "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz" + "version" "6.0.1" + dependencies: + "flat-cache" "^3.0.4" + +"file-loader@^6.2.0": + "integrity" "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==" + "resolved" "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz" + "version" "6.2.0" + dependencies: + "loader-utils" "^2.0.0" + "schema-utils" "^3.0.0" + +"filelist@^1.0.1": + "integrity" "sha512-z7O0IS8Plc39rTCq6i6iHxk43duYOn8uFJiWSewIq0Bww1RNybVHSCjahmcC87ZqAm4OTvFzlzeGu3XAzG1ctQ==" + "resolved" "https://registry.npmjs.org/filelist/-/filelist-1.0.2.tgz" + "version" "1.0.2" + dependencies: + "minimatch" "^3.0.4" + +"filesize@^8.0.6": + "integrity" "sha512-sHvRqTiwdmcuzqet7iVwsbwF6UrV3wIgDf2SHNdY1Hgl8PC45HZg/0xtdw6U2izIV4lccnrY9ftl6wZFNdjYMg==" + "resolved" "https://registry.npmjs.org/filesize/-/filesize-8.0.6.tgz" + "version" "8.0.6" + +"fill-range@^7.0.1": + "integrity" "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==" + "resolved" "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz" + "version" "7.0.1" + dependencies: + "to-regex-range" "^5.0.1" + +"finalhandler@~1.1.2": + "integrity" "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==" + "resolved" "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz" + "version" "1.1.2" + dependencies: + "debug" "2.6.9" + "encodeurl" "~1.0.2" + "escape-html" "~1.0.3" + "on-finished" "~2.3.0" + "parseurl" "~1.3.3" + "statuses" "~1.5.0" + "unpipe" "~1.0.0" + +"find-cache-dir@^3.3.1": + "integrity" "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==" + "resolved" "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz" + "version" "3.3.2" + dependencies: + "commondir" "^1.0.1" + "make-dir" "^3.0.2" + "pkg-dir" "^4.1.0" + +"find-up@^2.1.0": + "integrity" "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=" + "resolved" "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz" + "version" "2.1.0" + dependencies: + "locate-path" "^2.0.0" + +"find-up@^3.0.0": + "integrity" "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==" + "resolved" "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz" + "version" "3.0.0" + dependencies: + "locate-path" "^3.0.0" + +"find-up@^4.0.0": + "integrity" "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==" + "resolved" "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" + "version" "4.1.0" + dependencies: + "locate-path" "^5.0.0" + "path-exists" "^4.0.0" + +"find-up@^4.1.0": + "integrity" "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==" + "resolved" "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" + "version" "4.1.0" + dependencies: + "locate-path" "^5.0.0" + "path-exists" "^4.0.0" + +"find-up@^5.0.0": + "integrity" "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==" + "resolved" "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz" + "version" "5.0.0" + dependencies: + "locate-path" "^6.0.0" + "path-exists" "^4.0.0" + +"flat-cache@^3.0.4": + "integrity" "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==" + "resolved" "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz" + "version" "3.0.4" + dependencies: + "flatted" "^3.1.0" + "rimraf" "^3.0.2" + +"flatted@^3.1.0": + "integrity" "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==" + "resolved" "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz" + "version" "3.2.4" + +"follow-redirects@^1.0.0": + "integrity" "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==" + "resolved" "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz" + "version" "1.14.8" + +"fork-ts-checker-webpack-plugin@^6.5.0": + "integrity" "sha512-cS178Y+xxtIjEUorcHddKS7yCMlrDPV31mt47blKKRfMd70Kxu5xruAFE2o9sDY6wVC5deuob/u/alD04YYHnw==" + "resolved" "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.0.tgz" + "version" "6.5.0" + dependencies: + "@babel/code-frame" "^7.8.3" + "@types/json-schema" "^7.0.5" + "chalk" "^4.1.0" + "chokidar" "^3.4.2" + "cosmiconfig" "^6.0.0" + "deepmerge" "^4.2.2" + "fs-extra" "^9.0.0" + "glob" "^7.1.6" + "memfs" "^3.1.2" + "minimatch" "^3.0.4" + "schema-utils" "2.7.0" + "semver" "^7.3.2" + "tapable" "^1.0.0" + +"form-data@^3.0.0": + "integrity" "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==" + "resolved" "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz" + "version" "3.0.1" + dependencies: + "asynckit" "^0.4.0" + "combined-stream" "^1.0.8" + "mime-types" "^2.1.12" + +"forwarded@0.2.0": + "integrity" "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + "resolved" "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz" + "version" "0.2.0" + +"fraction.js@^4.1.2": + "integrity" "sha512-o2RiJQ6DZaR/5+Si0qJUIy637QMRudSi9kU/FFzx9EZazrIdnBgpU+3sEWCxAVhH2RtxW2Oz+T4p2o8uOPVcgA==" + "resolved" "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.2.tgz" + "version" "4.1.2" + +"fresh@0.5.2": + "integrity" "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + "resolved" "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz" + "version" "0.5.2" + +"fs-extra@^10.0.0": + "integrity" "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==" + "resolved" "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz" + "version" "10.0.0" + dependencies: + "graceful-fs" "^4.2.0" + "jsonfile" "^6.0.1" + "universalify" "^2.0.0" + +"fs-extra@^9.0.0": + "integrity" "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==" + "resolved" "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz" + "version" "9.1.0" + dependencies: + "at-least-node" "^1.0.0" + "graceful-fs" "^4.2.0" + "jsonfile" "^6.0.1" + "universalify" "^2.0.0" + +"fs-extra@^9.0.1": + "integrity" "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==" + "resolved" "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz" + "version" "9.1.0" + dependencies: + "at-least-node" "^1.0.0" + "graceful-fs" "^4.2.0" + "jsonfile" "^6.0.1" + "universalify" "^2.0.0" + +"fs-monkey@1.0.3": + "integrity" "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==" + "resolved" "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz" + "version" "1.0.3" + +"fs.realpath@^1.0.0": + "integrity" "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + "resolved" "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" + "version" "1.0.0" + +"fsevents@^2.3.2", "fsevents@~2.3.2": + "integrity" "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==" + "resolved" "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" + "version" "2.3.2" + +"function-bind@^1.1.1": + "integrity" "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "resolved" "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" + "version" "1.1.1" + +"functional-red-black-tree@^1.0.1": + "integrity" "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=" + "resolved" "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz" + "version" "1.0.1" + +"gensync@^1.0.0-beta.2": + "integrity" "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==" + "resolved" "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz" + "version" "1.0.0-beta.2" + +"get-caller-file@^2.0.5": + "integrity" "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + "resolved" "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" + "version" "2.0.5" + +"get-intrinsic@^1.0.2", "get-intrinsic@^1.1.0", "get-intrinsic@^1.1.1": + "integrity" "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==" + "resolved" "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz" + "version" "1.1.1" + dependencies: + "function-bind" "^1.1.1" + "has" "^1.0.3" + "has-symbols" "^1.0.1" + +"get-own-enumerable-property-symbols@^3.0.0": + "integrity" "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==" + "resolved" "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz" + "version" "3.0.2" + +"get-package-type@^0.1.0": + "integrity" "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==" + "resolved" "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz" + "version" "0.1.0" + +"get-stream@^6.0.0": + "integrity" "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" + "resolved" "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz" + "version" "6.0.1" + +"get-symbol-description@^1.0.0": + "integrity" "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==" + "resolved" "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz" + "version" "1.0.0" + dependencies: + "call-bind" "^1.0.2" + "get-intrinsic" "^1.1.1" + +"glob-parent@^5.1.2": + "integrity" "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==" + "resolved" "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" + "version" "5.1.2" + dependencies: + "is-glob" "^4.0.1" + +"glob-parent@^6.0.1", "glob-parent@^6.0.2": + "integrity" "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==" + "resolved" "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz" + "version" "6.0.2" + dependencies: + "is-glob" "^4.0.3" + +"glob-parent@~5.1.2": + "integrity" "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==" + "resolved" "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" + "version" "5.1.2" + dependencies: + "is-glob" "^4.0.1" + +"glob-to-regexp@^0.4.1": + "integrity" "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" + "resolved" "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz" + "version" "0.4.1" + +"glob@^7.1.1", "glob@^7.1.2", "glob@^7.1.3", "glob@^7.1.4", "glob@^7.1.6": + "integrity" "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==" + "resolved" "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz" + "version" "7.2.0" + dependencies: + "fs.realpath" "^1.0.0" + "inflight" "^1.0.4" + "inherits" "2" + "minimatch" "^3.0.4" + "once" "^1.3.0" + "path-is-absolute" "^1.0.0" + +"global-modules@^2.0.0": + "integrity" "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==" + "resolved" "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz" + "version" "2.0.0" + dependencies: + "global-prefix" "^3.0.0" + +"global-prefix@^3.0.0": + "integrity" "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==" + "resolved" "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz" + "version" "3.0.0" + dependencies: + "ini" "^1.3.5" + "kind-of" "^6.0.2" + "which" "^1.3.1" + +"globals@^11.1.0": + "integrity" "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" + "resolved" "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz" + "version" "11.12.0" + +"globals@^13.6.0": + "integrity" "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==" + "resolved" "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz" + "version" "13.12.0" + dependencies: + "type-fest" "^0.20.2" + +"globals@^13.9.0": + "integrity" "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==" + "resolved" "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz" + "version" "13.12.0" + dependencies: + "type-fest" "^0.20.2" + +"globby@^11.0.1", "globby@^11.0.4": + "integrity" "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==" + "resolved" "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz" + "version" "11.1.0" + dependencies: + "array-union" "^2.1.0" + "dir-glob" "^3.0.1" + "fast-glob" "^3.2.9" + "ignore" "^5.2.0" + "merge2" "^1.4.1" + "slash" "^3.0.0" + +"graceful-fs@^4.1.2", "graceful-fs@^4.1.6", "graceful-fs@^4.2.0", "graceful-fs@^4.2.4", "graceful-fs@^4.2.6", "graceful-fs@^4.2.9": + "integrity" "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==" + "resolved" "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz" + "version" "4.2.9" + +"gzip-size@^6.0.0": + "integrity" "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==" + "resolved" "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz" + "version" "6.0.0" + dependencies: + "duplexer" "^0.1.2" + +"handle-thing@^2.0.0": + "integrity" "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==" + "resolved" "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz" + "version" "2.0.1" + +"harmony-reflect@^1.4.6": + "integrity" "sha512-WJTeyp0JzGtHcuMsi7rw2VwtkvLa+JyfEKJCFyfcS0+CDkjQ5lHPu7zEhFZP+PDSRrEgXa5Ah0l1MbgbE41XjA==" + "resolved" "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.1.tgz" + "version" "1.6.1" + +"has-bigints@^1.0.1": + "integrity" "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==" + "resolved" "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz" + "version" "1.0.1" + +"has-flag@^3.0.0": + "integrity" "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + "resolved" "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" + "version" "3.0.0" + +"has-flag@^4.0.0": + "integrity" "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + "resolved" "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" + "version" "4.0.0" + +"has-symbols@^1.0.1", "has-symbols@^1.0.2": + "integrity" "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" + "resolved" "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz" + "version" "1.0.2" + +"has-tostringtag@^1.0.0": + "integrity" "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==" + "resolved" "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz" + "version" "1.0.0" + dependencies: + "has-symbols" "^1.0.2" + +"has@^1.0.3": + "integrity" "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==" + "resolved" "https://registry.npmjs.org/has/-/has-1.0.3.tgz" + "version" "1.0.3" + dependencies: + "function-bind" "^1.1.1" + +"he@^1.2.0": + "integrity" "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" + "resolved" "https://registry.npmjs.org/he/-/he-1.2.0.tgz" + "version" "1.2.0" + +"hoopy@^0.1.4": + "integrity" "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==" + "resolved" "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz" + "version" "0.1.4" + +"hpack.js@^2.1.6": + "integrity" "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=" + "resolved" "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz" + "version" "2.1.6" + dependencies: + "inherits" "^2.0.1" + "obuf" "^1.0.0" + "readable-stream" "^2.0.1" + "wbuf" "^1.1.0" + +"html-encoding-sniffer@^2.0.1": + "integrity" "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==" + "resolved" "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz" + "version" "2.0.1" + dependencies: + "whatwg-encoding" "^1.0.5" + +"html-entities@^2.1.0", "html-entities@^2.3.2": + "integrity" "sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ==" + "resolved" "https://registry.npmjs.org/html-entities/-/html-entities-2.3.2.tgz" + "version" "2.3.2" + +"html-escaper@^2.0.0": + "integrity" "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==" + "resolved" "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz" + "version" "2.0.2" + +"html-minifier-terser@^6.0.2": + "integrity" "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==" + "resolved" "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz" + "version" "6.1.0" + dependencies: + "camel-case" "^4.1.2" + "clean-css" "^5.2.2" + "commander" "^8.3.0" + "he" "^1.2.0" + "param-case" "^3.0.4" + "relateurl" "^0.2.7" + "terser" "^5.10.0" + +"html-webpack-plugin@^5.5.0": + "integrity" "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==" + "resolved" "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz" + "version" "5.5.0" + dependencies: + "@types/html-minifier-terser" "^6.0.0" + "html-minifier-terser" "^6.0.2" + "lodash" "^4.17.21" + "pretty-error" "^4.0.0" + "tapable" "^2.0.0" + +"htmlparser2@^6.1.0": + "integrity" "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==" + "resolved" "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz" + "version" "6.1.0" + dependencies: + "domelementtype" "^2.0.1" + "domhandler" "^4.0.0" + "domutils" "^2.5.2" + "entities" "^2.0.0" + +"http-deceiver@^1.2.7": + "integrity" "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=" + "resolved" "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz" + "version" "1.2.7" + +"http-errors@~1.6.2": + "integrity" "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=" + "resolved" "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz" + "version" "1.6.3" + dependencies: + "depd" "~1.1.2" + "inherits" "2.0.3" + "setprototypeof" "1.1.0" + "statuses" ">= 1.4.0 < 2" + +"http-errors@1.8.1": + "integrity" "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==" + "resolved" "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz" + "version" "1.8.1" + dependencies: + "depd" "~1.1.2" + "inherits" "2.0.4" + "setprototypeof" "1.2.0" + "statuses" ">= 1.5.0 < 2" + "toidentifier" "1.0.1" + +"http-parser-js@>=0.5.1": + "integrity" "sha512-x+JVEkO2PoM8qqpbPbOL3cqHPwerep7OwzK7Ay+sMQjKzaKCqWvjoXm5tqMP9tXWWTnTzAjIhXg+J99XYuPhPA==" + "resolved" "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.5.tgz" + "version" "0.5.5" + +"http-proxy-agent@^4.0.1": + "integrity" "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==" + "resolved" "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz" + "version" "4.0.1" + dependencies: + "@tootallnate/once" "1" + "agent-base" "6" + "debug" "4" + +"http-proxy-middleware@^2.0.0": + "integrity" "sha512-cfaXRVoZxSed/BmkA7SwBVNI9Kj7HFltaE5rqYOub5kWzWZ+gofV2koVN1j2rMW7pEfSSlCHGJ31xmuyFyfLOg==" + "resolved" "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.1.tgz" + "version" "2.0.1" + dependencies: + "@types/http-proxy" "^1.17.5" + "http-proxy" "^1.18.1" + "is-glob" "^4.0.1" + "is-plain-obj" "^3.0.0" + "micromatch" "^4.0.2" + +"http-proxy@^1.18.1": + "integrity" "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==" + "resolved" "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz" + "version" "1.18.1" + dependencies: + "eventemitter3" "^4.0.0" + "follow-redirects" "^1.0.0" + "requires-port" "^1.0.0" + +"https-proxy-agent@^5.0.0": + "integrity" "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==" + "resolved" "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz" + "version" "5.0.0" + dependencies: + "agent-base" "6" + "debug" "4" + +"human-signals@^2.1.0": + "integrity" "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==" + "resolved" "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" + "version" "2.1.0" + +"iconv-lite@^0.6.3": + "integrity" "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==" + "resolved" "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz" + "version" "0.6.3" + dependencies: + "safer-buffer" ">= 2.1.2 < 3.0.0" + +"iconv-lite@0.4.24": + "integrity" "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==" + "resolved" "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" + "version" "0.4.24" + dependencies: + "safer-buffer" ">= 2.1.2 < 3" + +"icss-utils@^5.0.0", "icss-utils@^5.1.0": + "integrity" "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==" + "resolved" "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz" + "version" "5.1.0" + +"idb@^6.1.4": + "integrity" "sha512-IJtugpKkiVXQn5Y+LteyBCNk1N8xpGV3wWZk9EVtZWH8DYkjBn0bX1XnGP9RkyZF0sAcywa6unHqSWKe7q4LGw==" + "resolved" "https://registry.npmjs.org/idb/-/idb-6.1.5.tgz" + "version" "6.1.5" + +"identity-obj-proxy@^3.0.0": + "integrity" "sha1-lNK9qWCERT7zb7xarsN+D3nx/BQ=" + "resolved" "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz" + "version" "3.0.0" + dependencies: + "harmony-reflect" "^1.4.6" + +"ignore@^4.0.6": + "integrity" "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==" + "resolved" "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz" + "version" "4.0.6" + +"ignore@^5.1.8": + "integrity" "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==" + "resolved" "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz" + "version" "5.2.0" + +"ignore@^5.2.0": + "integrity" "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==" + "resolved" "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz" + "version" "5.2.0" + +"immer@^9.0.7": + "integrity" "sha512-lk7UNmSbAukB5B6dh9fnh5D0bJTOFKxVg2cyJWTYrWRfhLrLMBquONcUs3aFq507hNoIZEDDh8lb8UtOizSMhA==" + "resolved" "https://registry.npmjs.org/immer/-/immer-9.0.12.tgz" + "version" "9.0.12" + +"import-fresh@^3.0.0", "import-fresh@^3.1.0", "import-fresh@^3.2.1": + "integrity" "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==" + "resolved" "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz" + "version" "3.3.0" + dependencies: + "parent-module" "^1.0.0" + "resolve-from" "^4.0.0" + +"import-local@^3.0.2": + "integrity" "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==" + "resolved" "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz" + "version" "3.1.0" + dependencies: + "pkg-dir" "^4.2.0" + "resolve-cwd" "^3.0.0" + +"imurmurhash@^0.1.4": + "integrity" "sha1-khi5srkoojixPcT7a21XbyMUU+o=" + "resolved" "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" + "version" "0.1.4" + +"indent-string@^4.0.0": + "integrity" "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==" + "resolved" "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz" + "version" "4.0.0" + +"inflight@^1.0.4": + "integrity" "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=" + "resolved" "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" + "version" "1.0.6" + dependencies: + "once" "^1.3.0" + "wrappy" "1" + +"inherits@^2.0.1", "inherits@^2.0.3", "inherits@~2.0.3", "inherits@2", "inherits@2.0.4": + "integrity" "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "resolved" "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" + "version" "2.0.4" + +"inherits@2.0.3": + "integrity" "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "resolved" "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + "version" "2.0.3" + +"ini@^1.3.5": + "integrity" "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + "resolved" "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz" + "version" "1.3.8" + +"internal-slot@^1.0.3": + "integrity" "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==" + "resolved" "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz" + "version" "1.0.3" + dependencies: + "get-intrinsic" "^1.1.0" + "has" "^1.0.3" + "side-channel" "^1.0.4" + +"ip@^1.1.0": + "integrity" "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" + "resolved" "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz" + "version" "1.1.5" + +"ipaddr.js@^2.0.1": + "integrity" "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==" + "resolved" "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz" + "version" "2.0.1" + +"ipaddr.js@1.9.1": + "integrity" "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + "resolved" "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz" + "version" "1.9.1" + +"is-arguments@^1.0.4": + "integrity" "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==" + "resolved" "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz" + "version" "1.1.1" + dependencies: + "call-bind" "^1.0.2" + "has-tostringtag" "^1.0.0" + +"is-arrayish@^0.2.1": + "integrity" "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + "resolved" "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" + "version" "0.2.1" + +"is-bigint@^1.0.1": + "integrity" "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==" + "resolved" "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz" + "version" "1.0.4" + dependencies: + "has-bigints" "^1.0.1" + +"is-binary-path@~2.1.0": + "integrity" "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==" + "resolved" "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz" + "version" "2.1.0" + dependencies: + "binary-extensions" "^2.0.0" + +"is-boolean-object@^1.1.0": + "integrity" "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==" + "resolved" "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz" + "version" "1.1.2" + dependencies: + "call-bind" "^1.0.2" + "has-tostringtag" "^1.0.0" + +"is-callable@^1.1.4", "is-callable@^1.2.4": + "integrity" "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==" + "resolved" "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz" + "version" "1.2.4" + +"is-core-module@^2.2.0", "is-core-module@^2.8.0": + "integrity" "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==" + "resolved" "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz" + "version" "2.8.1" + dependencies: + "has" "^1.0.3" + +"is-date-object@^1.0.1": + "integrity" "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==" + "resolved" "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz" + "version" "1.0.5" + dependencies: + "has-tostringtag" "^1.0.0" + +"is-docker@^2.0.0", "is-docker@^2.1.1": + "integrity" "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==" + "resolved" "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz" + "version" "2.2.1" + +"is-extglob@^2.1.1": + "integrity" "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + "resolved" "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" + "version" "2.1.1" + +"is-fullwidth-code-point@^3.0.0": + "integrity" "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + "resolved" "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" + "version" "3.0.0" + +"is-generator-fn@^2.0.0": + "integrity" "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==" + "resolved" "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz" + "version" "2.1.0" + +"is-glob@^4.0.0", "is-glob@^4.0.1", "is-glob@^4.0.3", "is-glob@~4.0.1": + "integrity" "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==" + "resolved" "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" + "version" "4.0.3" + dependencies: + "is-extglob" "^2.1.1" + +"is-module@^1.0.0": + "integrity" "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=" + "resolved" "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz" + "version" "1.0.0" + +"is-negative-zero@^2.0.1": + "integrity" "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==" + "resolved" "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz" + "version" "2.0.2" + +"is-number-object@^1.0.4": + "integrity" "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==" + "resolved" "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz" + "version" "1.0.6" + dependencies: + "has-tostringtag" "^1.0.0" + +"is-number@^7.0.0": + "integrity" "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + "resolved" "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" + "version" "7.0.0" + +"is-obj@^1.0.1": + "integrity" "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=" + "resolved" "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz" + "version" "1.0.1" + +"is-path-cwd@^2.2.0": + "integrity" "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==" + "resolved" "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz" + "version" "2.2.0" + +"is-path-inside@^3.0.2": + "integrity" "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==" + "resolved" "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz" + "version" "3.0.3" + +"is-plain-obj@^3.0.0": + "integrity" "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==" + "resolved" "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz" + "version" "3.0.0" + +"is-potential-custom-element-name@^1.0.1": + "integrity" "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" + "resolved" "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz" + "version" "1.0.1" + +"is-regex@^1.0.4", "is-regex@^1.1.4": + "integrity" "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==" + "resolved" "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz" + "version" "1.1.4" + dependencies: + "call-bind" "^1.0.2" + "has-tostringtag" "^1.0.0" + +"is-regexp@^1.0.0": + "integrity" "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=" + "resolved" "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz" + "version" "1.0.0" + +"is-root@^2.1.0": + "integrity" "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==" + "resolved" "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz" + "version" "2.1.0" + +"is-shared-array-buffer@^1.0.1": + "integrity" "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==" + "resolved" "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz" + "version" "1.0.1" + +"is-stream@^2.0.0": + "integrity" "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" + "resolved" "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz" + "version" "2.0.1" + +"is-string@^1.0.5", "is-string@^1.0.7": + "integrity" "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==" + "resolved" "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz" + "version" "1.0.7" + dependencies: + "has-tostringtag" "^1.0.0" + +"is-symbol@^1.0.2", "is-symbol@^1.0.3": + "integrity" "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==" + "resolved" "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz" + "version" "1.0.4" + dependencies: + "has-symbols" "^1.0.2" + +"is-typedarray@^1.0.0": + "integrity" "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + "resolved" "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" + "version" "1.0.0" + +"is-weakref@^1.0.1": + "integrity" "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==" + "resolved" "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz" + "version" "1.0.2" + dependencies: + "call-bind" "^1.0.2" + +"is-wsl@^2.2.0": + "integrity" "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==" + "resolved" "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz" + "version" "2.2.0" + dependencies: + "is-docker" "^2.0.0" + +"isarray@~1.0.0": + "integrity" "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + "resolved" "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + "version" "1.0.0" + +"isexe@^2.0.0": + "integrity" "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + "resolved" "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" + "version" "2.0.0" + +"istanbul-lib-coverage@^3.0.0", "istanbul-lib-coverage@^3.2.0": + "integrity" "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==" + "resolved" "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz" + "version" "3.2.0" + +"istanbul-lib-instrument@^5.0.4", "istanbul-lib-instrument@^5.1.0": + "integrity" "sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q==" + "resolved" "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz" + "version" "5.1.0" + dependencies: + "@babel/core" "^7.12.3" + "@babel/parser" "^7.14.7" + "@istanbuljs/schema" "^0.1.2" + "istanbul-lib-coverage" "^3.2.0" + "semver" "^6.3.0" + +"istanbul-lib-report@^3.0.0": + "integrity" "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==" + "resolved" "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz" + "version" "3.0.0" + dependencies: + "istanbul-lib-coverage" "^3.0.0" + "make-dir" "^3.0.0" + "supports-color" "^7.1.0" + +"istanbul-lib-source-maps@^4.0.0": + "integrity" "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==" + "resolved" "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz" + "version" "4.0.1" + dependencies: + "debug" "^4.1.1" + "istanbul-lib-coverage" "^3.0.0" + "source-map" "^0.6.1" + +"istanbul-reports@^3.1.3": + "integrity" "sha512-x9LtDVtfm/t1GFiLl3NffC7hz+I1ragvgX1P/Lg1NlIagifZDKUkuuaAxH/qpwj2IuEfD8G2Bs/UKp+sZ/pKkg==" + "resolved" "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.3.tgz" + "version" "3.1.3" + dependencies: + "html-escaper" "^2.0.0" + "istanbul-lib-report" "^3.0.0" + +"jake@^10.6.1": + "integrity" "sha512-eLpKyrfG3mzvGE2Du8VoPbeSkRry093+tyNjdYaBbJS9v17knImYGNXQCUV0gLxQtF82m3E8iRb/wdSQZLoq7A==" + "resolved" "https://registry.npmjs.org/jake/-/jake-10.8.2.tgz" + "version" "10.8.2" + dependencies: + "async" "0.9.x" + "chalk" "^2.4.2" + "filelist" "^1.0.1" + "minimatch" "^3.0.4" + +"jest-changed-files@^27.4.2": + "integrity" "sha512-/9x8MjekuzUQoPjDHbBiXbNEBauhrPU2ct7m8TfCg69ywt1y/N+yYwGh3gCpnqUS3klYWDU/lSNgv+JhoD2k1A==" + "resolved" "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.4.2.tgz" + "version" "27.4.2" + dependencies: + "@jest/types" "^27.4.2" + "execa" "^5.0.0" + "throat" "^6.0.1" + +"jest-circus@^27.4.6": + "integrity" "sha512-UA7AI5HZrW4wRM72Ro80uRR2Fg+7nR0GESbSI/2M+ambbzVuA63mn5T1p3Z/wlhntzGpIG1xx78GP2YIkf6PhQ==" + "resolved" "https://registry.npmjs.org/jest-circus/-/jest-circus-27.4.6.tgz" + "version" "27.4.6" + dependencies: + "@jest/environment" "^27.4.6" + "@jest/test-result" "^27.4.6" + "@jest/types" "^27.4.2" + "@types/node" "*" + "chalk" "^4.0.0" + "co" "^4.6.0" + "dedent" "^0.7.0" + "expect" "^27.4.6" + "is-generator-fn" "^2.0.0" + "jest-each" "^27.4.6" + "jest-matcher-utils" "^27.4.6" + "jest-message-util" "^27.4.6" + "jest-runtime" "^27.4.6" + "jest-snapshot" "^27.4.6" + "jest-util" "^27.4.2" + "pretty-format" "^27.4.6" + "slash" "^3.0.0" + "stack-utils" "^2.0.3" + "throat" "^6.0.1" + +"jest-cli@^27.4.7": + "integrity" "sha512-zREYhvjjqe1KsGV15mdnxjThKNDgza1fhDT+iUsXWLCq3sxe9w5xnvyctcYVT5PcdLSjv7Y5dCwTS3FCF1tiuw==" + "resolved" "https://registry.npmjs.org/jest-cli/-/jest-cli-27.4.7.tgz" + "version" "27.4.7" + dependencies: + "@jest/core" "^27.4.7" + "@jest/test-result" "^27.4.6" + "@jest/types" "^27.4.2" + "chalk" "^4.0.0" + "exit" "^0.1.2" + "graceful-fs" "^4.2.4" + "import-local" "^3.0.2" + "jest-config" "^27.4.7" + "jest-util" "^27.4.2" + "jest-validate" "^27.4.6" + "prompts" "^2.0.1" + "yargs" "^16.2.0" + +"jest-config@^27.4.7": + "integrity" "sha512-xz/o/KJJEedHMrIY9v2ParIoYSrSVY6IVeE4z5Z3i101GoA5XgfbJz+1C8EYPsv7u7f39dS8F9v46BHDhn0vlw==" + "resolved" "https://registry.npmjs.org/jest-config/-/jest-config-27.4.7.tgz" + "version" "27.4.7" + dependencies: + "@babel/core" "^7.8.0" + "@jest/test-sequencer" "^27.4.6" + "@jest/types" "^27.4.2" + "babel-jest" "^27.4.6" + "chalk" "^4.0.0" + "ci-info" "^3.2.0" + "deepmerge" "^4.2.2" + "glob" "^7.1.1" + "graceful-fs" "^4.2.4" + "jest-circus" "^27.4.6" + "jest-environment-jsdom" "^27.4.6" + "jest-environment-node" "^27.4.6" + "jest-get-type" "^27.4.0" + "jest-jasmine2" "^27.4.6" + "jest-regex-util" "^27.4.0" + "jest-resolve" "^27.4.6" + "jest-runner" "^27.4.6" + "jest-util" "^27.4.2" + "jest-validate" "^27.4.6" + "micromatch" "^4.0.4" + "pretty-format" "^27.4.6" + "slash" "^3.0.0" + +"jest-diff@^27.4.6": + "integrity" "sha512-zjaB0sh0Lb13VyPsd92V7HkqF6yKRH9vm33rwBt7rPYrpQvS1nCvlIy2pICbKta+ZjWngYLNn4cCK4nyZkjS/w==" + "resolved" "https://registry.npmjs.org/jest-diff/-/jest-diff-27.4.6.tgz" + "version" "27.4.6" + dependencies: + "chalk" "^4.0.0" + "diff-sequences" "^27.4.0" + "jest-get-type" "^27.4.0" + "pretty-format" "^27.4.6" + +"jest-docblock@^27.4.0": + "integrity" "sha512-7TBazUdCKGV7svZ+gh7C8esAnweJoG+SvcF6Cjqj4l17zA2q1cMwx2JObSioubk317H+cjcHgP+7fTs60paulg==" + "resolved" "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.4.0.tgz" + "version" "27.4.0" + dependencies: + "detect-newline" "^3.0.0" + +"jest-each@^27.4.6": + "integrity" "sha512-n6QDq8y2Hsmn22tRkgAk+z6MCX7MeVlAzxmZDshfS2jLcaBlyhpF3tZSJLR+kXmh23GEvS0ojMR8i6ZeRvpQcA==" + "resolved" "https://registry.npmjs.org/jest-each/-/jest-each-27.4.6.tgz" + "version" "27.4.6" + dependencies: + "@jest/types" "^27.4.2" + "chalk" "^4.0.0" + "jest-get-type" "^27.4.0" + "jest-util" "^27.4.2" + "pretty-format" "^27.4.6" + +"jest-environment-jsdom@^27.4.6": + "integrity" "sha512-o3dx5p/kHPbUlRvSNjypEcEtgs6LmvESMzgRFQE6c+Prwl2JLA4RZ7qAnxc5VM8kutsGRTB15jXeeSbJsKN9iA==" + "resolved" "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.4.6.tgz" + "version" "27.4.6" + dependencies: + "@jest/environment" "^27.4.6" + "@jest/fake-timers" "^27.4.6" + "@jest/types" "^27.4.2" + "@types/node" "*" + "jest-mock" "^27.4.6" + "jest-util" "^27.4.2" + "jsdom" "^16.6.0" + +"jest-environment-node@^27.4.6": + "integrity" "sha512-yfHlZ9m+kzTKZV0hVfhVu6GuDxKAYeFHrfulmy7Jxwsq4V7+ZK7f+c0XP/tbVDMQW7E4neG2u147hFkuVz0MlQ==" + "resolved" "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.4.6.tgz" + "version" "27.4.6" + dependencies: + "@jest/environment" "^27.4.6" + "@jest/fake-timers" "^27.4.6" + "@jest/types" "^27.4.2" + "@types/node" "*" + "jest-mock" "^27.4.6" + "jest-util" "^27.4.2" + +"jest-get-type@^27.4.0": + "integrity" "sha512-tk9o+ld5TWq41DkK14L4wox4s2D9MtTpKaAVzXfr5CUKm5ZK2ExcaFE0qls2W71zE/6R2TxxrK9w2r6svAFDBQ==" + "resolved" "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.4.0.tgz" + "version" "27.4.0" + +"jest-haste-map@^27.4.6": + "integrity" "sha512-0tNpgxg7BKurZeFkIOvGCkbmOHbLFf4LUQOxrQSMjvrQaQe3l6E8x6jYC1NuWkGo5WDdbr8FEzUxV2+LWNawKQ==" + "resolved" "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.4.6.tgz" + "version" "27.4.6" + dependencies: + "@jest/types" "^27.4.2" + "@types/graceful-fs" "^4.1.2" + "@types/node" "*" + "anymatch" "^3.0.3" + "fb-watchman" "^2.0.0" + "graceful-fs" "^4.2.4" + "jest-regex-util" "^27.4.0" + "jest-serializer" "^27.4.0" + "jest-util" "^27.4.2" + "jest-worker" "^27.4.6" + "micromatch" "^4.0.4" + "walker" "^1.0.7" + optionalDependencies: + "fsevents" "^2.3.2" + +"jest-jasmine2@^27.4.6": + "integrity" "sha512-uAGNXF644I/whzhsf7/qf74gqy9OuhvJ0XYp8SDecX2ooGeaPnmJMjXjKt0mqh1Rl5dtRGxJgNrHlBQIBfS5Nw==" + "resolved" "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.4.6.tgz" + "version" "27.4.6" + dependencies: + "@jest/environment" "^27.4.6" + "@jest/source-map" "^27.4.0" + "@jest/test-result" "^27.4.6" + "@jest/types" "^27.4.2" + "@types/node" "*" + "chalk" "^4.0.0" + "co" "^4.6.0" + "expect" "^27.4.6" + "is-generator-fn" "^2.0.0" + "jest-each" "^27.4.6" + "jest-matcher-utils" "^27.4.6" + "jest-message-util" "^27.4.6" + "jest-runtime" "^27.4.6" + "jest-snapshot" "^27.4.6" + "jest-util" "^27.4.2" + "pretty-format" "^27.4.6" + "throat" "^6.0.1" + +"jest-leak-detector@^27.4.6": + "integrity" "sha512-kkaGixDf9R7CjHm2pOzfTxZTQQQ2gHTIWKY/JZSiYTc90bZp8kSZnUMS3uLAfwTZwc0tcMRoEX74e14LG1WapA==" + "resolved" "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.4.6.tgz" + "version" "27.4.6" + dependencies: + "jest-get-type" "^27.4.0" + "pretty-format" "^27.4.6" + +"jest-matcher-utils@^27.4.6": + "integrity" "sha512-XD4PKT3Wn1LQnRAq7ZsTI0VRuEc9OrCPFiO1XL7bftTGmfNF0DcEwMHRgqiu7NGf8ZoZDREpGrCniDkjt79WbA==" + "resolved" "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.4.6.tgz" + "version" "27.4.6" + dependencies: + "chalk" "^4.0.0" + "jest-diff" "^27.4.6" + "jest-get-type" "^27.4.0" + "pretty-format" "^27.4.6" + +"jest-message-util@^27.4.6": + "integrity" "sha512-0p5szriFU0U74czRSFjH6RyS7UYIAkn/ntwMuOwTGWrQIOh5NzXXrq72LOqIkJKKvFbPq+byZKuBz78fjBERBA==" + "resolved" "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.4.6.tgz" + "version" "27.4.6" + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^27.4.2" + "@types/stack-utils" "^2.0.0" + "chalk" "^4.0.0" + "graceful-fs" "^4.2.4" + "micromatch" "^4.0.4" + "pretty-format" "^27.4.6" + "slash" "^3.0.0" + "stack-utils" "^2.0.3" + +"jest-mock@^27.4.6": + "integrity" "sha512-kvojdYRkst8iVSZ1EJ+vc1RRD9llueBjKzXzeCytH3dMM7zvPV/ULcfI2nr0v0VUgm3Bjt3hBCQvOeaBz+ZTHw==" + "resolved" "https://registry.npmjs.org/jest-mock/-/jest-mock-27.4.6.tgz" + "version" "27.4.6" + dependencies: + "@jest/types" "^27.4.2" + "@types/node" "*" + +"jest-pnp-resolver@^1.2.2": + "integrity" "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==" + "resolved" "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz" + "version" "1.2.2" + +"jest-regex-util@^27.0.0", "jest-regex-util@^27.4.0": + "integrity" "sha512-WeCpMpNnqJYMQoOjm1nTtsgbR4XHAk1u00qDoNBQoykM280+/TmgA5Qh5giC1ecy6a5d4hbSsHzpBtu5yvlbEg==" + "resolved" "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.4.0.tgz" + "version" "27.4.0" + +"jest-resolve-dependencies@^27.4.6": + "integrity" "sha512-W85uJZcFXEVZ7+MZqIPCscdjuctruNGXUZ3OHSXOfXR9ITgbUKeHj+uGcies+0SsvI5GtUfTw4dY7u9qjTvQOw==" + "resolved" "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.4.6.tgz" + "version" "27.4.6" + dependencies: + "@jest/types" "^27.4.2" + "jest-regex-util" "^27.4.0" + "jest-snapshot" "^27.4.6" + +"jest-resolve@*", "jest-resolve@^27.4.2", "jest-resolve@^27.4.6": + "integrity" "sha512-SFfITVApqtirbITKFAO7jOVN45UgFzcRdQanOFzjnbd+CACDoyeX7206JyU92l4cRr73+Qy/TlW51+4vHGt+zw==" + "resolved" "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.4.6.tgz" + "version" "27.4.6" + dependencies: + "@jest/types" "^27.4.2" + "chalk" "^4.0.0" + "graceful-fs" "^4.2.4" + "jest-haste-map" "^27.4.6" + "jest-pnp-resolver" "^1.2.2" + "jest-util" "^27.4.2" + "jest-validate" "^27.4.6" + "resolve" "^1.20.0" + "resolve.exports" "^1.1.0" + "slash" "^3.0.0" + +"jest-runner@^27.4.6": + "integrity" "sha512-IDeFt2SG4DzqalYBZRgbbPmpwV3X0DcntjezPBERvnhwKGWTW7C5pbbA5lVkmvgteeNfdd/23gwqv3aiilpYPg==" + "resolved" "https://registry.npmjs.org/jest-runner/-/jest-runner-27.4.6.tgz" + "version" "27.4.6" + dependencies: + "@jest/console" "^27.4.6" + "@jest/environment" "^27.4.6" + "@jest/test-result" "^27.4.6" + "@jest/transform" "^27.4.6" + "@jest/types" "^27.4.2" + "@types/node" "*" + "chalk" "^4.0.0" + "emittery" "^0.8.1" + "exit" "^0.1.2" + "graceful-fs" "^4.2.4" + "jest-docblock" "^27.4.0" + "jest-environment-jsdom" "^27.4.6" + "jest-environment-node" "^27.4.6" + "jest-haste-map" "^27.4.6" + "jest-leak-detector" "^27.4.6" + "jest-message-util" "^27.4.6" + "jest-resolve" "^27.4.6" + "jest-runtime" "^27.4.6" + "jest-util" "^27.4.2" + "jest-worker" "^27.4.6" + "source-map-support" "^0.5.6" + "throat" "^6.0.1" + +"jest-runtime@^27.4.6": + "integrity" "sha512-eXYeoR/MbIpVDrjqy5d6cGCFOYBFFDeKaNWqTp0h6E74dK0zLHzASQXJpl5a2/40euBmKnprNLJ0Kh0LCndnWQ==" + "resolved" "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.4.6.tgz" + "version" "27.4.6" + dependencies: + "@jest/environment" "^27.4.6" + "@jest/fake-timers" "^27.4.6" + "@jest/globals" "^27.4.6" + "@jest/source-map" "^27.4.0" + "@jest/test-result" "^27.4.6" + "@jest/transform" "^27.4.6" + "@jest/types" "^27.4.2" + "chalk" "^4.0.0" + "cjs-module-lexer" "^1.0.0" + "collect-v8-coverage" "^1.0.0" + "execa" "^5.0.0" + "glob" "^7.1.3" + "graceful-fs" "^4.2.4" + "jest-haste-map" "^27.4.6" + "jest-message-util" "^27.4.6" + "jest-mock" "^27.4.6" + "jest-regex-util" "^27.4.0" + "jest-resolve" "^27.4.6" + "jest-snapshot" "^27.4.6" + "jest-util" "^27.4.2" + "slash" "^3.0.0" + "strip-bom" "^4.0.0" + +"jest-serializer@^27.4.0": + "integrity" "sha512-RDhpcn5f1JYTX2pvJAGDcnsNTnsV9bjYPU8xcV+xPwOXnUPOQwf4ZEuiU6G9H1UztH+OapMgu/ckEVwO87PwnQ==" + "resolved" "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.4.0.tgz" + "version" "27.4.0" + dependencies: + "@types/node" "*" + "graceful-fs" "^4.2.4" + +"jest-snapshot@^27.4.6": + "integrity" "sha512-fafUCDLQfzuNP9IRcEqaFAMzEe7u5BF7mude51wyWv7VRex60WznZIC7DfKTgSIlJa8aFzYmXclmN328aqSDmQ==" + "resolved" "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.4.6.tgz" + "version" "27.4.6" + dependencies: + "@babel/core" "^7.7.2" + "@babel/generator" "^7.7.2" + "@babel/plugin-syntax-typescript" "^7.7.2" + "@babel/traverse" "^7.7.2" + "@babel/types" "^7.0.0" + "@jest/transform" "^27.4.6" + "@jest/types" "^27.4.2" + "@types/babel__traverse" "^7.0.4" + "@types/prettier" "^2.1.5" + "babel-preset-current-node-syntax" "^1.0.0" + "chalk" "^4.0.0" + "expect" "^27.4.6" + "graceful-fs" "^4.2.4" + "jest-diff" "^27.4.6" + "jest-get-type" "^27.4.0" + "jest-haste-map" "^27.4.6" + "jest-matcher-utils" "^27.4.6" + "jest-message-util" "^27.4.6" + "jest-util" "^27.4.2" + "natural-compare" "^1.4.0" + "pretty-format" "^27.4.6" + "semver" "^7.3.2" + +"jest-util@^27.4.2": + "integrity" "sha512-YuxxpXU6nlMan9qyLuxHaMMOzXAl5aGZWCSzben5DhLHemYQxCc4YK+4L3ZrCutT8GPQ+ui9k5D8rUJoDioMnA==" + "resolved" "https://registry.npmjs.org/jest-util/-/jest-util-27.4.2.tgz" + "version" "27.4.2" + dependencies: + "@jest/types" "^27.4.2" + "@types/node" "*" + "chalk" "^4.0.0" + "ci-info" "^3.2.0" + "graceful-fs" "^4.2.4" + "picomatch" "^2.2.3" + +"jest-validate@^27.4.6": + "integrity" "sha512-872mEmCPVlBqbA5dToC57vA3yJaMRfIdpCoD3cyHWJOMx+SJwLNw0I71EkWs41oza/Er9Zno9XuTkRYCPDUJXQ==" + "resolved" "https://registry.npmjs.org/jest-validate/-/jest-validate-27.4.6.tgz" + "version" "27.4.6" + dependencies: + "@jest/types" "^27.4.2" + "camelcase" "^6.2.0" + "chalk" "^4.0.0" + "jest-get-type" "^27.4.0" + "leven" "^3.1.0" + "pretty-format" "^27.4.6" + +"jest-watch-typeahead@^1.0.0": + "integrity" "sha512-jxoszalAb394WElmiJTFBMzie/RDCF+W7Q29n5LzOPtcoQoHWfdUtHFkbhgf5NwWe8uMOxvKb/g7ea7CshfkTw==" + "resolved" "https://registry.npmjs.org/jest-watch-typeahead/-/jest-watch-typeahead-1.0.0.tgz" + "version" "1.0.0" + dependencies: + "ansi-escapes" "^4.3.1" + "chalk" "^4.0.0" + "jest-regex-util" "^27.0.0" + "jest-watcher" "^27.0.0" + "slash" "^4.0.0" + "string-length" "^5.0.1" + "strip-ansi" "^7.0.1" + +"jest-watcher@^27.0.0", "jest-watcher@^27.4.6": + "integrity" "sha512-yKQ20OMBiCDigbD0quhQKLkBO+ObGN79MO4nT7YaCuQ5SM+dkBNWE8cZX0FjU6czwMvWw6StWbe+Wv4jJPJ+fw==" + "resolved" "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.4.6.tgz" + "version" "27.4.6" + dependencies: + "@jest/test-result" "^27.4.6" + "@jest/types" "^27.4.2" + "@types/node" "*" + "ansi-escapes" "^4.2.1" + "chalk" "^4.0.0" + "jest-util" "^27.4.2" + "string-length" "^4.0.1" + +"jest-worker@^26.2.1": + "integrity" "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==" + "resolved" "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz" + "version" "26.6.2" + dependencies: + "@types/node" "*" + "merge-stream" "^2.0.0" + "supports-color" "^7.0.0" + +"jest-worker@^27.0.2", "jest-worker@^27.3.1", "jest-worker@^27.4.1", "jest-worker@^27.4.6": + "integrity" "sha512-gHWJF/6Xi5CTG5QCvROr6GcmpIqNYpDJyc8A1h/DyXqH1tD6SnRCM0d3U5msV31D2LB/U+E0M+W4oyvKV44oNw==" + "resolved" "https://registry.npmjs.org/jest-worker/-/jest-worker-27.4.6.tgz" + "version" "27.4.6" + dependencies: + "@types/node" "*" + "merge-stream" "^2.0.0" + "supports-color" "^8.0.0" + +"jest@^27.0.0", "jest@^27.4.3": + "integrity" "sha512-8heYvsx7nV/m8m24Vk26Y87g73Ba6ueUd0MWed/NXMhSZIm62U/llVbS0PJe1SHunbyXjJ/BqG1z9bFjGUIvTg==" + "resolved" "https://registry.npmjs.org/jest/-/jest-27.4.7.tgz" + "version" "27.4.7" + dependencies: + "@jest/core" "^27.4.7" + "import-local" "^3.0.2" + "jest-cli" "^27.4.7" + +"js-tokens@^3.0.0 || ^4.0.0", "js-tokens@^4.0.0": + "integrity" "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + "resolved" "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" + "version" "4.0.0" + +"js-yaml@^3.13.1": + "integrity" "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==" + "resolved" "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz" + "version" "3.14.1" + dependencies: + "argparse" "^1.0.7" + "esprima" "^4.0.0" + +"js-yaml@^4.1.0": + "integrity" "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==" + "resolved" "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz" + "version" "4.1.0" + dependencies: + "argparse" "^2.0.1" + +"jsdom@^16.6.0": + "integrity" "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==" + "resolved" "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz" + "version" "16.7.0" + dependencies: + "abab" "^2.0.5" + "acorn" "^8.2.4" + "acorn-globals" "^6.0.0" + "cssom" "^0.4.4" + "cssstyle" "^2.3.0" + "data-urls" "^2.0.0" + "decimal.js" "^10.2.1" + "domexception" "^2.0.1" + "escodegen" "^2.0.0" + "form-data" "^3.0.0" + "html-encoding-sniffer" "^2.0.1" + "http-proxy-agent" "^4.0.1" + "https-proxy-agent" "^5.0.0" + "is-potential-custom-element-name" "^1.0.1" + "nwsapi" "^2.2.0" + "parse5" "6.0.1" + "saxes" "^5.0.1" + "symbol-tree" "^3.2.4" + "tough-cookie" "^4.0.0" + "w3c-hr-time" "^1.0.2" + "w3c-xmlserializer" "^2.0.0" + "webidl-conversions" "^6.1.0" + "whatwg-encoding" "^1.0.5" + "whatwg-mimetype" "^2.3.0" + "whatwg-url" "^8.5.0" + "ws" "^7.4.6" + "xml-name-validator" "^3.0.0" + +"jsesc@^2.5.1": + "integrity" "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" + "resolved" "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz" + "version" "2.5.2" + +"jsesc@~0.5.0": + "integrity" "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=" + "resolved" "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz" + "version" "0.5.0" + +"json-parse-better-errors@^1.0.2": + "integrity" "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + "resolved" "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz" + "version" "1.0.2" + +"json-parse-even-better-errors@^2.3.0": + "integrity" "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + "resolved" "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz" + "version" "2.3.1" + +"json-schema-traverse@^0.4.1": + "integrity" "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "resolved" "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" + "version" "0.4.1" + +"json-schema-traverse@^1.0.0": + "integrity" "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + "resolved" "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz" + "version" "1.0.0" + +"json-schema@^0.4.0": + "integrity" "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + "resolved" "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz" + "version" "0.4.0" + +"json-stable-stringify-without-jsonify@^1.0.1": + "integrity" "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=" + "resolved" "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz" + "version" "1.0.1" + +"json5@^1.0.1": + "integrity" "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==" + "resolved" "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz" + "version" "1.0.1" + dependencies: + "minimist" "^1.2.0" + +"json5@^2.1.2", "json5@^2.2.0": + "integrity" "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==" + "resolved" "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz" + "version" "2.2.0" + dependencies: + "minimist" "^1.2.5" + +"jsonfile@^6.0.1": + "integrity" "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==" + "resolved" "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz" + "version" "6.1.0" + dependencies: + "universalify" "^2.0.0" + optionalDependencies: + "graceful-fs" "^4.1.6" + +"jsonpointer@^5.0.0": + "integrity" "sha512-PNYZIdMjVIvVgDSYKTT63Y+KZ6IZvGRNNWcxwD+GNnUz1MKPfv30J8ueCjdwcN0nDx2SlshgyB7Oy0epAzVRRg==" + "resolved" "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.0.tgz" + "version" "5.0.0" + +"jsx-ast-utils@^2.4.1 || ^3.0.0", "jsx-ast-utils@^3.2.1": + "integrity" "sha512-uP5vu8xfy2F9A6LGC22KO7e2/vGTS1MhP+18f++ZNlf0Ohaxbc9nIEwHAsejlJKyzfZzU5UIhe5ItYkitcZnZA==" + "resolved" "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.1.tgz" + "version" "3.2.1" + dependencies: + "array-includes" "^3.1.3" + "object.assign" "^4.1.2" + +"kind-of@^6.0.2": + "integrity" "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" + "resolved" "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz" + "version" "6.0.3" + +"kleur@^3.0.3": + "integrity" "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==" + "resolved" "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz" + "version" "3.0.3" + +"klona@^2.0.4", "klona@^2.0.5": + "integrity" "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==" + "resolved" "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz" + "version" "2.0.5" + +"language-subtag-registry@~0.3.2": + "integrity" "sha512-L0IqwlIXjilBVVYKFT37X9Ih11Um5NEl9cbJIuU/SwP/zEEAbBPOnEeeuxVMf45ydWQRDQN3Nqc96OgbH1K+Pg==" + "resolved" "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz" + "version" "0.3.21" + +"language-tags@^1.0.5": + "integrity" "sha1-0yHbxNowuovzAk4ED6XBRmH5GTo=" + "resolved" "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz" + "version" "1.0.5" + dependencies: + "language-subtag-registry" "~0.3.2" + +"leven@^3.1.0": + "integrity" "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==" + "resolved" "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz" + "version" "3.1.0" + +"levn@^0.4.1": + "integrity" "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==" + "resolved" "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz" + "version" "0.4.1" + dependencies: + "prelude-ls" "^1.2.1" + "type-check" "~0.4.0" + +"levn@~0.3.0": + "integrity" "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=" + "resolved" "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz" + "version" "0.3.0" + dependencies: + "prelude-ls" "~1.1.2" + "type-check" "~0.3.2" + +"lilconfig@^2.0.3", "lilconfig@^2.0.4": + "integrity" "sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA==" + "resolved" "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.4.tgz" + "version" "2.0.4" + +"lines-and-columns@^1.1.6": + "integrity" "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + "resolved" "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz" + "version" "1.2.4" + +"loader-runner@^4.2.0": + "integrity" "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==" + "resolved" "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz" + "version" "4.2.0" + +"loader-utils@^1.4.0": + "integrity" "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==" + "resolved" "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz" + "version" "1.4.0" + dependencies: + "big.js" "^5.2.2" + "emojis-list" "^3.0.0" + "json5" "^1.0.1" + +"loader-utils@^2.0.0": + "integrity" "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==" + "resolved" "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz" + "version" "2.0.2" + dependencies: + "big.js" "^5.2.2" + "emojis-list" "^3.0.0" + "json5" "^2.1.2" + +"loader-utils@^3.2.0": + "integrity" "sha512-HVl9ZqccQihZ7JM85dco1MvO9G+ONvxoGa9rkhzFsneGLKSUg1gJf9bWzhRhcvm2qChhWpebQhP44qxjKIUCaQ==" + "resolved" "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.0.tgz" + "version" "3.2.0" + +"locate-path@^2.0.0": + "integrity" "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=" + "resolved" "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz" + "version" "2.0.0" + dependencies: + "p-locate" "^2.0.0" + "path-exists" "^3.0.0" + +"locate-path@^3.0.0": + "integrity" "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==" + "resolved" "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz" + "version" "3.0.0" + dependencies: + "p-locate" "^3.0.0" + "path-exists" "^3.0.0" + +"locate-path@^5.0.0": + "integrity" "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==" + "resolved" "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz" + "version" "5.0.0" + dependencies: + "p-locate" "^4.1.0" + +"locate-path@^6.0.0": + "integrity" "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==" + "resolved" "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz" + "version" "6.0.0" + dependencies: + "p-locate" "^5.0.0" + +"lodash.debounce@^4.0.8": + "integrity" "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" + "resolved" "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz" + "version" "4.0.8" + +"lodash.memoize@^4.1.2": + "integrity" "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=" + "resolved" "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz" + "version" "4.1.2" + +"lodash.merge@^4.6.2": + "integrity" "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + "resolved" "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" + "version" "4.6.2" + +"lodash.sortby@^4.7.0": + "integrity" "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=" + "resolved" "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz" + "version" "4.7.0" + +"lodash.uniq@^4.5.0": + "integrity" "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" + "resolved" "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz" + "version" "4.5.0" + +"lodash@^4.17.14", "lodash@^4.17.20", "lodash@^4.17.21", "lodash@^4.7.0": + "integrity" "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "resolved" "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" + "version" "4.17.21" + +"loose-envify@^1.1.0", "loose-envify@^1.4.0": + "integrity" "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==" + "resolved" "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz" + "version" "1.4.0" + dependencies: + "js-tokens" "^3.0.0 || ^4.0.0" + +"lower-case@^2.0.2": + "integrity" "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==" + "resolved" "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz" + "version" "2.0.2" + dependencies: + "tslib" "^2.0.3" + +"lru-cache@^6.0.0": + "integrity" "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==" + "resolved" "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz" + "version" "6.0.0" + dependencies: + "yallist" "^4.0.0" + +"magic-string@^0.25.0", "magic-string@^0.25.7": + "integrity" "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==" + "resolved" "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz" + "version" "0.25.7" + dependencies: + "sourcemap-codec" "^1.4.4" + +"make-dir@^3.0.0", "make-dir@^3.0.2", "make-dir@^3.1.0": + "integrity" "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==" + "resolved" "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz" + "version" "3.1.0" + dependencies: + "semver" "^6.0.0" + +"makeerror@1.0.12": + "integrity" "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==" + "resolved" "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz" + "version" "1.0.12" + dependencies: + "tmpl" "1.0.5" + +"mdn-data@2.0.14": + "integrity" "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" + "resolved" "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz" + "version" "2.0.14" + +"mdn-data@2.0.4": + "integrity" "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==" + "resolved" "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz" + "version" "2.0.4" + +"media-typer@0.3.0": + "integrity" "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + "resolved" "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz" + "version" "0.3.0" + +"memfs@^3.1.2", "memfs@^3.2.2": + "integrity" "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==" + "resolved" "https://registry.npmjs.org/memfs/-/memfs-3.4.1.tgz" + "version" "3.4.1" + dependencies: + "fs-monkey" "1.0.3" + +"merge-descriptors@1.0.1": + "integrity" "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + "resolved" "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz" + "version" "1.0.1" + +"merge-stream@^2.0.0": + "integrity" "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + "resolved" "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz" + "version" "2.0.0" + +"merge2@^1.3.0", "merge2@^1.4.1": + "integrity" "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" + "resolved" "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" + "version" "1.4.1" + +"methods@~1.1.2": + "integrity" "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + "resolved" "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz" + "version" "1.1.2" + +"micromatch@^4.0.2", "micromatch@^4.0.4": + "integrity" "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==" + "resolved" "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz" + "version" "4.0.4" + dependencies: + "braces" "^3.0.1" + "picomatch" "^2.2.3" + +"mime-db@>= 1.43.0 < 2", "mime-db@1.51.0": + "integrity" "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==" + "resolved" "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz" + "version" "1.51.0" + +"mime-types@^2.1.12", "mime-types@^2.1.27", "mime-types@^2.1.31", "mime-types@~2.1.17", "mime-types@~2.1.24": + "integrity" "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==" + "resolved" "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz" + "version" "2.1.34" + dependencies: + "mime-db" "1.51.0" + +"mime@1.6.0": + "integrity" "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + "resolved" "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz" + "version" "1.6.0" + +"mimic-fn@^2.1.0": + "integrity" "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + "resolved" "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz" + "version" "2.1.0" + +"mini-css-extract-plugin@^2.4.5": + "integrity" "sha512-khHpc29bdsE9EQiGSLqQieLyMbGca+bkC42/BBL1gXC8yAS0nHpOTUCBYUK6En1FuRdfE9wKXhGtsab8vmsugg==" + "resolved" "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.4.6.tgz" + "version" "2.4.6" + dependencies: + "schema-utils" "^4.0.0" + +"minimalistic-assert@^1.0.0": + "integrity" "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + "resolved" "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz" + "version" "1.0.1" + +"minimatch@^3.0.4", "minimatch@3.0.4": + "integrity" "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==" + "resolved" "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" + "version" "3.0.4" + dependencies: + "brace-expansion" "^1.1.7" + +"minimist@^1.1.1", "minimist@^1.2.0", "minimist@^1.2.5": + "integrity" "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + "resolved" "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz" + "version" "1.2.5" + +"mkdirp@^0.5.5", "mkdirp@~0.5.1": + "integrity" "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==" + "resolved" "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz" + "version" "0.5.5" + dependencies: + "minimist" "^1.2.5" + +"ms@^2.1.1", "ms@2.1.2": + "integrity" "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "resolved" "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" + "version" "2.1.2" + +"ms@2.0.0": + "integrity" "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "resolved" "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" + "version" "2.0.0" + +"ms@2.1.3": + "integrity" "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "resolved" "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" + "version" "2.1.3" + +"multicast-dns-service-types@^1.1.0": + "integrity" "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=" + "resolved" "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz" + "version" "1.1.0" + +"multicast-dns@^6.0.1": + "integrity" "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==" + "resolved" "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz" + "version" "6.2.3" + dependencies: + "dns-packet" "^1.3.1" + "thunky" "^1.0.2" + +"nanoid@^3.1.30": + "integrity" "sha512-F8mf7R3iT9bvThBoW4tGXhXFHCctyCiUUPrWF8WaTqa3h96d9QybkSeba43XVOOE3oiLfkVDe4bT8MeGmkrTxw==" + "resolved" "https://registry.npmjs.org/nanoid/-/nanoid-3.1.32.tgz" + "version" "3.1.32" + +"natural-compare@^1.4.0": + "integrity" "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=" + "resolved" "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" + "version" "1.4.0" + +"negotiator@0.6.2": + "integrity" "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + "resolved" "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz" + "version" "0.6.2" + +"neo-async@^2.6.2": + "integrity" "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + "resolved" "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz" + "version" "2.6.2" + +"no-case@^3.0.4": + "integrity" "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==" + "resolved" "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz" + "version" "3.0.4" + dependencies: + "lower-case" "^2.0.2" + "tslib" "^2.0.3" + +"node-forge@^1.2.0": + "integrity" "sha512-Fcvtbb+zBcZXbTTVwqGA5W+MKBj56UjVRevvchv5XrcyXbmNdesfZL37nlcWOfpgHhgmxApw3tQbTr4CqNmX4w==" + "resolved" "https://registry.npmjs.org/node-forge/-/node-forge-1.2.1.tgz" + "version" "1.2.1" + +"node-int64@^0.4.0": + "integrity" "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=" + "resolved" "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz" + "version" "0.4.0" + +"node-releases@^2.0.1": + "integrity" "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==" + "resolved" "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz" + "version" "2.0.1" + +"normalize-path@^3.0.0", "normalize-path@~3.0.0": + "integrity" "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + "resolved" "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" + "version" "3.0.0" + +"normalize-range@^0.1.2": + "integrity" "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=" + "resolved" "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz" + "version" "0.1.2" + +"normalize-url@^6.0.1": + "integrity" "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==" + "resolved" "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz" + "version" "6.1.0" + +"npm-run-path@^4.0.1": + "integrity" "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==" + "resolved" "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz" + "version" "4.0.1" + dependencies: + "path-key" "^3.0.0" + +"nth-check@^1.0.2": + "integrity" "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==" + "resolved" "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz" + "version" "1.0.2" + dependencies: + "boolbase" "~1.0.0" + +"nth-check@^2.0.1": + "integrity" "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==" + "resolved" "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz" + "version" "2.0.1" + dependencies: + "boolbase" "^1.0.0" + +"nwsapi@^2.2.0": + "integrity" "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==" + "resolved" "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz" + "version" "2.2.0" + +"object-assign@^4.1.1": + "integrity" "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + "resolved" "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + "version" "4.1.1" + +"object-hash@^2.2.0": + "integrity" "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==" + "resolved" "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz" + "version" "2.2.0" + +"object-inspect@^1.11.0", "object-inspect@^1.9.0": + "integrity" "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==" + "resolved" "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz" + "version" "1.12.0" + +"object-is@^1.0.1": + "integrity" "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==" + "resolved" "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz" + "version" "1.1.5" + dependencies: + "call-bind" "^1.0.2" + "define-properties" "^1.1.3" + +"object-keys@^1.0.12", "object-keys@^1.1.1": + "integrity" "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + "resolved" "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz" + "version" "1.1.1" + +"object.assign@^4.1.0", "object.assign@^4.1.2": + "integrity" "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==" + "resolved" "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz" + "version" "4.1.2" + dependencies: + "call-bind" "^1.0.0" + "define-properties" "^1.1.3" + "has-symbols" "^1.0.1" + "object-keys" "^1.1.1" + +"object.entries@^1.1.5": + "integrity" "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==" + "resolved" "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz" + "version" "1.1.5" + dependencies: + "call-bind" "^1.0.2" + "define-properties" "^1.1.3" + "es-abstract" "^1.19.1" + +"object.fromentries@^2.0.5": + "integrity" "sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw==" + "resolved" "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.5.tgz" + "version" "2.0.5" + dependencies: + "call-bind" "^1.0.2" + "define-properties" "^1.1.3" + "es-abstract" "^1.19.1" + +"object.getownpropertydescriptors@^2.1.0": + "integrity" "sha512-VdDoCwvJI4QdC6ndjpqFmoL3/+HxffFBbcJzKi5hwLLqqx3mdbedRpfZDdK0SrOSauj8X4GzBvnDZl4vTN7dOw==" + "resolved" "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.3.tgz" + "version" "2.1.3" + dependencies: + "call-bind" "^1.0.2" + "define-properties" "^1.1.3" + "es-abstract" "^1.19.1" + +"object.hasown@^1.1.0": + "integrity" "sha512-MhjYRfj3GBlhSkDHo6QmvgjRLXQ2zndabdf3nX0yTyZK9rPfxb6uRpAac8HXNLy1GpqWtZ81Qh4v3uOls2sRAg==" + "resolved" "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.0.tgz" + "version" "1.1.0" + dependencies: + "define-properties" "^1.1.3" + "es-abstract" "^1.19.1" + +"object.values@^1.1.0", "object.values@^1.1.5": + "integrity" "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==" + "resolved" "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz" + "version" "1.1.5" + dependencies: + "call-bind" "^1.0.2" + "define-properties" "^1.1.3" + "es-abstract" "^1.19.1" + +"obuf@^1.0.0", "obuf@^1.1.2": + "integrity" "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" + "resolved" "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz" + "version" "1.1.2" + +"on-finished@~2.3.0": + "integrity" "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=" + "resolved" "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz" + "version" "2.3.0" + dependencies: + "ee-first" "1.1.1" + +"on-headers@~1.0.2": + "integrity" "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" + "resolved" "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz" + "version" "1.0.2" + +"once@^1.3.0": + "integrity" "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=" + "resolved" "https://registry.npmjs.org/once/-/once-1.4.0.tgz" + "version" "1.4.0" + dependencies: + "wrappy" "1" + +"onetime@^5.1.2": + "integrity" "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==" + "resolved" "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz" + "version" "5.1.2" + dependencies: + "mimic-fn" "^2.1.0" + +"open@^8.0.9", "open@^8.4.0": + "integrity" "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==" + "resolved" "https://registry.npmjs.org/open/-/open-8.4.0.tgz" + "version" "8.4.0" + dependencies: + "define-lazy-prop" "^2.0.0" + "is-docker" "^2.1.1" + "is-wsl" "^2.2.0" + +"optionator@^0.8.1": + "integrity" "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==" + "resolved" "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz" + "version" "0.8.3" + dependencies: + "deep-is" "~0.1.3" + "fast-levenshtein" "~2.0.6" + "levn" "~0.3.0" + "prelude-ls" "~1.1.2" + "type-check" "~0.3.2" + "word-wrap" "~1.2.3" + +"optionator@^0.9.1": + "integrity" "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==" + "resolved" "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz" + "version" "0.9.1" + dependencies: + "deep-is" "^0.1.3" + "fast-levenshtein" "^2.0.6" + "levn" "^0.4.1" + "prelude-ls" "^1.2.1" + "type-check" "^0.4.0" + "word-wrap" "^1.2.3" + +"p-limit@^1.1.0": + "integrity" "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==" + "resolved" "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz" + "version" "1.3.0" + dependencies: + "p-try" "^1.0.0" + +"p-limit@^2.0.0": + "integrity" "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==" + "resolved" "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" + "version" "2.3.0" + dependencies: + "p-try" "^2.0.0" + +"p-limit@^2.2.0": + "integrity" "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==" + "resolved" "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" + "version" "2.3.0" + dependencies: + "p-try" "^2.0.0" + +"p-limit@^3.0.2": + "integrity" "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==" + "resolved" "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" + "version" "3.1.0" + dependencies: + "yocto-queue" "^0.1.0" + +"p-locate@^2.0.0": + "integrity" "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=" + "resolved" "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz" + "version" "2.0.0" + dependencies: + "p-limit" "^1.1.0" + +"p-locate@^3.0.0": + "integrity" "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==" + "resolved" "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz" + "version" "3.0.0" + dependencies: + "p-limit" "^2.0.0" + +"p-locate@^4.1.0": + "integrity" "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==" + "resolved" "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz" + "version" "4.1.0" + dependencies: + "p-limit" "^2.2.0" + +"p-locate@^5.0.0": + "integrity" "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==" + "resolved" "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz" + "version" "5.0.0" + dependencies: + "p-limit" "^3.0.2" + +"p-map@^4.0.0": + "integrity" "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==" + "resolved" "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz" + "version" "4.0.0" + dependencies: + "aggregate-error" "^3.0.0" + +"p-retry@^4.5.0": + "integrity" "sha512-e2xXGNhZOZ0lfgR9kL34iGlU8N/KO0xZnQxVEwdeOvpqNDQfdnxIYizvWtK8RglUa3bGqI8g0R/BdfzLMxRkiA==" + "resolved" "https://registry.npmjs.org/p-retry/-/p-retry-4.6.1.tgz" + "version" "4.6.1" + dependencies: + "@types/retry" "^0.12.0" + "retry" "^0.13.1" + +"p-try@^1.0.0": + "integrity" "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" + "resolved" "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz" + "version" "1.0.0" + +"p-try@^2.0.0": + "integrity" "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + "resolved" "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" + "version" "2.2.0" + +"param-case@^3.0.4": + "integrity" "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==" + "resolved" "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz" + "version" "3.0.4" + dependencies: + "dot-case" "^3.0.4" + "tslib" "^2.0.3" + +"parent-module@^1.0.0": + "integrity" "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==" + "resolved" "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" + "version" "1.0.1" + dependencies: + "callsites" "^3.0.0" + +"parse-json@^5.0.0": + "integrity" "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==" + "resolved" "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz" + "version" "5.2.0" + dependencies: + "@babel/code-frame" "^7.0.0" + "error-ex" "^1.3.1" + "json-parse-even-better-errors" "^2.3.0" + "lines-and-columns" "^1.1.6" + +"parse5@6.0.1": + "integrity" "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" + "resolved" "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz" + "version" "6.0.1" + +"parseurl@~1.3.2", "parseurl@~1.3.3": + "integrity" "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + "resolved" "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz" + "version" "1.3.3" + +"pascal-case@^3.1.2": + "integrity" "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==" + "resolved" "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz" + "version" "3.1.2" + dependencies: + "no-case" "^3.0.4" + "tslib" "^2.0.3" + +"path-exists@^3.0.0": + "integrity" "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + "resolved" "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz" + "version" "3.0.0" + +"path-exists@^4.0.0": + "integrity" "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + "resolved" "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" + "version" "4.0.0" + +"path-is-absolute@^1.0.0": + "integrity" "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + "resolved" "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + "version" "1.0.1" + +"path-key@^3.0.0", "path-key@^3.1.0": + "integrity" "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + "resolved" "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" + "version" "3.1.1" + +"path-parse@^1.0.6", "path-parse@^1.0.7": + "integrity" "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + "resolved" "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" + "version" "1.0.7" + +"path-to-regexp@0.1.7": + "integrity" "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + "resolved" "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz" + "version" "0.1.7" + +"path-type@^4.0.0": + "integrity" "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" + "resolved" "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" + "version" "4.0.0" + +"performance-now@^2.1.0": + "integrity" "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + "resolved" "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz" + "version" "2.1.0" + +"picocolors@^0.2.1": + "integrity" "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" + "resolved" "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz" + "version" "0.2.1" + +"picocolors@^1.0.0": + "integrity" "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "resolved" "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz" + "version" "1.0.0" + +"picomatch@^2.0.4", "picomatch@^2.2.1", "picomatch@^2.2.2", "picomatch@^2.2.3": + "integrity" "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" + "resolved" "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" + "version" "2.3.1" + +"pirates@^4.0.4": + "integrity" "sha512-ZIrVPH+A52Dw84R0L3/VS9Op04PuQ2SEoJL6bkshmiTic/HldyW9Tf7oH5mhJZBK7NmDx27vSMrYEXPXclpDKw==" + "resolved" "https://registry.npmjs.org/pirates/-/pirates-4.0.4.tgz" + "version" "4.0.4" + +"pkg-dir@^4.1.0", "pkg-dir@^4.2.0": + "integrity" "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==" + "resolved" "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz" + "version" "4.2.0" + dependencies: + "find-up" "^4.0.0" + +"pkg-up@^3.1.0": + "integrity" "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==" + "resolved" "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz" + "version" "3.1.0" + dependencies: + "find-up" "^3.0.0" + +"portfinder@^1.0.28": + "integrity" "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==" + "resolved" "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz" + "version" "1.0.28" + dependencies: + "async" "^2.6.2" + "debug" "^3.1.1" + "mkdirp" "^0.5.5" + +"postcss-attribute-case-insensitive@^5.0.0": + "integrity" "sha512-b4g9eagFGq9T5SWX4+USfVyjIb3liPnjhHHRMP7FMB2kFVpYyfEscV0wP3eaXhKlcHKUut8lt5BGoeylWA/dBQ==" + "resolved" "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.0.tgz" + "version" "5.0.0" + dependencies: + "postcss-selector-parser" "^6.0.2" + +"postcss-browser-comments@^4": + "integrity" "sha512-X9X9/WN3KIvY9+hNERUqX9gncsgBA25XaeR+jshHz2j8+sYyHktHw1JdKuMjeLpGktXidqDhA7b/qm1mrBDmgg==" + "resolved" "https://registry.npmjs.org/postcss-browser-comments/-/postcss-browser-comments-4.0.0.tgz" + "version" "4.0.0" + +"postcss-calc@^8.2.0": + "integrity" "sha512-B5R0UeB4zLJvxNt1FVCaDZULdzsKLPc6FhjFJ+xwFiq7VG4i9cuaJLxVjNtExNK8ocm3n2o4unXXLiVX1SCqxA==" + "resolved" "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.2.tgz" + "version" "8.2.2" + dependencies: + "postcss-selector-parser" "^6.0.2" + "postcss-value-parser" "^4.0.2" + +"postcss-color-functional-notation@^4.2.1": + "integrity" "sha512-62OBIXCjRXpQZcFOYIXwXBlpAVWrYk8ek1rcjvMING4Q2cf0ipyN9qT+BhHA6HmftGSEnFQu2qgKO3gMscl3Rw==" + "resolved" "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.1.tgz" + "version" "4.2.1" + dependencies: + "postcss-value-parser" "^4.2.0" + +"postcss-color-hex-alpha@^8.0.2": + "integrity" "sha512-gyx8RgqSmGVK156NAdKcsfkY3KPGHhKqvHTL3hhveFrBBToguKFzhyiuk3cljH6L4fJ0Kv+JENuPXs1Wij27Zw==" + "resolved" "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.2.tgz" + "version" "8.0.2" + dependencies: + "postcss-value-parser" "^4.2.0" + +"postcss-color-rebeccapurple@^7.0.2": + "integrity" "sha512-SFc3MaocHaQ6k3oZaFwH8io6MdypkUtEy/eXzXEB1vEQlO3S3oDc/FSZA8AsS04Z25RirQhlDlHLh3dn7XewWw==" + "resolved" "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.0.2.tgz" + "version" "7.0.2" + dependencies: + "postcss-value-parser" "^4.2.0" + +"postcss-colormin@^5.2.3": + "integrity" "sha512-dra4xoAjub2wha6RUXAgadHEn2lGxbj8drhFcIGLOMn914Eu7DkPUurugDXgstwttCYkJtZ/+PkWRWdp3UHRIA==" + "resolved" "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.2.3.tgz" + "version" "5.2.3" + dependencies: + "browserslist" "^4.16.6" + "caniuse-api" "^3.0.0" + "colord" "^2.9.1" + "postcss-value-parser" "^4.2.0" + +"postcss-convert-values@^5.0.2": + "integrity" "sha512-KQ04E2yadmfa1LqXm7UIDwW1ftxU/QWZmz6NKnHnUvJ3LEYbbcX6i329f/ig+WnEByHegulocXrECaZGLpL8Zg==" + "resolved" "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.0.2.tgz" + "version" "5.0.2" + dependencies: + "postcss-value-parser" "^4.1.0" + +"postcss-custom-media@^8.0.0": + "integrity" "sha512-FvO2GzMUaTN0t1fBULDeIvxr5IvbDXcIatt6pnJghc736nqNgsGao5NT+5+WVLAQiTt6Cb3YUms0jiPaXhL//g==" + "resolved" "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.0.tgz" + "version" "8.0.0" + +"postcss-custom-properties@^12.1.2": + "integrity" "sha512-Zvd+k66PHBYYPiXtdjNVx2l54Y9kQC7K1eUHzBND97RW/ayNxfaPOW+9NL3r0nsVbX1asPLdkDj585Wg0NBJCA==" + "resolved" "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.2.tgz" + "version" "12.1.2" + dependencies: + "postcss-value-parser" "^4.2.0" + +"postcss-custom-selectors@^6.0.0": + "integrity" "sha512-/1iyBhz/W8jUepjGyu7V1OPcGbc636snN1yXEQCinb6Bwt7KxsiU7/bLQlp8GwAXzCh7cobBU5odNn/2zQWR8Q==" + "resolved" "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-6.0.0.tgz" + "version" "6.0.0" + dependencies: + "postcss-selector-parser" "^6.0.4" + +"postcss-dir-pseudo-class@^6.0.3": + "integrity" "sha512-qiPm+CNAlgXiMf0J5IbBBEXA9l/Q5HGsNGkL3znIwT2ZFRLGY9U2fTUpa4lqCUXQOxaLimpacHeQC80BD2qbDw==" + "resolved" "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.3.tgz" + "version" "6.0.3" + dependencies: + "postcss-selector-parser" "^6.0.8" + +"postcss-discard-comments@^5.0.1": + "integrity" "sha512-lgZBPTDvWrbAYY1v5GYEv8fEO/WhKOu/hmZqmCYfrpD6eyDWWzAOsl2rF29lpvziKO02Gc5GJQtlpkTmakwOWg==" + "resolved" "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.0.1.tgz" + "version" "5.0.1" + +"postcss-discard-duplicates@^5.0.1": + "integrity" "sha512-svx747PWHKOGpAXXQkCc4k/DsWo+6bc5LsVrAsw+OU+Ibi7klFZCyX54gjYzX4TH+f2uzXjRviLARxkMurA2bA==" + "resolved" "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.0.1.tgz" + "version" "5.0.1" + +"postcss-discard-empty@^5.0.1": + "integrity" "sha512-vfU8CxAQ6YpMxV2SvMcMIyF2LX1ZzWpy0lqHDsOdaKKLQVQGVP1pzhrI9JlsO65s66uQTfkQBKBD/A5gp9STFw==" + "resolved" "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.0.1.tgz" + "version" "5.0.1" + +"postcss-discard-overridden@^5.0.2": + "integrity" "sha512-+56BLP6NSSUuWUXjRgAQuho1p5xs/hU5Sw7+xt9S3JSg+7R6+WMGnJW7Hre/6tTuZ2xiXMB42ObkiZJ2hy/Pew==" + "resolved" "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.0.2.tgz" + "version" "5.0.2" + +"postcss-double-position-gradients@^3.0.4": + "integrity" "sha512-qz+s5vhKJlsHw8HjSs+HVk2QGFdRyC68KGRQGX3i+GcnUjhWhXQEmCXW6siOJkZ1giu0ddPwSO6I6JdVVVPoog==" + "resolved" "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-3.0.4.tgz" + "version" "3.0.4" + dependencies: + "postcss-value-parser" "^4.2.0" + +"postcss-env-function@^4.0.4": + "integrity" "sha512-0ltahRTPtXSIlEZFv7zIvdEib7HN0ZbUQxrxIKn8KbiRyhALo854I/CggU5lyZe6ZBvSTJ6Al2vkZecI2OhneQ==" + "resolved" "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-4.0.4.tgz" + "version" "4.0.4" + dependencies: + "postcss-value-parser" "^4.2.0" + +"postcss-flexbugs-fixes@^5.0.2": + "integrity" "sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ==" + "resolved" "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-5.0.2.tgz" + "version" "5.0.2" + +"postcss-focus-visible@^6.0.3": + "integrity" "sha512-ozOsg+L1U8S+rxSHnJJiET6dNLyADcPHhEarhhtCI9DBLGOPG/2i4ddVoFch9LzrBgb8uDaaRI4nuid2OM82ZA==" + "resolved" "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-6.0.3.tgz" + "version" "6.0.3" + dependencies: + "postcss-selector-parser" "^6.0.8" + +"postcss-focus-within@^5.0.3": + "integrity" "sha512-fk9y2uFS6/Kpp7/A9Hz9Z4rlFQ8+tzgBcQCXAFSrXFGAbKx+4ZZOmmfHuYjCOMegPWoz0pnC6fNzi8j7Xyqp5Q==" + "resolved" "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-5.0.3.tgz" + "version" "5.0.3" + dependencies: + "postcss-selector-parser" "^6.0.8" + +"postcss-font-variant@^5.0.0": + "integrity" "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==" + "resolved" "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz" + "version" "5.0.0" + +"postcss-gap-properties@^3.0.2": + "integrity" "sha512-EaMy/pbxtQnKDsnbEjdqlkCkROTQZzolcLKgIE+3b7EuJfJydH55cZeHfm+MtIezXRqhR80VKgaztO/vHq94Fw==" + "resolved" "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.2.tgz" + "version" "3.0.2" + +"postcss-image-set-function@^4.0.4": + "integrity" "sha512-BlEo9gSTj66lXjRNByvkMK9dEdEGFXRfGjKRi9fo8s0/P3oEk74cAoonl/utiM50E2OPVb/XSu+lWvdW4KtE/Q==" + "resolved" "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-4.0.4.tgz" + "version" "4.0.4" + dependencies: + "postcss-value-parser" "^4.2.0" + +"postcss-initial@^4.0.1": + "integrity" "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==" + "resolved" "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz" + "version" "4.0.1" + +"postcss-js@^4.0.0": + "integrity" "sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==" + "resolved" "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.0.tgz" + "version" "4.0.0" + dependencies: + "camelcase-css" "^2.0.1" + +"postcss-lab-function@^4.0.3": + "integrity" "sha512-MH4tymWmefdZQ7uVG/4icfLjAQmH6o2NRYyVh2mKoB4RXJp9PjsyhZwhH4ouaCQHvg+qJVj3RzeAR1EQpIlXZA==" + "resolved" "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.0.3.tgz" + "version" "4.0.3" + dependencies: + "postcss-value-parser" "^4.2.0" + +"postcss-load-config@^3.1.0": + "integrity" "sha512-c/9XYboIbSEUZpiD1UQD0IKiUe8n9WHYV7YFe7X7J+ZwCsEKkUJSFWjS9hBU1RR9THR7jMXst8sxiqP0jjo2mg==" + "resolved" "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.1.tgz" + "version" "3.1.1" + dependencies: + "lilconfig" "^2.0.4" + "yaml" "^1.10.2" + +"postcss-loader@^6.2.1": + "integrity" "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==" + "resolved" "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz" + "version" "6.2.1" + dependencies: + "cosmiconfig" "^7.0.0" + "klona" "^2.0.5" + "semver" "^7.3.5" + +"postcss-logical@^5.0.3": + "integrity" "sha512-P5NcHWYrif0vK8rgOy/T87vg0WRIj3HSknrvp1wzDbiBeoDPVmiVRmkown2eSQdpPveat/MC1ess5uhzZFVnqQ==" + "resolved" "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.3.tgz" + "version" "5.0.3" + +"postcss-media-minmax@^5.0.0": + "integrity" "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==" + "resolved" "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz" + "version" "5.0.0" + +"postcss-merge-longhand@^5.0.4": + "integrity" "sha512-2lZrOVD+d81aoYkZDpWu6+3dTAAGkCKbV5DoRhnIR7KOULVrI/R7bcMjhrH9KTRy6iiHKqmtG+n/MMj1WmqHFw==" + "resolved" "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.0.4.tgz" + "version" "5.0.4" + dependencies: + "postcss-value-parser" "^4.1.0" + "stylehacks" "^5.0.1" + +"postcss-merge-rules@^5.0.4": + "integrity" "sha512-yOj7bW3NxlQxaERBB0lEY1sH5y+RzevjbdH4DBJurjKERNpknRByFNdNe+V72i5pIZL12woM9uGdS5xbSB+kDQ==" + "resolved" "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.0.4.tgz" + "version" "5.0.4" + dependencies: + "browserslist" "^4.16.6" + "caniuse-api" "^3.0.0" + "cssnano-utils" "^3.0.0" + "postcss-selector-parser" "^6.0.5" + +"postcss-minify-font-values@^5.0.2": + "integrity" "sha512-R6MJZryq28Cw0AmnyhXrM7naqJZZLoa1paBltIzh2wM7yb4D45TLur+eubTQ4jCmZU9SGeZdWsc5KcSoqTMeTg==" + "resolved" "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.0.2.tgz" + "version" "5.0.2" + dependencies: + "postcss-value-parser" "^4.2.0" + +"postcss-minify-gradients@^5.0.4": + "integrity" "sha512-RVwZA7NC4R4J76u8X0Q0j+J7ItKUWAeBUJ8oEEZWmtv3Xoh19uNJaJwzNpsydQjk6PkuhRrK+YwwMf+c+68EYg==" + "resolved" "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.0.4.tgz" + "version" "5.0.4" + dependencies: + "colord" "^2.9.1" + "cssnano-utils" "^3.0.0" + "postcss-value-parser" "^4.2.0" + +"postcss-minify-params@^5.0.3": + "integrity" "sha512-NY92FUikE+wralaiVexFd5gwb7oJTIDhgTNeIw89i1Ymsgt4RWiPXfz3bg7hDy4NL6gepcThJwOYNtZO/eNi7Q==" + "resolved" "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.0.3.tgz" + "version" "5.0.3" + dependencies: + "alphanum-sort" "^1.0.2" + "browserslist" "^4.16.6" + "cssnano-utils" "^3.0.0" + "postcss-value-parser" "^4.2.0" + +"postcss-minify-selectors@^5.1.1": + "integrity" "sha512-TOzqOPXt91O2luJInaVPiivh90a2SIK5Nf1Ea7yEIM/5w+XA5BGrZGUSW8aEx9pJ/oNj7ZJBhjvigSiBV+bC1Q==" + "resolved" "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.1.1.tgz" + "version" "5.1.1" + dependencies: + "alphanum-sort" "^1.0.2" + "postcss-selector-parser" "^6.0.5" + +"postcss-modules-extract-imports@^3.0.0": + "integrity" "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==" + "resolved" "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz" + "version" "3.0.0" + +"postcss-modules-local-by-default@^4.0.0": + "integrity" "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==" + "resolved" "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz" + "version" "4.0.0" + dependencies: + "icss-utils" "^5.0.0" + "postcss-selector-parser" "^6.0.2" + "postcss-value-parser" "^4.1.0" + +"postcss-modules-scope@^3.0.0": + "integrity" "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==" + "resolved" "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz" + "version" "3.0.0" + dependencies: + "postcss-selector-parser" "^6.0.4" + +"postcss-modules-values@^4.0.0": + "integrity" "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==" + "resolved" "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz" + "version" "4.0.0" + dependencies: + "icss-utils" "^5.0.0" + +"postcss-nested@5.0.6": + "integrity" "sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==" + "resolved" "https://registry.npmjs.org/postcss-nested/-/postcss-nested-5.0.6.tgz" + "version" "5.0.6" + dependencies: + "postcss-selector-parser" "^6.0.6" + +"postcss-nesting@^10.1.2": + "integrity" "sha512-dJGmgmsvpzKoVMtDMQQG/T6FSqs6kDtUDirIfl4KnjMCiY9/ETX8jdKyCd20swSRAbUYkaBKV20pxkzxoOXLqQ==" + "resolved" "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.1.2.tgz" + "version" "10.1.2" + dependencies: + "postcss-selector-parser" "^6.0.8" + +"postcss-normalize-charset@^5.0.1": + "integrity" "sha512-6J40l6LNYnBdPSk+BHZ8SF+HAkS4q2twe5jnocgd+xWpz/mx/5Sa32m3W1AA8uE8XaXN+eg8trIlfu8V9x61eg==" + "resolved" "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.0.1.tgz" + "version" "5.0.1" + +"postcss-normalize-display-values@^5.0.2": + "integrity" "sha512-RxXoJPUR0shSjkMMzgEZDjGPrgXUVYyWA/YwQRicb48H15OClPuaDR7tYokLAlGZ2tCSENEN5WxjgxSD5m4cUw==" + "resolved" "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.0.2.tgz" + "version" "5.0.2" + dependencies: + "postcss-value-parser" "^4.2.0" + +"postcss-normalize-positions@^5.0.2": + "integrity" "sha512-tqghWFVDp2btqFg1gYob1etPNxXLNh3uVeWgZE2AQGh6b2F8AK2Gj36v5Vhyh+APwIzNjmt6jwZ9pTBP+/OM8g==" + "resolved" "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.0.2.tgz" + "version" "5.0.2" + dependencies: + "postcss-value-parser" "^4.2.0" + +"postcss-normalize-repeat-style@^5.0.2": + "integrity" "sha512-/rIZn8X9bBzC7KvY4iKUhXUGW3MmbXwfPF23jC9wT9xTi7kAvgj8sEgwxjixBmoL6MVa4WOgxNz2hAR6wTK8tw==" + "resolved" "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.0.2.tgz" + "version" "5.0.2" + dependencies: + "postcss-value-parser" "^4.2.0" + +"postcss-normalize-string@^5.0.2": + "integrity" "sha512-zaI1yzwL+a/FkIzUWMQoH25YwCYxi917J4pYm1nRXtdgiCdnlTkx5eRzqWEC64HtRa06WCJ9TIutpb6GmW4gFw==" + "resolved" "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.0.2.tgz" + "version" "5.0.2" + dependencies: + "postcss-value-parser" "^4.2.0" + +"postcss-normalize-timing-functions@^5.0.2": + "integrity" "sha512-Ao0PP6MoYsRU1LxeVUW740ioknvdIUmfr6uAA3xWlQJ9s69/Tupy8qwhuKG3xWfl+KvLMAP9p2WXF9cwuk/7Bg==" + "resolved" "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.0.2.tgz" + "version" "5.0.2" + dependencies: + "postcss-value-parser" "^4.2.0" + +"postcss-normalize-unicode@^5.0.2": + "integrity" "sha512-3y/V+vjZ19HNcTizeqwrbZSUsE69ZMRHfiiyLAJb7C7hJtYmM4Gsbajy7gKagu97E8q5rlS9k8FhojA8cpGhWw==" + "resolved" "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.0.2.tgz" + "version" "5.0.2" + dependencies: + "browserslist" "^4.16.6" + "postcss-value-parser" "^4.2.0" + +"postcss-normalize-url@^5.0.4": + "integrity" "sha512-cNj3RzK2pgQQyNp7dzq0dqpUpQ/wYtdDZM3DepPmFjCmYIfceuD9VIAcOdvrNetjIU65g1B4uwdP/Krf6AFdXg==" + "resolved" "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.0.4.tgz" + "version" "5.0.4" + dependencies: + "normalize-url" "^6.0.1" + "postcss-value-parser" "^4.2.0" + +"postcss-normalize-whitespace@^5.0.2": + "integrity" "sha512-CXBx+9fVlzSgbk0IXA/dcZn9lXixnQRndnsPC5ht3HxlQ1bVh77KQDL1GffJx1LTzzfae8ftMulsjYmO2yegxA==" + "resolved" "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.0.2.tgz" + "version" "5.0.2" + dependencies: + "postcss-value-parser" "^4.2.0" + +"postcss-normalize@^10.0.1": + "integrity" "sha512-+5w18/rDev5mqERcG3W5GZNMJa1eoYYNGo8gB7tEwaos0ajk3ZXAI4mHGcNT47NE+ZnZD1pEpUOFLvltIwmeJA==" + "resolved" "https://registry.npmjs.org/postcss-normalize/-/postcss-normalize-10.0.1.tgz" + "version" "10.0.1" + dependencies: + "@csstools/normalize.css" "*" + "postcss-browser-comments" "^4" + "sanitize.css" "*" + +"postcss-ordered-values@^5.0.3": + "integrity" "sha512-T9pDS+P9bWeFvqivXd5ACzQmrCmHjv3ZP+djn8E1UZY7iK79pFSm7i3WbKw2VSmFmdbMm8sQ12OPcNpzBo3Z2w==" + "resolved" "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.0.3.tgz" + "version" "5.0.3" + dependencies: + "cssnano-utils" "^3.0.0" + "postcss-value-parser" "^4.2.0" + +"postcss-overflow-shorthand@^3.0.2": + "integrity" "sha512-odBMVt6PTX7jOE9UNvmnLrFzA9pXS44Jd5shFGGtSHY80QCuJF+14McSy0iavZggRZ9Oj//C9vOKQmexvyEJMg==" + "resolved" "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.2.tgz" + "version" "3.0.2" + +"postcss-page-break@^3.0.4": + "integrity" "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==" + "resolved" "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz" + "version" "3.0.4" + +"postcss-place@^7.0.3": + "integrity" "sha512-tDQ3m+GYoOar+KoQgj+pwPAvGHAp/Sby6vrFiyrELrMKQJ4AejL0NcS0mm296OKKYA2SRg9ism/hlT/OLhBrdQ==" + "resolved" "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.3.tgz" + "version" "7.0.3" + dependencies: + "postcss-value-parser" "^4.2.0" + +"postcss-preset-env@^7.0.1": + "integrity" "sha512-Ok0DhLfwrcNGrBn8sNdy1uZqWRk/9FId0GiQ39W4ILop5GHtjJs8bu1MY9isPwHInpVEPWjb4CEcEaSbBLpfwA==" + "resolved" "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.2.3.tgz" + "version" "7.2.3" + dependencies: + "autoprefixer" "^10.4.2" + "browserslist" "^4.19.1" + "caniuse-lite" "^1.0.30001299" + "css-blank-pseudo" "^3.0.2" + "css-has-pseudo" "^3.0.3" + "css-prefers-color-scheme" "^6.0.2" + "cssdb" "^5.0.0" + "postcss-attribute-case-insensitive" "^5.0.0" + "postcss-color-functional-notation" "^4.2.1" + "postcss-color-hex-alpha" "^8.0.2" + "postcss-color-rebeccapurple" "^7.0.2" + "postcss-custom-media" "^8.0.0" + "postcss-custom-properties" "^12.1.2" + "postcss-custom-selectors" "^6.0.0" + "postcss-dir-pseudo-class" "^6.0.3" + "postcss-double-position-gradients" "^3.0.4" + "postcss-env-function" "^4.0.4" + "postcss-focus-visible" "^6.0.3" + "postcss-focus-within" "^5.0.3" + "postcss-font-variant" "^5.0.0" + "postcss-gap-properties" "^3.0.2" + "postcss-image-set-function" "^4.0.4" + "postcss-initial" "^4.0.1" + "postcss-lab-function" "^4.0.3" + "postcss-logical" "^5.0.3" + "postcss-media-minmax" "^5.0.0" + "postcss-nesting" "^10.1.2" + "postcss-overflow-shorthand" "^3.0.2" + "postcss-page-break" "^3.0.4" + "postcss-place" "^7.0.3" + "postcss-pseudo-class-any-link" "^7.0.2" + "postcss-replace-overflow-wrap" "^4.0.0" + "postcss-selector-not" "^5.0.0" + +"postcss-pseudo-class-any-link@^7.0.2": + "integrity" "sha512-CG35J1COUH7OOBgpw5O+0koOLUd5N4vUGKUqSAuIe4GiuLHWU96Pqp+UPC8QITTd12zYAFx76pV7qWT/0Aj/TA==" + "resolved" "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.0.2.tgz" + "version" "7.0.2" + dependencies: + "postcss-selector-parser" "^6.0.8" + +"postcss-reduce-initial@^5.0.2": + "integrity" "sha512-v/kbAAQ+S1V5v9TJvbGkV98V2ERPdU6XvMcKMjqAlYiJ2NtsHGlKYLPjWWcXlaTKNxooId7BGxeraK8qXvzKtw==" + "resolved" "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.0.2.tgz" + "version" "5.0.2" + dependencies: + "browserslist" "^4.16.6" + "caniuse-api" "^3.0.0" + +"postcss-reduce-transforms@^5.0.2": + "integrity" "sha512-25HeDeFsgiPSUx69jJXZn8I06tMxLQJJNF5h7i9gsUg8iP4KOOJ8EX8fj3seeoLt3SLU2YDD6UPnDYVGUO7DEA==" + "resolved" "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.0.2.tgz" + "version" "5.0.2" + dependencies: + "postcss-value-parser" "^4.2.0" + +"postcss-replace-overflow-wrap@^4.0.0": + "integrity" "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==" + "resolved" "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz" + "version" "4.0.0" + +"postcss-selector-not@^5.0.0": + "integrity" "sha512-/2K3A4TCP9orP4TNS7u3tGdRFVKqz/E6pX3aGnriPG0jU78of8wsUcqE4QAhWEU0d+WnMSF93Ah3F//vUtK+iQ==" + "resolved" "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-5.0.0.tgz" + "version" "5.0.0" + dependencies: + "balanced-match" "^1.0.0" + +"postcss-selector-parser@^6.0.2", "postcss-selector-parser@^6.0.4", "postcss-selector-parser@^6.0.5", "postcss-selector-parser@^6.0.6", "postcss-selector-parser@^6.0.8": + "integrity" "sha512-D5PG53d209Z1Uhcc0qAZ5U3t5HagH3cxu+WLZ22jt3gLUpXM4eXXfiO14jiDWST3NNooX/E8wISfOhZ9eIjGTQ==" + "resolved" "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.8.tgz" + "version" "6.0.8" + dependencies: + "cssesc" "^3.0.0" + "util-deprecate" "^1.0.2" + +"postcss-svgo@^5.0.3": + "integrity" "sha512-41XZUA1wNDAZrQ3XgWREL/M2zSw8LJPvb5ZWivljBsUQAGoEKMYm6okHsTjJxKYI4M75RQEH4KYlEM52VwdXVA==" + "resolved" "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.0.3.tgz" + "version" "5.0.3" + dependencies: + "postcss-value-parser" "^4.1.0" + "svgo" "^2.7.0" + +"postcss-unique-selectors@^5.0.2": + "integrity" "sha512-w3zBVlrtZm7loQWRPVC0yjUwwpty7OM6DnEHkxcSQXO1bMS3RJ+JUS5LFMSDZHJcvGsRwhZinCWVqn8Kej4EDA==" + "resolved" "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.0.2.tgz" + "version" "5.0.2" + dependencies: + "alphanum-sort" "^1.0.2" + "postcss-selector-parser" "^6.0.5" + +"postcss-value-parser@^4.0.2", "postcss-value-parser@^4.1.0", "postcss-value-parser@^4.2.0": + "integrity" "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + "resolved" "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz" + "version" "4.2.0" + +"postcss@^7.0.0 || ^8.0.1", "postcss@^8", "postcss@^8.0.0", "postcss@^8.0.2", "postcss@^8.0.3", "postcss@^8.0.9", "postcss@^8.1.0", "postcss@^8.1.2", "postcss@^8.1.4", "postcss@^8.2.14", "postcss@^8.2.15", "postcss@^8.2.2", "postcss@^8.3", "postcss@^8.3.3", "postcss@^8.3.5", "postcss@^8.4", "postcss@^8.4.4", "postcss@>= 8", "postcss@>=8": + "integrity" "sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg==" + "resolved" "https://registry.npmjs.org/postcss/-/postcss-8.4.5.tgz" + "version" "8.4.5" + dependencies: + "nanoid" "^3.1.30" + "picocolors" "^1.0.0" + "source-map-js" "^1.0.1" + +"postcss@^7.0.35": + "integrity" "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==" + "resolved" "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz" + "version" "7.0.39" + dependencies: + "picocolors" "^0.2.1" + "source-map" "^0.6.1" + +"prelude-ls@^1.2.1": + "integrity" "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" + "resolved" "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" + "version" "1.2.1" + +"prelude-ls@~1.1.2": + "integrity" "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" + "resolved" "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz" + "version" "1.1.2" + +"pretty-bytes@^5.3.0", "pretty-bytes@^5.4.1": + "integrity" "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==" + "resolved" "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz" + "version" "5.6.0" + +"pretty-error@^4.0.0": + "integrity" "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==" + "resolved" "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz" + "version" "4.0.0" + dependencies: + "lodash" "^4.17.20" + "renderkid" "^3.0.0" + +"pretty-format@^27.4.6": + "integrity" "sha512-NblstegA1y/RJW2VyML+3LlpFjzx62cUrtBIKIWDXEDkjNeleA7Od7nrzcs/VLQvAeV4CgSYhrN39DRN88Qi/g==" + "resolved" "https://registry.npmjs.org/pretty-format/-/pretty-format-27.4.6.tgz" + "version" "27.4.6" + dependencies: + "ansi-regex" "^5.0.1" + "ansi-styles" "^5.0.0" + "react-is" "^17.0.1" + +"process-nextick-args@~2.0.0": + "integrity" "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + "resolved" "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz" + "version" "2.0.1" + +"progress@^2.0.0": + "integrity" "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" + "resolved" "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz" + "version" "2.0.3" + +"promise@^8.1.0": + "integrity" "sha512-W04AqnILOL/sPRXziNicCjSNRruLAuIHEOVBazepu0545DDNGYHz7ar9ZgZ1fMU8/MA4mVxp5rkBWRi6OXIy3Q==" + "resolved" "https://registry.npmjs.org/promise/-/promise-8.1.0.tgz" + "version" "8.1.0" + dependencies: + "asap" "~2.0.6" + +"prompts@^2.0.1", "prompts@^2.4.2": + "integrity" "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==" + "resolved" "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz" + "version" "2.4.2" + dependencies: + "kleur" "^3.0.3" + "sisteransi" "^1.0.5" + +"prop-types@^15.6.2", "prop-types@^15.7.2": + "integrity" "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==" + "resolved" "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz" + "version" "15.7.2" + dependencies: + "loose-envify" "^1.4.0" + "object-assign" "^4.1.1" + "react-is" "^16.8.1" + +"proxy-addr@~2.0.7": + "integrity" "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==" + "resolved" "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz" + "version" "2.0.7" + dependencies: + "forwarded" "0.2.0" + "ipaddr.js" "1.9.1" + +"psl@^1.1.33": + "integrity" "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + "resolved" "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz" + "version" "1.8.0" + +"punycode@^2.1.0", "punycode@^2.1.1": + "integrity" "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + "resolved" "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz" + "version" "2.1.1" + +"q@^1.1.2": + "integrity" "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" + "resolved" "https://registry.npmjs.org/q/-/q-1.5.1.tgz" + "version" "1.5.1" + +"qs@6.9.6": + "integrity" "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==" + "resolved" "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz" + "version" "6.9.6" + +"queue-microtask@^1.2.2": + "integrity" "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" + "resolved" "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" + "version" "1.2.3" + +"quick-lru@^5.1.1": + "integrity" "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" + "resolved" "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz" + "version" "5.1.1" + +"raf@^3.4.1": + "integrity" "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==" + "resolved" "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz" + "version" "3.4.1" + dependencies: + "performance-now" "^2.1.0" + +"randombytes@^2.1.0": + "integrity" "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==" + "resolved" "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz" + "version" "2.1.0" + dependencies: + "safe-buffer" "^5.1.0" + +"range-parser@^1.2.1", "range-parser@~1.2.1": + "integrity" "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + "resolved" "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz" + "version" "1.2.1" + +"raw-body@2.4.2": + "integrity" "sha512-RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqeeQ==" + "resolved" "https://registry.npmjs.org/raw-body/-/raw-body-2.4.2.tgz" + "version" "2.4.2" + dependencies: + "bytes" "3.1.1" + "http-errors" "1.8.1" + "iconv-lite" "0.4.24" + "unpipe" "1.0.0" + +"react-app-polyfill@^3.0.0": + "integrity" "sha512-sZ41cxiU5llIB003yxxQBYrARBqe0repqPTTYBTmMqTz9szeBbE37BehCE891NZsmdZqqP+xWKdT3eo3vOzN8w==" + "resolved" "https://registry.npmjs.org/react-app-polyfill/-/react-app-polyfill-3.0.0.tgz" + "version" "3.0.0" + dependencies: + "core-js" "^3.19.2" + "object-assign" "^4.1.1" + "promise" "^8.1.0" + "raf" "^3.4.1" + "regenerator-runtime" "^0.13.9" + "whatwg-fetch" "^3.6.2" + +"react-dev-utils@^12.0.0": + "integrity" "sha512-xBQkitdxozPxt1YZ9O1097EJiVpwHr9FoAuEVURCKV0Av8NBERovJauzP7bo1ThvuhZ4shsQ1AJiu4vQpoT1AQ==" + "resolved" "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.0.tgz" + "version" "12.0.0" + dependencies: + "@babel/code-frame" "^7.16.0" + "address" "^1.1.2" + "browserslist" "^4.18.1" + "chalk" "^4.1.2" + "cross-spawn" "^7.0.3" + "detect-port-alt" "^1.1.6" + "escape-string-regexp" "^4.0.0" + "filesize" "^8.0.6" + "find-up" "^5.0.0" + "fork-ts-checker-webpack-plugin" "^6.5.0" + "global-modules" "^2.0.0" + "globby" "^11.0.4" + "gzip-size" "^6.0.0" + "immer" "^9.0.7" + "is-root" "^2.1.0" + "loader-utils" "^3.2.0" + "open" "^8.4.0" + "pkg-up" "^3.1.0" + "prompts" "^2.4.2" + "react-error-overlay" "^6.0.10" + "recursive-readdir" "^2.2.2" + "shell-quote" "^1.7.3" + "strip-ansi" "^6.0.1" + "text-table" "^0.2.0" + +"react-dom@^16.9.0": + "integrity" "sha512-YFT2rxO9hM70ewk9jq0y6sQk8cL02xm4+IzYBz75CQGlClQQ1Bxq0nhHF6OtSbit+AIahujJgb/CPRibFkMNJQ==" + "resolved" "https://registry.npmjs.org/react-dom/-/react-dom-16.9.0.tgz" + "version" "16.9.0" + dependencies: + "loose-envify" "^1.1.0" + "object-assign" "^4.1.1" + "prop-types" "^15.6.2" + "scheduler" "^0.15.0" + +"react-error-overlay@^6.0.10": + "integrity" "sha512-mKR90fX7Pm5seCOfz8q9F+66VCc1PGsWSBxKbITjfKVQHMNF2zudxHnMdJiB1fRCb+XsbQV9sO9DCkgsMQgBIA==" + "resolved" "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.10.tgz" + "version" "6.0.10" + +"react-is@^16.8.1": + "integrity" "sha512-tJBzzzIgnnRfEm046qRcURvwQnZVXmuCbscxUO5RWrGTXpon2d4c8mI0D8WE6ydVIm29JiLB6+RslkIvym9Rjw==" + "resolved" "https://registry.npmjs.org/react-is/-/react-is-16.9.0.tgz" + "version" "16.9.0" + +"react-is@^17.0.1": + "integrity" "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + "resolved" "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz" + "version" "17.0.2" + +"react-refresh@^0.11.0", "react-refresh@>=0.10.0 <1.0.0": + "integrity" "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==" + "resolved" "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz" + "version" "0.11.0" + +"react-scripts@^5.0.0": + "integrity" "sha512-3i0L2CyIlROz7mxETEdfif6Sfhh9Lfpzi10CtcGs1emDQStmZfWjJbAIMtRD0opVUjQuFWqHZyRZ9PPzKCFxWg==" + "resolved" "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.0.tgz" + "version" "5.0.0" + dependencies: + "@babel/core" "^7.16.0" + "@pmmmwh/react-refresh-webpack-plugin" "^0.5.3" + "@svgr/webpack" "^5.5.0" + "babel-jest" "^27.4.2" + "babel-loader" "^8.2.3" + "babel-plugin-named-asset-import" "^0.3.8" + "babel-preset-react-app" "^10.0.1" + "bfj" "^7.0.2" + "browserslist" "^4.18.1" + "camelcase" "^6.2.1" + "case-sensitive-paths-webpack-plugin" "^2.4.0" + "css-loader" "^6.5.1" + "css-minimizer-webpack-plugin" "^3.2.0" + "dotenv" "^10.0.0" + "dotenv-expand" "^5.1.0" + "eslint" "^8.3.0" + "eslint-config-react-app" "^7.0.0" + "eslint-webpack-plugin" "^3.1.1" + "file-loader" "^6.2.0" + "fs-extra" "^10.0.0" + "html-webpack-plugin" "^5.5.0" + "identity-obj-proxy" "^3.0.0" + "jest" "^27.4.3" + "jest-resolve" "^27.4.2" + "jest-watch-typeahead" "^1.0.0" + "mini-css-extract-plugin" "^2.4.5" + "postcss" "^8.4.4" + "postcss-flexbugs-fixes" "^5.0.2" + "postcss-loader" "^6.2.1" + "postcss-normalize" "^10.0.1" + "postcss-preset-env" "^7.0.1" + "prompts" "^2.4.2" + "react-app-polyfill" "^3.0.0" + "react-dev-utils" "^12.0.0" + "react-refresh" "^0.11.0" + "resolve" "^1.20.0" + "resolve-url-loader" "^4.0.0" + "sass-loader" "^12.3.0" + "semver" "^7.3.5" + "source-map-loader" "^3.0.0" + "style-loader" "^3.3.1" + "tailwindcss" "^3.0.2" + "terser-webpack-plugin" "^5.2.5" + "webpack" "^5.64.4" + "webpack-dev-server" "^4.6.0" + "webpack-manifest-plugin" "^4.0.2" + "workbox-webpack-plugin" "^6.4.1" + optionalDependencies: + "fsevents" "^2.3.2" + +"react@^16.0.0", "react@^16.9.0", "react@>= 16": + "integrity" "sha512-+7LQnFBwkiw+BobzOF6N//BdoNw0ouwmSJTEm9cglOOmsg/TMiFHZLe2sEoN5M7LgJTj9oHH0gxklfnQe66S1w==" + "resolved" "https://registry.npmjs.org/react/-/react-16.9.0.tgz" + "version" "16.9.0" + dependencies: + "loose-envify" "^1.1.0" + "object-assign" "^4.1.1" + "prop-types" "^15.6.2" + +"readable-stream@^2.0.1": + "integrity" "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==" + "resolved" "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz" + "version" "2.3.7" + dependencies: + "core-util-is" "~1.0.0" + "inherits" "~2.0.3" + "isarray" "~1.0.0" + "process-nextick-args" "~2.0.0" + "safe-buffer" "~5.1.1" + "string_decoder" "~1.1.1" + "util-deprecate" "~1.0.1" + +"readable-stream@^3.0.6": + "integrity" "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==" + "resolved" "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz" + "version" "3.6.0" + dependencies: + "inherits" "^2.0.3" + "string_decoder" "^1.1.1" + "util-deprecate" "^1.0.1" + +"readdirp@~3.6.0": + "integrity" "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==" + "resolved" "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz" + "version" "3.6.0" + dependencies: + "picomatch" "^2.2.1" + +"recursive-readdir@^2.2.2": + "integrity" "sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==" + "resolved" "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz" + "version" "2.2.2" + dependencies: + "minimatch" "3.0.4" + +"regenerate-unicode-properties@^9.0.0": + "integrity" "sha512-3E12UeNSPfjrgwjkR81m5J7Aw/T55Tu7nUyZVQYCKEOs+2dkxEY+DpPtZzO4YruuiPb7NkYLVcyJC4+zCbk5pA==" + "resolved" "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-9.0.0.tgz" + "version" "9.0.0" + dependencies: + "regenerate" "^1.4.2" + +"regenerate@^1.4.2": + "integrity" "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" + "resolved" "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz" + "version" "1.4.2" + +"regenerator-runtime@^0.13.4", "regenerator-runtime@^0.13.9": + "integrity" "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + "resolved" "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz" + "version" "0.13.9" + +"regenerator-transform@^0.14.2": + "integrity" "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==" + "resolved" "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz" + "version" "0.14.5" + dependencies: + "@babel/runtime" "^7.8.4" + +"regex-parser@^2.2.11": + "integrity" "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==" + "resolved" "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz" + "version" "2.2.11" + +"regexp.prototype.flags@^1.2.0", "regexp.prototype.flags@^1.3.1": + "integrity" "sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==" + "resolved" "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz" + "version" "1.3.1" + dependencies: + "call-bind" "^1.0.2" + "define-properties" "^1.1.3" + +"regexpp@^3.2.0": + "integrity" "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==" + "resolved" "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz" + "version" "3.2.0" + +"regexpu-core@^4.7.1": + "integrity" "sha512-1F6bYsoYiz6is+oz70NWur2Vlh9KWtswuRuzJOfeYUrfPX2o8n74AnUVaOGDbUqVGO9fNHu48/pjJO4sNVwsOg==" + "resolved" "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.8.0.tgz" + "version" "4.8.0" + dependencies: + "regenerate" "^1.4.2" + "regenerate-unicode-properties" "^9.0.0" + "regjsgen" "^0.5.2" + "regjsparser" "^0.7.0" + "unicode-match-property-ecmascript" "^2.0.0" + "unicode-match-property-value-ecmascript" "^2.0.0" + +"regjsgen@^0.5.2": + "integrity" "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==" + "resolved" "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz" + "version" "0.5.2" + +"regjsparser@^0.7.0": + "integrity" "sha512-A4pcaORqmNMDVwUjWoTzuhwMGpP+NykpfqAsEgI1FSH/EzC7lrN5TMd+kN8YCovX+jMpu8eaqXgXPCa0g8FQNQ==" + "resolved" "https://registry.npmjs.org/regjsparser/-/regjsparser-0.7.0.tgz" + "version" "0.7.0" + dependencies: + "jsesc" "~0.5.0" + +"relateurl@^0.2.7": + "integrity" "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=" + "resolved" "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz" + "version" "0.2.7" + +"renderkid@^3.0.0": + "integrity" "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==" + "resolved" "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz" + "version" "3.0.0" + dependencies: + "css-select" "^4.1.3" + "dom-converter" "^0.2.0" + "htmlparser2" "^6.1.0" + "lodash" "^4.17.21" + "strip-ansi" "^6.0.1" + +"require-directory@^2.1.1": + "integrity" "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + "resolved" "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" + "version" "2.1.1" + +"require-from-string@^2.0.2": + "integrity" "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" + "resolved" "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz" + "version" "2.0.2" + +"requires-port@^1.0.0": + "integrity" "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + "resolved" "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz" + "version" "1.0.0" + +"resolve-cwd@^3.0.0": + "integrity" "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==" + "resolved" "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz" + "version" "3.0.0" + dependencies: + "resolve-from" "^5.0.0" + +"resolve-from@^4.0.0": + "integrity" "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" + "resolved" "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" + "version" "4.0.0" + +"resolve-from@^5.0.0": + "integrity" "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==" + "resolved" "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz" + "version" "5.0.0" + +"resolve-url-loader@^4.0.0": + "integrity" "sha512-05VEMczVREcbtT7Bz+C+96eUO5HDNvdthIiMB34t7FcF8ehcu4wC0sSgPUubs3XW2Q3CNLJk/BJrCU9wVRymiA==" + "resolved" "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-4.0.0.tgz" + "version" "4.0.0" + dependencies: + "adjust-sourcemap-loader" "^4.0.0" + "convert-source-map" "^1.7.0" + "loader-utils" "^2.0.0" + "postcss" "^7.0.35" + "source-map" "0.6.1" + +"resolve.exports@^1.1.0": + "integrity" "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==" + "resolved" "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz" + "version" "1.1.0" + +"resolve@^1.14.2", "resolve@^1.19.0", "resolve@^1.20.0", "resolve@^1.21.0": + "integrity" "sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA==" + "resolved" "https://registry.npmjs.org/resolve/-/resolve-1.21.0.tgz" + "version" "1.21.0" + dependencies: + "is-core-module" "^2.8.0" + "path-parse" "^1.0.7" + "supports-preserve-symlinks-flag" "^1.0.0" + +"resolve@^2.0.0-next.3": + "integrity" "sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==" + "resolved" "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz" + "version" "2.0.0-next.3" + dependencies: + "is-core-module" "^2.2.0" + "path-parse" "^1.0.6" + +"retry@^0.13.1": + "integrity" "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==" + "resolved" "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz" + "version" "0.13.1" + +"reusify@^1.0.4": + "integrity" "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" + "resolved" "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" + "version" "1.0.4" + +"rimraf@^3.0.0", "rimraf@^3.0.2": + "integrity" "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==" + "resolved" "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" + "version" "3.0.2" + dependencies: + "glob" "^7.1.3" + +"rollup-plugin-terser@^7.0.0": + "integrity" "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==" + "resolved" "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz" + "version" "7.0.2" + dependencies: + "@babel/code-frame" "^7.10.4" + "jest-worker" "^26.2.1" + "serialize-javascript" "^4.0.0" + "terser" "^5.0.0" + +"rollup@^1.20.0 || ^2.0.0", "rollup@^1.20.0||^2.0.0", "rollup@^2.0.0", "rollup@^2.43.1": + "integrity" "sha512-nps0idjmD+NXl6OREfyYXMn/dar3WGcyKn+KBzPdaLecub3x/LrId0wUcthcr8oZUAcZAR8NKcfGGFlNgGL1kQ==" + "resolved" "https://registry.npmjs.org/rollup/-/rollup-2.63.0.tgz" + "version" "2.63.0" + optionalDependencies: + "fsevents" "~2.3.2" + +"run-parallel@^1.1.9": + "integrity" "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==" + "resolved" "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" + "version" "1.2.0" + dependencies: + "queue-microtask" "^1.2.2" + +"safe-buffer@^5.0.1", "safe-buffer@^5.1.0", "safe-buffer@>=5.1.0", "safe-buffer@~5.1.0", "safe-buffer@~5.1.1", "safe-buffer@5.1.2": + "integrity" "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "resolved" "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" + "version" "5.1.2" + +"safe-buffer@~5.2.0": + "integrity" "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + "resolved" "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" + "version" "5.2.1" + +"safe-buffer@5.2.1": + "integrity" "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + "resolved" "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" + "version" "5.2.1" + +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": + "integrity" "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "resolved" "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" + "version" "2.1.2" + +"sanitize.css@*": + "integrity" "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==" + "resolved" "https://registry.npmjs.org/sanitize.css/-/sanitize.css-13.0.0.tgz" + "version" "13.0.0" + +"sass-loader@^12.3.0": + "integrity" "sha512-7xN+8khDIzym1oL9XyS6zP6Ges+Bo2B2xbPrjdMHEYyV3AQYhd/wXeru++3ODHF0zMjYmVadblSKrPrjEkL8mg==" + "resolved" "https://registry.npmjs.org/sass-loader/-/sass-loader-12.4.0.tgz" + "version" "12.4.0" + dependencies: + "klona" "^2.0.4" + "neo-async" "^2.6.2" + +"sax@~1.2.4": + "integrity" "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + "resolved" "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz" + "version" "1.2.4" + +"saxes@^5.0.1": + "integrity" "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==" + "resolved" "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz" + "version" "5.0.1" + dependencies: + "xmlchars" "^2.2.0" + +"scheduler@^0.15.0": + "integrity" "sha512-xAefmSfN6jqAa7Kuq7LIJY0bwAPG3xlCj0HMEBQk1lxYiDKZscY2xJ5U/61ZTrYbmNQbXa+gc7czPkVo11tnCg==" + "resolved" "https://registry.npmjs.org/scheduler/-/scheduler-0.15.0.tgz" + "version" "0.15.0" + dependencies: + "loose-envify" "^1.1.0" + "object-assign" "^4.1.1" + +"schema-utils@^2.6.5": + "integrity" "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==" + "resolved" "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz" + "version" "2.7.1" + dependencies: + "@types/json-schema" "^7.0.5" + "ajv" "^6.12.4" + "ajv-keywords" "^3.5.2" + +"schema-utils@^3.0.0", "schema-utils@^3.1.0", "schema-utils@^3.1.1": + "integrity" "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==" + "resolved" "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz" + "version" "3.1.1" + dependencies: + "@types/json-schema" "^7.0.8" + "ajv" "^6.12.5" + "ajv-keywords" "^3.5.2" + +"schema-utils@^4.0.0": + "integrity" "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==" + "resolved" "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz" + "version" "4.0.0" + dependencies: + "@types/json-schema" "^7.0.9" + "ajv" "^8.8.0" + "ajv-formats" "^2.1.1" + "ajv-keywords" "^5.0.0" + +"schema-utils@2.7.0": + "integrity" "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==" + "resolved" "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz" + "version" "2.7.0" + dependencies: + "@types/json-schema" "^7.0.4" + "ajv" "^6.12.2" + "ajv-keywords" "^3.4.1" + +"select-hose@^2.0.0": + "integrity" "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=" + "resolved" "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz" + "version" "2.0.0" + +"selfsigned@^2.0.0": + "integrity" "sha512-cUdFiCbKoa1mZ6osuJs2uDHrs0k0oprsKveFiiaBKCNq3SYyb5gs2HxhQyDNLCmL51ZZThqi4YNDpCK6GOP1iQ==" + "resolved" "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.0.tgz" + "version" "2.0.0" + dependencies: + "node-forge" "^1.2.0" + +"semver@^6.0.0": + "integrity" "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "resolved" "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" + "version" "6.3.0" + +"semver@^6.1.1": + "integrity" "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "resolved" "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" + "version" "6.3.0" + +"semver@^6.1.2": + "integrity" "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "resolved" "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" + "version" "6.3.0" + +"semver@^6.3.0": + "integrity" "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "resolved" "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" + "version" "6.3.0" + +"semver@^7.2.1", "semver@^7.3.2", "semver@^7.3.5": + "integrity" "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==" + "resolved" "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz" + "version" "7.3.5" + dependencies: + "lru-cache" "^6.0.0" + +"semver@7.0.0": + "integrity" "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==" + "resolved" "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz" + "version" "7.0.0" + +"send@0.17.2": + "integrity" "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==" + "resolved" "https://registry.npmjs.org/send/-/send-0.17.2.tgz" + "version" "0.17.2" + dependencies: + "debug" "2.6.9" + "depd" "~1.1.2" + "destroy" "~1.0.4" + "encodeurl" "~1.0.2" + "escape-html" "~1.0.3" + "etag" "~1.8.1" + "fresh" "0.5.2" + "http-errors" "1.8.1" + "mime" "1.6.0" + "ms" "2.1.3" + "on-finished" "~2.3.0" + "range-parser" "~1.2.1" + "statuses" "~1.5.0" + +"serialize-javascript@^4.0.0": + "integrity" "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==" + "resolved" "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz" + "version" "4.0.0" + dependencies: + "randombytes" "^2.1.0" + +"serialize-javascript@^6.0.0": + "integrity" "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==" + "resolved" "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz" + "version" "6.0.0" + dependencies: + "randombytes" "^2.1.0" + +"serve-index@^1.9.1": + "integrity" "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=" + "resolved" "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz" + "version" "1.9.1" + dependencies: + "accepts" "~1.3.4" + "batch" "0.6.1" + "debug" "2.6.9" + "escape-html" "~1.0.3" + "http-errors" "~1.6.2" + "mime-types" "~2.1.17" + "parseurl" "~1.3.2" + +"serve-static@1.14.2": + "integrity" "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==" + "resolved" "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz" + "version" "1.14.2" + dependencies: + "encodeurl" "~1.0.2" + "escape-html" "~1.0.3" + "parseurl" "~1.3.3" + "send" "0.17.2" + +"setprototypeof@1.1.0": + "integrity" "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + "resolved" "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz" + "version" "1.1.0" + +"setprototypeof@1.2.0": + "integrity" "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + "resolved" "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz" + "version" "1.2.0" + +"shebang-command@^2.0.0": + "integrity" "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==" + "resolved" "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" + "version" "2.0.0" + dependencies: + "shebang-regex" "^3.0.0" + +"shebang-regex@^3.0.0": + "integrity" "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + "resolved" "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" + "version" "3.0.0" + +"shell-quote@^1.7.3": + "integrity" "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==" + "resolved" "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz" + "version" "1.7.3" + +"side-channel@^1.0.4": + "integrity" "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==" + "resolved" "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz" + "version" "1.0.4" + dependencies: + "call-bind" "^1.0.0" + "get-intrinsic" "^1.0.2" + "object-inspect" "^1.9.0" + +"signal-exit@^3.0.2", "signal-exit@^3.0.3": + "integrity" "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==" + "resolved" "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz" + "version" "3.0.6" + +"sisteransi@^1.0.5": + "integrity" "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" + "resolved" "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz" + "version" "1.0.5" + +"slash@^3.0.0": + "integrity" "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" + "resolved" "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz" + "version" "3.0.0" + +"slash@^4.0.0": + "integrity" "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==" + "resolved" "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz" + "version" "4.0.0" + +"sockjs@^0.3.21": + "integrity" "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==" + "resolved" "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz" + "version" "0.3.24" + dependencies: + "faye-websocket" "^0.11.3" + "uuid" "^8.3.2" + "websocket-driver" "^0.7.4" + +"source-list-map@^2.0.0", "source-list-map@^2.0.1": + "integrity" "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==" + "resolved" "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz" + "version" "2.0.1" + +"source-map-js@^1.0.1": + "integrity" "sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA==" + "resolved" "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.1.tgz" + "version" "1.0.1" + +"source-map-loader@^3.0.0": + "integrity" "sha512-Vp1UsfyPvgujKQzi4pyDiTOnE3E4H+yHvkVRN3c/9PJmQS4CQJExvcDvaX/D+RV+xQben9HJ56jMJS3CgUeWyA==" + "resolved" "https://registry.npmjs.org/source-map-loader/-/source-map-loader-3.0.1.tgz" + "version" "3.0.1" + dependencies: + "abab" "^2.0.5" + "iconv-lite" "^0.6.3" + "source-map-js" "^1.0.1" + +"source-map-support@^0.5.6", "source-map-support@~0.5.20": + "integrity" "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==" + "resolved" "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" + "version" "0.5.21" + dependencies: + "buffer-from" "^1.0.0" + "source-map" "^0.6.0" + +"source-map-url@^0.4.0": + "integrity" "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==" + "resolved" "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz" + "version" "0.4.1" + +"source-map@^0.5.0": + "integrity" "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + "resolved" "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" + "version" "0.5.7" + +"source-map@^0.6.0": + "integrity" "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "resolved" "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" + "version" "0.6.1" + +"source-map@^0.6.1", "source-map@0.6.1": + "integrity" "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "resolved" "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" + "version" "0.6.1" + +"source-map@^0.7.3": + "integrity" "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==" + "resolved" "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz" + "version" "0.7.3" + +"source-map@^0.8.0-beta.0": + "integrity" "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==" + "resolved" "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz" + "version" "0.8.0-beta.0" + dependencies: + "whatwg-url" "^7.0.0" + +"source-map@~0.6.0": + "integrity" "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "resolved" "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" + "version" "0.6.1" + +"source-map@~0.6.1": + "integrity" "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "resolved" "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" + "version" "0.6.1" + +"source-map@~0.7.2": + "integrity" "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==" + "resolved" "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz" + "version" "0.7.3" + +"sourcemap-codec@^1.4.4": + "integrity" "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" + "resolved" "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz" + "version" "1.4.8" + +"spdy-transport@^3.0.0": + "integrity" "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==" + "resolved" "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz" + "version" "3.0.0" + dependencies: + "debug" "^4.1.0" + "detect-node" "^2.0.4" + "hpack.js" "^2.1.6" + "obuf" "^1.1.2" + "readable-stream" "^3.0.6" + "wbuf" "^1.7.3" + +"spdy@^4.0.2": + "integrity" "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==" + "resolved" "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz" + "version" "4.0.2" + dependencies: + "debug" "^4.1.0" + "handle-thing" "^2.0.0" + "http-deceiver" "^1.2.7" + "select-hose" "^2.0.0" + "spdy-transport" "^3.0.0" + +"sprintf-js@~1.0.2": + "integrity" "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + "resolved" "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" + "version" "1.0.3" + +"stable@^0.1.8": + "integrity" "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==" + "resolved" "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz" + "version" "0.1.8" + +"stack-utils@^2.0.3": + "integrity" "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==" + "resolved" "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz" + "version" "2.0.5" + dependencies: + "escape-string-regexp" "^2.0.0" + +"stackframe@^1.1.1": + "integrity" "sha512-GrdeshiRmS1YLMYgzF16olf2jJ/IzxXY9lhKOskuVziubpTYcYqyOwYeJKzQkwy7uN0fYSsbsC4RQaXf9LCrYA==" + "resolved" "https://registry.npmjs.org/stackframe/-/stackframe-1.2.0.tgz" + "version" "1.2.0" + +"statuses@>= 1.4.0 < 2", "statuses@>= 1.5.0 < 2", "statuses@~1.5.0": + "integrity" "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + "resolved" "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" + "version" "1.5.0" + +"string_decoder@^1.1.1": + "integrity" "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==" + "resolved" "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" + "version" "1.3.0" + dependencies: + "safe-buffer" "~5.2.0" + +"string_decoder@~1.1.1": + "integrity" "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==" + "resolved" "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" + "version" "1.1.1" + dependencies: + "safe-buffer" "~5.1.0" + +"string-length@^4.0.1": + "integrity" "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==" + "resolved" "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz" + "version" "4.0.2" + dependencies: + "char-regex" "^1.0.2" + "strip-ansi" "^6.0.0" + +"string-length@^5.0.1": + "integrity" "sha512-9Ep08KAMUn0OadnVaBuRdE2l615CQ508kr0XMadjClfYpdCyvrbFp6Taebo8yyxokQ4viUd/xPPUA4FGgUa0ow==" + "resolved" "https://registry.npmjs.org/string-length/-/string-length-5.0.1.tgz" + "version" "5.0.1" + dependencies: + "char-regex" "^2.0.0" + "strip-ansi" "^7.0.1" + +"string-natural-compare@^3.0.1": + "integrity" "sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==" + "resolved" "https://registry.npmjs.org/string-natural-compare/-/string-natural-compare-3.0.1.tgz" + "version" "3.0.1" + +"string-width@^4.1.0", "string-width@^4.2.0": + "integrity" "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==" + "resolved" "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" + "version" "4.2.3" + dependencies: + "emoji-regex" "^8.0.0" + "is-fullwidth-code-point" "^3.0.0" + "strip-ansi" "^6.0.1" + +"string.prototype.matchall@^4.0.6": + "integrity" "sha512-6WgDX8HmQqvEd7J+G6VtAahhsQIssiZ8zl7zKh1VDMFyL3hRTJP4FTNA3RbIp2TOQ9AYNDcc7e3fH0Qbup+DBg==" + "resolved" "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.6.tgz" + "version" "4.0.6" + dependencies: + "call-bind" "^1.0.2" + "define-properties" "^1.1.3" + "es-abstract" "^1.19.1" + "get-intrinsic" "^1.1.1" + "has-symbols" "^1.0.2" + "internal-slot" "^1.0.3" + "regexp.prototype.flags" "^1.3.1" + "side-channel" "^1.0.4" + +"string.prototype.trimend@^1.0.4": + "integrity" "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==" + "resolved" "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz" + "version" "1.0.4" + dependencies: + "call-bind" "^1.0.2" + "define-properties" "^1.1.3" + +"string.prototype.trimstart@^1.0.4": + "integrity" "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==" + "resolved" "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz" + "version" "1.0.4" + dependencies: + "call-bind" "^1.0.2" + "define-properties" "^1.1.3" + +"stringify-object@^3.3.0": + "integrity" "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==" + "resolved" "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz" + "version" "3.3.0" + dependencies: + "get-own-enumerable-property-symbols" "^3.0.0" + "is-obj" "^1.0.1" + "is-regexp" "^1.0.0" + +"strip-ansi@^6.0.0", "strip-ansi@^6.0.1": + "integrity" "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==" + "resolved" "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" + "version" "6.0.1" + dependencies: + "ansi-regex" "^5.0.1" + +"strip-ansi@^7.0.0": + "integrity" "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==" + "resolved" "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz" + "version" "7.0.1" + dependencies: + "ansi-regex" "^6.0.1" + +"strip-ansi@^7.0.1": + "integrity" "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==" + "resolved" "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz" + "version" "7.0.1" + dependencies: + "ansi-regex" "^6.0.1" + +"strip-bom@^3.0.0": + "integrity" "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=" + "resolved" "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz" + "version" "3.0.0" + +"strip-bom@^4.0.0": + "integrity" "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==" + "resolved" "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz" + "version" "4.0.0" + +"strip-comments@^2.0.1": + "integrity" "sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==" + "resolved" "https://registry.npmjs.org/strip-comments/-/strip-comments-2.0.1.tgz" + "version" "2.0.1" + +"strip-final-newline@^2.0.0": + "integrity" "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" + "resolved" "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz" + "version" "2.0.0" + +"strip-json-comments@^3.1.0", "strip-json-comments@^3.1.1": + "integrity" "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" + "resolved" "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" + "version" "3.1.1" + +"style-loader@^3.3.1": + "integrity" "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==" + "resolved" "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz" + "version" "3.3.1" + +"stylehacks@^5.0.1": + "integrity" "sha512-Es0rVnHIqbWzveU1b24kbw92HsebBepxfcqe5iix7t9j0PQqhs0IxXVXv0pY2Bxa08CgMkzD6OWql7kbGOuEdA==" + "resolved" "https://registry.npmjs.org/stylehacks/-/stylehacks-5.0.1.tgz" + "version" "5.0.1" + dependencies: + "browserslist" "^4.16.0" + "postcss-selector-parser" "^6.0.4" + +"supports-color@^5.3.0": + "integrity" "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==" + "resolved" "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" + "version" "5.5.0" + dependencies: + "has-flag" "^3.0.0" + +"supports-color@^7.0.0": + "integrity" "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==" + "resolved" "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" + "version" "7.2.0" + dependencies: + "has-flag" "^4.0.0" + +"supports-color@^7.1.0": + "integrity" "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==" + "resolved" "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" + "version" "7.2.0" + dependencies: + "has-flag" "^4.0.0" + +"supports-color@^8.0.0": + "integrity" "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==" + "resolved" "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz" + "version" "8.1.1" + dependencies: + "has-flag" "^4.0.0" + +"supports-hyperlinks@^2.0.0": + "integrity" "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==" + "resolved" "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz" + "version" "2.2.0" + dependencies: + "has-flag" "^4.0.0" + "supports-color" "^7.0.0" + +"supports-preserve-symlinks-flag@^1.0.0": + "integrity" "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" + "resolved" "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" + "version" "1.0.0" + +"svg-parser@^2.0.2": + "integrity" "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" + "resolved" "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz" + "version" "2.0.4" + +"svgo@^1.2.2": + "integrity" "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==" + "resolved" "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz" + "version" "1.3.2" + dependencies: + "chalk" "^2.4.1" + "coa" "^2.0.2" + "css-select" "^2.0.0" + "css-select-base-adapter" "^0.1.1" + "css-tree" "1.0.0-alpha.37" + "csso" "^4.0.2" + "js-yaml" "^3.13.1" + "mkdirp" "~0.5.1" + "object.values" "^1.1.0" + "sax" "~1.2.4" + "stable" "^0.1.8" + "unquote" "~1.1.1" + "util.promisify" "~1.0.0" + +"svgo@^2.7.0": + "integrity" "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==" + "resolved" "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz" + "version" "2.8.0" + dependencies: + "@trysound/sax" "0.2.0" + "commander" "^7.2.0" + "css-select" "^4.1.3" + "css-tree" "^1.1.3" + "csso" "^4.2.0" + "picocolors" "^1.0.0" + "stable" "^0.1.8" + +"symbol-tree@^3.2.4": + "integrity" "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" + "resolved" "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz" + "version" "3.2.4" + +"tailwindcss@^3.0.2": + "integrity" "sha512-raRPGFwQSGXn/3h0ttHND9jyPYfqk/ur2NXtlQuK25+ZnrCjlH1s1j4/oPswHGMoZzGNykUVycZ/LcROanUE0A==" + "resolved" "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.0.13.tgz" + "version" "3.0.13" + dependencies: + "arg" "^5.0.1" + "chalk" "^4.1.2" + "chokidar" "^3.5.2" + "color-name" "^1.1.4" + "cosmiconfig" "^7.0.1" + "detective" "^5.2.0" + "didyoumean" "^1.2.2" + "dlv" "^1.1.3" + "fast-glob" "^3.2.7" + "glob-parent" "^6.0.2" + "is-glob" "^4.0.3" + "normalize-path" "^3.0.0" + "object-hash" "^2.2.0" + "postcss-js" "^4.0.0" + "postcss-load-config" "^3.1.0" + "postcss-nested" "5.0.6" + "postcss-selector-parser" "^6.0.8" + "postcss-value-parser" "^4.2.0" + "quick-lru" "^5.1.1" + "resolve" "^1.21.0" + +"tapable@^1.0.0": + "integrity" "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==" + "resolved" "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz" + "version" "1.1.3" + +"tapable@^2.0.0", "tapable@^2.1.1", "tapable@^2.2.0": + "integrity" "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==" + "resolved" "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz" + "version" "2.2.1" + +"temp-dir@^2.0.0": + "integrity" "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==" + "resolved" "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz" + "version" "2.0.0" + +"tempy@^0.6.0": + "integrity" "sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==" + "resolved" "https://registry.npmjs.org/tempy/-/tempy-0.6.0.tgz" + "version" "0.6.0" + dependencies: + "is-stream" "^2.0.0" + "temp-dir" "^2.0.0" + "type-fest" "^0.16.0" + "unique-string" "^2.0.0" + +"terminal-link@^2.0.0": + "integrity" "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==" + "resolved" "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz" + "version" "2.1.1" + dependencies: + "ansi-escapes" "^4.2.1" + "supports-hyperlinks" "^2.0.0" + +"terser-webpack-plugin@^5.1.3", "terser-webpack-plugin@^5.2.5": + "integrity" "sha512-LPIisi3Ol4chwAaPP8toUJ3L4qCM1G0wao7L3qNv57Drezxj6+VEyySpPw4B1HSO2Eg/hDY/MNF5XihCAoqnsQ==" + "resolved" "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.0.tgz" + "version" "5.3.0" + dependencies: + "jest-worker" "^27.4.1" + "schema-utils" "^3.1.1" + "serialize-javascript" "^6.0.0" + "source-map" "^0.6.1" + "terser" "^5.7.2" + +"terser@^5.0.0", "terser@^5.10.0", "terser@^5.7.2": + "integrity" "sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==" + "resolved" "https://registry.npmjs.org/terser/-/terser-5.10.0.tgz" + "version" "5.10.0" + dependencies: + "commander" "^2.20.0" + "source-map" "~0.7.2" + "source-map-support" "~0.5.20" + +"test-exclude@^6.0.0": + "integrity" "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==" + "resolved" "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz" + "version" "6.0.0" + dependencies: + "@istanbuljs/schema" "^0.1.2" + "glob" "^7.1.4" + "minimatch" "^3.0.4" + +"text-table@^0.2.0": + "integrity" "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" + "resolved" "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" + "version" "0.2.0" + +"throat@^6.0.1": + "integrity" "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==" + "resolved" "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz" + "version" "6.0.1" + +"thunky@^1.0.2": + "integrity" "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" + "resolved" "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz" + "version" "1.1.0" + +"timsort@^0.3.0": + "integrity" "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" + "resolved" "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz" + "version" "0.3.0" + +"tmpl@1.0.5": + "integrity" "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==" + "resolved" "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz" + "version" "1.0.5" + +"to-fast-properties@^2.0.0": + "integrity" "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" + "resolved" "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz" + "version" "2.0.0" + +"to-regex-range@^5.0.1": + "integrity" "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==" + "resolved" "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" + "version" "5.0.1" + dependencies: + "is-number" "^7.0.0" + +"toidentifier@1.0.1": + "integrity" "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + "resolved" "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz" + "version" "1.0.1" + +"tough-cookie@^4.0.0": + "integrity" "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==" + "resolved" "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz" + "version" "4.0.0" + dependencies: + "psl" "^1.1.33" + "punycode" "^2.1.1" + "universalify" "^0.1.2" + +"tr46@^1.0.1": + "integrity" "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=" + "resolved" "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz" + "version" "1.0.1" + dependencies: + "punycode" "^2.1.0" + +"tr46@^2.1.0": + "integrity" "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==" + "resolved" "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz" + "version" "2.1.0" + dependencies: + "punycode" "^2.1.1" + +"tryer@^1.0.1": + "integrity" "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==" + "resolved" "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz" + "version" "1.0.1" + +"tsconfig-paths@^3.12.0": + "integrity" "sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg==" + "resolved" "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.12.0.tgz" + "version" "3.12.0" + dependencies: + "@types/json5" "^0.0.29" + "json5" "^1.0.1" + "minimist" "^1.2.0" + "strip-bom" "^3.0.0" + +"tslib@^1.8.1": + "integrity" "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + "resolved" "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" + "version" "1.14.1" + +"tslib@^2.0.3": + "integrity" "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + "resolved" "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz" + "version" "2.3.1" + +"tsutils@^3.21.0": + "integrity" "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==" + "resolved" "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz" + "version" "3.21.0" + dependencies: + "tslib" "^1.8.1" + +"type-check@^0.4.0", "type-check@~0.4.0": + "integrity" "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==" + "resolved" "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz" + "version" "0.4.0" + dependencies: + "prelude-ls" "^1.2.1" + +"type-check@~0.3.2": + "integrity" "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=" + "resolved" "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz" + "version" "0.3.2" + dependencies: + "prelude-ls" "~1.1.2" + +"type-detect@4.0.8": + "integrity" "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==" + "resolved" "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz" + "version" "4.0.8" + +"type-fest@^0.16.0": + "integrity" "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==" + "resolved" "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz" + "version" "0.16.0" + +"type-fest@^0.20.2": + "integrity" "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==" + "resolved" "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz" + "version" "0.20.2" + +"type-fest@^0.21.3", "type-fest@>=0.17.0 <3.0.0": + "integrity" "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==" + "resolved" "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz" + "version" "0.21.3" + +"type-is@~1.6.18": + "integrity" "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==" + "resolved" "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz" + "version" "1.6.18" + dependencies: + "media-typer" "0.3.0" + "mime-types" "~2.1.24" + +"typedarray-to-buffer@^3.1.5": + "integrity" "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==" + "resolved" "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz" + "version" "3.1.5" + dependencies: + "is-typedarray" "^1.0.0" + +"typescript@^3.2.1 || ^4", "typescript@>= 2.7", "typescript@>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta": + "integrity" "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==" + "resolved" "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz" + "version" "4.5.4" + +"unbox-primitive@^1.0.1": + "integrity" "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==" + "resolved" "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz" + "version" "1.0.1" + dependencies: + "function-bind" "^1.1.1" + "has-bigints" "^1.0.1" + "has-symbols" "^1.0.2" + "which-boxed-primitive" "^1.0.2" + +"unicode-canonical-property-names-ecmascript@^2.0.0": + "integrity" "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==" + "resolved" "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz" + "version" "2.0.0" + +"unicode-match-property-ecmascript@^2.0.0": + "integrity" "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==" + "resolved" "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz" + "version" "2.0.0" + dependencies: + "unicode-canonical-property-names-ecmascript" "^2.0.0" + "unicode-property-aliases-ecmascript" "^2.0.0" + +"unicode-match-property-value-ecmascript@^2.0.0": + "integrity" "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==" + "resolved" "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz" + "version" "2.0.0" + +"unicode-property-aliases-ecmascript@^2.0.0": + "integrity" "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==" + "resolved" "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz" + "version" "2.0.0" + +"unique-string@^2.0.0": + "integrity" "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==" + "resolved" "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz" + "version" "2.0.0" + dependencies: + "crypto-random-string" "^2.0.0" + +"universalify@^0.1.2": + "integrity" "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + "resolved" "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz" + "version" "0.1.2" + +"universalify@^2.0.0": + "integrity" "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" + "resolved" "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz" + "version" "2.0.0" + +"unpipe@~1.0.0", "unpipe@1.0.0": + "integrity" "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + "resolved" "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" + "version" "1.0.0" + +"unquote@~1.1.1": + "integrity" "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=" + "resolved" "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz" + "version" "1.1.1" + +"upath@^1.2.0": + "integrity" "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==" + "resolved" "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz" + "version" "1.2.0" + +"uri-js@^4.2.2": + "integrity" "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==" + "resolved" "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" + "version" "4.4.1" + dependencies: + "punycode" "^2.1.0" + +"util-deprecate@^1.0.1", "util-deprecate@^1.0.2", "util-deprecate@~1.0.1": + "integrity" "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + "resolved" "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + "version" "1.0.2" + +"util.promisify@~1.0.0": + "integrity" "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==" + "resolved" "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz" + "version" "1.0.1" + dependencies: + "define-properties" "^1.1.3" + "es-abstract" "^1.17.2" + "has-symbols" "^1.0.1" + "object.getownpropertydescriptors" "^2.1.0" + +"utila@~0.4": + "integrity" "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=" + "resolved" "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz" + "version" "0.4.0" + +"utils-merge@1.0.1": + "integrity" "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + "resolved" "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz" + "version" "1.0.1" + +"uuid@^8.3.2": + "integrity" "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + "resolved" "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" + "version" "8.3.2" + +"v8-compile-cache@^2.0.3": + "integrity" "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==" + "resolved" "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz" + "version" "2.3.0" + +"v8-to-istanbul@^8.1.0": + "integrity" "sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==" + "resolved" "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz" + "version" "8.1.1" + dependencies: + "@types/istanbul-lib-coverage" "^2.0.1" + "convert-source-map" "^1.6.0" + "source-map" "^0.7.3" + +"vary@~1.1.2": + "integrity" "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + "resolved" "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz" + "version" "1.1.2" + +"w3c-hr-time@^1.0.2": + "integrity" "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==" + "resolved" "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz" + "version" "1.0.2" + dependencies: + "browser-process-hrtime" "^1.0.0" + +"w3c-xmlserializer@^2.0.0": + "integrity" "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==" + "resolved" "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz" + "version" "2.0.0" + dependencies: + "xml-name-validator" "^3.0.0" + +"walker@^1.0.7": + "integrity" "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==" + "resolved" "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz" + "version" "1.0.8" + dependencies: + "makeerror" "1.0.12" + +"watchpack@^2.3.1": + "integrity" "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==" + "resolved" "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz" + "version" "2.3.1" + dependencies: + "glob-to-regexp" "^0.4.1" + "graceful-fs" "^4.1.2" + +"wbuf@^1.1.0", "wbuf@^1.7.3": + "integrity" "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==" + "resolved" "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz" + "version" "1.7.3" + dependencies: + "minimalistic-assert" "^1.0.0" + +"webidl-conversions@^4.0.2": + "integrity" "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==" + "resolved" "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz" + "version" "4.0.2" + +"webidl-conversions@^5.0.0": + "integrity" "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==" + "resolved" "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz" + "version" "5.0.0" + +"webidl-conversions@^6.1.0": + "integrity" "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==" + "resolved" "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz" + "version" "6.1.0" + +"webpack-dev-middleware@^5.3.0": + "integrity" "sha512-MouJz+rXAm9B1OTOYaJnn6rtD/lWZPy2ufQCH3BPs8Rloh/Du6Jze4p7AeLYHkVi0giJnYLaSGDC7S+GM9arhg==" + "resolved" "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.0.tgz" + "version" "5.3.0" + dependencies: + "colorette" "^2.0.10" + "memfs" "^3.2.2" + "mime-types" "^2.1.31" + "range-parser" "^1.2.1" + "schema-utils" "^4.0.0" + +"webpack-dev-server@^4.6.0", "webpack-dev-server@3.x || 4.x": + "integrity" "sha512-mlxq2AsIw2ag016nixkzUkdyOE8ST2GTy34uKSABp1c4nhjZvH90D5ZRR+UOLSsG4Z3TFahAi72a3ymRtfRm+Q==" + "resolved" "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.7.3.tgz" + "version" "4.7.3" + dependencies: + "@types/bonjour" "^3.5.9" + "@types/connect-history-api-fallback" "^1.3.5" + "@types/serve-index" "^1.9.1" + "@types/sockjs" "^0.3.33" + "@types/ws" "^8.2.2" + "ansi-html-community" "^0.0.8" + "bonjour" "^3.5.0" + "chokidar" "^3.5.2" + "colorette" "^2.0.10" + "compression" "^1.7.4" + "connect-history-api-fallback" "^1.6.0" + "default-gateway" "^6.0.3" + "del" "^6.0.0" + "express" "^4.17.1" + "graceful-fs" "^4.2.6" + "html-entities" "^2.3.2" + "http-proxy-middleware" "^2.0.0" + "ipaddr.js" "^2.0.1" + "open" "^8.0.9" + "p-retry" "^4.5.0" + "portfinder" "^1.0.28" + "schema-utils" "^4.0.0" + "selfsigned" "^2.0.0" + "serve-index" "^1.9.1" + "sockjs" "^0.3.21" + "spdy" "^4.0.2" + "strip-ansi" "^7.0.0" + "webpack-dev-middleware" "^5.3.0" + "ws" "^8.1.0" + +"webpack-manifest-plugin@^4.0.2": + "integrity" "sha512-YXUAwxtfKIJIKkhg03MKuiFAD72PlrqCiwdwO4VEXdRO5V0ORCNwaOwAZawPZalCbmH9kBDmXnNeQOw+BIEiow==" + "resolved" "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-4.1.1.tgz" + "version" "4.1.1" + dependencies: + "tapable" "^2.0.0" + "webpack-sources" "^2.2.0" + +"webpack-sources@^1.4.3": + "integrity" "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==" + "resolved" "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz" + "version" "1.4.3" + dependencies: + "source-list-map" "^2.0.0" + "source-map" "~0.6.1" + +"webpack-sources@^2.2.0": + "integrity" "sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA==" + "resolved" "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.3.1.tgz" + "version" "2.3.1" + dependencies: + "source-list-map" "^2.0.1" + "source-map" "^0.6.1" + +"webpack-sources@^3.2.2": + "integrity" "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==" + "resolved" "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz" + "version" "3.2.3" + +"webpack@^4.0.0 || ^5.0.0", "webpack@^4.37.0 || ^5.0.0", "webpack@^4.4.0 || ^5.9.0", "webpack@^4.44.2 || ^5.47.0", "webpack@^5.0.0", "webpack@^5.1.0", "webpack@^5.20.0", "webpack@^5.64.4", "webpack@>= 4", "webpack@>=2", "webpack@>=4.43.0 <6.0.0": + "integrity" "sha512-NJNtGT7IKpGzdW7Iwpn/09OXz9inIkeIQ/ibY6B+MdV1x6+uReqz/5z1L89ezWnpPDWpXF0TY5PCYKQdWVn8Vg==" + "resolved" "https://registry.npmjs.org/webpack/-/webpack-5.66.0.tgz" + "version" "5.66.0" + dependencies: + "@types/eslint-scope" "^3.7.0" + "@types/estree" "^0.0.50" + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/wasm-edit" "1.11.1" + "@webassemblyjs/wasm-parser" "1.11.1" + "acorn" "^8.4.1" + "acorn-import-assertions" "^1.7.6" + "browserslist" "^4.14.5" + "chrome-trace-event" "^1.0.2" + "enhanced-resolve" "^5.8.3" + "es-module-lexer" "^0.9.0" + "eslint-scope" "5.1.1" + "events" "^3.2.0" + "glob-to-regexp" "^0.4.1" + "graceful-fs" "^4.2.9" + "json-parse-better-errors" "^1.0.2" + "loader-runner" "^4.2.0" + "mime-types" "^2.1.27" + "neo-async" "^2.6.2" + "schema-utils" "^3.1.0" + "tapable" "^2.1.1" + "terser-webpack-plugin" "^5.1.3" + "watchpack" "^2.3.1" + "webpack-sources" "^3.2.2" + +"websocket-driver@^0.7.4", "websocket-driver@>=0.5.1": + "integrity" "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==" + "resolved" "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz" + "version" "0.7.4" + dependencies: + "http-parser-js" ">=0.5.1" + "safe-buffer" ">=5.1.0" + "websocket-extensions" ">=0.1.1" + +"websocket-extensions@>=0.1.1": + "integrity" "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==" + "resolved" "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz" + "version" "0.1.4" + +"whatwg-encoding@^1.0.5": + "integrity" "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==" + "resolved" "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz" + "version" "1.0.5" + dependencies: + "iconv-lite" "0.4.24" + +"whatwg-fetch@^3.6.2": + "integrity" "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==" + "resolved" "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz" + "version" "3.6.2" + +"whatwg-mimetype@^2.3.0": + "integrity" "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==" + "resolved" "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz" + "version" "2.3.0" + +"whatwg-url@^7.0.0": + "integrity" "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==" + "resolved" "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz" + "version" "7.1.0" + dependencies: + "lodash.sortby" "^4.7.0" + "tr46" "^1.0.1" + "webidl-conversions" "^4.0.2" + +"whatwg-url@^8.0.0", "whatwg-url@^8.5.0": + "integrity" "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==" + "resolved" "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz" + "version" "8.7.0" + dependencies: + "lodash" "^4.7.0" + "tr46" "^2.1.0" + "webidl-conversions" "^6.1.0" + +"which-boxed-primitive@^1.0.2": + "integrity" "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==" + "resolved" "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz" + "version" "1.0.2" + dependencies: + "is-bigint" "^1.0.1" + "is-boolean-object" "^1.1.0" + "is-number-object" "^1.0.4" + "is-string" "^1.0.5" + "is-symbol" "^1.0.3" + +"which@^1.3.1": + "integrity" "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==" + "resolved" "https://registry.npmjs.org/which/-/which-1.3.1.tgz" + "version" "1.3.1" + dependencies: + "isexe" "^2.0.0" + +"which@^2.0.1": + "integrity" "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==" + "resolved" "https://registry.npmjs.org/which/-/which-2.0.2.tgz" + "version" "2.0.2" + dependencies: + "isexe" "^2.0.0" + +"word-wrap@^1.2.3", "word-wrap@~1.2.3": + "integrity" "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" + "resolved" "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz" + "version" "1.2.3" + +"workbox-background-sync@6.4.2": + "integrity" "sha512-P7c8uG5X2k+DMICH9xeSA9eUlCOjHHYoB42Rq+RtUpuwBxUOflAXR1zdsMWj81LopE4gjKXlTw7BFd1BDAHo7g==" + "resolved" "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-6.4.2.tgz" + "version" "6.4.2" + dependencies: + "idb" "^6.1.4" + "workbox-core" "6.4.2" + +"workbox-broadcast-update@6.4.2": + "integrity" "sha512-qnBwQyE0+PWFFc/n4ISXINE49m44gbEreJUYt2ldGH3+CNrLmJ1egJOOyUqqu9R4Eb7QrXcmB34ClXG7S37LbA==" + "resolved" "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-6.4.2.tgz" + "version" "6.4.2" + dependencies: + "workbox-core" "6.4.2" + +"workbox-build@6.4.2": + "integrity" "sha512-WMdYLhDIsuzViOTXDH+tJ1GijkFp5khSYolnxR/11zmfhNDtuo7jof72xPGFy+KRpsz6tug39RhivCj77qqO0w==" + "resolved" "https://registry.npmjs.org/workbox-build/-/workbox-build-6.4.2.tgz" + "version" "6.4.2" + dependencies: + "@apideck/better-ajv-errors" "^0.3.1" + "@babel/core" "^7.11.1" + "@babel/preset-env" "^7.11.0" + "@babel/runtime" "^7.11.2" + "@rollup/plugin-babel" "^5.2.0" + "@rollup/plugin-node-resolve" "^11.2.1" + "@rollup/plugin-replace" "^2.4.1" + "@surma/rollup-plugin-off-main-thread" "^2.2.3" + "ajv" "^8.6.0" + "common-tags" "^1.8.0" + "fast-json-stable-stringify" "^2.1.0" + "fs-extra" "^9.0.1" + "glob" "^7.1.6" + "lodash" "^4.17.20" + "pretty-bytes" "^5.3.0" + "rollup" "^2.43.1" + "rollup-plugin-terser" "^7.0.0" + "source-map" "^0.8.0-beta.0" + "source-map-url" "^0.4.0" + "stringify-object" "^3.3.0" + "strip-comments" "^2.0.1" + "tempy" "^0.6.0" + "upath" "^1.2.0" + "workbox-background-sync" "6.4.2" + "workbox-broadcast-update" "6.4.2" + "workbox-cacheable-response" "6.4.2" + "workbox-core" "6.4.2" + "workbox-expiration" "6.4.2" + "workbox-google-analytics" "6.4.2" + "workbox-navigation-preload" "6.4.2" + "workbox-precaching" "6.4.2" + "workbox-range-requests" "6.4.2" + "workbox-recipes" "6.4.2" + "workbox-routing" "6.4.2" + "workbox-strategies" "6.4.2" + "workbox-streams" "6.4.2" + "workbox-sw" "6.4.2" + "workbox-window" "6.4.2" + +"workbox-cacheable-response@6.4.2": + "integrity" "sha512-9FE1W/cKffk1AJzImxgEN0ceWpyz1tqNjZVtA3/LAvYL3AC5SbIkhc7ZCO82WmO9IjTfu8Vut2X/C7ViMSF7TA==" + "resolved" "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-6.4.2.tgz" + "version" "6.4.2" + dependencies: + "workbox-core" "6.4.2" + +"workbox-core@6.4.2": + "integrity" "sha512-1U6cdEYPcajRXiboSlpJx6U7TvhIKbxRRerfepAJu2hniKwJ3DHILjpU/zx3yvzSBCWcNJDoFalf7Vgd7ey/rw==" + "resolved" "https://registry.npmjs.org/workbox-core/-/workbox-core-6.4.2.tgz" + "version" "6.4.2" + +"workbox-expiration@6.4.2": + "integrity" "sha512-0hbpBj0tDnW+DZOUmwZqntB/8xrXOgO34i7s00Si/VlFJvvpRKg1leXdHHU8ykoSBd6+F2KDcMP3swoCi5guLw==" + "resolved" "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-6.4.2.tgz" + "version" "6.4.2" + dependencies: + "idb" "^6.1.4" + "workbox-core" "6.4.2" + +"workbox-google-analytics@6.4.2": + "integrity" "sha512-u+gxs3jXovPb1oul4CTBOb+T9fS1oZG+ZE6AzS7l40vnyfJV79DaLBvlpEZfXGv3CjMdV1sT/ltdOrKzo7HcGw==" + "resolved" "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-6.4.2.tgz" + "version" "6.4.2" + dependencies: + "workbox-background-sync" "6.4.2" + "workbox-core" "6.4.2" + "workbox-routing" "6.4.2" + "workbox-strategies" "6.4.2" + +"workbox-navigation-preload@6.4.2": + "integrity" "sha512-viyejlCtlKsbJCBHwhSBbWc57MwPXvUrc8P7d+87AxBGPU+JuWkT6nvBANgVgFz6FUhCvRC8aYt+B1helo166g==" + "resolved" "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-6.4.2.tgz" + "version" "6.4.2" + dependencies: + "workbox-core" "6.4.2" + +"workbox-precaching@6.4.2": + "integrity" "sha512-CZ6uwFN/2wb4noHVlALL7UqPFbLfez/9S2GAzGAb0Sk876ul9ukRKPJJ6gtsxfE2HSTwqwuyNVa6xWyeyJ1XSA==" + "resolved" "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-6.4.2.tgz" + "version" "6.4.2" + dependencies: + "workbox-core" "6.4.2" + "workbox-routing" "6.4.2" + "workbox-strategies" "6.4.2" + +"workbox-range-requests@6.4.2": + "integrity" "sha512-SowF3z69hr3Po/w7+xarWfzxJX/3Fo0uSG72Zg4g5FWWnHpq2zPvgbWerBZIa81zpJVUdYpMa3akJJsv+LaO1Q==" + "resolved" "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-6.4.2.tgz" + "version" "6.4.2" + dependencies: + "workbox-core" "6.4.2" + +"workbox-recipes@6.4.2": + "integrity" "sha512-/oVxlZFpAjFVbY+3PoGEXe8qyvtmqMrTdWhbOfbwokNFtUZ/JCtanDKgwDv9x3AebqGAoJRvQNSru0F4nG+gWA==" + "resolved" "https://registry.npmjs.org/workbox-recipes/-/workbox-recipes-6.4.2.tgz" + "version" "6.4.2" + dependencies: + "workbox-cacheable-response" "6.4.2" + "workbox-core" "6.4.2" + "workbox-expiration" "6.4.2" + "workbox-precaching" "6.4.2" + "workbox-routing" "6.4.2" + "workbox-strategies" "6.4.2" + +"workbox-routing@6.4.2": + "integrity" "sha512-0ss/n9PAcHjTy4Ad7l2puuod4WtsnRYu9BrmHcu6Dk4PgWeJo1t5VnGufPxNtcuyPGQ3OdnMdlmhMJ57sSrrSw==" + "resolved" "https://registry.npmjs.org/workbox-routing/-/workbox-routing-6.4.2.tgz" + "version" "6.4.2" + dependencies: + "workbox-core" "6.4.2" + +"workbox-strategies@6.4.2": + "integrity" "sha512-YXh9E9dZGEO1EiPC3jPe2CbztO5WT8Ruj8wiYZM56XqEJp5YlGTtqRjghV+JovWOqkWdR+amJpV31KPWQUvn1Q==" + "resolved" "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-6.4.2.tgz" + "version" "6.4.2" + dependencies: + "workbox-core" "6.4.2" + +"workbox-streams@6.4.2": + "integrity" "sha512-ROEGlZHGVEgpa5bOZefiJEVsi5PsFjJG9Xd+wnDbApsCO9xq9rYFopF+IRq9tChyYzhBnyk2hJxbQVWphz3sog==" + "resolved" "https://registry.npmjs.org/workbox-streams/-/workbox-streams-6.4.2.tgz" + "version" "6.4.2" + dependencies: + "workbox-core" "6.4.2" + "workbox-routing" "6.4.2" + +"workbox-sw@6.4.2": + "integrity" "sha512-A2qdu9TLktfIM5NE/8+yYwfWu+JgDaCkbo5ikrky2c7r9v2X6DcJ+zSLphNHHLwM/0eVk5XVf1mC5HGhYpMhhg==" + "resolved" "https://registry.npmjs.org/workbox-sw/-/workbox-sw-6.4.2.tgz" + "version" "6.4.2" + +"workbox-webpack-plugin@^6.4.1": + "integrity" "sha512-CiEwM6kaJRkx1cP5xHksn13abTzUqMHiMMlp5Eh/v4wRcedgDTyv6Uo8+Hg9MurRbHDosO5suaPyF9uwVr4/CQ==" + "resolved" "https://registry.npmjs.org/workbox-webpack-plugin/-/workbox-webpack-plugin-6.4.2.tgz" + "version" "6.4.2" + dependencies: + "fast-json-stable-stringify" "^2.1.0" + "pretty-bytes" "^5.4.1" + "source-map-url" "^0.4.0" + "upath" "^1.2.0" + "webpack-sources" "^1.4.3" + "workbox-build" "6.4.2" + +"workbox-window@6.4.2": + "integrity" "sha512-KVyRKmrJg7iB+uym/B/CnEUEFG9CvnTU1Bq5xpXHbtgD9l+ShDekSl1wYpqw/O0JfeeQVOFb8CiNfvnwWwqnWQ==" + "resolved" "https://registry.npmjs.org/workbox-window/-/workbox-window-6.4.2.tgz" + "version" "6.4.2" + dependencies: + "@types/trusted-types" "^2.0.2" + "workbox-core" "6.4.2" + +"wrap-ansi@^7.0.0": + "integrity" "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==" + "resolved" "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" + "version" "7.0.0" + dependencies: + "ansi-styles" "^4.0.0" + "string-width" "^4.1.0" + "strip-ansi" "^6.0.0" + +"wrappy@1": + "integrity" "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "resolved" "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + "version" "1.0.2" + +"write-file-atomic@^3.0.0": + "integrity" "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==" + "resolved" "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz" + "version" "3.0.3" + dependencies: + "imurmurhash" "^0.1.4" + "is-typedarray" "^1.0.0" + "signal-exit" "^3.0.2" + "typedarray-to-buffer" "^3.1.5" + +"ws@^7.4.6": + "integrity" "sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA==" + "resolved" "https://registry.npmjs.org/ws/-/ws-7.5.6.tgz" + "version" "7.5.6" + +"ws@^8.1.0": + "integrity" "sha512-IHVsKe2pjajSUIl4KYMQOdlyliovpEPquKkqbwswulszzI7r0SfQrxnXdWAEqOlDCLrVSJzo+O1hAwdog2sKSQ==" + "resolved" "https://registry.npmjs.org/ws/-/ws-8.4.0.tgz" + "version" "8.4.0" + +"xml-name-validator@^3.0.0": + "integrity" "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==" + "resolved" "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz" + "version" "3.0.0" + +"xmlchars@^2.2.0": + "integrity" "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" + "resolved" "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz" + "version" "2.2.0" + +"xtend@^4.0.2": + "integrity" "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + "resolved" "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz" + "version" "4.0.2" + +"y18n@^5.0.5": + "integrity" "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" + "resolved" "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" + "version" "5.0.8" + +"yallist@^4.0.0": + "integrity" "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "resolved" "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" + "version" "4.0.0" + +"yaml@^1.10.0", "yaml@^1.10.2", "yaml@^1.7.2": + "integrity" "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" + "resolved" "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz" + "version" "1.10.2" + +"yargs-parser@^20.2.2": + "integrity" "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==" + "resolved" "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz" + "version" "20.2.9" + +"yargs@^16.2.0": + "integrity" "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==" + "resolved" "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz" + "version" "16.2.0" + dependencies: + "cliui" "^7.0.2" + "escalade" "^3.1.1" + "get-caller-file" "^2.0.5" + "require-directory" "^2.1.1" + "string-width" "^4.2.0" + "y18n" "^5.0.5" + "yargs-parser" "^20.2.2" + +"yocto-queue@^0.1.0": + "integrity" "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" + "resolved" "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" + "version" "0.1.0" diff --git a/dapr/quickstarts/tutorials/pub-sub/react-form/package.json b/dapr/quickstarts/tutorials/pub-sub/react-form/package.json new file mode 100644 index 0000000..07908d3 --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/react-form/package.json @@ -0,0 +1,24 @@ +{ + "name": "react-form", + "version": "1.0.0", + "private": true, + "description": "This project builds and serves the react form client along with the services that it depends on", + "main": "server.js", + "scripts": { + "client": "cd client && yarn start", + "server": "nodemon server.js", + "dev": "concurrently --kill-others-on-fail \"yarn server\" \"yarn client\"", + "start": "node server.js", + "buildclient": "cd client && npm install && npm run build", + "build": "npm run buildclient && npm install" + }, + "author": "Ryan Volum", + "license": "ISC", + "dependencies": { + "express": "^4.17.3", + "axios": "^0.26.0" + }, + "devDependencies": { + "concurrently": "^4.1.1" + } +} diff --git a/dapr/quickstarts/tutorials/pub-sub/react-form/server.js b/dapr/quickstarts/tutorials/pub-sub/react-form/server.js new file mode 100644 index 0000000..22a7a83 --- /dev/null +++ b/dapr/quickstarts/tutorials/pub-sub/react-form/server.js @@ -0,0 +1,35 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// ------------------------------------------------------------ + +const express = require('express'); +const axios = require('axios'); +const path = require('path'); +const bodyParser = require('body-parser'); + +const app = express(); + +app.use(express.json()); + +const daprPort = process.env.DAPR_HTTP_PORT ?? 3500; +const daprUrl = `http://localhost:${daprPort}/v1.0`; +const port = 8080; +const pubsubName = 'pubsub'; + +// Publish to topic (messageType) using Dapr pub-sub +app.post('/publish', async (req, res) => { + console.log("Publishing: ", req.body); + await axios.post(`${daprUrl}/publish/${pubsubName}/${req.body?.messageType}`, req.body); + return res.sendStatus(200); +}); + +// Serve static files +app.use(express.static(path.join(__dirname, 'client/build'))); + +// Map default route to React client +app.get('/', async function (_req, res) { + await res.sendFile(path.join(__dirname, 'client/build', 'index.html')); +}); + +app.listen(process.env.PORT || port, () => console.log(`Listening on port ${port}!`)); \ No newline at end of file diff --git a/dapr/quickstarts/tutorials/secretstore/README.md b/dapr/quickstarts/tutorials/secretstore/README.md new file mode 100644 index 0000000..ab61a69 --- /dev/null +++ b/dapr/quickstarts/tutorials/secretstore/README.md @@ -0,0 +1,423 @@ +# Secrets store + +This tutorial shows you how to use the Dapr secrets API to access secrets from secret stores. Dapr allows us to deploy the same microservice from the local machines to Kubernetes. Correspondingly, this quickstart has instructions for deploying this project [locally](#Run-Locally) or in [Kubernetes](#Run-in-Kubernetes). + + +## Prerequisites + + +### Prerequisites to run Locally +- [Dapr CLI with Dapr initialized](https://docs.dapr.io/getting-started/install-dapr-cli/) +- [Node.js version 14 or greater](https://nodejs.org/en/) +- [Postman](https://www.getpostman.com/) [Optional] + +### Prerequisites to run in Kubernetes + +This quickstart requires you to have the following installed on your machine: +- [Docker](https://docs.docker.com/) +- [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) +- A Kubernetes cluster, such as [Minikube](https://docs.dapr.io/operations/hosting/kubernetes/cluster/setup-minikube/), [AKS](https://docs.dapr.io/operations/hosting/kubernetes/cluster/setup-aks/) + +Also, unless you have already done so, clone the repository with the quickstarts and ````cd```` into the right directory: +``` +git clone [-b ] https://github.com/dapr/quickstarts.git +cd quickstarts +``` +> **Note**: See https://github.com/dapr/quickstarts#supported-dapr-runtime-version for supported tags. Use `git clone https://github.com/dapr/quickstarts.git` when using the edge version of dapr runtime. + +## Run Locally + +### Step 1 - Setup Dapr on your local machine + +Follow [instructions](https://docs.dapr.io/getting-started/install-dapr-cli/) to download and install the Dapr CLI and initialize Dapr. + +### Step 2 - Understand the code and configuration + +Navigate to the secretstore quickstart and the node folder within that location. + +```bash +cd secretstore/node +``` + +In the `app.js` you'll find a simple `express` application, which exposes a few routes and handlers. First, take a look at the top of the file: + +```js +const daprPort = process.env.DAPR_HTTP_PORT || 3500; +const secretStoreName = process.env.SECRET_STORE; +const secretName = 'mysecret' +``` + +The `secretStoreName` is read from an environment variable where the value `kubernetes` is injected for a Kubernetes deployment and for local development the environment variable must be set to `localsecretstore` value. + +Next take a look at the `getsecret` handler: + +```js +app.get('/getsecret', (_req, res) => { + const url = `${secretsUrl}/${secretStoreName}/${secretName}?metadata.namespace=default` + console.log("Fetching URL: %s", url) + fetch(url) + .then(res => res.json()) + .then(json => { + let secretBuffer = new Buffer(json["mysecret"]) + let encodedSecret = secretBuffer.toString('base64') + console.log("Base64 encoded secret is: %s", encodedSecret) + return res.send(encodedSecret) + }) +}); +``` + +The code gets the the secret called `mysecret` from the secret store and displays a Base64 encoded version of the secret. + +In `secrets.json` file, you'll find a secret `mysecret`. + +```json +{ + "mysecret": "abcd" +} +``` + +In the components folder, there is a `local-secret-store.yaml` which defines a local file secret store component to be used by Dapr. + + +```yaml +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: localsecretstore + namespace: default +spec: + type: secretstores.local.file + version: v1 + metadata: + - name: secretsFile + value: secrets.json + - name: nestedSeparator + value: ":" +``` + +The component defines a local secret store with the secrets file path as the `secrets.json` file. + +> Note: You can also use a local secret store that uses [environment variables](https://docs.dapr.io/operations/components/setup-secret-store/supported-secret-stores/envvar-secret-store/) for secrets instead of a [local file](https://docs.dapr.io/operations/components/setup-secret-store/supported-secret-stores/file-secret-store/). + +### Step 3 - Export Secret Store name and run Node.js app with Dapr + +Export Secret Store name as environment variable: + +``` +Linux/Mac OS: +export SECRET_STORE="localsecretstore" + +Windows: +set SECRET_STORE=localsecretstore +``` + +Install dependencies + + + +```bash +npm install +``` + + + +Run Node.js app with Dapr with the local secret store component: + + + +```bash +dapr run --app-id nodeapp --components-path ./components --app-port 3000 --dapr-http-port 3500 node app.js +``` + + + +The command starts the Dapr application and finally after it is completely initialized, you should see the logs + +``` +Updating metadata for app command: node app.js +✅ You're up and running! Both Dapr and your app logs will appear here. +``` + +### Step 4 - Access the secret + +Make a request to the node app to fetch the secret. You can use the command below: + + + +```bash +curl -k http://localhost:3000/getsecret +``` + + + +The output should be your base64 encoded secret `YWJjZA==` + +### Step 5 - Observe the logs of the app + +The application logs should be similar to the following: +``` +== APP == Fetching URL: http://localhost:3500/v1.0/secrets/localsecretstore/mysecret?metadata.namespace=default + +== APP == Base64 encoded secret is: YWJjZA== +``` + +### Step 6 - Cleanup + +To stop your services from running, simply stop the "dapr run" process. Alternatively, you can spin down each of your services with the Dapr CLI "stop" command. For example, to spin down both services, run these commands in a new command line terminal: + + + +```bash +dapr stop --app-id nodeapp +``` + + + +To see that services have stopped running, run `dapr list`, noting that your service no longer appears! + +## Run in Kubernetes + +### Step 1 - Setup Dapr on your Kubernetes cluster + +The first thing you need is an RBAC enabled Kubernetes cluster. This could be running on your machine using Minikube, or it could be a fully-fledged cluser in Azure using [AKS](https://azure.microsoft.com/en-us/services/kubernetes-service/). + +Once you have a cluster, follow the steps below to deploy Dapr to it. For more details, see [Deploy Dapr on a Kubernetes cluster](https://docs.dapr.io/operations/hosting/kubernetes/kubernetes-deploy/). + +> Please note, the CLI will install to the dapr-system namespace by default. If this namespace does not exist, the CLI will create it. +> If you need to deploy to a different namespace, you can use ```-n mynamespace```. + +``` +dapr init --kubernetes --wait +``` + +Sample output: +``` +⌛ Making the jump to hyperspace... + Note: To install Dapr using Helm, see here: https://docs.dapr.io/getting-started/install-dapr-kubernetes/#install-with-helm-advanced + +✅ Deploying the Dapr control plane to your cluster... +✅ Success! Dapr has been installed to namespace dapr-system. To verify, run `dapr status -k' in your terminal. To get started, go here: https://aka.ms/dapr-getting-started +``` + +> Without the ```--wait``` flag the Dapr CLI will exit as soon as the kubernetes deployments are created. Kubernetes deployments are asyncronous by default, so we use ```--wait``` here to make sure the dapr control plane is completely deployed and running before continuing. + +### Step 2 - Configure a secret in the secret store + +Dapr can use a number of different secret stores (AWS Secret Manager, Azure Key Vault, GCP Secret Manager, Kubernetes, etc) to retrieve secrets. For this demo, you'll use the [Kubernetes secret store](https://kubernetes.io/docs/concepts/configuration/secret/) (For instructions on other secret stores, please refer to [this documentation](https://docs.dapr.io/developing-applications/building-blocks/secrets/howto-secrets/)). + +1. Add your secret to a file `./mysecret`. For example, if your password is "abcd", then the contents of `./mysecret` should be "abcd" + +> Note: For Windows make sure the file does not contain an extension as the name of the file becomes the metadata key to retrieve the secret. +2. Create the secret in the Kubernetes secret store. Note, the name of the secret here is "mysecret" and will be used in a later step. + + + +```bash +kubectl create secret generic mysecret --from-file ./mysecret +``` + + + +3. You can check that the secret is indeed stored in the Kubernetes secret store by running the command: +``` +kubectl get secret mysecret -o yaml +``` + You can see the output as below where the secret is stored in the secret store in Base64 encoded format +``` +% kubectl get secret mysecret -o yaml +apiVersion: v1 +data: + mysecret: U0dmQXAzcUc5Qg== +kind: Secret +metadata: + creationTimestamp: "2020-09-30T17:33:12Z" + managedFields: + - apiVersion: v1 + fieldsType: FieldsV1 + fieldsV1: + f:data: + .: {} + f:mysecret: {} + f:type: {} + manager: kubectl-create + operation: Update + time: "2020-09-30T17:33:12Z" + name: mysecret + namespace: default + resourceVersion: "6617" + selfLink: /api/v1/namespaces/default/secrets/mysecret + uid: 4734ad91-0863-4dec-a200-5e191b8cab20 +type: Opaque +``` + + +### Step 3 - Deploy the Node.js app with the Dapr sidecar + + + +```bash +kubectl apply -f deploy/node.yaml +``` + +Kubernetes deployments are asyncronous. This means you'll need to wait for the deployment to complete before moving on to the next steps. You can do so with the following command: + +```bash +kubectl rollout status deploy/nodeapp +``` + + + + +There are several different ways to access a Kubernetes service depending on which platform you are using. Port forwarding is one consistent way to access a service, whether it is hosted locally or on a cloud Kubernetes provider like AKS. + + + +```bash +kubectl port-forward service/nodeapp 8000:80 +``` + + + +This will make your service available on http://localhost:8000. + +> **Optional**: If you are using a public cloud provider, you can substitue your EXTERNAL-IP address instead of port forwarding. You can find it with: + +```bash +kubectl get svc nodeapp +``` + + +### Step 4 - Access the secret +Make a request to the node app to fetch the secret. You can use the command below: + + + +```bash +curl -k http://localhost:8000/getsecret +``` + + + +The output should be your base64 encoded secret + +### Step 5 - Observe Logs + +Now that the node app is running, watch messages come through. + +Get the logs of the Node.js app: + + + +```bash +kubectl logs --selector=app=node -c node +``` + + + +If all went well, you should see logs like this: + +``` +Node App listening on port 3000! +Fetching URL: http://localhost:3500/v1.0/secrets/kubernetes/mysecret?metadata.namespace=default +Base64 encoded secret is: eHl6OTg3Ng== +``` + +In these logs, you can see that the node app is making a request to dapr to fetch the secret from the secret store. Note: mysecret is the secret that you created in Step 2 + +### Step 6 - Cleanup + +Once you're done, you can spin down your Kubernetes resources by navigating to the `./deploy` directory and running: + + + +```bash +kubectl delete -f ./deploy/node.yaml +``` + +```bash +kubectl delete secret mysecret +``` + + + +This will spin down the node app. + +### Updating the Node application and deploying in Kubernetes + +If you want to update the node app, you can do the following: + +1. Update Node code as you see fit! +2. Navigate to the node app directory you want to build a new image for. +3. Run `docker build -t . `. You can name your image whatever you like. If you're planning on hosting it on docker hub, then it should start with `/`. +4. Once your image has built you can see it on your machines by running `docker images`. +5. To publish your docker image to docker hub (or another registry), first login: `docker login`. Then run`docker push `. +6. Update your .yaml file to reflect the new image name. +7. Deploy your updated Dapr enabled app: `kubectl apply -f node.yaml`. + + +## Related Links +- [Secret store overview](https://kubernetes.io/docs/concepts/configuration/secret/) +- [Secret store API reference](https://docs.dapr.io/reference/api/secrets_api/) +- [Setup a secret store](https://docs.dapr.io/developing-applications/building-blocks/secrets/howto-secrets/) + +## Next steps: + +- Explore additional [quickstarts](../../README.md#quickstarts) diff --git a/dapr/quickstarts/tutorials/secretstore/deploy/node.yaml b/dapr/quickstarts/tutorials/secretstore/deploy/node.yaml new file mode 100644 index 0000000..b168020 --- /dev/null +++ b/dapr/quickstarts/tutorials/secretstore/deploy/node.yaml @@ -0,0 +1,45 @@ +kind: Service +apiVersion: v1 +metadata: + name: nodeapp + labels: + app: node +spec: + selector: + app: node + ports: + - protocol: TCP + port: 80 + targetPort: 3000 + type: LoadBalancer + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nodeapp + labels: + app: node +spec: + replicas: 1 + selector: + matchLabels: + app: node + template: + metadata: + labels: + app: node + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "nodeapp" + dapr.io/app-port: "3000" + spec: + containers: + - name: node + image: ghcr.io/dapr/samples/secretstorenode:latest + env: + - name: SECRET_STORE + value: "kubernetes" + ports: + - containerPort: 3000 + imagePullPolicy: Always diff --git a/dapr/quickstarts/tutorials/secretstore/makefile b/dapr/quickstarts/tutorials/secretstore/makefile new file mode 100644 index 0000000..aef5be2 --- /dev/null +++ b/dapr/quickstarts/tutorials/secretstore/makefile @@ -0,0 +1,5 @@ +DOCKER_IMAGE_PREFIX ?=secretstore +APPS ?=node + +include ../docker.mk +include ../validate.mk diff --git a/dapr/quickstarts/tutorials/secretstore/node/.gitignore b/dapr/quickstarts/tutorials/secretstore/node/.gitignore new file mode 100644 index 0000000..cfe9975 --- /dev/null +++ b/dapr/quickstarts/tutorials/secretstore/node/.gitignore @@ -0,0 +1,2 @@ +node_modules +.dockerfiles diff --git a/dapr/quickstarts/tutorials/secretstore/node/Dockerfile b/dapr/quickstarts/tutorials/secretstore/node/Dockerfile new file mode 100644 index 0000000..5c3326e --- /dev/null +++ b/dapr/quickstarts/tutorials/secretstore/node/Dockerfile @@ -0,0 +1,6 @@ +FROM node:17-alpine +WORKDIR /app +COPY . . +RUN npm install +EXPOSE 3000 +CMD [ "node", "app.js" ] diff --git a/dapr/quickstarts/tutorials/secretstore/node/app.js b/dapr/quickstarts/tutorials/secretstore/node/app.js new file mode 100644 index 0000000..d418077 --- /dev/null +++ b/dapr/quickstarts/tutorials/secretstore/node/app.js @@ -0,0 +1,44 @@ +// +// Copyright 2021 The Dapr Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +const express = require('express'); +const bodyParser = require('body-parser'); +require('isomorphic-fetch'); +require('base-64') +require('node-fetch') + +const app = express(); +app.use(bodyParser.json()); + +const daprPort = process.env.DAPR_HTTP_PORT || 3500; +const secretStoreName = process.env.SECRET_STORE; +const secretName = 'mysecret' + +const secretsUrl = `http://localhost:${daprPort}/v1.0/secrets`; + +const port = 3000; + +app.get('/getsecret', (_req, res) => { + const url = `${secretsUrl}/${secretStoreName}/${secretName}?metadata.namespace=default` + console.log("Fetching URL: %s", url) + fetch(url) + .then(res => res.json()) + .then(json => { + let secretBuffer = Buffer.from(json["mysecret"]) + let encodedSecret = secretBuffer.toString('base64') + console.log("Base64 encoded secret is: %s", encodedSecret) + return res.send(encodedSecret) + }) +}); + +app.listen(port, () => console.log(`Node App listening on port ${port}!`)); diff --git a/dapr/quickstarts/tutorials/secretstore/node/components/local-secret-store.yaml b/dapr/quickstarts/tutorials/secretstore/node/components/local-secret-store.yaml new file mode 100644 index 0000000..28a1c6d --- /dev/null +++ b/dapr/quickstarts/tutorials/secretstore/node/components/local-secret-store.yaml @@ -0,0 +1,13 @@ +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: localsecretstore + namespace: default +spec: + type: secretstores.local.file + version: v1 + metadata: + - name: secretsFile + value: secrets.json + - name: nestedSeparator + value: ":" \ No newline at end of file diff --git a/dapr/quickstarts/tutorials/secretstore/node/mysecret b/dapr/quickstarts/tutorials/secretstore/node/mysecret new file mode 100644 index 0000000..5370ccd --- /dev/null +++ b/dapr/quickstarts/tutorials/secretstore/node/mysecret @@ -0,0 +1 @@ +xyz9876 \ No newline at end of file diff --git a/dapr/quickstarts/tutorials/secretstore/node/package.json b/dapr/quickstarts/tutorials/secretstore/node/package.json new file mode 100644 index 0000000..07c7d5c --- /dev/null +++ b/dapr/quickstarts/tutorials/secretstore/node/package.json @@ -0,0 +1,18 @@ +{ + "name": "node_server", + "version": "1.0.0", + "private": true, + "description": "", + "main": "app.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "body-parser": "^1.18.3", + "express": "^4.16.4", + "isomorphic-fetch": "^2.2.1", + "base-64": "^0.1.0" + } +} diff --git a/dapr/quickstarts/tutorials/secretstore/node/secrets.json b/dapr/quickstarts/tutorials/secretstore/node/secrets.json new file mode 100644 index 0000000..c32a5c3 --- /dev/null +++ b/dapr/quickstarts/tutorials/secretstore/node/secrets.json @@ -0,0 +1,3 @@ +{ + "mysecret": "abcd" +} \ No newline at end of file diff --git a/dapr/quickstarts/tutorials/validate.mk b/dapr/quickstarts/tutorials/validate.mk new file mode 100644 index 0000000..8cd78d9 --- /dev/null +++ b/dapr/quickstarts/tutorials/validate.mk @@ -0,0 +1,5 @@ + +MM_SHELL ?= bash -c + +validate: + mm.py -l -s "${MM_SHELL}" README.md \ No newline at end of file diff --git a/dapr/quickstarts/validate.mk b/dapr/quickstarts/validate.mk new file mode 100644 index 0000000..e38b67f --- /dev/null +++ b/dapr/quickstarts/validate.mk @@ -0,0 +1,10 @@ + +MM_SHELL ?= bash -c + +all: install_mm validate + +validate: + mm.py -l -s "${MM_SHELL}" README.md + +install_mm: + type mm.py || sudo pip install mechanical-markdown