├── .editorconfig
├── .gcloudignore
├── .github
└── workflows
│ ├── conformance.yml
│ ├── lint.yml
│ └── unit.yml
├── .gitignore
├── .gitmodules
├── .kokoro
├── autorelease.bat
├── autorelease.sh
└── populatesecrets.sh
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── RELEASING.md
├── build-release.sh
├── build.sh
├── create-release-commit.sh
├── docs
├── README.md
├── customization.md
├── deployment.md
├── examples.md
├── history.md
├── launch-settings.md
├── packages.md
└── testing.md
├── examples
├── .gcloudignore
├── Directory.Build.props
├── Directory.Build.targets
├── OpenFunction.Examples.AdvancedDependencyInjection
│ ├── Function.cs
│ └── OpenFunction.Examples.AdvancedDependencyInjection.csproj
├── OpenFunction.Examples.Configuration
│ ├── Function.cs
│ ├── OpenFunction.Examples.Configuration.csproj
│ ├── appsettings.Development.json
│ ├── appsettings.Production.json
│ └── appsettings.json
├── OpenFunction.Examples.CustomConfiguration
│ ├── Function.cs
│ └── OpenFunction.Examples.CustomConfiguration.csproj
├── OpenFunction.Examples.CustomEventDataFunction
│ ├── Function.cs
│ └── OpenFunction.Examples.CustomEventDataFunction.csproj
├── OpenFunction.Examples.FSharpEventFunction
│ ├── Function.fs
│ └── OpenFunction.Examples.FSharpEventFunction.fsproj
├── OpenFunction.Examples.FSharpHttpFunction
│ ├── Function.fs
│ └── OpenFunction.Examples.FSharpHttpFunction.fsproj
├── OpenFunction.Examples.FSharpUntypedEventFunction
│ ├── Function.fs
│ └── OpenFunction.Examples.FSharpUntypedEventFunction.fsproj
├── OpenFunction.Examples.IntegrationTests
│ ├── AdvancedDependencyInjectionTest.cs
│ ├── OpenFunction.Examples.IntegrationTests.csproj
│ ├── SimpleDependencyInjectionTest.cs
│ ├── SimpleDependencyInjectionUnitTest.cs
│ ├── SimpleHttpFunctionTest.cs
│ ├── SimpleHttpFunctionTest_WithFunctionTestBase.cs
│ ├── SimpleHttpFunctionTest_WithTestServerFixture.cs
│ ├── SimpleHttpFunctionTest_WithTestServerInCtor.cs
│ ├── SimpleHttpFunctionTest_WithTestServerInTest.cs
│ └── TestableDependenciesTest.cs
├── OpenFunction.Examples.Middleware
│ ├── Function.cs
│ └── OpenFunction.Examples.Middleware.csproj
├── OpenFunction.Examples.MultiProjectDependency
│ ├── BusinessLogic.cs
│ └── OpenFunction.Examples.MultiProjectDependency.csproj
├── OpenFunction.Examples.MultiProjectFunction
│ ├── Function.cs
│ └── OpenFunction.Examples.MultiProjectFunction.csproj
├── OpenFunction.Examples.SimpleDependencyInjection
│ ├── Function.cs
│ └── OpenFunction.Examples.SimpleDependencyInjection.csproj
├── OpenFunction.Examples.SimpleEventFunction
│ ├── Function.cs
│ └── OpenFunction.Examples.SimpleEventFunction.csproj
├── OpenFunction.Examples.SimpleHttpFunction
│ ├── Function.cs
│ └── OpenFunction.Examples.SimpleHttpFunction.csproj
├── OpenFunction.Examples.SimpleUntypedEventFunction
│ ├── Function.cs
│ └── OpenFunction.Examples.SimpleUntypedEventFunction.csproj
├── OpenFunction.Examples.StorageImageAnnotator
│ ├── Function.cs
│ ├── OpenFunction.Examples.StorageImageAnnotator.csproj
│ └── README.md
├── OpenFunction.Examples.TestableDependencies
│ ├── Function.cs
│ └── OpenFunction.Examples.TestableDependencies.csproj
├── OpenFunction.Examples.TimeZoneConverter
│ ├── ConversionResult.cs
│ ├── ConversionType.cs
│ ├── Function.cs
│ ├── OpenFunction.Examples.TimeZoneConverter.csproj
│ └── README.md
├── OpenFunction.Examples.VbEventFunction
│ ├── CloudFunction.vb
│ └── OpenFunction.Examples.VbEventFunction.vbproj
├── OpenFunction.Examples.VbHttpFunction
│ ├── CloudFunction.vb
│ └── OpenFunction.Examples.VbHttpFunction.vbproj
├── OpenFunction.Examples.VbUntypedEventFunction
│ ├── CloudFunction.vb
│ └── OpenFunction.Examples.VbUntypedEventFunction.vbproj
├── OpenFunction.Examples.sln
├── README.md
├── copyright.txt
└── generate.sh
├── global.json
├── install-local-templates.sh
├── run-conformance-tests.sh
├── src
├── CommonProperties.xml
├── Directory.Build.targets
├── NuGetIcon.png
├── OpenFunction.ConformanceTests
│ ├── HttpFunction.cs
│ ├── OpenFunction.ConformanceTests.csproj
│ └── UntypedCloudEventFunction.cs
├── OpenFunction.Framework.Tests
│ ├── CloudEventAdapterTDataTest.cs
│ ├── CloudEventAdapterTest.cs
│ ├── GcfEvents
│ │ ├── EventDeserializationTest.cs
│ │ ├── GcfConvertersTest.cs
│ │ ├── GcfEventResources.cs
│ │ ├── emulator_pubsub.json
│ │ ├── firebase-analytics-no-app-id.json
│ │ ├── firebase-analytics-no-event-name.json
│ │ ├── firebase-analytics.json
│ │ ├── firebase-auth1.json
│ │ ├── firebase-auth2.json
│ │ ├── firebase-db1.json
│ │ ├── firebase-db2.json
│ │ ├── firebase-db3.json
│ │ ├── firebase-db4.json
│ │ ├── firebase-db5.json
│ │ ├── firebase-db6.json
│ │ ├── firebase-db7.json
│ │ ├── firebase-db8.json
│ │ ├── firebase-dbdelete1.json
│ │ ├── firebase-dbdelete2.json
│ │ ├── firebase-remote-config.json
│ │ ├── firestore_complex.json
│ │ ├── firestore_simple.json
│ │ ├── legacy_pubsub.json
│ │ ├── legacy_storage_change.json
│ │ ├── pubsub_binary.json
│ │ ├── pubsub_text.json
│ │ ├── pubsub_text_microsecond_precision.json
│ │ ├── raw_pubsub.json
│ │ └── storage.json
│ ├── OpenFunction.Framework.Tests.csproj
│ └── TestResourceHelper.cs
├── OpenFunction.Framework
│ ├── AssemblyInfo.cs
│ ├── CloudEventAdapter.cs
│ ├── CloudEventAdapterTData.cs
│ ├── GcfEvents
│ │ ├── Context.cs
│ │ ├── GcfConverters.cs
│ │ ├── Request.cs
│ │ └── Resource.cs
│ ├── ICloudEventFunction.cs
│ ├── IHttpFunction.cs
│ ├── OpenFunction.Framework.csproj
│ └── Preconditions.cs
├── OpenFunction.Hosting.Tests
│ ├── ApplicationConfigurationTest.cs
│ ├── CommandLineArgumentsConfigurationTest.cs
│ ├── CustomCloudEventDataTestBase.cs
│ ├── FunctionTargetTest.cs
│ ├── FunctionsEnvironmentVariablesConfigurationSourceTest.cs
│ ├── FunctionsStartupAttributeTest.cs
│ ├── FunctionsStartupTest.cs
│ ├── Logging
│ │ ├── JsonConsoleLoggerTest.cs
│ │ └── SimpleConsoleLoggerTest.cs
│ └── OpenFunction.Hosting.Tests.csproj
├── OpenFunction.Hosting
│ ├── AssemblyInfo.cs
│ ├── EntryPoint.cs
│ ├── Extensions
│ │ ├── ApplicationBuilderExtensions.cs
│ │ ├── FunctionsFrameworkConfigurationExtensions.cs
│ │ ├── FunctionsFrameworkLoggingExtensions.cs
│ │ ├── FunctionsFrameworkServiceCollectionExtensions.cs
│ │ └── FunctionsFrameworkWebHostBuilderExtensions.cs
│ ├── FunctionsEnvironmentVariablesConfigurationSource.cs
│ ├── FunctionsFrameworkOptions.cs
│ ├── FunctionsStartup.cs
│ ├── FunctionsStartupAttribute.cs
│ ├── HostingInternals.cs
│ ├── Logging
│ │ ├── FactoryLoggerProvider.cs
│ │ ├── JsonConsoleLogger.cs
│ │ ├── LoggerBase.cs
│ │ └── SimpleConsoleLogger.cs
│ ├── OpenFunction.Hosting.csproj
│ ├── Preconditions.cs
│ └── targets
│ │ ├── OpenFunction.Hosting.props
│ │ └── OpenFunction.Hosting.targets
├── OpenFunction.Templates
│ ├── OpenFunction.Templates.csproj
│ └── templates
│ │ ├── event-function-cs
│ │ ├── .template.config
│ │ │ ├── icon.png
│ │ │ ├── ide.host.json
│ │ │ └── template.json
│ │ ├── Function.cs
│ │ └── MyFunction.csproj
│ │ ├── event-function-fs
│ │ ├── .template.config
│ │ │ ├── icon.png
│ │ │ ├── ide.host.json
│ │ │ └── template.json
│ │ ├── Function.fs
│ │ └── MyFunction.fsproj
│ │ ├── event-function-vb
│ │ ├── .template.config
│ │ │ ├── icon.png
│ │ │ ├── ide.host.json
│ │ │ └── template.json
│ │ ├── CloudFunction.vb
│ │ └── MyFunction.vbproj
│ │ ├── http-function-cs
│ │ ├── .template.config
│ │ │ ├── icon.png
│ │ │ ├── ide.host.json
│ │ │ └── template.json
│ │ ├── Function.cs
│ │ └── MyFunction.csproj
│ │ ├── http-function-fs
│ │ ├── .template.config
│ │ │ ├── icon.png
│ │ │ ├── ide.host.json
│ │ │ └── template.json
│ │ ├── Function.fs
│ │ └── MyFunction.fsproj
│ │ ├── http-function-vb
│ │ ├── .template.config
│ │ │ ├── icon.png
│ │ │ ├── ide.host.json
│ │ │ └── template.json
│ │ ├── CloudFunction.vb
│ │ └── MyFunction.vbproj
│ │ ├── untyped-event-function-cs
│ │ ├── .template.config
│ │ │ ├── icon.png
│ │ │ ├── ide.host.json
│ │ │ └── template.json
│ │ ├── Function.cs
│ │ └── MyFunction.csproj
│ │ ├── untyped-event-function-fs
│ │ ├── .template.config
│ │ │ ├── icon.png
│ │ │ ├── ide.host.json
│ │ │ └── template.json
│ │ ├── Function.fs
│ │ └── MyFunction.fsproj
│ │ └── untyped-event-function-vb
│ │ ├── .template.config
│ │ ├── icon.png
│ │ ├── ide.host.json
│ │ └── template.json
│ │ ├── CloudFunction.vb
│ │ └── MyFunction.vbproj
├── OpenFunction.Testing.Tests
│ ├── FunctionTestBaseTest.cs
│ └── OpenFunction.Testing.Tests.csproj
├── OpenFunction.Testing
│ ├── AssemblyInfo.cs
│ ├── FunctionTestBase.cs
│ ├── FunctionTestServer.cs
│ ├── FunctionTestServerBuilder.cs
│ ├── LoggerTypeNameHelper.cs
│ ├── MemoryLogger.cs
│ ├── MemoryLoggerProvider.cs
│ ├── OpenFunction.Testing.csproj
│ ├── Preconditions.cs
│ └── TestLogEntry.cs
├── OpenFunction.sln
└── OpenFunction.snk
├── update-googleevents-references.sh
└── update-project-references.sh
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*.cs]
4 | csharp_space_after_cast = true
5 |
--------------------------------------------------------------------------------
/.gcloudignore:
--------------------------------------------------------------------------------
1 | # This file specifies files that are *not* uploaded to Google Cloud Platform
2 | # using gcloud. It follows the same syntax as .gitignore, with the addition of
3 | # "#!include" directives (which insert the entries of the given .gitignore-style
4 | # file at that point).
5 | #
6 | # For more information, run:
7 | # $ gcloud topic gcloudignore
8 | #
9 | .gcloudignore
10 | # If you would like to upload your .git directory, .gitignore file or files
11 | # from your .gitignore file, remove the corresponding line
12 | # below:
13 | .git
14 | .gitignore
15 |
16 | node_modules
17 | #!include:.gitignore
18 |
19 | # Ignore the submodule
20 | functions-framework-conformance/
21 |
--------------------------------------------------------------------------------
/.github/workflows/conformance.yml:
--------------------------------------------------------------------------------
1 | name: .NET Conformance CI
2 | on:
3 | push:
4 | branches:
5 | - main
6 | pull_request:
7 | jobs:
8 | run-conformance:
9 | strategy:
10 | matrix:
11 | dotnet: [ "3.1.x", "6.0.x" ]
12 | os: [ubuntu-latest, macos-latest, windows-latest]
13 | runs-on: ${{ matrix.os }}
14 |
15 | # Avoid unnecessary output
16 | env:
17 | DOTNET_NOLOGO: true
18 | DOTNET_CLI_TELEMETRY_OPTOUT: true
19 |
20 | steps:
21 | - name: Checkout repo
22 | uses: actions/checkout@v2
23 | with:
24 | submodules: true
25 |
26 | - name: Setup .NET Core
27 | uses: actions/setup-dotnet@v2
28 | with:
29 | dotnet-version: ${{ matrix.dotnet }}
30 |
31 | - name: Setup Go
32 | uses: actions/setup-go@v2
33 | with:
34 | go-version: '^1.15.2'
35 |
36 | - name: Clear NuGet cache
37 | run: dotnet nuget locals all --clear
38 |
39 | - name: Run conformance tests
40 | shell: bash
41 | run: ./run-conformance-tests.sh
42 |
43 | - name: Upload logs on failure
44 | uses: actions/upload-artifact@v2
45 | if: failure()
46 | with:
47 | name: conformance-logs-${{ matrix.os }}-${{ matrix.dotnet }}
48 | path: tmp/conformance-test-output
49 |
--------------------------------------------------------------------------------
/.github/workflows/lint.yml:
--------------------------------------------------------------------------------
1 | name: .NET Lint CI
2 | on:
3 | push:
4 | branches:
5 | - main
6 | pull_request:
7 | jobs:
8 | lint:
9 | runs-on: ubuntu-18.04
10 | steps:
11 | - name: Checkout repo
12 | uses: actions/checkout@v2
13 | with:
14 | submodules: true
15 | - name: Setup .NET Core
16 | uses: actions/setup-dotnet@v2
17 | - run: dotnet tool install -g dotnet-format
18 | - run: dotnet-format src
--------------------------------------------------------------------------------
/.github/workflows/unit.yml:
--------------------------------------------------------------------------------
1 | name: .NET Unit CI
2 | on:
3 | push:
4 | branches:
5 | - main
6 | pull_request:
7 | jobs:
8 | test:
9 | strategy:
10 | matrix:
11 | dotnet: [ "3.1.x", "6.0.x" ]
12 | os: [ubuntu-latest, macos-latest, windows-latest]
13 | runs-on: ${{ matrix.os }}
14 |
15 | # Avoid unnecessary output
16 | env:
17 | DOTNET_NOLOGO: true
18 | DOTNET_CLI_TELEMETRY_OPTOUT: true
19 |
20 | steps:
21 | - name: Checkout repo
22 | uses: actions/checkout@v2
23 | with:
24 | submodules: true
25 |
26 | - name: Setup .NET Core
27 | uses: actions/setup-dotnet@v2
28 | with:
29 | dotnet-version: ${{ matrix.dotnet }}
30 |
31 | - name: Clear NuGet cache
32 | run: dotnet nuget locals all --clear
33 |
34 | - name: Build and unit test
35 | shell: bash
36 | run: ./build.sh
37 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | *.*~
3 | project.lock.json
4 | .DS_Store
5 | *.pyc
6 | nupkg/
7 | tmp/
8 |
9 | # Visual Studio Code
10 | .vscode
11 |
12 | # Rider
13 | .idea
14 |
15 | # User-specific files
16 | *.suo
17 | *.user
18 | *.userosscache
19 | *.sln.docstates
20 |
21 | # Launch settings, e.g. from running samples
22 | launchSettings.json
23 |
24 | # Build results
25 | [Dd]ebug/
26 | [Dd]ebugPublic/
27 | [Rr]elease/
28 | [Rr]eleases/
29 | x64/
30 | x86/
31 | build/
32 | bld/
33 | [Bb]in/
34 | [Oo]bj/
35 | [Oo]ut/
36 | msbuild.log
37 | msbuild.err
38 | msbuild.wrn
39 |
40 | # Visual Studio 2015
41 | .vs/
42 |
43 | # CodeRush
44 | .cr/
45 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "functions-framework-conformance"]
2 | path = functions-framework-conformance
3 | url = https://github.com/GoogleCloudPlatform/functions-framework-conformance
4 |
--------------------------------------------------------------------------------
/.kokoro/autorelease.bat:
--------------------------------------------------------------------------------
1 | :: See documentation in type-shell-output.bat
2 |
3 | cd /d %~dp0
4 | "C:\Program Files\Git\bin\bash.exe" autorelease.sh
5 |
--------------------------------------------------------------------------------
/.kokoro/autorelease.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Environment variables:
4 | # - COMMITTISH_OVERRIDE: The commit to actually build the release from, if not the one that has been checked out
5 | # - SKIP_NUGET_PUSH: If non-empty, the push to nuget.org is skipped
6 |
7 | set -e
8 |
9 | SCRIPT=$(readlink -f "$0")
10 | SCRIPT_DIR=$(dirname "$SCRIPT")
11 |
12 | cd $SCRIPT_DIR/..
13 |
14 | # Make sure secrets are loaded in a well known location before running releasetool
15 | source $SCRIPT_DIR/populatesecrets.sh
16 | populate_all_secrets
17 |
18 | NUGET_API_KEY="$(cat "$SECRETS_LOCATION"/google-cloud-nuget-api-key)"
19 |
20 | # Make sure we have the most recent version of pip, then install the gcp-releasetool package
21 | python -m pip install --upgrade pip
22 | python -m pip install gcp-releasetool
23 | python -m releasetool publish-reporter-script > /tmp/publisher-script
24 |
25 | # The publish reporter script uses "python3" which doesn't exist on Windows.
26 | # Work out what we should use instead.
27 | # Try to detect Python 3. It's quite different between Windows and Linux.
28 | if which python > /dev/null && python --version 2>&1 | grep -q "Python 3"; then declare -r PYTHON3=python
29 | elif which py > /dev/null && py -3 --version 2>&1 | grep -q "Python 3"; then declare -r PYTHON3="py -3"
30 | elif which python3 > /dev/null && python3 --version 2>&1 | grep -q "Python 3"; then declare -r PYTHON3=python3
31 | else
32 | echo "Unable to detect Python 3 installation."
33 | exit 1
34 | fi
35 |
36 | # Fix up the publish reporter script using $PYTHON3. We assume this won't
37 | # be harmful within sed - at the moment it's always "python", "py -3" or "python3".
38 | sed -i "s/python3/$PYTHON3/g" /tmp/publisher-script
39 |
40 | source /tmp/publisher-script
41 |
42 | COMMITTISH=$COMMITTISH_OVERRIDE
43 | if [[ $COMMITTISH_OVERRIDE = "" ]]
44 | then
45 | COMMITTISH=HEAD
46 | fi
47 |
48 | TAG=$(git tag --points-at $COMMITTISH | head -n 1)
49 |
50 | if [[ $TAG = "" ]]
51 | then
52 | echo "Committish $COMMITTISH does not point at a tag. Aborting."
53 | exit 1
54 | fi
55 |
56 | echo "Building with tag $TAG"
57 |
58 | # Build the release and run the tests.
59 | ./build-release.sh $TAG
60 |
61 | if [[ $SKIP_NUGET_PUSH = "" ]]
62 | then
63 | echo "Pushing NuGet packages"
64 | # Push the changes to nuget.
65 | cd ./tmp/release/nupkg
66 | for pkg in *.nupkg
67 | do
68 | dotnet nuget push -s https://api.nuget.org/v3/index.json -k $NUGET_API_KEY $pkg
69 | done
70 | cd ../../..
71 | else
72 | echo "Skipping NuGet push"
73 | fi
74 |
--------------------------------------------------------------------------------
/.kokoro/populatesecrets.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | declare -r SECRETS_LOCATION="${KOKORO_GFILE_DIR}/secret_manager"
4 |
5 | # Populates secrets from Secret Manager according
6 | # to what's specified on env variables.
7 | populate_all_secrets(){
8 | echo "Creating folder on disk for secrets: ${SECRETS_LOCATION}"
9 | mkdir -p ${SECRETS_LOCATION}
10 |
11 | # Currently we don't have any COMMON_SECRETS
12 | # populate_secrets "${COMMON_SECRETS}" "cloud-sharp-jenkins"
13 | populate_secrets "${JOB_SECRETS}" "cloud-sharp-jenkins"
14 | populate_secrets "${EXTERNAL_SECRETS}" "cloud-devrel-kokoro-resources"
15 | }
16 |
17 | populate_secrets(){
18 | local env_var=$1
19 | local project=$2
20 | for key in $(echo ${env_var} | sed "s/,/ /g")
21 | do
22 | echo "Retrieving secret ${key}"
23 | gcloud secrets versions access latest \
24 | --project $project \
25 | --secret $key > \
26 | "${SECRETS_LOCATION}/$key"
27 | if [[ $? == 0 ]]; then
28 | echo "Secret written to ${SECRETS_LOCATION}/${key}"
29 | else
30 | echo "Error retrieving secret ${key}"
31 | exit 1
32 | fi
33 | done
34 | }
35 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to Contribute
2 |
3 | We'd love to accept your patches and contributions to this project. There are
4 | just a few small guidelines you need to follow.
5 |
6 | ## Contributor License Agreement
7 |
8 | Contributions to this project must be accompanied by a Contributor License
9 | Agreement. You (or your employer) retain the copyright to your contribution;
10 | this simply gives us permission to use and redistribute your contributions as
11 | part of the project. Head over to to see
12 | your current agreements on file or to sign a new one.
13 |
14 | You generally only need to submit a CLA once, so if you've already submitted one
15 | (even if it was for a different project), you probably don't need to do it
16 | again.
17 |
18 | ## Code reviews
19 |
20 | All submissions, including submissions by project members, require review. We
21 | use GitHub pull requests for this purpose. Consult
22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
23 | information on using pull requests.
24 |
25 | ## Community Guidelines
26 |
27 | This project follows [Google's Open Source Community
28 | Guidelines](https://opensource.google.com/conduct/).
--------------------------------------------------------------------------------
/RELEASING.md:
--------------------------------------------------------------------------------
1 | # Releasing the .NET Functions Frameworks packages
2 |
3 | This is a largely automated process. Steps:
4 |
5 | - Decide on a new version number, following semantic versioning, but
6 | don't change any files.
7 | - Document the new version in [docs/history.md]() (but don't commit)
8 | - Run `./create-release-commit.sh {new-version}` from the root directory
9 | - Push the commit to GitHub and create a pull request
10 | - Add the "autorelease: pending" tag to the PR, and request a review
11 | - Once the PR is merged:
12 | - A GitHub release and tags will be created automatically
13 | - A Kokoro job will be launched to build and publish the NuGet packages
14 |
15 | The `create-release-commit.sh` script is responsible for:
16 |
17 | - Updating the `src/CommonProperties.xml` file which contains the version number
18 | - Updating all project references in templates and examples
19 | - Updating `README.md` to give instructions for installing the templates
20 | - Committing all changes with an appropriate message based on the
21 | version history
22 |
23 | Note that the script expects the version history file to follow the
24 | existing format. It assumes that the third line of the file is the
25 | header for the new release, and uses everything from the fourth line
26 | until the next "##" line, which is expected to be the previous
27 | release.
28 |
--------------------------------------------------------------------------------
/build-release.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | if [[ -z "$1" ]]
4 | then
5 | echo "Please specify the release tag"
6 | exit 1
7 | fi
8 |
9 | set -e
10 |
11 | rm -rf tmp
12 | mkdir tmp
13 |
14 | git clone https://github.com/OpenFunction/functions-framework-dotnet.git \
15 | --depth 1 -b $1 --recursive tmp/release
16 |
17 | cd tmp/release
18 | ./build.sh
19 |
20 |
--------------------------------------------------------------------------------
/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | export ContinuousIntegrationBuild=true
6 | export Configuration=Release
7 | # When building examples, build against the version in this
8 | # repo rather than against NuGet; this allows us to make breaking
9 | # changes.
10 | export LocalFunctionsFramework=true
11 |
12 | echo Building...
13 | dotnet build -nologo -clp:NoSummary -v quiet src
14 | dotnet build -nologo -clp:NoSummary -v quiet examples
15 |
16 | echo Testing...
17 | dotnet test -nologo --no-build -v quiet src
18 | dotnet test -nologo --no-build -v quiet examples
19 |
20 | echo Packing...
21 | rm -rf nupkg
22 | dotnet pack -nologo -v quiet src -o $PWD/nupkg
23 |
24 | echo Created packages:
25 | ls nupkg
26 |
--------------------------------------------------------------------------------
/create-release-commit.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | # Script to automate the release process. Assumptions:
6 | # - docs/history.md has already been updated (but not committed)
7 | # - any Google.Events references have already been updated
8 | # - the version number in *this* repo has not been updated
9 |
10 | # Pass in the new version number as the sole command line argument
11 |
12 | if [[ -z "$1" ]]
13 | then
14 | echo 'Please specify the new version number.'
15 | exit 1
16 | fi
17 |
18 | OLD_VERSION=$(grep '.*' src/CommonProperties.xml | sed -e 's/<\/\?Version>//g' | sed 's/ //g')
19 | NEW_VERSION=$1
20 |
21 | if [[ $OLD_VERSION == $NEW_VERSION ]]
22 | then
23 | echo "Error: already at $OLD_VERSION. Either finish manually, or revert version changes."
24 | exit 1
25 | fi
26 |
27 | # Update CommonProperties.xml
28 | sed -i -e "s/.*<\/Version>/$NEW_VERSION<\/Version>/g" src/CommonProperties.xml
29 |
30 | # Update all references/templates
31 | ./update-project-references.sh
32 |
33 | # Update the README instructions for template installation.
34 | # The binary mode (-b) here is to preserve existing line endings.
35 | # (At some point we should probably make all line endings consistent...)
36 | sed -i -b -e "s/Templates::$OLD_VERSION/Templates::$NEW_VERSION/g" README.md
37 |
38 | # Build the commit message up as a file for simplicity
39 | mkdir -p tmp
40 |
41 | echo "Release Functions Framework .NET packages version $NEW_VERSION" > tmp/commit.txt
42 | echo "" >> tmp/commit.txt
43 | echo "Changes since $OLD_VERSION:" >> tmp/commit.txt
44 | # Skip the first three lines of docs/history.md (header, blank line, subheader for this release)
45 | # Then take lines until we get to the next release, skipping that line with "head".
46 | tail -n +4 docs/history.md | sed '/##/q' | head -n -1 >> tmp/commit.txt
47 |
48 | # TODO: Automate finding the packages we're releasing
49 | echo "Packages in this release:" >> tmp/commit.txt
50 | echo "- Release OpenFunction.Framework version $NEW_VERSION" >> tmp/commit.txt
51 | echo "- Release OpenFunction.Hosting version $NEW_VERSION" >> tmp/commit.txt
52 | echo "- Release OpenFunction.Templates version $NEW_VERSION" >> tmp/commit.txt
53 | echo "- Release OpenFunction.Testing version $NEW_VERSION" >> tmp/commit.txt
54 |
55 | # Commit!
56 | git commit -a -F tmp/commit.txt
57 |
58 | echo "Created commit:"
59 | git show -s
60 |
61 | echo ""
62 | echo "Changes committed updating from $OLD_VERSION to $NEW_VERSION. Please push to GitHub."
63 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # Functions Framework for .NET documentation
2 |
3 | This directory provides documentation for those who need more detail
4 | than is given in the [top-level README](../README.md). This
5 | documentation is not published separately; the Markdown files should
6 | be readable in GitHub.
7 |
8 | ## Packages
9 |
10 | This repository contains the source code for the following NuGet packages:
11 |
12 | - [OpenFunction.Framework](https://www.nuget.org/packages/OpenFunction.Framework)
13 | is a very small package, primarily containing interfaces for Cloud
14 | Functions to implement, as well as adapters between function types.
15 | - [OpenFunction.Hosting](https://www.nuget.org/packages/OpenFunction.Hosting)
16 | contains code to start up an ASP.NET Core webserver based on
17 | conventional environment variables etc.
18 | - [OpenFunction.Testing](https://www.nuget.org/packages/OpenFunction.Testing)
19 | contains code to help simplify testing functions.
20 | - [OpenFunction.Templates](https://www.nuget.org/packages/OpenFunction.Templates)
21 | contains templates for the `dotnet` command line to create a very
22 | simple getting-started experience.
23 |
24 | See [the packages guide](packages.md) for more details about each
25 | package.
26 |
27 | ## Repository layout
28 |
29 | The repository is split into the following directories:
30 |
31 | - [src](../src): Source code for the production packages and tests
32 | - [examples](../examples): Source code for example functions
33 | - [docs](.): Documentation
34 |
35 | The example functions are split into their own directory (with their
36 | own solution file) so that we can have many examples, each of which
37 | is a complete, standlone project. The example functions refer to the
38 | projects in `src` to allow for easy development. However, the expectation
39 | is that unless they use unreleased features, each example could be
40 | extracted from the repo, project references changed to package
41 | references, additional MSBuild imports removed, and the example should still work.
42 |
43 | ## Additional documentation in this directory:
44 |
45 | - [Version History](history.md)
46 | - [Package Details](packages.md)
47 | - [Deployment](deployment.md)
48 | - [Testing Functions](testing.md)
49 | - [Examples](examples.md)
50 | - [Customization using Functions Startup classes](customization.md)
51 | - [Microsoft.NET.Sdk.Web and launchSettings.json](launch-settings.md)
52 |
--------------------------------------------------------------------------------
/docs/launch-settings.md:
--------------------------------------------------------------------------------
1 | # Microsoft.NET.Sdk.Web and launchSettings.json
2 |
3 | You may well have reached this page due to a link in an exception,
4 | while trying to start a server using the Functions Framework Hosting
5 | package.
6 |
7 | If you start a project with an `Sdk` attribute of
8 | "Microsoft.NET.Sdk.Web" in Visual Studio, it will create a
9 | `launchSettings.json` file if one doesn't already exist, and pass a
10 | single string, `%LAUNCHER_ARGS%` to the `Main` method.
11 |
12 | That's useful for a regular ASP.NET Core application, but isn't
13 | appropriate for a Functions Framework application using the
14 | OpenFunction.Framework.Hosting package which *always* uses
15 | Kestrel, gets its port from the `PORT` environment variable etc.
16 |
17 | There are three simple solutions to this issue. In order of
18 | simplicity and preference:
19 |
20 | - Change the `Sdk` attribute in the root element of your project
21 | file to "Microsoft.NET.Sdk", and delete any existing
22 | `launchSettings.json` file. At that point Visual Studio will
23 | treat it as a regular console application project. Using the
24 | Debugging property tab in Visual Studio will create a new
25 | `launchSettings.json`, but the way that file is used does not
26 | interfere with the Functions Framework Hosting package.
27 | - Add an element
28 | `true`
29 | within a `` in your project file, and delete any
30 | existing `launchSettings.json` file. That will stop Visual Studio
31 | from creating a `launchSettings.json` file, so you won't experience
32 | this problem.
33 | - Stop using the Functions Framework Hosting package. If you want to run your
34 | function as part of a regular ASP.NET Core app, you can do so - and
35 | you *may* find it useful to still refer to the
36 | OpenFunction.Framework package, just not the hosting package.
37 |
38 | There may be more complex options you wish to investigate if none
39 | of the above options work in your situation. It would be useful if
40 | you could [file an issue](https://github.com/OpenFunction/functions-framework-dotnet/issues/new)
41 | to provide some context, so we can help you find those options and
42 | perhaps work around the problem within the framework in the future.
43 |
--------------------------------------------------------------------------------
/examples/.gcloudignore:
--------------------------------------------------------------------------------
1 | # This file tells "gcloud functions deploy" which files and folders
2 | # to ignore. This is important when deploying multi-project functions,
3 | # as otherwise a lot of unnecessary files (particularly in the bin and
4 | # obj directories) may be uploaded.
5 |
6 | .gcloudignore
7 | bin/
8 | obj/
9 |
--------------------------------------------------------------------------------
/examples/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
7 |
9 |
11 |
12 |
--------------------------------------------------------------------------------
/examples/Directory.Build.targets:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | $([System.IO.Path]::Combine('$(IntermediateOutputPath)','$(TargetFrameworkMoniker).AssemblyAttributes$(DefaultLanguageSourceExtension)'))
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.AdvancedDependencyInjection/OpenFunction.Examples.AdvancedDependencyInjection.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net6.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.Configuration/OpenFunction.Examples.Configuration.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net6.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.Configuration/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "DbConnection": {
3 | "Instance": "TestInstance",
4 | "Database": "Development Test Database"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.Configuration/appsettings.Production.json:
--------------------------------------------------------------------------------
1 | {
2 | "DbConnection": {
3 | "Instance": "ProductionInstance"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.Configuration/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "DbConnection": {
3 | "Instance": "DefaultInstance",
4 | "Database": "DefaultDatabase"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.CustomConfiguration/Function.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using OpenFunction.Framework;
16 | using OpenFunction.Hosting;
17 | using Microsoft.AspNetCore.Hosting;
18 | using Microsoft.AspNetCore.Http;
19 | using Microsoft.Extensions.Configuration;
20 | using Steeltoe.Extensions.Configuration.RandomValue;
21 | using System.Threading.Tasks;
22 |
23 | namespace OpenFunction.Examples.CustomConfiguration
24 | {
25 |
26 | ///
27 | /// The startup class can be used to perform additional configuration, including
28 | /// adding application configuration sources, reconfiguring logging, providing services
29 | /// for dependency injection, and adding middleware to the eventual application pipeline.
30 | /// In this case, we add the "random value" provider from Steeltoe.
31 | /// See https://steeltoe.io/docs/2/configuration/random-value-provider for more details.
32 | ///
33 | public class Startup : FunctionsStartup
34 | {
35 | public override void ConfigureAppConfiguration(WebHostBuilderContext context, IConfigurationBuilder configuration) =>
36 | configuration.AddRandomValueSource();
37 | }
38 |
39 | [FunctionsStartup(typeof(Startup))]
40 | public class Function : IHttpFunction
41 | {
42 | private readonly IConfiguration _configuration;
43 |
44 | public Function(IConfiguration configuration) =>
45 | _configuration = configuration;
46 |
47 | public async Task HandleAsync(HttpContext context)
48 | {
49 | int randomValue = _configuration.GetValue("random:int");
50 | await context.Response.WriteAsync($"Here's a random integer: {randomValue}");
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.CustomConfiguration/OpenFunction.Examples.CustomConfiguration.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net6.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.CustomEventDataFunction/Function.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2021, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using CloudNative.CloudEvents;
16 | using CloudNative.CloudEvents.NewtonsoftJson;
17 | using OpenFunction.Framework;
18 | using OpenFunction.Hosting;
19 | using Microsoft.AspNetCore.Hosting;
20 | using Microsoft.Extensions.DependencyInjection;
21 | using Microsoft.Extensions.Logging;
22 | using Newtonsoft.Json;
23 | using System.Threading;
24 | using System.Threading.Tasks;
25 |
26 | namespace OpenFunction.Examples.CustomEventDataFunction
27 | {
28 | ///
29 | /// Startup class to inject a suitable CloudEventFormatter. Our CustomData type
30 | /// has attributes for Json.NET, but no CloudEventFormatterAttribute, so we inject
31 | /// a suitable JsonEventFormatter.
32 | ///
33 | public class Startup : FunctionsStartup
34 | {
35 | public override void ConfigureServices(WebHostBuilderContext context, IServiceCollection services) =>
36 | services.AddSingleton(new JsonEventFormatter());
37 | }
38 |
39 | ///
40 | /// A function that can be triggered by a CloudEvent containing data of type
41 | /// CustomData in a JSON event format.
42 | ///
43 | [FunctionsStartup(typeof(Startup))]
44 | public class Function : ICloudEventFunction
45 | {
46 | private readonly ILogger _logger;
47 |
48 | public Function(ILogger logger) =>
49 | _logger = logger;
50 |
51 | public Task HandleAsync(CloudEvent cloudEvent, CustomData data, CancellationToken cancellationToken)
52 | {
53 | _logger.LogInformation("Data received. TextValue={value}", data.TextValue);
54 | return Task.CompletedTask;
55 | }
56 | }
57 |
58 | public class CustomData
59 | {
60 | [JsonProperty("text")]
61 | public string TextValue { get; set; }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.CustomEventDataFunction/OpenFunction.Examples.CustomEventDataFunction.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net6.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.FSharpEventFunction/OpenFunction.Examples.FSharpEventFunction.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net6.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.FSharpHttpFunction/Function.fs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | namespace OpenFunction.Examples.FSharpHttpFunction
16 |
17 | open OpenFunction.Framework
18 | open Microsoft.AspNetCore.Http
19 |
20 | type Function() =
21 | interface IHttpFunction with
22 | ///
23 | /// Logic for your function goes here.
24 | ///
25 | /// The HTTP context, containing the request and the response.
26 | /// A task representing the asynchronous operation.
27 | member this.HandleAsync context =
28 | async {
29 | do! context.Response.WriteAsync "Hello, Functions Framework." |> Async.AwaitTask
30 | } |> Async.StartAsTask :> _
31 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.FSharpHttpFunction/OpenFunction.Examples.FSharpHttpFunction.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net6.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.FSharpUntypedEventFunction/Function.fs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | namespace OpenFunction.Examples.FSharpUntypedEventFunction
16 |
17 | open OpenFunction.Framework
18 | open System.Threading.Tasks
19 |
20 | type Function() =
21 | interface ICloudEventFunction with
22 | ///
23 | /// Logic for your function goes here. Note that a CloudEvent function just consumes an event;
24 | /// it doesn't provide any response.
25 | ///
26 | /// The CloudEvent your function should consume.
27 | /// A cancellation token that is notified if the request is aborted.
28 | /// A task representing the asynchronous operation.
29 | member this.HandleAsync(cloudEvent, cancellationToken) =
30 | printfn "CloudEvent information:"
31 | printfn "ID: %s" cloudEvent.Id
32 | printfn "Source: %A" cloudEvent.Source
33 | printfn "Type: %s" cloudEvent.Type
34 | printfn "Subject: %s" cloudEvent.Subject
35 | printfn "DataSchema: %A" cloudEvent.DataSchema
36 | printfn "DataContentType: %O" cloudEvent.DataContentType
37 | printfn "Time: %s" (match Option.ofNullable cloudEvent.Time with
38 | | Some time -> time.ToUniversalTime().ToString "yyyy-MM-dd'T'HH:mm:ss.fff'Z'"
39 | | None -> "")
40 | printfn "SpecVersion: %O" cloudEvent.SpecVersion
41 | printfn "Data: %A" cloudEvent.Data
42 |
43 | // In this example, we don't need to perform any asynchronous operations, so we
44 | // just return a completed Task to conform to the interface.
45 | Task.CompletedTask
46 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.FSharpUntypedEventFunction/OpenFunction.Examples.FSharpUntypedEventFunction.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net6.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.IntegrationTests/AdvancedDependencyInjectionTest.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using OpenFunction.Testing;
16 | using System;
17 | using System.Text.Json;
18 | using System.Threading.Tasks;
19 | using Xunit;
20 |
21 | namespace OpenFunction.Examples.IntegrationTests
22 | {
23 | public class AdvancedDependencyInjectionTest : FunctionTestBase
24 | {
25 | [Fact]
26 | public async Task SingletonOperationIdRemainsStable()
27 | {
28 | // Make two requests to the function. They should both return the same SingletonOperationId value.
29 | var response1 = await CallFunctionAsync();
30 | var response2 = await CallFunctionAsync();
31 |
32 | Assert.Equal(response1.SingletonOperationId, response2.SingletonOperationId);
33 | }
34 |
35 | [Fact]
36 | public async Task ScopedOperationIdChangesPerRequest()
37 | {
38 | // Make two requests to the function. They should provide different ScopedOperationId values.
39 | var response1 = await CallFunctionAsync();
40 | var response2 = await CallFunctionAsync();
41 |
42 | Assert.NotEqual(response1.ScopedOperationId, response2.ScopedOperationId);
43 | }
44 |
45 | private async Task CallFunctionAsync()
46 | {
47 | var content = await ExecuteHttpGetRequestAsync();
48 | return JsonSerializer.Deserialize(content);
49 | }
50 |
51 | private class ResponseModel
52 | {
53 | public Guid SingletonOperationId { get; set; }
54 | public Guid ScopedOperationId { get; set; }
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.IntegrationTests/OpenFunction.Examples.IntegrationTests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | false
6 |
8 | false
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.IntegrationTests/SimpleDependencyInjectionTest.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using OpenFunction.Testing;
16 | using Microsoft.Extensions.Logging;
17 | using System.Threading.Tasks;
18 | using Xunit;
19 |
20 | namespace OpenFunction.Examples.IntegrationTests
21 | {
22 | public class SimpleDependencyInjectionTest
23 | {
24 | [Fact]
25 | public async Task LogEntryIsRecorded()
26 | {
27 | using (var server = new FunctionTestServer())
28 | {
29 | // We shouldn't have any log entries (for the function's category) at the start of the test.
30 | Assert.Empty(server.GetFunctionLogEntries());
31 | // Note: server.GetFunctionLogEntries() is equivalent to
32 | // server.GetLogEntries(typeof(SimpleDependencyInjection.Function)
33 |
34 | var client = server.CreateClient();
35 |
36 | // Make a request to the function.
37 | var response = await client.GetAsync("sample-path");
38 | response.EnsureSuccessStatusCode();
39 |
40 | // Check that we got the expected log entry.
41 | var logs = server.GetFunctionLogEntries();
42 | var entry = Assert.Single(logs);
43 |
44 | Assert.Equal(LogLevel.Information, entry.Level);
45 | Assert.Equal("Function called with path /sample-path", entry.Message);
46 | }
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.IntegrationTests/SimpleDependencyInjectionUnitTest.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 | using OpenFunction.Testing;
15 | using Microsoft.AspNetCore.Http;
16 | using Microsoft.Extensions.Logging;
17 | using System.Threading.Tasks;
18 | using Xunit;
19 |
20 | namespace OpenFunction.Examples.IntegrationTests
21 | {
22 | ///
23 | /// Example of a unit test using a MemoryLogger. Often integration tests are
24 | /// the simplest form of testing for functions, but MemoryLogger allows log entries
25 | /// to be tested in unit tests by implementing ILogger.
26 | ///
27 | public class SimpleDependencyInjectionUnitTest
28 | {
29 | [Fact]
30 | public async Task LogEntryIsRecorded()
31 | {
32 | var logger = new MemoryLogger();
33 | var function = new SimpleDependencyInjection.Function(logger);
34 |
35 | // Constructing the function does not create any log entries.
36 | Assert.Empty(logger.ListLogEntries());
37 |
38 | // Make a request to the function.
39 | var context = new DefaultHttpContext
40 | {
41 | Request = { Path = "/sample-path" }
42 | };
43 | await function.HandleAsync(context);
44 |
45 | var logs = logger.ListLogEntries();
46 | var entry = Assert.Single(logs);
47 |
48 | Assert.Equal(LogLevel.Information, entry.Level);
49 | Assert.Equal("Function called with path /sample-path", entry.Message);
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.IntegrationTests/SimpleHttpFunctionTest.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using Microsoft.AspNetCore.Hosting;
16 | using Microsoft.AspNetCore.TestHost;
17 | using Microsoft.Extensions.DependencyInjection;
18 | using Microsoft.Extensions.Hosting;
19 | using System.Threading.Tasks;
20 | using Xunit;
21 |
22 | namespace OpenFunction.Examples.IntegrationTests
23 | {
24 | ///
25 | /// Simple example of an integration test against a Cloud Function, without using
26 | /// the OpenFunction.Testing package.
27 | ///
28 | public class SimpleHttpFunctionTest
29 | {
30 | [Fact]
31 | public async Task FunctionWritesHelloFunctionsFramework()
32 | {
33 | // Various other extension methods are available to configure logging,
34 | // startups, application configurers, along with reading configuration
35 | // from the command line and environment variables - but those aren't
36 | // required for this test.
37 | var builder = Host.CreateDefaultBuilder()
38 | .ConfigureWebHostDefaults(webHostBuilder => webHostBuilder
39 | .ConfigureServices(services => services.AddFunctionTarget())
40 | .Configure((context, app) => app.UseFunctionsFramework(context))
41 | .UseTestServer());
42 | using var server = await builder.StartAsync();
43 | using var client = server.GetTestServer().CreateClient();
44 |
45 | // Make a request to the function, and test that the response looks how we expect it to.
46 | using var response = await client.GetAsync("request-uri");
47 | response.EnsureSuccessStatusCode();
48 | var content = await response.Content.ReadAsStringAsync();
49 | Assert.Equal("Hello, Functions Framework.", content);
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.IntegrationTests/SimpleHttpFunctionTest_WithFunctionTestBase.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using OpenFunction.Testing;
16 | using System.Threading.Tasks;
17 | using Xunit;
18 |
19 | namespace OpenFunction.Examples.IntegrationTests
20 | {
21 | public class SimpleHttpFunctionTest_WithFunctionTestBase : FunctionTestBase
22 | {
23 | [Fact]
24 | public async Task FunctionWritesHelloFunctionsFramework()
25 | {
26 | string content = await ExecuteHttpGetRequestAsync();
27 | Assert.Equal("Hello, Functions Framework.", content);
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.IntegrationTests/SimpleHttpFunctionTest_WithTestServerFixture.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using OpenFunction.Testing;
16 | using System.Threading.Tasks;
17 | using Xunit;
18 |
19 | namespace OpenFunction.Examples.IntegrationTests
20 | {
21 | ///
22 | /// Simple example of an integration test creating a
23 | /// as a fixture. This uses the same test server across multiple tests.
24 | ///
25 | public class SimpleHttpFunctionTest_WithTestServerFixture : IClassFixture>
26 | {
27 | // The function test server created automatically by xUnit. This will be disposed after all tests have run.
28 | private readonly FunctionTestServer _server;
29 |
30 | public SimpleHttpFunctionTest_WithTestServerFixture(FunctionTestServer server) =>
31 | _server = server;
32 |
33 | [Fact]
34 | public async Task FunctionWritesHelloFunctionsFramework()
35 | {
36 | var client = _server.CreateClient();
37 |
38 | // Make a request to the function, and test that the response looks how we expect it to.
39 | var response = await client.GetAsync("request-uri");
40 | response.EnsureSuccessStatusCode();
41 | var content = await response.Content.ReadAsStringAsync();
42 |
43 | Assert.Equal("Hello, Functions Framework.", content);
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.IntegrationTests/SimpleHttpFunctionTest_WithTestServerInCtor.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using OpenFunction.Testing;
16 | using System;
17 | using System.Threading.Tasks;
18 | using Xunit;
19 |
20 | namespace OpenFunction.Examples.IntegrationTests
21 | {
22 | ///
23 | /// Simple example of an integration test creating a
24 | /// in the constructor. This uses a different test server for each test, but makes the setup common
25 | /// across all tests.
26 | ///
27 | public class SimpleHttpFunctionTest_WithTestServerInCtor : IDisposable
28 | {
29 | // The function test server created in the constructor.
30 | private readonly FunctionTestServer _server;
31 |
32 | public SimpleHttpFunctionTest_WithTestServerInCtor() =>
33 | _server = new FunctionTestServer();
34 |
35 | // Dispose of the function test server, which in turn disposes of the underlying test server.
36 | // xUnit calls this automatically when a test is complete.
37 | public void Dispose() => _server.Dispose();
38 |
39 | [Fact]
40 | public async Task FunctionWritesHelloFunctionsFramework()
41 | {
42 | var client = _server.CreateClient();
43 |
44 | // Make a request to the function, and test that the response looks how we expect it to.
45 | var response = await client.GetAsync("request-uri");
46 | response.EnsureSuccessStatusCode();
47 | var content = await response.Content.ReadAsStringAsync();
48 |
49 | Assert.Equal("Hello, Functions Framework.", content);
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.IntegrationTests/SimpleHttpFunctionTest_WithTestServerInTest.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using OpenFunction.Testing;
16 | using System.Threading.Tasks;
17 | using Xunit;
18 |
19 | namespace OpenFunction.Examples.IntegrationTests
20 | {
21 | ///
22 | /// Simple example of an integration test creating a
23 | /// inside a test.
24 | ///
25 | public class SimpleHttpFunctionTest_WithTestServerInTest
26 | {
27 | [Fact]
28 | public async Task FunctionWritesHelloFunctionsFramework()
29 | {
30 | using (var server = new FunctionTestServer())
31 | {
32 | var client = server.CreateClient();
33 |
34 | // Make a request to the function, and test that the response looks how we expect it to.
35 | var response = await client.GetAsync("request-uri");
36 | response.EnsureSuccessStatusCode();
37 | var content = await response.Content.ReadAsStringAsync();
38 |
39 | Assert.Equal("Hello, Functions Framework.", content);
40 | }
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.IntegrationTests/TestableDependenciesTest.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using OpenFunction.Hosting;
16 | using OpenFunction.Testing;
17 | using Microsoft.AspNetCore.Hosting;
18 | using Microsoft.Extensions.DependencyInjection;
19 | using System.Threading.Tasks;
20 | using Xunit;
21 |
22 | namespace OpenFunction.Examples.IntegrationTests
23 | {
24 | ///
25 | /// By default, the test server will use the same Functions Startup classes
26 | /// as normal. Applying the FunctionsStartup attribute to the test class (and/or the assembly)
27 | /// signals to FunctionTestBase which startups should be used to inject test dependencies
28 | /// instead of the production ones. An alternative is to declare a parameterless constructor
29 | /// that creates a FunctionTestServer to pass into the base class constructor.
30 | ///
31 | [FunctionsStartup(typeof(TestStartup))]
32 | public class TestableDependenciesTest : FunctionTestBase
33 | {
34 | [Fact]
35 | public async Task FunctionOutputDoesNotReferToProduction()
36 | {
37 | string text = await ExecuteHttpGetRequestAsync();
38 | Assert.DoesNotContain("Production dependency", text);
39 | Assert.Contains("Test dependency", text);
40 | }
41 |
42 | // Functions Startup class specified in the FunctionTestStartup attribute on the test class,
43 | // so that test-only dependencies can be used.
44 | private class TestStartup : FunctionsStartup
45 | {
46 | public override void ConfigureServices(WebHostBuilderContext context, IServiceCollection services) =>
47 | services.AddSingleton();
48 | }
49 |
50 | public class TestDependency : TestableDependencies.IDependency
51 | {
52 | public string Name => "Test dependency";
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.Middleware/Function.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using OpenFunction.Framework;
16 | using OpenFunction.Hosting;
17 | using Microsoft.AspNetCore.Builder;
18 | using Microsoft.AspNetCore.Hosting;
19 | using Microsoft.AspNetCore.Http;
20 | using Microsoft.Extensions.Logging;
21 | using System.Diagnostics;
22 | using System.Threading.Tasks;
23 |
24 | namespace OpenFunction.Examples.Middleware
25 | {
26 | ///
27 | /// The startup class can be used to perform additional configuration, including
28 | /// adding application configuration sources, reconfiguring logging, providing services
29 | /// for dependency injection, and adding middleware to the eventual application pipeline.
30 | /// In this case, we add a simple piece of middleware to the request pipeline.
31 | ///
32 | public class Startup : FunctionsStartup
33 | {
34 | public override void Configure(WebHostBuilderContext context, IApplicationBuilder app) =>
35 | app.UseMiddleware();
36 | }
37 |
38 | ///
39 | /// This middleware just provides a single log entry per successful request.
40 | /// (This is not terribly useful as middleware, but it demonstrates the concept simply.)
41 | ///
42 | public class SampleMiddleware
43 | {
44 | private readonly RequestDelegate _next;
45 |
46 | public SampleMiddleware(RequestDelegate next) =>
47 | _next = next;
48 |
49 | public async Task InvokeAsync(HttpContext context, ILogger logger)
50 | {
51 | Stopwatch sw = Stopwatch.StartNew();
52 | await _next(context);
53 | sw.Stop();
54 | logger.LogInformation("Path: {path}; Status: {status}; Time: {time}ms",
55 | context.Request.Path, context.Response.StatusCode, sw.Elapsed.TotalMilliseconds);
56 | }
57 | }
58 |
59 | ///
60 | /// The actual Cloud Function.
61 | ///
62 | [FunctionsStartup(typeof(Startup))]
63 | public class Function : IHttpFunction
64 | {
65 | public async Task HandleAsync(HttpContext context) =>
66 | await context.Response.WriteAsync("Response to be logged by middleware.");
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.Middleware/OpenFunction.Examples.Middleware.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net6.0
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.MultiProjectDependency/BusinessLogic.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | namespace OpenFunction.Examples.MultiProjectDependency
16 | {
17 | ///
18 | /// See the comments in MultiProjectFunction.Function for the purpose
19 | /// of this class.
20 | ///
21 | public class BusinessLogic
22 | {
23 | ///
24 | /// Performs some general business logic which might be needed from multiple
25 | /// applications, so belongs in a separate class library.
26 | ///
27 | public string PerformGeneralBusinessLogic() => "Profit!";
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.MultiProjectDependency/OpenFunction.Examples.MultiProjectDependency.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.MultiProjectFunction/Function.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using OpenFunction.Examples.MultiProjectDependency;
16 | using OpenFunction.Framework;
17 | using Microsoft.AspNetCore.Http;
18 | using System.Threading.Tasks;
19 |
20 | namespace OpenFunction.Examples.MultiProjectFunction
21 | {
22 | ///
23 | /// Function demonstrating how to use a project dependency from the function
24 | /// project to a class library. When deploying to Google Cloud Functions, the
25 | /// class library code still needs to be uploaded as part of the build, and the
26 | /// GOOGLE_BUILDABLE build-time environment variable needs to be set to the
27 | /// path to the function project.
28 | ///
29 | public class Function : IHttpFunction
30 | {
31 | public async Task HandleAsync(HttpContext context)
32 | {
33 | var logic = new BusinessLogic();
34 | var result = logic.PerformGeneralBusinessLogic();
35 | await context.Response.WriteAsync($"Result: {result}");
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.MultiProjectFunction/OpenFunction.Examples.MultiProjectFunction.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net6.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.SimpleDependencyInjection/Function.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using OpenFunction.Framework;
16 | using Microsoft.AspNetCore.Http;
17 | using Microsoft.Extensions.Logging;
18 | using System.Threading.Tasks;
19 |
20 | namespace OpenFunction.Examples.SimpleDependencyInjection
21 | {
22 | ///
23 | /// Simple example of dependency injection, with no additional configuration.
24 | /// Loggers are provided out-of-the-box by ASP.NET Core, which means you can inject a logger
25 | /// into your function constructor.
26 | ///
27 | public class Function : IHttpFunction
28 | {
29 | private readonly ILogger _logger;
30 |
31 | public Function(ILogger logger) =>
32 | _logger = logger;
33 |
34 | public async Task HandleAsync(HttpContext context)
35 | {
36 | _logger.LogInformation("Function called with path {path}", context.Request.Path);
37 | await context.Response.WriteAsync("Written the request path to the logger provided by dependency injection.");
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.SimpleDependencyInjection/OpenFunction.Examples.SimpleDependencyInjection.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net6.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.SimpleEventFunction/OpenFunction.Examples.SimpleEventFunction.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net6.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.SimpleHttpFunction/Function.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using Microsoft.AspNetCore.Http;
16 | using System.Threading.Tasks;
17 | //using Google.Cloud.Functions.Framework;
18 | using OpenFunction.Framework;
19 |
20 | namespace OpenFunction.Examples.SimpleHttpFunction
21 | {
22 | public class Function : IHttpFunction
23 | {
24 | ///
25 | /// Logic for your function goes here.
26 | ///
27 | /// The HTTP context, containing the request and the response.
28 | /// A task representing the asynchronous operation.
29 | public async Task HandleAsync(HttpContext context)
30 | {
31 | await context.Response.WriteAsync("Hello, Functions Framework.");
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.SimpleHttpFunction/OpenFunction.Examples.SimpleHttpFunction.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net6.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.SimpleUntypedEventFunction/Function.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using CloudNative.CloudEvents;
16 | using OpenFunction.Framework;
17 | using System;
18 | using System.Threading;
19 | using System.Threading.Tasks;
20 |
21 | namespace OpenFunction.Examples.SimpleUntypedEventFunction
22 | {
23 | public class Function : ICloudEventFunction
24 | {
25 | ///
26 | /// Logic for your function goes here. Note that a CloudEvent function just consumes an event;
27 | /// it doesn't provide any response.
28 | ///
29 | /// The CloudEvent your function should consume.
30 | /// A cancellation token that is notified if the request is aborted.
31 | /// A task representing the asynchronous operation.
32 | public Task HandleAsync(CloudEvent cloudEvent, CancellationToken cancellationToken)
33 | {
34 | Console.WriteLine("CloudEvent information:");
35 | Console.WriteLine($"ID: {cloudEvent.Id}");
36 | Console.WriteLine($"Source: {cloudEvent.Source}");
37 | Console.WriteLine($"Type: {cloudEvent.Type}");
38 | Console.WriteLine($"Subject: {cloudEvent.Subject}");
39 | Console.WriteLine($"DataSchema: {cloudEvent.DataSchema}");
40 | Console.WriteLine($"DataContentType: {cloudEvent.DataContentType}");
41 | Console.WriteLine($"Time: {cloudEvent.Time?.ToUniversalTime():yyyy-MM-dd'T'HH:mm:ss.fff'Z'}");
42 | Console.WriteLine($"SpecVersion: {cloudEvent.SpecVersion}");
43 | Console.WriteLine($"Data: {cloudEvent.Data}");
44 |
45 | // In this example, we don't need to perform any asynchronous operations, so the
46 | // method doesn't need to be declared async.
47 | return Task.CompletedTask;
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.SimpleUntypedEventFunction/OpenFunction.Examples.SimpleUntypedEventFunction.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net6.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.StorageImageAnnotator/OpenFunction.Examples.StorageImageAnnotator.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net6.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.StorageImageAnnotator/README.md:
--------------------------------------------------------------------------------
1 | # StorageImageAnnotator example function
2 |
3 | This function demonstrates an event handler for Google Cloud Storage.
4 |
5 | It should be attached to a trigger type of
6 | "google.storage.object.finalize", so it will be called whenever an
7 | object finishes uploading in a Storage bucket.
8 |
9 | If the file is not a JPEG image (content type "image/jpeg", extension
10 | ".jpg"), it's skipped.
11 |
12 | Otherwise, the function uses the [Google Cloud Vision
13 | API](https://cloud.google.com/vision) to detect faces, text,
14 | landmarks, logos and objects within the image. Once the Vision API
15 | has returned a response, the function formats the response as a text
16 | file, and uploads it as a new Google Cloud Storage object with the
17 | same name as the original one, but an extension of ".txt".
18 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.TestableDependencies/Function.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using OpenFunction.Framework;
16 | using OpenFunction.Hosting;
17 | using Microsoft.AspNetCore.Hosting;
18 | using Microsoft.AspNetCore.Http;
19 | using Microsoft.Extensions.DependencyInjection;
20 | using System.Threading.Tasks;
21 |
22 | namespace OpenFunction.Examples.TestableDependencies
23 | {
24 | // The dependency interface required by the function.
25 | public interface IDependency
26 | {
27 | public string Name { get; }
28 | }
29 |
30 | // The production dependency, configured in the Startup class.
31 | public class ProductionDependency : IDependency
32 | {
33 | public string Name => "Production dependency. Don't use me in tests!";
34 | }
35 |
36 | ///
37 | /// Adds the production dependency to the service collection.
38 | ///
39 | public class Startup : FunctionsStartup
40 | {
41 | public override void ConfigureServices(WebHostBuilderContext context, IServiceCollection services) =>
42 | services.AddSingleton();
43 | }
44 |
45 | [FunctionsStartup(typeof(Startup))]
46 | public class Function : IHttpFunction
47 | {
48 | private readonly IDependency _dependency;
49 |
50 | public Function(IDependency dependency) =>
51 | _dependency = dependency;
52 |
53 | public async Task HandleAsync(HttpContext context)
54 | {
55 | await context.Response.WriteAsync($"Dependency configured for function: {_dependency.Name}");
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.TestableDependencies/OpenFunction.Examples.TestableDependencies.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net6.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.TimeZoneConverter/ConversionResult.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using System.Text.Json.Serialization;
16 |
17 | namespace OpenFunction.Examples.TimeZoneConverter
18 | {
19 | // Note: Input and Result could be LocalDateTime values, if we use NodaTime.Serialization.SystemTextJson.
20 | // But we've already got a LocalDateTimePattern available, so it's simplest just to format with that.
21 |
22 | ///
23 | /// The result of a zone-to-zone conversion.
24 | ///
25 | public sealed class ConversionResult
26 | {
27 | ///
28 | /// The version of the data used, e.g. "TZDB: 2019b (mapping: 14742)".
29 | ///
30 | [JsonPropertyName("data_version")]
31 | public string DataVersion { get; }
32 |
33 | ///
34 | /// The local date/time used as input and interpreted in .
35 | ///
36 | [JsonPropertyName("input")]
37 | public string Input { get; }
38 |
39 | ///
40 | /// The ID of the time zone to convert from.
41 | ///
42 | [JsonPropertyName("from_zone")]
43 | public string FromZone { get; }
44 |
45 | ///
46 | /// The ID of the time zone to convert to.
47 | ///
48 | [JsonPropertyName("to_zone")]
49 | public string ToZone { get; }
50 |
51 | ///
52 | /// The result date/time in .
53 | ///
54 | [JsonPropertyName("result")]
55 | public string Result { get; }
56 |
57 | ///
58 | /// The conversion type. While most local date/time values map 1:1 with instants in time,
59 | /// daylight saving transitions and other offset changes mean that some local date/time values
60 | /// are skipped entirely or occur twice.
61 | ///
62 | [JsonPropertyName("conversion_type")]
63 | public ConversionType ConversionType { get; }
64 |
65 | public ConversionResult(string dataVersion, string input, string fromZone, string toZone, string result, ConversionType conversionType) =>
66 | (DataVersion, Input, FromZone, ToZone, Result, ConversionType) = (dataVersion, input, fromZone, toZone, result, conversionType);
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.TimeZoneConverter/ConversionType.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | namespace OpenFunction.Examples.TimeZoneConverter
16 | {
17 | ///
18 | /// The type of conversion that was performed as part of a .
19 | ///
20 | public enum ConversionType
21 | {
22 | ///
23 | /// The input was unambiguous, mapping to a single instant in time.
24 | ///
25 | Unambiguous,
26 |
27 | ///
28 | /// The input was ambiguous, usually due to occurring within a "fall back" daylight saving transition.
29 | /// The conversion to the target time zone of each value did not resolve this ambiguity.
30 | /// The result is the earlier of the results.
31 | ///
32 | AmbiguousInputAmbiguousResult,
33 |
34 | ///
35 | /// The input was ambiguous, usually due to occurring within a "fall back" daylight saving transition.
36 | /// However, after converting both possible instants to the target time zone, the results are the same.
37 | /// This is usually due to converting between time zones which observe the same daylight saving transitions.
38 | ///
39 | AmbiguousInputUnambiguousResult,
40 |
41 | ///
42 | /// The input was skipped, usually due to occurring within a "spring forward" daylight saving transition.
43 | /// The result is provided by shifting the input value by the length of the "gap" in local time (usually one hour).
44 | ///
45 | SkippedInputForwardShiftedResult
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.TimeZoneConverter/OpenFunction.Examples.TimeZoneConverter.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net6.0
5 | Enable
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.VbEventFunction/OpenFunction.Examples.VbEventFunction.vbproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | OpenFunction.Examples.VbEventFunction
5 | net6.0
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.VbHttpFunction/CloudFunction.vb:
--------------------------------------------------------------------------------
1 | ' Copyright 2020, Google LLC
2 | '
3 | ' Licensed under the Apache License, Version 2.0 (the "License");
4 | ' you may not use this file except in compliance with the License.
5 | ' You may obtain a copy of the License at
6 | '
7 | ' https://www.apache.org/licenses/LICENSE-2.0
8 | '
9 | ' Unless required by applicable law or agreed to in writing, software
10 | ' distributed under the License is distributed on an "AS IS" BASIS,
11 | ' WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | ' See the License for the specific language governing permissions and
13 | ' limitations under the License.
14 |
15 | Imports OpenFunction.Framework
16 | Imports Microsoft.AspNetCore.Http
17 |
18 | Public Class CloudFunction
19 | Implements IHttpFunction
20 |
21 | '''
22 | ''' Logic for your function goes here.
23 | '''
24 | ''' The HTTP context, containing the request and the response.
25 | ''' A task representing the asynchronous operation.
26 | Public Async Function HandleAsync(context As HttpContext) As Task Implements IHttpFunction.HandleAsync
27 | Await context.Response.WriteAsync("Hello, Functions Framework.")
28 | End Function
29 | End Class
30 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.VbHttpFunction/OpenFunction.Examples.VbHttpFunction.vbproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | OpenFunction.Examples.VbHttpFunction
5 | net6.0
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.VbUntypedEventFunction/CloudFunction.vb:
--------------------------------------------------------------------------------
1 | ' Copyright 2020, Google LLC
2 | '
3 | ' Licensed under the Apache License, Version 2.0 (the "License");
4 | ' you may not use this file except in compliance with the License.
5 | ' You may obtain a copy of the License at
6 | '
7 | ' https://www.apache.org/licenses/LICENSE-2.0
8 | '
9 | ' Unless required by applicable law or agreed to in writing, software
10 | ' distributed under the License is distributed on an "AS IS" BASIS,
11 | ' WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | ' See the License for the specific language governing permissions and
13 | ' limitations under the License.
14 |
15 | Imports CloudNative.CloudEvents
16 | Imports OpenFunction.Framework
17 | Imports System.Threading
18 |
19 | Public Class CloudFunction
20 | Implements ICloudEventFunction
21 |
22 | '''
23 | ''' Logic for your function goes here. Note that a CloudEvent function just consumes an event;
24 | ''' it doesn't provide any response.
25 | '''
26 | ''' The CloudEvent your function should consume.
27 | ''' A cancellation token that is notified if the request is aborted.
28 | ''' A task representing the asynchronous operation.
29 | Public Function HandleAsync(cloudEvent As CloudEvent, cancellationToken As CancellationToken) As Task _
30 | Implements ICloudEventFunction.HandleAsync
31 | Console.WriteLine("CloudEvent information:")
32 | Console.WriteLine($"ID: {cloudEvent.Id}")
33 | Console.WriteLine($"Source: {cloudEvent.Source}")
34 | Console.WriteLine($"Type: {cloudEvent.Type}")
35 | Console.WriteLine($"Subject: {cloudEvent.Subject}")
36 | Console.WriteLine($"DataSchema: {cloudEvent.DataSchema}")
37 | Console.WriteLine($"DataContentType: {cloudEvent.DataContentType}")
38 | Console.WriteLine($"Time: {cloudEvent.Time?.ToUniversalTime():yyyy-MM-dd'T'HH:mm:ss.fff'Z'}")
39 | Console.WriteLine($"SpecVersion: {cloudEvent.SpecVersion}")
40 | Console.WriteLine($"Data: {cloudEvent.Data}")
41 |
42 | ' In this example, we don't need to perform any asynchronous operations, so the
43 | ' function doesn't need to be declared as Async.
44 | Return Task.CompletedTask
45 | End Function
46 | End Class
47 |
--------------------------------------------------------------------------------
/examples/OpenFunction.Examples.VbUntypedEventFunction/OpenFunction.Examples.VbUntypedEventFunction.vbproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | OpenFunction.Examples.VbUntypedEventFunction
5 | net6.0
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/examples/README.md:
--------------------------------------------------------------------------------
1 | # Example source code
2 |
3 | ## Documentation
4 |
5 | For information about running these samples, along with a brief
6 | description of what each example demonstrates, see the
7 | [examples documentation page](../docs/examples.md).
8 |
9 | ## Generating
10 |
11 | The following projects are the result of creating new projects from
12 | the templates:
13 |
14 | - OpenFunction.Examples.SimpleHttpFunction
15 | - OpenFunction.Examples.SimpleEventFunction
16 | - OpenFunction.Examples.SimpleUntypedEventFunction
17 | - OpenFunction.Examples.FSharpHttpFunction
18 | - OpenFunction.Examples.FSharpEventFunction
19 | - OpenFunction.Examples.FSharpUntypedEventFunction
20 | - OpenFunction.Examples.VbHttpFunction
21 | - OpenFunction.Examples.VbEventFunction
22 | - OpenFunction.Examples.VbUntypedEventFunction
23 |
24 | In each case, after creating the project, a copyright notice is
25 | added to the code.
26 |
27 | When built as they are, the example projects refer to packages on
28 | nuget.org, which makes deployment to Google Cloud Functions simpler,
29 | and is closer to the regular developer experience.
30 |
31 | To build against the local version of the Functions Framework (in
32 | the `src` directory), set an MSBuild property of
33 | `LocalFunctionsFramework` to any non-empty value. This is often most
34 | simply done from the command line. You can then either run the
35 | example with `dotnet run`, or start Visual Studio with the
36 | environment variable set.
37 |
38 | To deploy an example using the local version of the Functions
39 | Framework, you can use `--set-build-env-vars` to set the
40 | `LOCALFUNCTIONSFRAMEWORK` environment variable (which must be in
41 | all-caps for the Buildpack to work) to true, along with
42 | `GOOGLE_BUILDABLE` to select the project. This has to be run from
43 | the root directory, in order to pick up `src` as well as `examples`.
44 | So for example, to run the `SimpleHttpFunction` example using a
45 | modified Functions Framework, you could run:
46 |
47 | ```sh
48 | gcloud functions deploy ff-test \
49 | --runtime=dotnet3 \
50 | --trigger-http \
51 | --set-build-env-vars=GOOGLE_BUILDABLE=examples/OpenFunction.Examples.SimpleHttpFunction,LOCALFUNCTIONSFRAMEWORK=true \
52 | --entry-point=OpenFunction.Examples.SimpleHttpFunction.Function
53 | ```
54 |
--------------------------------------------------------------------------------
/examples/copyright.txt:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 |
--------------------------------------------------------------------------------
/examples/generate.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | generate() {
6 | declare -r DIRECTORY=$1
7 | declare -r TEMPLATE_NAME=$2
8 | declare -r LANG=$3
9 |
10 | echo "Generating $DIRECTORY"
11 |
12 | rm -rf $DIRECTORY
13 | mkdir $DIRECTORY
14 | cd $DIRECTORY
15 |
16 | # Create the project and source files
17 | dotnet new $TEMPLATE_NAME -lang $LANG > /dev/null
18 |
19 | # Add copyright notices
20 | for source in *.??
21 | do
22 | mv $source $source.tmp
23 | cat ../copyright.txt $source.tmp > $source
24 | # Convert comment format in VB
25 | if [[ "$LANG" == "vb" ]]
26 | then
27 | sed -i -e "s/^\/\//\'/g" $source
28 | fi
29 | rm $source.tmp
30 | done
31 |
32 | cd ..
33 | }
34 |
35 | generate OpenFunction.Examples.SimpleHttpFunction of-http 'c#'
36 | generate OpenFunction.Examples.SimpleEventFunction of-event 'c#'
37 | generate OpenFunction.Examples.SimpleUntypedEventFunction of-untyped-event 'c#'
38 | generate OpenFunction.Examples.FSharpHttpFunction of-http 'f#'
39 | generate OpenFunction.Examples.FSharpEventFunction of-event 'f#'
40 | generate OpenFunction.Examples.FSharpUntypedEventFunction of-untyped-event 'f#'
41 | generate OpenFunction.Examples.VbHttpFunction of-http 'vb'
42 | generate OpenFunction.Examples.VbEventFunction of-event 'vb'
43 | generate OpenFunction.Examples.VbUntypedEventFunction of-untyped-event 'vb'
44 |
--------------------------------------------------------------------------------
/global.json:
--------------------------------------------------------------------------------
1 | {
2 | "sdk": {
3 | "version": "6.0.201",
4 | "rollForward": "minor"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/install-local-templates.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # This script builds a templates package using a date-based version, and installs it via "dotnet new -i"
4 |
5 | REAL_VERSION=$(grep '.*' src/CommonProperties.xml | sed -e 's/<\/\?Version>//g' | sed 's/ //g')
6 |
7 | VERSION=$REAL_VERSION-$(date -u +%Y%m%d%H%M)
8 |
9 | export Configuration=Release
10 | dotnet pack -p:Version=$VERSION src/OpenFunction.Templates
11 |
12 | NUGET_SOURCE=$PWD/src/OpenFunction.Templates/bin/Release/
13 |
14 | # Come out of the functions-framework-dotnet directory to use the default SDK,
15 | # which is what VS uses.
16 | (cd .. && dotnet new -i OpenFunction.Templates::$VERSION --nuget-source=$NUGET_SOURCE)
17 |
18 | # Also install within functions-framework-dotnet directory so that it will
19 | # be installed for use in examples/generate.sh
20 | dotnet new -i OpenFunction.Templates::$VERSION --nuget-source=$NUGET_SOURCE
21 |
22 | echo "Installed local templates as version $VERSION"
23 |
--------------------------------------------------------------------------------
/run-conformance-tests.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Runs the conformance tests from
4 | # https://github.com/GoogleCloudPlatform/functions-framework-conformance
5 |
6 | # That repository is included as a submodule already, under
7 | # functions-framework-conformance
8 |
9 | # This script checks that the submodule is already present, but
10 | # does not assume it has already been built.
11 | # It assumes that "go" is already in the path.
12 |
13 | set -e
14 |
15 | # Path to the root of the conformance repository. By default this
16 | # is the submodule under functions-framework-dotnet, but it can be
17 | # easily tweaked to simplify testing of local changes to the conformance
18 | # tests.
19 | CONFORMANCE_REPO=functions-framework-conformance
20 |
21 | rm -rf tmp/conformance-test-output
22 | mkdir -p tmp/conformance-test-output
23 |
24 | if [[ ! -f $CONFORMANCE_REPO/README.md ]]
25 | then
26 | echo "Conformance test repo not found. Init and update submodules."
27 | exit 1
28 | fi
29 |
30 | # Build the conformance test framework
31 | echo "Building conformance test framework"
32 | # Regenerate the events - normally a no-op, but it makes it
33 | # simpler to update the events.
34 | (cd $CONFORMANCE_REPO && go generate ./...)
35 | # Build the conformance test client itself
36 | (cd $CONFORMANCE_REPO/client && go build)
37 |
38 | # Build the conformance functions up-front
39 | echo "Building conformance functions"
40 | dotnet build -nologo -clp:NoSummary -v quiet -c Release src/OpenFunction.ConformanceTests
41 |
42 | CLIENT_BINARY=$CONFORMANCE_REPO/client/client
43 | if [[ $OSTYPE =~ ^win* || $OSTYPE =~ ^msys* || $OSTYPE =~ ^cygwin* ]]
44 | then
45 | CLIENT_BINARY=${CLIENT_BINARY}.exe
46 | fi
47 |
48 | # Run the Functions Framework once as a "warm-up", killing it after 5 seconds.
49 | # This is necessary on MacOS for non-obvious reasons;
50 | # it's possible that the conformance test runner just expects it to be
51 | # ready a little bit earlier than it is.
52 | # TODO: Remove this when we can.
53 | echo "Running Functions Framework for 5 seconds as a warm-up step."
54 | dotnet src/OpenFunction.ConformanceTests/bin/Release/net6.0/OpenFunction.ConformanceTests.dll HttpFunction &
55 | DOTNETPID=$!
56 | sleep 5
57 | kill $DOTNETPID
58 |
59 | # Note: we run the DLL directly rather than using "dotnet run" as this
60 | # responds more correctly to being killed by the conformance test runner
61 | # on Linux.
62 | DOTNET_DLL=src/OpenFunction.ConformanceTests/bin/Release/net6.0/OpenFunction.ConformanceTests.dll
63 |
64 | echo "Using conformance test runner binary: $CLIENT_BINARY"
65 | echo "Using Functions Framework test binary: $DOTNET_DLL"
66 |
67 | # Run the tests
68 | run_test() {
69 | TEST_TYPE=$1
70 | TEST_FUNCTION=$2
71 | echo "Running '$TEST_TYPE' test with function '$TEST_FUNCTION'"
72 | (cd tmp/conformance-test-output && \
73 | mkdir $TEST_TYPE && \
74 | cd $TEST_TYPE && \
75 | ../../../$CLIENT_BINARY \
76 | -buildpacks=false \
77 | -type=$TEST_TYPE \
78 | -cmd="dotnet ../../../$DOTNET_DLL $TEST_FUNCTION" \
79 | )
80 | }
81 |
82 | run_test http HttpFunction
83 | run_test cloudevent UntypedCloudEventFunction
84 |
85 | echo "Tests completed successfully"
86 |
--------------------------------------------------------------------------------
/src/CommonProperties.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 0.1.0
7 |
8 |
9 |
10 |
11 | NET6.0
12 | true
13 | ../OpenFunction.snk
14 | true
15 | true
16 | true
17 | Enable
18 | Library
19 |
20 |
21 | true
22 |
23 |
24 |
25 |
26 | True
27 | Copyright 2022 OpenFunction
28 | OpenFunction
29 |
30 | https://github.com/OpenFunction/OpenFunction/blob/main/docs/images/openfunction.png
31 | NuGetIcon.png
32 | LICENSE
33 | https://github.com/OpenFunction/functions-framework-dotnet
34 | git
35 | https://github.com/OpenFunction/functions-framework-dotnet
36 |
37 |
38 | $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb
39 | true
40 | true
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/src/Directory.Build.targets:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | $([System.IO.Path]::Combine('$(IntermediateOutputPath)','$(TargetFrameworkMoniker).AssemblyAttributes$(DefaultLanguageSourceExtension)'))
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/NuGetIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenFunction/functions-framework-dotnet/89267fa0a726ed8fbebdcdf522e9c227347e3e53/src/NuGetIcon.png
--------------------------------------------------------------------------------
/src/OpenFunction.ConformanceTests/HttpFunction.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using OpenFunction.Framework;
16 | using Microsoft.AspNetCore.Http;
17 | using System.IO;
18 | using System.Threading.Tasks;
19 |
20 | // Note: no namespace, to make it simpler to run as part of conformance testing
21 | public class HttpFunction : IHttpFunction
22 | {
23 | public async Task HandleAsync(HttpContext context)
24 | {
25 | using var output = File.OpenWrite("function_output.json");
26 | await context.Request.Body.CopyToAsync(output);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/OpenFunction.ConformanceTests/OpenFunction.ConformanceTests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Exe
8 | net6.0
9 | False
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/OpenFunction.ConformanceTests/UntypedCloudEventFunction.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using CloudNative.CloudEvents;
16 | using CloudNative.CloudEvents.SystemTextJson;
17 | using OpenFunction.Framework;
18 | using System.IO;
19 | using System.Threading;
20 | using System.Threading.Tasks;
21 |
22 | // Note: no namespace, to make it simpler to run as part of conformance testing
23 | public class UntypedCloudEventFunction : ICloudEventFunction
24 | {
25 | public async Task HandleAsync(CloudEvent cloudEvent, CancellationToken cancellationToken)
26 | {
27 | // Write out a structured JSON representation of the CloudEvent
28 | var formatter = new JsonEventFormatter();
29 | var bytes = formatter.EncodeStructuredModeMessage(cloudEvent, out var contentType);
30 | await File.WriteAllBytesAsync("function_output.json", bytes.ToArray());
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/OpenFunction.Framework.Tests/GcfEvents/GcfEventResources.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using Microsoft.AspNetCore.Http;
16 |
17 | namespace OpenFunction.Framework.Tests.GcfEvents
18 | {
19 | ///
20 | /// Helpers for resources accessed cross multiple tests.
21 | ///
22 | internal static class GcfEventResources
23 | {
24 | internal static HttpContext CreateHttpContext(string resourceName, string? path = null) =>
25 | new DefaultHttpContext
26 | {
27 | Request =
28 | {
29 | Body = TestResourceHelper.LoadResource(typeof(GcfEventResources), resourceName),
30 | ContentType = "application/json",
31 | Path = path is null ? PathString.Empty : (PathString) path
32 | }
33 | };
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/OpenFunction.Framework.Tests/GcfEvents/emulator_pubsub.json:
--------------------------------------------------------------------------------
1 | {
2 | "subscription": "projects\/emulator-project\/subscriptions\/test-subscription",
3 | "message": {
4 | "data": "VGVzdCBtZXNzYWdlIGZyb20gZW11bGF0b3I=",
5 | "messageId": "1",
6 | "attributes": {
7 | "attr1": "attr-value1"
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/src/OpenFunction.Framework.Tests/GcfEvents/firebase-analytics-no-app-id.json:
--------------------------------------------------------------------------------
1 | {
2 | "eventId": "analytics-1606955191246909-14239195990770746-5322866668684751502",
3 | "eventType": "providers/google.firebase.analytics/eventTypes/event.log",
4 | "resource": "projects/my-project-id/events/session_start",
5 | "timestamp": "2020-12-03T00:26:41.321623382Z",
6 | "data": {
7 | "eventDim": [
8 | {
9 | "date": "20201202",
10 | "name": "session_start",
11 | "params": {
12 | "engaged_session_event": { "intValue": "1" },
13 | "firebase_conversion": { "intValue": "1" },
14 | "firebase_event_origin": { "stringValue": "auto" },
15 | "firebase_screen": { "stringValue": "app_menu" },
16 | "firebase_screen_class": { "stringValue": "AppActivity" },
17 | "firebase_screen_id": { "intValue": "-2415111648950109400" },
18 | "ga_session_id": { "intValue": "1606965190" },
19 | "ga_session_number": { "intValue": "7" },
20 | "session_engaged": { "intValue": "1" }
21 | },
22 | "previousTimestampMicros": "1606951997533000",
23 | "timestampMicros": "1606955191246909"
24 | }
25 | ],
26 | "userDim": {
27 | "bundleInfo": {
28 | "bundleSequenceId": 58,
29 | "serverTimestampOffsetMicros": "875910"
30 | },
31 | "deviceInfo": {
32 | "deviceCategory": "mobile",
33 | "deviceModel": "SM-A307G",
34 | "deviceTimeZoneOffsetSeconds": -10800,
35 | "mobileBrandName": "Samsung",
36 | "mobileMarketingName": "Galaxy A30s",
37 | "mobileModelName": "SM-A307G",
38 | "platformVersion": "10",
39 | "resettableDeviceId": "aaaaaa-1111-bbbb-2222-dddddddddddd",
40 | "userDefaultLanguage": "es-us"
41 | },
42 | "firstOpenTimestampMicros": "1606882687506000",
43 | "geoInfo": {
44 | "city": "Burzaco",
45 | "continent": "005",
46 | "country": "Argentina",
47 | "region": "Buenos Aires Province"
48 | },
49 | "userId": "0123456789abcdef0123456789abcdef",
50 | "userProperties": {
51 | "completed_tutorial": {
52 | "setTimestampUsec": "1606948068187909",
53 | "value": { "stringValue": "true" }
54 | },
55 | "first_open_time": {
56 | "setTimestampUsec": "1606882688381909",
57 | "value": { "intValue": "1606885200000" }
58 | },
59 | "last_level": {
60 | "index": 10,
61 | "setTimestampUsec": "1606952210498909",
62 | "value": { "stringValue": "school" }
63 | },
64 | "user_id": {
65 | "setTimestampUsec": "1606955180040909",
66 | "value": { "stringValue": "abcdef0123456789abcdef0123456789" }
67 | }
68 | }
69 | }
70 | }
71 | }
--------------------------------------------------------------------------------
/src/OpenFunction.Framework.Tests/GcfEvents/firebase-analytics-no-event-name.json:
--------------------------------------------------------------------------------
1 | {
2 | "eventId": "analytics-1606955191246909-14239195990770746-5322866668684751502",
3 | "eventType": "providers/google.firebase.analytics/eventTypes/event.log",
4 | "resource": "projects/my-project-id/events",
5 | "timestamp": "2020-12-03T00:26:41.321623382Z",
6 | "data": {
7 | "eventDim": [
8 | {
9 | "date": "20201202",
10 | "name": "session_start",
11 | "params": {
12 | "engaged_session_event": { "intValue": "1" },
13 | "firebase_conversion": { "intValue": "1" },
14 | "firebase_event_origin": { "stringValue": "auto" },
15 | "firebase_screen": { "stringValue": "app_menu" },
16 | "firebase_screen_class": { "stringValue": "AppActivity" },
17 | "firebase_screen_id": { "intValue": "-2415111648950109400" },
18 | "ga_session_id": { "intValue": "1606965190" },
19 | "ga_session_number": { "intValue": "7" },
20 | "session_engaged": { "intValue": "1" }
21 | },
22 | "previousTimestampMicros": "1606951997533000",
23 | "timestampMicros": "1606955191246909"
24 | }
25 | ],
26 | "userDim": {
27 | "appInfo": {
28 | "appId": "com.example.exampleapp",
29 | "appInstanceId": "aaabbb11122233344455566677788899",
30 | "appPlatform": "ANDROID",
31 | "appStore": "com.android.vending",
32 | "appVersion": "1.67"
33 | },
34 | "bundleInfo": {
35 | "bundleSequenceId": 58,
36 | "serverTimestampOffsetMicros": "875910"
37 | },
38 | "deviceInfo": {
39 | "deviceCategory": "mobile",
40 | "deviceModel": "SM-A307G",
41 | "deviceTimeZoneOffsetSeconds": -10800,
42 | "mobileBrandName": "Samsung",
43 | "mobileMarketingName": "Galaxy A30s",
44 | "mobileModelName": "SM-A307G",
45 | "platformVersion": "10",
46 | "resettableDeviceId": "aaaaaa-1111-bbbb-2222-dddddddddddd",
47 | "userDefaultLanguage": "es-us"
48 | },
49 | "firstOpenTimestampMicros": "1606882687506000",
50 | "geoInfo": {
51 | "city": "Burzaco",
52 | "continent": "005",
53 | "country": "Argentina",
54 | "region": "Buenos Aires Province"
55 | },
56 | "userId": "0123456789abcdef0123456789abcdef",
57 | "userProperties": {
58 | "completed_tutorial": {
59 | "setTimestampUsec": "1606948068187909",
60 | "value": { "stringValue": "true" }
61 | },
62 | "first_open_time": {
63 | "setTimestampUsec": "1606882688381909",
64 | "value": { "intValue": "1606885200000" }
65 | },
66 | "last_level": {
67 | "index": 10,
68 | "setTimestampUsec": "1606952210498909",
69 | "value": { "stringValue": "school" }
70 | },
71 | "user_id": {
72 | "setTimestampUsec": "1606955180040909",
73 | "value": { "stringValue": "abcdef0123456789abcdef0123456789" }
74 | }
75 | }
76 | }
77 | }
78 | }
--------------------------------------------------------------------------------
/src/OpenFunction.Framework.Tests/GcfEvents/firebase-analytics.json:
--------------------------------------------------------------------------------
1 | {
2 | "eventId": "analytics-1606955191246909-14239195990770746-5322866668684751502",
3 | "eventType": "providers/google.firebase.analytics/eventTypes/event.log",
4 | "resource": "projects/my-project-id/events/session_start",
5 | "timestamp": "2020-12-03T00:26:41.321623382Z",
6 | "data": {
7 | "eventDim": [
8 | {
9 | "date": "20201202",
10 | "name": "session_start",
11 | "params": {
12 | "engaged_session_event": { "intValue": "1" },
13 | "firebase_conversion": { "intValue": "1" },
14 | "firebase_event_origin": { "stringValue": "auto" },
15 | "firebase_screen": { "stringValue": "app_menu" },
16 | "firebase_screen_class": { "stringValue": "AppActivity" },
17 | "firebase_screen_id": { "intValue": "-2415111648950109400" },
18 | "ga_session_id": { "intValue": "1606965190" },
19 | "ga_session_number": { "intValue": "7" },
20 | "session_engaged": { "intValue": "1" }
21 | },
22 | "previousTimestampMicros": "1606951997533000",
23 | "timestampMicros": "1606955191246909"
24 | }
25 | ],
26 | "userDim": {
27 | "appInfo": {
28 | "appId": "com.example.exampleapp",
29 | "appInstanceId": "aaabbb11122233344455566677788899",
30 | "appPlatform": "ANDROID",
31 | "appStore": "com.android.vending",
32 | "appVersion": "1.67"
33 | },
34 | "bundleInfo": {
35 | "bundleSequenceId": 58,
36 | "serverTimestampOffsetMicros": "875910"
37 | },
38 | "deviceInfo": {
39 | "deviceCategory": "mobile",
40 | "deviceModel": "SM-A307G",
41 | "deviceTimeZoneOffsetSeconds": -10800,
42 | "mobileBrandName": "Samsung",
43 | "mobileMarketingName": "Galaxy A30s",
44 | "mobileModelName": "SM-A307G",
45 | "platformVersion": "10",
46 | "resettableDeviceId": "aaaaaa-1111-bbbb-2222-dddddddddddd",
47 | "userDefaultLanguage": "es-us"
48 | },
49 | "firstOpenTimestampMicros": "1606882687506000",
50 | "geoInfo": {
51 | "city": "Burzaco",
52 | "continent": "005",
53 | "country": "Argentina",
54 | "region": "Buenos Aires Province"
55 | },
56 | "userId": "0123456789abcdef0123456789abcdef",
57 | "userProperties": {
58 | "completed_tutorial": {
59 | "setTimestampUsec": "1606948068187909",
60 | "value": { "stringValue": "true" }
61 | },
62 | "first_open_time": {
63 | "setTimestampUsec": "1606882688381909",
64 | "value": { "intValue": "1606885200000" }
65 | },
66 | "last_level": {
67 | "index": 10,
68 | "setTimestampUsec": "1606952210498909",
69 | "value": { "stringValue": "school" }
70 | },
71 | "user_id": {
72 | "setTimestampUsec": "1606955180040909",
73 | "value": { "stringValue": "abcdef0123456789abcdef0123456789" }
74 | }
75 | }
76 | }
77 | }
78 | }
--------------------------------------------------------------------------------
/src/OpenFunction.Framework.Tests/GcfEvents/firebase-auth1.json:
--------------------------------------------------------------------------------
1 | {
2 | "data": {
3 | "email": "test@nowhere.com",
4 | "metadata": {
5 | "createdAt": "2020-05-26T10:42:27Z",
6 | "lastSignedInAt": "2020-05-29T11:00:00Z"
7 | },
8 | "providerData": [
9 | {
10 | "email": "test@nowhere.com",
11 | "providerId": "password",
12 | "uid": "test@nowhere.com"
13 | }
14 | ],
15 | "uid": "UUpby3s4spZre6kHsgVSPetzQ8l2"
16 | },
17 | "eventId": "4423b4fa-c39b-4f79-b338-977a018e9b55",
18 | "eventType": "providers/firebase.auth/eventTypes/user.create",
19 | "notSupported": {
20 | },
21 | "resource": "projects/my-project-id",
22 | "timestamp": "2020-05-26T10:42:27.088Z"
23 | }
24 |
--------------------------------------------------------------------------------
/src/OpenFunction.Framework.Tests/GcfEvents/firebase-auth2.json:
--------------------------------------------------------------------------------
1 | {
2 | "data": {
3 | "email": "test@nowhere.com",
4 | "metadata": {
5 | "createdAt": "2020-05-26T10:42:27Z"
6 | },
7 | "providerData": [
8 | {
9 | "email": "test@nowhere.com",
10 | "providerId": "password",
11 | "uid": "test@nowhere.com"
12 | }
13 | ],
14 | "uid": "UUpby3s4spZre6kHsgVSPetzQ8l2"
15 | },
16 | "eventId": "5fd71bdc-4955-421f-9fc3-552ac3abead8",
17 | "eventType": "providers/firebase.auth/eventTypes/user.delete",
18 | "notSupported": {
19 | },
20 | "resource": "projects/my-project-id",
21 | "timestamp": "2020-05-26T10:47:14.205Z"
22 | }
23 |
--------------------------------------------------------------------------------
/src/OpenFunction.Framework.Tests/GcfEvents/firebase-db1.json:
--------------------------------------------------------------------------------
1 | {
2 | "eventType": "providers/google.firebase.database/eventTypes/ref.write",
3 | "params": {
4 | "child": "xyz"
5 | },
6 | "auth": {
7 | "admin": true
8 | },
9 | "domain":"firebaseio.com",
10 | "data": {
11 | "data": null,
12 | "delta": {
13 | "grandchild": "other"
14 | }
15 | },
16 | "resource": "projects/_/instances/my-project-id/refs/gcf-test/xyz",
17 | "timestamp": "2020-05-21T11:15:34.178Z",
18 | "eventId": "/SnHth9OSlzK1Puj85kk4tDbF90="
19 | }
20 |
--------------------------------------------------------------------------------
/src/OpenFunction.Framework.Tests/GcfEvents/firebase-db2.json:
--------------------------------------------------------------------------------
1 | {
2 | "eventType": "providers/google.firebase.database/eventTypes/ref.write",
3 | "params": {
4 | "child": "xyz"
5 | },
6 | "auth": {
7 | "admin": true
8 | },
9 | "domain":"europe-west1.firebasedatabase.app",
10 | "data": {
11 | "data": {
12 | "grandchild": "other"
13 | },
14 | "delta": {
15 | "grandchild": "other changed"
16 | }
17 | },
18 | "resource": "projects/_/instances/my-project-id/refs/gcf-test/xyz",
19 | "timestamp": "2020-05-21T11:17:09.217Z",
20 | "eventId": "xaU5mk9HaahKU/T/XlPo/1ansu8="
21 | }
22 |
--------------------------------------------------------------------------------
/src/OpenFunction.Framework.Tests/GcfEvents/firebase-db3.json:
--------------------------------------------------------------------------------
1 | {
2 | "eventType": "providers/google.firebase.database/eventTypes/ref.write",
3 | "params": {
4 | "child": "abc"
5 | },
6 | "auth": {
7 | "admin": true
8 | },
9 | "domain":"firebaseio.com",
10 | "data": {
11 | "data": null,
12 | "delta": 10
13 | },
14 | "resource": "projects/_/instances/my-project-id/refs/gcf-test/abc",
15 | "timestamp": "2020-05-21T11:17:17.809Z",
16 | "eventId": "kbAmkXNSQhmARA0JB5lcCt1Zpsg="
17 | }
18 |
--------------------------------------------------------------------------------
/src/OpenFunction.Framework.Tests/GcfEvents/firebase-db4.json:
--------------------------------------------------------------------------------
1 | {
2 | "eventType": "providers/google.firebase.database/eventTypes/ref.write",
3 | "params": {
4 | "child": "xyz"
5 | },
6 | "auth": {
7 | "admin": true
8 | },
9 | "domain":"firebaseio.com",
10 | "data": {
11 | "data": {
12 | "grandchild": "other changed"
13 | },
14 | "delta": {
15 | "deeply": {
16 | "nested": {
17 | "text": "This is deeply nested",
18 | "text2": "Second value"
19 | }
20 | }
21 | }
22 | },
23 | "resource": "projects/_/instances/my-project-id/refs/gcf-test/xyz",
24 | "timestamp": "2020-05-21T11:19:22.677Z",
25 | "eventId": "AlEKy6dsGnn48ubD7Y5QOoROj4U="
26 | }
27 |
--------------------------------------------------------------------------------
/src/OpenFunction.Framework.Tests/GcfEvents/firebase-db5.json:
--------------------------------------------------------------------------------
1 | {
2 | "eventType": "providers/google.firebase.database/eventTypes/ref.write",
3 | "params": {
4 | "child": "xyz"
5 | },
6 | "auth": {
7 | "admin": true
8 | },
9 | "domain":"firebaseio.com",
10 | "data": {
11 | "data": {
12 | "deeply": {
13 | "nested": {
14 | "text": "This is deeply nested",
15 | "text2": "Second value"
16 | }
17 | },
18 | "grandchild": "other changed"
19 | },
20 | "delta": {
21 | "deeply": {
22 | "abc": "def"
23 | }
24 | }
25 | },
26 | "resource": "projects/_/instances/my-project-id/refs/gcf-test/xyz",
27 | "timestamp": "2020-05-21T11:21:07.809Z",
28 | "eventId": "9WBpn32ssA33ympcsuq/5JJmnDM="
29 | }
--------------------------------------------------------------------------------
/src/OpenFunction.Framework.Tests/GcfEvents/firebase-db6.json:
--------------------------------------------------------------------------------
1 | {
2 | "eventType": "providers/google.firebase.database/eventTypes/ref.write",
3 | "params": {
4 | "child": "xyz"
5 | },
6 | "auth": {
7 | "admin": true
8 | },
9 | "domain":"firebaseio.com",
10 | "data": {
11 | "data": {
12 | "deeply": {
13 | "abc": "def",
14 | "nested": {
15 | "text": "This is deeply nested",
16 | "text2": "Second value"
17 | }
18 | },
19 | "grandchild": "other changed"
20 | },
21 | "delta": {
22 | "deeply": {
23 | "nested": null
24 | }
25 | }
26 | },
27 | "resource": "projects/_/instances/my-project-id/refs/gcf-test/xyz",
28 | "timestamp": "2020-05-21T11:22:36.728Z",
29 | "eventId": "mtM1+41X04VNxshbrbl7ua3wnTI="
30 | }
31 |
--------------------------------------------------------------------------------
/src/OpenFunction.Framework.Tests/GcfEvents/firebase-db7.json:
--------------------------------------------------------------------------------
1 | {
2 | "eventType": "providers/google.firebase.database/eventTypes/ref.write",
3 | "params": {
4 | "child": "xyz"
5 | },
6 | "auth": {
7 | "admin": true
8 | },
9 | "domain":"firebaseio.com",
10 | "data": {
11 | "data": {
12 | "deeply": {
13 | "abc": "def"
14 | },
15 | "grandchild": "other changed"
16 | },
17 | "delta": {
18 | "deeply": {
19 | "abc": 10
20 | }
21 | }
22 | },
23 | "resource": "projects/_/instances/my-project-id/refs/gcf-test/xyz",
24 | "timestamp": "2020-05-21T11:23:46.225Z",
25 | "eventId": "fvnCmPWm8q4PPEKFRfrjuNxbQ00="
26 | }
--------------------------------------------------------------------------------
/src/OpenFunction.Framework.Tests/GcfEvents/firebase-db8.json:
--------------------------------------------------------------------------------
1 | {
2 | "eventType": "providers/google.firebase.database/eventTypes/ref.write",
3 | "params": {
4 | },
5 | "auth": {
6 | "admin": true
7 | },
8 | "domain":"firebaseio.com",
9 | "data": {
10 | "data": {
11 | "gcf-test": {
12 | "abc": 10,
13 | "child1": "value1",
14 | "xyz": {
15 | "deeply": {
16 | "abc": 11
17 | },
18 | "grandchild": "other changed"
19 | }
20 | },
21 | "not-gcf-test": "Foo"
22 | },
23 | "delta": {
24 | "gcf-test": {
25 | "xyz": {
26 | "deeply": {
27 | "abc": 12
28 | }
29 | }
30 | }
31 | }
32 | },
33 | "resource": "projects/_/instances/my-project-id/refs/",
34 | "timestamp": "2020-05-21T11:33:01.789Z",
35 | "eventId": "MazIvLoUshV35XHwjH+2rfP7uvk="
36 | }
--------------------------------------------------------------------------------
/src/OpenFunction.Framework.Tests/GcfEvents/firebase-dbdelete1.json:
--------------------------------------------------------------------------------
1 | {
2 | "eventType": "providers/google.firebase.database/eventTypes/ref.delete",
3 | "params": {
4 | "child": "xyz"
5 | },
6 | "auth": {
7 | "admin": true
8 | },
9 | "domain":"firebaseio.com",
10 | "data": {
11 | "data": {
12 | "grandchild": "other changed"
13 | },
14 | "delta": null
15 | },
16 | "resource": "projects/_/instances/my-project-id/refs/gcf-test/xyz",
17 | "timestamp": "2020-05-21T11:53:45.337Z",
18 | "eventId": "oIcVXHEMZfhQMNs/yD4nwpuKE0s="
19 | }
--------------------------------------------------------------------------------
/src/OpenFunction.Framework.Tests/GcfEvents/firebase-dbdelete2.json:
--------------------------------------------------------------------------------
1 | {
2 | "eventType": "providers/google.firebase.database/eventTypes/ref.delete",
3 | "params": {
4 | "child": "abc"
5 | },
6 | "auth": {
7 | "admin": true
8 | },
9 | "domain":"firebaseio.com",
10 | "data": {
11 | "data": 10,
12 | "delta": null
13 | },
14 | "resource": "projects/_/instances/my-project-id/refs/gcf-test/abc",
15 | "timestamp": "2020-05-21T11:56:12.833Z",
16 | "eventId": "KVLKeFKjFP2jepddr+EPGC0ZQ20="
17 | }
18 |
--------------------------------------------------------------------------------
/src/OpenFunction.Framework.Tests/GcfEvents/firebase-remote-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "context":{
3 | "eventId":"4087a7d9-4c27-42f1-8a06-1f17b2d5fe49",
4 | "eventType":"google.firebase.remoteconfig.update",
5 | "resource":{
6 | "name":"projects/sample-project",
7 | "service":"firebaseremoteconfig.googleapis.com"
8 | },
9 | "timestamp":"2020-11-16T16:35:33.592Z"
10 | },
11 | "data":{
12 | "updateOrigin":"CONSOLE",
13 | "updateTime":"2020-11-16T16:35:33.569229Z",
14 | "updateType":"INCREMENTAL_UPDATE",
15 | "updateUser":{
16 | "email":"test@nowhere.com"
17 | },
18 | "versionNumber":"5"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/OpenFunction.Framework.Tests/GcfEvents/firestore_complex.json:
--------------------------------------------------------------------------------
1 | {
2 | "data": {
3 | "oldValue": {},
4 | "updateMask": {},
5 | "value": {
6 | "createTime": "2020-04-23T14:25:05.349632Z",
7 | "fields": {
8 | "arrayValue": {
9 | "arrayValue": {
10 | "values": [
11 | {
12 | "integerValue": "1"
13 | },
14 | {
15 | "integerValue": "2"
16 | }
17 | ]
18 | }
19 | },
20 | "booleanValue": {
21 | "booleanValue": true
22 | },
23 | "doubleValue": {
24 | "doubleValue": 5.5
25 | },
26 | "geoPointValue": {
27 | "geoPointValue": {
28 | "latitude": 51.4543,
29 | "longitude": -0.9781
30 | }
31 | },
32 | "intValue": {
33 | "integerValue": "50"
34 | },
35 | "mapValue": {
36 | "mapValue": {
37 | "fields": {
38 | "field1": {
39 | "stringValue": "x"
40 | },
41 | "field2": {
42 | "arrayValue": {
43 | "values": [
44 | {
45 | "stringValue": "x"
46 | },
47 | {
48 | "integerValue": "1"
49 | }
50 | ]
51 | }
52 | }
53 | }
54 | }
55 | },
56 | "nullValue": {
57 | "nullValue": null
58 | },
59 | "referenceValue": {
60 | "referenceValue": "projects/project-id/databases/(default)/documents/foo/bar/baz/qux"
61 | },
62 | "stringValue": {
63 | "stringValue": "text"
64 | },
65 | "timestampValue": {
66 | "timestampValue": "2020-04-23T14:23:53.241Z"
67 | }
68 | },
69 | "name": "projects/project-id/databases/(default)/documents/gcf-test/IH75dRdeYJKd4uuQiqch",
70 | "updateTime": "2020-04-23T14:25:05.349632Z"
71 | }
72 | },
73 | "eventId": "9babded5-e5f2-41af-a46a-06ba6bd84739-0",
74 | "eventType": "providers/cloud.firestore/eventTypes/document.write",
75 | "notSupported": {},
76 | "params": {
77 | "doc": "IH75dRdeYJKd4uuQiqch"
78 | },
79 | "resource": "projects/project-id/databases/(default)/documents/gcf-test/IH75dRdeYJKd4uuQiqch",
80 | "timestamp": "2020-04-23T14:25:05.349632Z"
81 | }
--------------------------------------------------------------------------------
/src/OpenFunction.Framework.Tests/GcfEvents/firestore_simple.json:
--------------------------------------------------------------------------------
1 | {
2 | "data":{
3 | "oldValue":{
4 | "createTime":"2020-04-23T09:58:53.211035Z",
5 | "fields":{
6 | "another test":{
7 | "stringValue":"asd"
8 | },
9 | "count":{
10 | "integerValue":"3"
11 | },
12 | "foo":{
13 | "stringValue":"bar"
14 | }
15 | },
16 | "name":"projects/project-id/databases/(default)/documents/gcf-test/2Vm2mI1d0wIaK2Waj5to",
17 | "updateTime":"2020-04-23T12:00:27.247187Z"
18 | },
19 | "updateMask":{
20 | "fieldPaths":[
21 | "count"
22 | ]
23 | },
24 | "value":{
25 | "createTime":"2020-04-23T09:58:53.211035Z",
26 | "fields":{
27 | "another test":{
28 | "stringValue":"asd"
29 | },
30 | "count":{
31 | "integerValue":"4"
32 | },
33 | "foo":{
34 | "stringValue":"bar"
35 | }
36 | },
37 | "name":"projects/project-id/databases/(default)/documents/gcf-test/2Vm2mI1d0wIaK2Waj5to",
38 | "updateTime":"2020-04-23T12:00:27.247187Z"
39 | }
40 | },
41 | "eventId":"7b8f1804-d38b-4b68-b37d-e2fb5d12d5a0-0",
42 | "eventType":"providers/cloud.firestore/eventTypes/document.write",
43 | "notSupported":{
44 |
45 | },
46 | "params":{
47 | "doc":"2Vm2mI1d0wIaK2Waj5to"
48 | },
49 | "resource":"projects/project-id/databases/(default)/documents/gcf-test/2Vm2mI1d0wIaK2Waj5to",
50 | "timestamp":"2020-04-23T12:00:27.247187Z"
51 | }
52 |
--------------------------------------------------------------------------------
/src/OpenFunction.Framework.Tests/GcfEvents/legacy_pubsub.json:
--------------------------------------------------------------------------------
1 | {
2 | "eventId": "1215011316659232",
3 | "timestamp": "2020-05-18T12:13:19.209Z",
4 | "eventType": "providers/cloud.pubsub/eventTypes/topic.publish",
5 | "resource": "projects/sample-project/topics/gcf-test",
6 | "data": {
7 | "@type": "type.googleapis.com/google.pubsub.v1.PubsubMessage",
8 | "attributes": {
9 | "attribute1": "value1"
10 | },
11 | "data": "VGhpcyBpcyBhIHNhbXBsZSBtZXNzYWdl"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/OpenFunction.Framework.Tests/GcfEvents/legacy_storage_change.json:
--------------------------------------------------------------------------------
1 | {
2 | "data": {
3 | "bucket": "sample-bucket",
4 | "crc32c": "AAAAAA==",
5 | "etag": "COu8mb3Dn+kCEAE=",
6 | "generation": "1588778055917163",
7 | "id": "sample-bucket/MyFile/1588778055917163",
8 | "kind": "storage#object",
9 | "md5Hash": "ZDQxZDhjZDk4ZjAwYjIwNGU5ODAwOTk4ZWNmODQyN2U=",
10 | "mediaLink": "https://www.googleapis.com/download/storage/v1/b/projectid-sample-bucket/o/MyFile?generation=1588778055917163\u0026alt=media",
11 | "metageneration": "1",
12 | "name": "MyFile",
13 | "resourceState": "not_exists",
14 | "selfLink": "https://www.googleapis.com/storage/v1/b/projectid-sample-bucket/o/MyFile",
15 | "size": "0",
16 | "storageClass": "MULTI_REGIONAL",
17 | "timeCreated": "2020-05-06T15:14:15.917Z",
18 | "timeDeleted": "2020-05-18T09:07:51.799Z",
19 | "timeStorageClassUpdated": "2020-05-06T15:14:15.917Z",
20 | "updated": "2020-05-06T15:14:15.917Z"
21 | },
22 | "eventId": "1200401551653202",
23 | "eventType": "providers/cloud.storage/eventTypes/object.change",
24 | "resource": "projects/_/buckets/sample-bucket/objects/MyFile#1588778055917163",
25 | "timestamp": "2020-05-18T09:07:51.799Z"
26 | }
27 |
--------------------------------------------------------------------------------
/src/OpenFunction.Framework.Tests/GcfEvents/pubsub_binary.json:
--------------------------------------------------------------------------------
1 | {
2 | "context": {
3 | "eventId":"1144231683168617",
4 | "timestamp":"2020-05-06T07:33:34.556Z",
5 | "eventType":"google.pubsub.topic.publish",
6 | "resource":{
7 | "service":"pubsub.googleapis.com",
8 | "name":"projects/sample-project/topics/gcf-test",
9 | "type":"type.googleapis.com/google.pubsub.v1.PubsubMessage"
10 | }
11 | },
12 | "data": {
13 | "@type": "type.googleapis.com/google.pubsub.v1.PubsubMessage",
14 | "data": "AQIDBA=="
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/OpenFunction.Framework.Tests/GcfEvents/pubsub_text.json:
--------------------------------------------------------------------------------
1 | {
2 | "context": {
3 | "eventId":"1144231683168617",
4 | "timestamp":"2020-05-06T07:33:34.556Z",
5 | "eventType":"google.pubsub.topic.publish",
6 | "resource":{
7 | "service":"pubsub.googleapis.com",
8 | "name":"projects/sample-project/topics/gcf-test",
9 | "type":"type.googleapis.com/google.pubsub.v1.PubsubMessage"
10 | }
11 | },
12 | "data": {
13 | "@type": "type.googleapis.com/google.pubsub.v1.PubsubMessage",
14 | "attributes": {
15 | "attr1":"attr1-value"
16 | },
17 | "data": "dGVzdCBtZXNzYWdlIDM="
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/OpenFunction.Framework.Tests/GcfEvents/pubsub_text_microsecond_precision.json:
--------------------------------------------------------------------------------
1 | {
2 | "context": {
3 | "eventId":"1144231683168617",
4 | "timestamp":"2020-05-06T07:33:34.556123Z",
5 | "eventType":"google.pubsub.topic.publish",
6 | "resource":{
7 | "service":"pubsub.googleapis.com",
8 | "name":"projects/sample-project/topics/gcf-test",
9 | "type":"type.googleapis.com/google.pubsub.v1.PubsubMessage"
10 | }
11 | },
12 | "data": {
13 | "@type": "type.googleapis.com/google.pubsub.v1.PubsubMessage",
14 | "attributes": {
15 | "attr1":"attr1-value"
16 | },
17 | "data": "dGVzdCBtZXNzYWdlIDM="
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/OpenFunction.Framework.Tests/GcfEvents/raw_pubsub.json:
--------------------------------------------------------------------------------
1 | {
2 | "message": {
3 | "attributes": { "attr1":"value1" },
4 | "data": "VGV4dCBtZXNzYWdl",
5 | "messageId":"4102184774039362",
6 | "message_id":"4102184774039362",
7 | "orderingKey":"orderxyz",
8 | "publishTime":"2022-02-15T11:28:32.942Z",
9 | "publish_time":"2022-02-15T11:28:32.942Z"
10 | },
11 | "subscription":"projects/sample-project/subscriptions/sample-subscription"
12 | }
--------------------------------------------------------------------------------
/src/OpenFunction.Framework.Tests/GcfEvents/storage.json:
--------------------------------------------------------------------------------
1 | {
2 | "context": {
3 | "eventId": "1147091835525187",
4 | "timestamp": "2020-04-23T07:38:57.772Z",
5 | "eventType": "google.storage.object.finalize",
6 | "resource": {
7 | "service": "storage.googleapis.com",
8 | "name": "projects/_/buckets/some-bucket/objects/folder/Test.cs",
9 | "type": "storage#object"
10 | }
11 | },
12 | "data": {
13 | "bucket": "some-bucket",
14 | "contentType": "text/plain",
15 | "crc32c": "rTVTeQ==",
16 | "etag": "CNHZkbuF/ugCEAE=",
17 | "generation": "1587627537231057",
18 | "id": "some-bucket/folder/Test.cs/1587627537231057",
19 | "kind": "storage#object",
20 | "md5Hash": "kF8MuJ5+CTJxvyhHS1xzRg==",
21 | "mediaLink": "https://www.googleapis.com/download/storage/v1/b/some-bucket/o/folder%2FTest.cs?generation=1587627537231057\u0026alt=media",
22 | "metageneration": "1",
23 | "name": "folder/Test.cs",
24 | "selfLink": "https://www.googleapis.com/storage/v1/b/some-bucket/o/folder/Test.cs",
25 | "size": "352",
26 | "storageClass": "MULTI_REGIONAL",
27 | "timeCreated": "2020-04-23T07:38:57.230Z",
28 | "timeStorageClassUpdated": "2020-04-23T07:38:57.230Z",
29 | "updated": "2020-04-23T07:38:57.230Z"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/OpenFunction.Framework.Tests/OpenFunction.Framework.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net6.0
4 | false
5 | true
6 | ../OpenFunction.snk
7 | true
8 | Enable
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/src/OpenFunction.Framework.Tests/TestResourceHelper.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using System;
16 | using System.IO;
17 |
18 | namespace OpenFunction.Framework.Tests
19 | {
20 | ///
21 | /// Helper methods to make it simpler to load embedded resources.
22 | ///
23 | internal class TestResourceHelper
24 | {
25 | public static Stream LoadResource(System.Type type, string relativeResourceName)
26 | {
27 | // In order to make diagnostics simpler, we load the whole resource into a MemoryStream
28 | // and return that.
29 | MemoryStream ret = new MemoryStream();
30 | using (var resource = type.Assembly.GetManifestResourceStream(type, relativeResourceName))
31 | {
32 | if (resource is null)
33 | {
34 | throw new ArgumentException($"Resource {relativeResourceName} not found");
35 | }
36 | resource.CopyTo(ret);
37 | }
38 | ret.Position = 0;
39 | return ret;
40 | }
41 |
42 | public static Stream LoadResource(string relativeResourceName) =>
43 | LoadResource(typeof(T), relativeResourceName);
44 |
45 | public static Stream LoadResource(object caller, string relativeResourceName) =>
46 | LoadResource(caller.GetType(), relativeResourceName);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/OpenFunction.Framework/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using System.Runtime.CompilerServices;
16 |
17 | [assembly: InternalsVisibleTo("OpenFunction.Framework.Tests,PublicKey=0024000004800000940000000602000000240000525341310004000001000100afab79952ee22215f12b4e09337e65509c943fbc22d7006bc371d581d0f0ebf0da5d8039aab2607fb68a138a5d80a71bc02b7ebf586dbe1f2493c0ab20423ababfd15ce74d2264a6b37745f3658f016abaad662182aaef634a60f1346fcc45343acab5b6781535a3134818e13fac895a6c106c0480e34bbb06cb123e5583d8d2")]
18 |
19 |
--------------------------------------------------------------------------------
/src/OpenFunction.Framework/GcfEvents/Context.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using System;
16 | using System.Text.Json.Serialization;
17 |
18 | namespace OpenFunction.Framework.GcfEvents
19 | {
20 | ///
21 | /// The context of a GCF event.
22 | ///
23 | internal sealed class Context
24 | {
25 | // Note: maps to CloudEvents ID
26 | ///
27 | /// A unique ID for the event.
28 | ///
29 | [JsonPropertyName("eventId")]
30 | public string? Id { get; set; }
31 |
32 | // Note: maps to CloudEvents time (optional)
33 | ///
34 | /// The date/time this event was created.
35 | ///
36 | [JsonPropertyName("timestamp")]
37 | public DateTimeOffset? Timestamp { get; set; }
38 |
39 | // Note: maps to CloudEvents type (possibly via additional mapping)
40 | ///
41 | /// The type of the event. For example: "google.pubsub.topic.publish".
42 | ///
43 | [JsonPropertyName("eventType")]
44 | public string? Type { get; set; }
45 |
46 | // Maps somewhat to the CloudEvents subject (optional) and subject (required)
47 | ///
48 | /// The resource associated with the event.
49 | ///
50 | [JsonPropertyName("resource")]
51 | public Resource Resource { get; set; } = new Resource();
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/OpenFunction.Framework/GcfEvents/Request.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using System;
16 | using System.Collections.Generic;
17 | using System.Text.Json.Serialization;
18 |
19 | namespace OpenFunction.Framework.GcfEvents
20 | {
21 | internal sealed class Request
22 | {
23 | [JsonPropertyName("context")]
24 | public Context Context { get; set; } = new Context();
25 |
26 | [JsonPropertyName("data")]
27 | public Dictionary Data { get; set; }
28 |
29 | [JsonPropertyName("resource")]
30 | public string? Resource { get; set; }
31 |
32 | [JsonPropertyName("timestamp")]
33 | public DateTimeOffset? Timestamp { get; set; }
34 |
35 | [JsonPropertyName("eventType")]
36 | public string? EventType { get; set; }
37 |
38 | [JsonPropertyName("eventId")]
39 | public string? EventId { get; set; }
40 |
41 | [JsonPropertyName("params")]
42 | public dynamic? Params { get; set; }
43 |
44 | [JsonPropertyName("domain")]
45 | public string? Domain { get; set; }
46 |
47 | ///
48 | /// Raw PubSub only: the subscription triggering the request.
49 | ///
50 | [JsonPropertyName("subscription")]
51 | public string? RawPubSubSubscription { get; set; }
52 |
53 | ///
54 | /// Raw PubSub only: the PubSub message.
55 | ///
56 | [JsonPropertyName("message")]
57 | public Dictionary? RawPubSubMessage { get; set; }
58 |
59 | public Request()
60 | {
61 | Context = null!;
62 | Data = null!;
63 | }
64 |
65 | ///
66 | /// Copies any top-level data into the context. Data
67 | /// already present in the context "wins".
68 | ///
69 | public void NormalizeContext()
70 | {
71 | Context ??= new Context();
72 | Context.Resource ??= new Resource();
73 | Context.Resource.Name ??= Resource;
74 | Context.Id ??= EventId;
75 | Context.Timestamp ??= Timestamp;
76 | Context.Type ??= EventType;
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/OpenFunction.Framework/GcfEvents/Resource.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using System.Text.Json.Serialization;
16 |
17 | namespace OpenFunction.Framework.GcfEvents
18 | {
19 | ///
20 | /// The resource that an event applies to.
21 | ///
22 | public sealed class Resource
23 | {
24 | ///
25 | /// The service that triggered the event.
26 | ///
27 | [JsonPropertyName("service")]
28 | public string? Service { get; set; }
29 |
30 | ///
31 | /// The name associated with the event.
32 | ///
33 | [JsonPropertyName("name")]
34 | public string? Name { get; set; }
35 |
36 | ///
37 | /// The type of the resource.
38 | ///
39 | [JsonPropertyName("type")]
40 | public string? Type { get; set; }
41 |
42 | // TODO: Do we want raw path? It looks like it's deprecated.
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/OpenFunction.Framework/ICloudEventFunction.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using CloudNative.CloudEvents;
16 | using System.Threading;
17 | using System.Threading.Tasks;
18 |
19 | namespace OpenFunction.Framework
20 | {
21 | ///
22 | /// Function accepting a CloudEvent without performing any type-specific data deserialization.
23 | ///
24 | public interface ICloudEventFunction
25 | {
26 | ///
27 | /// Asynchronously handles the specified CloudEvent.
28 | ///
29 | /// The CloudEvent extracted from the request.
30 | /// A cancellation token which indicates if the request is aborted.
31 | /// A task representing the potentially-asynchronous handling of the event.
32 | /// If the task completes, the function is deemed to be successful.
33 | Task HandleAsync(CloudEvent cloudEvent, CancellationToken cancellationToken);
34 | }
35 |
36 | ///
37 | /// Function accepting a CloudEvent expecting a particular data type, which is automatically deserialized
38 | /// before function invocation.
39 | ///
40 | /// The expected type of the CloudEvent data. This type must be decorated
41 | /// with to indicate how to deserialize the CloudEvent.
42 | public interface ICloudEventFunction where TData : class
43 | {
44 | ///
45 | /// Asynchronously handles the specified CloudEvent.
46 | ///
47 | /// The original CloudEvent extracted from the request.
48 | /// The deserialized object constructed from the data.
49 | /// A cancellation token which indicates if the request is aborted.
50 | /// A task representing the potentially-asynchronous handling of the event.
51 | /// If the task completes, the function is deemed to be successful.
52 | Task HandleAsync(CloudEvent cloudEvent, TData data, CancellationToken cancellationToken);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/OpenFunction.Framework/IHttpFunction.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using Microsoft.AspNetCore.Http;
16 | using System.Threading.Tasks;
17 |
18 | namespace OpenFunction.Framework
19 | {
20 | ///
21 | /// A simple HTTP function, which populates the .
22 | ///
23 | public interface IHttpFunction
24 | {
25 | ///
26 | /// Asynchronously handles the request in ,
27 | /// populating to indicate the result.
28 | ///
29 | /// The HTTP context containing the request and response.
30 | /// A task to indicate when the request is complete.
31 | Task HandleAsync(HttpContext context);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/OpenFunction.Framework/OpenFunction.Framework.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | OpenFunction Framework
6 | enable
7 | OpenFunction Framework
8 | Openfunction
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/OpenFunction.Framework/Preconditions.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using System;
16 |
17 | namespace OpenFunction.Framework
18 | {
19 | ///
20 | /// Simple preconditions class, internal to avoid any conflicts and compatibility issues.
21 | ///
22 | internal static class Preconditions
23 | {
24 | internal static T CheckNotNull(T value, string paramName) =>
25 | value ?? throw new ArgumentNullException(paramName);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/OpenFunction.Hosting.Tests/ApplicationConfigurationTest.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using OpenFunction.Framework;
16 | using Microsoft.AspNetCore.Hosting;
17 | using Microsoft.AspNetCore.Http;
18 | using Microsoft.AspNetCore.TestHost;
19 | using Microsoft.Extensions.DependencyInjection;
20 | using Microsoft.Extensions.Hosting;
21 | using System.Net;
22 | using System.Threading.Tasks;
23 | using Xunit;
24 |
25 | namespace OpenFunction.Hosting.Tests
26 | {
27 | ///
28 | /// Tests for the final part of configuring the "application".
29 | ///
30 | public partial class ApplicationConfigurationTest
31 | {
32 | [Theory]
33 | [InlineData("robots.txt", HttpStatusCode.NotFound, "")]
34 | [InlineData("favicon.ico", HttpStatusCode.NotFound, "")]
35 | [InlineData("foo.txt", HttpStatusCode.OK, "Test message")]
36 | public async Task PathHandling(string path, HttpStatusCode expectedStatus, string expectedText)
37 | {
38 | var builder = Host.CreateDefaultBuilder()
39 | .ConfigureWebHostDefaults(webHostBuilder => webHostBuilder
40 | .ConfigureServices(services => services.AddFunctionTarget())
41 | .Configure((context, app) => app.UseFunctionsFramework(context))
42 | .UseTestServer());
43 | using var server = await builder.StartAsync();
44 | using var client = server.GetTestServer().CreateClient();
45 | using var response = await client.GetAsync(path);
46 | Assert.Equal(expectedStatus, response.StatusCode);
47 | var content = await response.Content.ReadAsStringAsync();
48 | Assert.Equal(expectedText, content);
49 | }
50 |
51 | public class SimpleFunction : IHttpFunction
52 | {
53 | public async Task HandleAsync(HttpContext context)
54 | {
55 | await context.Response.WriteAsync("Test message");
56 | }
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/OpenFunction.Hosting.Tests/FunctionsStartupAttributeTest.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using OpenFunction.Hosting;
16 | using System;
17 | using System.Collections.Generic;
18 | using System.Text;
19 | using Xunit;
20 |
21 | [assembly: FunctionsStartup(typeof(OpenFunction.Hosting.Tests.FunctionsStartupAttributeTest.TestStartup1), Order = 4)]
22 | [assembly: FunctionsStartup(typeof(OpenFunction.Hosting.Tests.FunctionsStartupAttributeTest.TestStartup2), Order = 2)]
23 |
24 | namespace OpenFunction.Hosting.Tests
25 | {
26 | public class FunctionsStartupAttributeTest
27 | {
28 | // Startups used for attribute detection.
29 | public class TestStartup1 : FunctionsStartup { }
30 | public class TestStartup2 : FunctionsStartup { }
31 | public class TestStartup3 : FunctionsStartup { }
32 |
33 | [FunctionsStartup(typeof(TestStartup3), Order = 3)]
34 | public class TargetTypeWithAttribute
35 | {
36 | }
37 |
38 | // This is the only test that uses the assembly attribute detection.
39 | // Everything else specifies the startups explicitly.
40 | [Fact]
41 | public void GetStartupTypes_NoType()
42 | {
43 | var actualTypes = FunctionsStartupAttribute.GetStartupTypes(typeof(FunctionsStartupAttributeTest).Assembly, null);
44 | // TestStartup2 comes before TestStartup1 due to the Order properties.
45 | var expectedTypes = new[] { typeof(TestStartup2), typeof(TestStartup1) };
46 | Assert.Equal(expectedTypes, actualTypes);
47 | }
48 |
49 | [Fact]
50 | public void GetStartupTypes_WithType()
51 | {
52 | var actualTypes = FunctionsStartupAttribute.GetStartupTypes(typeof(FunctionsStartupAttributeTest).Assembly, typeof(TargetTypeWithAttribute));
53 | // Ordering is based on the Order properties
54 | var expectedTypes = new[] { typeof(TestStartup2), typeof(TestStartup3), typeof(TestStartup1) };
55 | Assert.Equal(expectedTypes, actualTypes);
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/OpenFunction.Hosting.Tests/OpenFunction.Hosting.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net6.0
4 | false
5 | true
6 | ../OpenFunction.snk
7 | true
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/OpenFunction.Hosting/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using System.Runtime.CompilerServices;
16 |
17 | [assembly: InternalsVisibleTo("OpenFunction.Hosting.Tests,PublicKey=0024000004800000940000000602000000240000525341310004000001000100afab79952ee22215f12b4e09337e65509c943fbc22d7006bc371d581d0f0ebf0da5d8039aab2607fb68a138a5d80a71bc02b7ebf586dbe1f2493c0ab20423ababfd15ce74d2264a6b37745f3658f016abaad662182aaef634a60f1346fcc45343acab5b6781535a3134818e13fac895a6c106c0480e34bbb06cb123e5583d8d2")]
18 |
--------------------------------------------------------------------------------
/src/OpenFunction.Hosting/Extensions/FunctionsFrameworkConfigurationExtensions.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 | using OpenFunction.Hosting;
15 |
16 | // Namespace by convention
17 | namespace Microsoft.Extensions.Configuration
18 | {
19 | ///
20 | /// Extensions for adding configuration for the Functions Framework.
21 | ///
22 | public static class FunctionsFrameworkConfigurationExtensions
23 | {
24 | ///
25 | /// Adds a configuration source for the Functions Framework based on environment variables (e.g. PORT and FUNCTION_TARGET).
26 | ///
27 | /// The configuration builder to add the source to.
28 | /// The original builder, for method chaining.
29 | public static IConfigurationBuilder AddFunctionsEnvironment(this IConfigurationBuilder builder) =>
30 | builder.Add(new FunctionsEnvironmentVariablesConfigurationSource());
31 |
32 | ///
33 | /// Adds a configuration source for the Functions Framework based on command line arguments.
34 | ///
35 | /// The configuration builder to add the source to.
36 | /// The command line arguments to use for configuration.
37 | /// The original builder, for method chaining.
38 | public static IConfigurationBuilder AddFunctionsCommandLine(this IConfigurationBuilder builder, string[] args) =>
39 | HostingInternals.AddCommandLineArguments(builder, args);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/OpenFunction.Hosting/Extensions/FunctionsFrameworkLoggingExtensions.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using OpenFunction.Hosting;
16 | using OpenFunction.Hosting.Logging;
17 | using Microsoft.AspNetCore.Hosting;
18 |
19 | // Namespace by convention
20 | namespace Microsoft.Extensions.Logging
21 | {
22 | ///
23 | /// Extensions for configuration logging for the Functions Framework.
24 | ///
25 | public static class FunctionsFrameworkLoggingExtensions
26 | {
27 | ///
28 | /// Adds a Functions Framework console logger, either using a "single line per log entry"
29 | /// plain text format (with separate lines for scopes and exceptions, when present) or a JSON format,
30 | /// depending on the execution environment.
31 | ///
32 | /// The logging builder to add the logger to.
33 | /// The context of the web host builder being configured.
34 | /// The original builder, for method chaining.
35 | public static ILoggingBuilder AddFunctionsConsoleLogging(this ILoggingBuilder builder, WebHostBuilderContext context)
36 | {
37 | var options = FunctionsFrameworkOptions.FromConfiguration(context.Configuration);
38 | ILoggerProvider provider = options.JsonLogging
39 | ? new FactoryLoggerProvider(category => new JsonConsoleLogger(category, System.Console.Out))
40 | : new FactoryLoggerProvider(category => new SimpleConsoleLogger(category, System.Console.Out));
41 | builder.AddProvider(provider);
42 | return builder;
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/OpenFunction.Hosting/FunctionsFrameworkOptions.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using Microsoft.AspNetCore.Hosting;
16 | using Microsoft.Extensions.Configuration;
17 | using System.Net;
18 |
19 | namespace OpenFunction.Hosting
20 | {
21 | ///
22 | /// Convenience type to allow options to be bound within .
23 | ///
24 | internal sealed class FunctionsFrameworkOptions
25 | {
26 | internal const string ConfigSection = "FunctionsFramework";
27 |
28 | public int Port { get; set; } = 8080;
29 | public string? FunctionTarget { get; set; }
30 | public string? Address { get; set; }
31 | public bool JsonLogging { get; set; }
32 |
33 | internal IPAddress GetIPAddress() =>
34 | Address switch
35 | {
36 | "Any" => IPAddress.Any,
37 | "Loopback" => IPAddress.Loopback,
38 | null => IPAddress.Loopback,
39 | string addr => IPAddress.Parse(addr)
40 | };
41 |
42 | internal static FunctionsFrameworkOptions FromConfiguration(IConfiguration configuration) =>
43 | configuration.GetSection(ConfigSection).Get() ?? new FunctionsFrameworkOptions();
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/OpenFunction.Hosting/Logging/FactoryLoggerProvider.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using Microsoft.Extensions.Logging;
16 | using System;
17 |
18 | namespace OpenFunction.Hosting.Logging
19 | {
20 | ///
21 | /// Simple logger provider that just calls a factory method each time it's asked for logger.
22 | ///
23 | internal class FactoryLoggerProvider : ILoggerProvider
24 | {
25 | private readonly Func _factory;
26 |
27 | internal FactoryLoggerProvider(Func factory) =>
28 | _factory = factory;
29 |
30 | public ILogger CreateLogger(string categoryName) => _factory(categoryName);
31 |
32 | public void Dispose()
33 | {
34 | // No-op
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/OpenFunction.Hosting/OpenFunction.Hosting.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | OpenFunction Framework Hosting
6 | The OpenFunction Framework Hosting package makes it easy to run a web server to execute functions.
7 | OpenFunction
8 | enable
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/OpenFunction.Hosting/Preconditions.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using System;
16 |
17 | namespace OpenFunction.Hosting
18 | {
19 | ///
20 | /// Simple preconditions class, internal to avoid any conflicts and compatibility issues.
21 | ///
22 | internal static class Preconditions
23 | {
24 | internal static T CheckNotNull(T value, string paramName) =>
25 | value ?? throw new ArgumentNullException(paramName);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/OpenFunction.Hosting/targets/OpenFunction.Hosting.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/OpenFunction.Templates.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Template
7 | $(VERSION)
8 | Templates for OpenFunction Framework
9 | Templates for OpenFunction Framework
10 | dotnet-new;templates;openfunction
11 |
12 | true
13 | false
14 | content
15 |
16 |
17 | $(NoWarn);NU5128
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/event-function-cs/.template.config/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenFunction/functions-framework-dotnet/89267fa0a726ed8fbebdcdf522e9c227347e3e53/src/OpenFunction.Templates/templates/event-function-cs/.template.config/icon.png
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/event-function-cs/.template.config/ide.host.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/vs-2017.3.host",
3 | "icon": "icon.png"
4 | }
5 |
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/event-function-cs/.template.config/template.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/template",
3 | "author": "OpenFunction",
4 | "classifications": [ "OpenFunction" ],
5 | "identity": "OpenFunction.Templates.CloudEventFunction.CSharp",
6 | "groupIdentity": "OpenFunction.CloudEventFunction",
7 | "name": "OpenFunction CloudEvent Function",
8 | "description": "A project template for creating an OpenFunction to respond to a specific CloudEvent type.",
9 | "shortName": "of-event",
10 | "sourceName": "MyFunction",
11 | "defaultName": "CloudEventFunction",
12 | "tags": {
13 | "language": "C#",
14 | "type": "project"
15 | },
16 | "preferNameDirectory": true
17 | }
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/event-function-cs/Function.cs:
--------------------------------------------------------------------------------
1 | using CloudNative.CloudEvents;
2 | using OpenFunction.Framework;
3 | using Google.Events.Protobuf.Cloud.Storage.V1;
4 | using System;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 |
8 | namespace MyFunction
9 | {
10 | ///
11 | /// A function that can be triggered in responses to changes in Google Cloud Storage.
12 | /// The type argument (StorageObjectData in this case) determines how the event payload is deserialized.
13 | /// The function must be deployed so that the trigger matches the expected payload type. (For example,
14 | /// deploying a function expecting a StorageObject payload will not work for a trigger that provides
15 | /// a FirestoreEvent.)
16 | ///
17 | public class Function : ICloudEventFunction
18 | {
19 | ///
20 | /// Logic for your function goes here. Note that a CloudEvent function just consumes an event;
21 | /// it doesn't provide any response.
22 | ///
23 | /// The CloudEvent your function should consume.
24 | /// The deserialized data within the CloudEvent.
25 | /// A cancellation token that is notified if the request is aborted.
26 | /// A task representing the asynchronous operation.
27 | public Task HandleAsync(CloudEvent cloudEvent, StorageObjectData data, CancellationToken cancellationToken)
28 | {
29 | Console.WriteLine("Storage object information:");
30 | Console.WriteLine($" Name: {data.Name}");
31 | Console.WriteLine($" Bucket: {data.Bucket}");
32 | Console.WriteLine($" Size: {data.Size}");
33 | Console.WriteLine($" Content type: {data.ContentType}");
34 | Console.WriteLine("CloudEvent information:");
35 | Console.WriteLine($" ID: {cloudEvent.Id}");
36 | Console.WriteLine($" Source: {cloudEvent.Source}");
37 | Console.WriteLine($" Type: {cloudEvent.Type}");
38 | Console.WriteLine($" Subject: {cloudEvent.Subject}");
39 | Console.WriteLine($" DataSchema: {cloudEvent.DataSchema}");
40 | Console.WriteLine($" DataContentType: {cloudEvent.DataContentType}");
41 | Console.WriteLine($" Time: {cloudEvent.Time?.ToUniversalTime():yyyy-MM-dd'T'HH:mm:ss.fff'Z'}");
42 | Console.WriteLine($" SpecVersion: {cloudEvent.SpecVersion}");
43 |
44 | // In this example, we don't need to perform any asynchronous operations, so the
45 | // method doesn't need to be declared async.
46 | return Task.CompletedTask;
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/event-function-cs/MyFunction.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net6.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/event-function-fs/.template.config/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenFunction/functions-framework-dotnet/89267fa0a726ed8fbebdcdf522e9c227347e3e53/src/OpenFunction.Templates/templates/event-function-fs/.template.config/icon.png
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/event-function-fs/.template.config/ide.host.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/vs-2017.3.host",
3 | "icon": "icon.png"
4 | }
5 |
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/event-function-fs/.template.config/template.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/template",
3 | "author": "OpenFunction",
4 | "classifications": [ "OpenFunction" ],
5 | "identity": "OpenFunction.Templates.CloudEventFunction.FSharp",
6 | "groupIdentity": "OpenFunction.CloudEventFunction",
7 | "name": "OpenFunction CloudEvent Function",
8 | "description": "A project template for creating an OpenFunction to respond to a specific CloudEvent type.",
9 | "shortName": "of-event",
10 | "sourceName": "MyFunction",
11 | "defaultName": "CloudEventFunction",
12 | "tags": {
13 | "language": "F#",
14 | "type": "project"
15 | },
16 | "preferNameDirectory": true
17 | }
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/event-function-fs/Function.fs:
--------------------------------------------------------------------------------
1 | namespace MyFunction
2 |
3 | open CloudNative.CloudEvents
4 | open OpenFunction.Framework
5 | open Google.Events.Protobuf.Cloud.Storage.V1
6 | open System.Threading.Tasks
7 |
8 | ///
9 | /// A function that can be triggered in responses to changes in Google Cloud Storage.
10 | /// The type argument (StorageObjectData in this case) determines how the event payload is deserialized.
11 | /// The function must be deployed so that the trigger matches the expected payload type. (For example,
12 | /// deploying a function expecting a StorageObject payload will not work for a trigger that provides
13 | /// a FirestoreEvent.)
14 | ///
15 | type Function() =
16 | interface ICloudEventFunction with
17 | ///
18 | /// Logic for your function goes here. Note that a CloudEvent function just consumes an event;
19 | /// it doesn't provide any response.
20 | ///
21 | /// The CloudEvent your function should consume.
22 | /// The deserialized data within the CloudEvent.
23 | /// A cancellation token that is notified if the request is aborted.
24 | /// A task representing the asynchronous operation.
25 | member this.HandleAsync(cloudEvent, data, cancellationToken) =
26 | printfn "Storage object information:"
27 | printfn " Name: %s" data.Name
28 | printfn " Bucket: %s" data.Bucket
29 | printfn " Size: %i" data.Size
30 | printfn " Content type: %s" data.ContentType
31 | printfn "CloudEvent information:"
32 | printfn " ID: %s" cloudEvent.Id
33 | printfn " Source: %O" cloudEvent.Source
34 | printfn " Type: %s" cloudEvent.Type
35 | printfn " Subject: %s" cloudEvent.Subject
36 | printfn " DataSchema: %O" cloudEvent.DataSchema
37 | printfn " DataContentType: %O" cloudEvent.DataContentType
38 | printfn " Time: %s" (match Option.ofNullable cloudEvent.Time with
39 | | Some time -> time.ToUniversalTime().ToString "yyyy-MM-dd'T'HH:mm:ss.fff'Z'"
40 | | None -> "")
41 | printfn " SpecVersion: %O" cloudEvent.SpecVersion
42 |
43 | // In this example, we don't need to perform any asynchronous operations, so we
44 | // just return an completed Task to conform to the interface.
45 | Task.CompletedTask
46 |
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/event-function-fs/MyFunction.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net6.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/event-function-vb/.template.config/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenFunction/functions-framework-dotnet/89267fa0a726ed8fbebdcdf522e9c227347e3e53/src/OpenFunction.Templates/templates/event-function-vb/.template.config/icon.png
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/event-function-vb/.template.config/ide.host.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/vs-2017.3.host",
3 | "icon": "icon.png"
4 | }
5 |
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/event-function-vb/.template.config/template.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/template",
3 | "author": "OpenFunction",
4 | "classifications": [ "OpenFunction" ],
5 | "identity": "OpenFunction.Templates.CloudEventFunction.VisualBasic",
6 | "groupIdentity": "OpenFunction.CloudEventFunction",
7 | "name": "OpenFunction CloudEvent Function",
8 | "description": "A project template for creating an OpenFunction to respond to a specific CloudEvent type.",
9 | "shortName": "of-event",
10 | "sourceName": "MyFunction",
11 | "defaultName": "CloudEventFunction",
12 | "tags": {
13 | "language": "VB",
14 | "type": "project"
15 | },
16 | "preferNameDirectory": true
17 | }
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/event-function-vb/CloudFunction.vb:
--------------------------------------------------------------------------------
1 | Imports CloudNative.CloudEvents
2 | Imports OpenFunction.Framework
3 | Imports Google.Events.Protobuf.Cloud.Storage.V1
4 | Imports System.Threading
5 |
6 | '''
7 | ''' A function that can be triggered in responses to changes in Google Cloud Storage.
8 | ''' The type argument (StorageObjectData in this case) determines how the event payload is deserialized.
9 | ''' The function must be deployed so that the trigger matches the expected payload type. (For example,
10 | ''' deploying a function expecting a StorageObject payload will not work for a trigger that provides
11 | ''' a FirestoreEvent.)
12 | '''
13 | Public Class CloudFunction
14 | Implements ICloudEventFunction(Of StorageObjectData)
15 |
16 | '''
17 | ''' Logic for your function goes here. Note that a CloudEvent function just consumes an event;
18 | ''' it doesn't provide any response.
19 | '''
20 | ''' The CloudEvent your function should consume.
21 | ''' The deserialized data within the CloudEvent.
22 | ''' A cancellation token that is notified if the request is aborted.
23 | ''' A task representing the asynchronous operation.
24 | Public Function HandleAsync(cloudEvent As CloudEvent, data As StorageObjectData, cancellationToken As CancellationToken) As Task _
25 | Implements ICloudEventFunction(Of StorageObjectData).HandleAsync
26 | Console.WriteLine("Storage object information:")
27 | Console.WriteLine($" Name: {data.Name}")
28 | Console.WriteLine($" Bucket: {data.Bucket}")
29 | Console.WriteLine($" Size: {data.Size}")
30 | Console.WriteLine($" Content type: {data.ContentType}")
31 | Console.WriteLine("CloudEvent information:")
32 | Console.WriteLine($" ID: {cloudEvent.Id}")
33 | Console.WriteLine($" Source: {cloudEvent.Source}")
34 | Console.WriteLine($" Type: {cloudEvent.Type}")
35 | Console.WriteLine($" Subject: {cloudEvent.Subject}")
36 | Console.WriteLine($" DataSchema: {cloudEvent.DataSchema}")
37 | Console.WriteLine($" DataContentType: {cloudEvent.DataContentType}")
38 | Console.WriteLine($" Time: {cloudEvent.Time?.ToUniversalTime():yyyy-MM-dd'T'HH:mm:ss.fff'Z'}")
39 | Console.WriteLine($" SpecVersion: {cloudEvent.SpecVersion}")
40 | ' In this example, we don't need to perform any asynchronous operations, so the
41 | ' function doesn't need to be declared as Async.
42 | Return Task.CompletedTask
43 | End Function
44 | End Class
45 |
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/event-function-vb/MyFunction.vbproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | MyFunction
5 | net6.0
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/http-function-cs/.template.config/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenFunction/functions-framework-dotnet/89267fa0a726ed8fbebdcdf522e9c227347e3e53/src/OpenFunction.Templates/templates/http-function-cs/.template.config/icon.png
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/http-function-cs/.template.config/ide.host.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/vs-2017.3.host",
3 | "icon": "icon.png"
4 | }
5 |
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/http-function-cs/.template.config/template.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/template",
3 | "author": "OpenFunction",
4 | "classifications": [ "OpenFunction" ],
5 | "identity": "OpenFunction.Templates.HttpFunction.CSharp",
6 | "groupIdentity": "OpenFunction.HttpFunction",
7 | "name": "OpenFunction HttpFunction",
8 | "description": "A project template for creating an OpenFunction to respond to HTTP requests.",
9 | "shortName": "of-http",
10 | "sourceName": "MyFunction",
11 | "defaultName": "CloudHttpFunction",
12 | "tags": {
13 | "language": "C#",
14 | "type": "project"
15 | },
16 | "preferNameDirectory": true
17 | }
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/http-function-cs/Function.cs:
--------------------------------------------------------------------------------
1 | using OpenFunction.Framework;
2 | using Microsoft.AspNetCore.Http;
3 | using System.Threading.Tasks;
4 |
5 | namespace MyFunction
6 | {
7 | public class Function : IHttpFunction
8 | {
9 | ///
10 | /// Logic for your function goes here.
11 | ///
12 | /// The HTTP context, containing the request and the response.
13 | /// A task representing the asynchronous operation.
14 | public async Task HandleAsync(HttpContext context)
15 | {
16 | await context.Response.WriteAsync("Hello, Functions Framework.");
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/http-function-cs/MyFunction.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net6.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/http-function-fs/.template.config/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenFunction/functions-framework-dotnet/89267fa0a726ed8fbebdcdf522e9c227347e3e53/src/OpenFunction.Templates/templates/http-function-fs/.template.config/icon.png
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/http-function-fs/.template.config/ide.host.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/vs-2017.3.host",
3 | "icon": "icon.png"
4 | }
5 |
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/http-function-fs/.template.config/template.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/template",
3 | "author": "OpenFunction",
4 | "classifications": [ "OpenFunction" ],
5 | "identity": "OpenFunction.Templates.HttpFunction.FSharp",
6 | "groupIdentity": "OpenFunction.HttpFunction",
7 | "name": "Google Cloud Functions HttpFunction",
8 | "description": "A project template for creating an OpenFunction to respond to HTTP requests.",
9 | "shortName": "of-http",
10 | "sourceName": "MyFunction",
11 | "defaultName": "CloudHttpFunction",
12 | "tags": {
13 | "language": "F#",
14 | "type": "project"
15 | },
16 | "preferNameDirectory": true
17 | }
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/http-function-fs/Function.fs:
--------------------------------------------------------------------------------
1 | namespace MyFunction
2 |
3 | open OpenFunction.Framework
4 | open Microsoft.AspNetCore.Http
5 |
6 | type Function() =
7 | interface IHttpFunction with
8 | ///
9 | /// Logic for your function goes here.
10 | ///
11 | /// The HTTP context, containing the request and the response.
12 | /// A task representing the asynchronous operation.
13 | member this.HandleAsync context =
14 | async {
15 | do! context.Response.WriteAsync "Hello, Functions Framework." |> Async.AwaitTask
16 | } |> Async.StartAsTask :> _
17 |
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/http-function-fs/MyFunction.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net6.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/http-function-vb/.template.config/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenFunction/functions-framework-dotnet/89267fa0a726ed8fbebdcdf522e9c227347e3e53/src/OpenFunction.Templates/templates/http-function-vb/.template.config/icon.png
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/http-function-vb/.template.config/ide.host.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/vs-2017.3.host",
3 | "icon": "icon.png"
4 | }
5 |
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/http-function-vb/.template.config/template.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/template",
3 | "author": "OpenFunction",
4 | "classifications": [ "OpenFunction" ],
5 | "identity": "OpenFunction.Templates.HttpFunction.VisualBasic",
6 | "groupIdentity": "OpenFunction.HttpFunction",
7 | "name": "OpenFunction HttpFunction",
8 | "description": "A project template for creating an OpenFunction to respond to HTTP requests.",
9 | "shortName": "of-http",
10 | "sourceName": "MyFunction",
11 | "defaultName": "CloudHttpFunction",
12 | "tags": {
13 | "language": "VB",
14 | "type": "project"
15 | },
16 | "preferNameDirectory": true
17 | }
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/http-function-vb/CloudFunction.vb:
--------------------------------------------------------------------------------
1 | Imports OpenFunction.Framework
2 | Imports Microsoft.AspNetCore.Http
3 |
4 | Public Class CloudFunction
5 | Implements IHttpFunction
6 |
7 | '''
8 | ''' Logic for your function goes here.
9 | '''
10 | ''' The HTTP context, containing the request and the response.
11 | ''' A task representing the asynchronous operation.
12 | Public Async Function HandleAsync(context As HttpContext) As Task Implements IHttpFunction.HandleAsync
13 | Await context.Response.WriteAsync("Hello, Functions Framework.")
14 | End Function
15 | End Class
16 |
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/http-function-vb/MyFunction.vbproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | MyFunction
5 | net6.0
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/untyped-event-function-cs/.template.config/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenFunction/functions-framework-dotnet/89267fa0a726ed8fbebdcdf522e9c227347e3e53/src/OpenFunction.Templates/templates/untyped-event-function-cs/.template.config/icon.png
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/untyped-event-function-cs/.template.config/ide.host.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/vs-2017.3.host",
3 | "icon": "icon.png"
4 | }
5 |
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/untyped-event-function-cs/.template.config/template.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/template",
3 | "author": "OpenFunction",
4 | "classifications": [ "OpenFunction" ],
5 | "identity": "OpenFunction.Templates.UntypedCloudEventFunction.CSharp",
6 | "groupIdentity": "OpenFunction.UntypedCloudEventFunction",
7 | "name": "OpenFunction CloudEvent Function (Untyped)",
8 | "description": "A project template for creating an OpenFunction to respond to any CloudEvent.",
9 | "shortName": "of-untyped-event",
10 | "sourceName": "MyFunction",
11 | "defaultName": "CloudEventFunction",
12 | "tags": {
13 | "language": "C#",
14 | "type": "project"
15 | },
16 | "preferNameDirectory": true
17 | }
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/untyped-event-function-cs/Function.cs:
--------------------------------------------------------------------------------
1 | using CloudNative.CloudEvents;
2 | using OpenFunction.Framework;
3 | using System;
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 |
7 | namespace MyFunction
8 | {
9 | public class Function : ICloudEventFunction
10 | {
11 | ///
12 | /// Logic for your function goes here. Note that a CloudEvent function just consumes an event;
13 | /// it doesn't provide any response.
14 | ///
15 | /// The CloudEvent your function should consume.
16 | /// A cancellation token that is notified if the request is aborted.
17 | /// A task representing the asynchronous operation.
18 | public Task HandleAsync(CloudEvent cloudEvent, CancellationToken cancellationToken)
19 | {
20 | Console.WriteLine("CloudEvent information:");
21 | Console.WriteLine($"ID: {cloudEvent.Id}");
22 | Console.WriteLine($"Source: {cloudEvent.Source}");
23 | Console.WriteLine($"Type: {cloudEvent.Type}");
24 | Console.WriteLine($"Subject: {cloudEvent.Subject}");
25 | Console.WriteLine($"DataSchema: {cloudEvent.DataSchema}");
26 | Console.WriteLine($"DataContentType: {cloudEvent.DataContentType}");
27 | Console.WriteLine($"Time: {cloudEvent.Time?.ToUniversalTime():yyyy-MM-dd'T'HH:mm:ss.fff'Z'}");
28 | Console.WriteLine($"SpecVersion: {cloudEvent.SpecVersion}");
29 | Console.WriteLine($"Data: {cloudEvent.Data}");
30 |
31 | // In this example, we don't need to perform any asynchronous operations, so the
32 | // method doesn't need to be declared async.
33 | return Task.CompletedTask;
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/untyped-event-function-cs/MyFunction.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net6.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/untyped-event-function-fs/.template.config/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenFunction/functions-framework-dotnet/89267fa0a726ed8fbebdcdf522e9c227347e3e53/src/OpenFunction.Templates/templates/untyped-event-function-fs/.template.config/icon.png
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/untyped-event-function-fs/.template.config/ide.host.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/vs-2017.3.host",
3 | "icon": "icon.png"
4 | }
5 |
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/untyped-event-function-fs/.template.config/template.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/template",
3 | "author": "OpenFunction",
4 | "classifications": [ "OpenFunction" ],
5 | "identity": "OpenFunction.Templates.UntypedCloudEventFunction.FSharp",
6 | "groupIdentity": "OpenFunction.UntypedCloudEventFunction",
7 | "name": "OpenFunction CloudEvent Function (Untyped)",
8 | "description": "A project template for creating an OpenFunction to respond to any CloudEvent.",
9 | "shortName": "of-untyped-event",
10 | "sourceName": "MyFunction",
11 | "defaultName": "CloudEventFunction",
12 | "tags": {
13 | "language": "F#",
14 | "type": "project"
15 | },
16 | "preferNameDirectory": true
17 | }
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/untyped-event-function-fs/Function.fs:
--------------------------------------------------------------------------------
1 | namespace MyFunction
2 |
3 | open OpenFunction.Framework
4 | open System.Threading.Tasks
5 |
6 | type Function() =
7 | interface ICloudEventFunction with
8 | ///
9 | /// Logic for your function goes here. Note that a CloudEvent function just consumes an event;
10 | /// it doesn't provide any response.
11 | ///
12 | /// The CloudEvent your function should consume.
13 | /// A cancellation token that is notified if the request is aborted.
14 | /// A task representing the asynchronous operation.
15 | member this.HandleAsync(cloudEvent, cancellationToken) =
16 | printfn "CloudEvent information:"
17 | printfn "ID: %s" cloudEvent.Id
18 | printfn "Source: %A" cloudEvent.Source
19 | printfn "Type: %s" cloudEvent.Type
20 | printfn "Subject: %s" cloudEvent.Subject
21 | printfn "DataSchema: %A" cloudEvent.DataSchema
22 | printfn "DataContentType: %O" cloudEvent.DataContentType
23 | printfn "Time: %s" (match Option.ofNullable cloudEvent.Time with
24 | | Some time -> time.ToUniversalTime().ToString "yyyy-MM-dd'T'HH:mm:ss.fff'Z'"
25 | | None -> "")
26 | printfn "SpecVersion: %O" cloudEvent.SpecVersion
27 | printfn "Data: %A" cloudEvent.Data
28 |
29 | // In this example, we don't need to perform any asynchronous operations, so we
30 | // just return a completed Task to conform to the interface.
31 | Task.CompletedTask
32 |
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/untyped-event-function-fs/MyFunction.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net6.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/untyped-event-function-vb/.template.config/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenFunction/functions-framework-dotnet/89267fa0a726ed8fbebdcdf522e9c227347e3e53/src/OpenFunction.Templates/templates/untyped-event-function-vb/.template.config/icon.png
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/untyped-event-function-vb/.template.config/ide.host.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/vs-2017.3.host",
3 | "icon": "icon.png"
4 | }
5 |
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/untyped-event-function-vb/.template.config/template.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/template",
3 | "author": "OpenFunction",
4 | "classifications": [ "OpenFunction" ],
5 | "identity": "OpenFunction.Templates.UntypedCloudEventFunction.VisualBasic",
6 | "groupIdentity": "OpenFunction.UntypedCloudEventFunction",
7 | "name": "OpenFunction CloudEvent Function (Untyped)",
8 | "description": "A project template for creating an OpenFunction to respond to any CloudEvent.",
9 | "shortName": "of-untyped-event",
10 | "sourceName": "MyFunction",
11 | "defaultName": "CloudEventFunction",
12 | "tags": {
13 | "language": "VB",
14 | "type": "project"
15 | },
16 | "preferNameDirectory": true
17 | }
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/untyped-event-function-vb/CloudFunction.vb:
--------------------------------------------------------------------------------
1 | Imports CloudNative.CloudEvents
2 | Imports OpenFunction.Framework
3 | Imports System.Threading
4 |
5 | Public Class CloudFunction
6 | Implements ICloudEventFunction
7 |
8 | '''
9 | ''' Logic for your function goes here. Note that a CloudEvent function just consumes an event;
10 | ''' it doesn't provide any response.
11 | '''
12 | ''' The CloudEvent your function should consume.
13 | ''' A cancellation token that is notified if the request is aborted.
14 | ''' A task representing the asynchronous operation.
15 | Public Function HandleAsync(cloudEvent As CloudEvent, cancellationToken As CancellationToken) As Task _
16 | Implements ICloudEventFunction.HandleAsync
17 | Console.WriteLine("CloudEvent information:")
18 | Console.WriteLine($"ID: {cloudEvent.Id}")
19 | Console.WriteLine($"Source: {cloudEvent.Source}")
20 | Console.WriteLine($"Type: {cloudEvent.Type}")
21 | Console.WriteLine($"Subject: {cloudEvent.Subject}")
22 | Console.WriteLine($"DataSchema: {cloudEvent.DataSchema}")
23 | Console.WriteLine($"DataContentType: {cloudEvent.DataContentType}")
24 | Console.WriteLine($"Time: {cloudEvent.Time?.ToUniversalTime():yyyy-MM-dd'T'HH:mm:ss.fff'Z'}")
25 | Console.WriteLine($"SpecVersion: {cloudEvent.SpecVersion}")
26 | Console.WriteLine($"Data: {cloudEvent.Data}")
27 |
28 | ' In this example, we don't need to perform any asynchronous operations, so the
29 | ' function doesn't need to be declared as Async.
30 | Return Task.CompletedTask
31 | End Function
32 | End Class
33 |
--------------------------------------------------------------------------------
/src/OpenFunction.Templates/templates/untyped-event-function-vb/MyFunction.vbproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | MyFunction
5 | net6.0
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/OpenFunction.Testing.Tests/FunctionTestBaseTest.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using CloudNative.CloudEvents;
16 | using OpenFunction.Framework;
17 | using Google.Events;
18 | using Google.Events.Protobuf.Cloud.Storage.V1;
19 | using Microsoft.Extensions.Logging;
20 | using System;
21 | using System.Threading;
22 | using System.Threading.Tasks;
23 | using Xunit;
24 |
25 | namespace OpenFunction.Testing.Tests
26 | {
27 | public class FunctionTestBaseTest
28 | {
29 | [Fact]
30 | public async Task ExecuteCloudEventAsync_CloudEvent()
31 | {
32 | var cloudEvent = new CloudEvent
33 | {
34 | Id = "event-id",
35 | Type = StorageObjectData.FinalizedCloudEventType,
36 | Source = new Uri("//storage", UriKind.RelativeOrAbsolute),
37 | Data = new StorageObjectData { Name = "test1" }
38 | };
39 |
40 | using var test = new Test();
41 | await test.ExecuteCloudEventRequestAsync(cloudEvent);
42 | var log = Assert.Single(test.GetFunctionLogEntries());
43 | Assert.Equal("Name: test1", log.Message);
44 | }
45 |
46 | [Fact]
47 | public async Task ExecuteCloudEventAsync_Components()
48 | {
49 | using var test = new Test();
50 | await test.ExecuteCloudEventRequestAsync(
51 | StorageObjectData.FinalizedCloudEventType,
52 | new StorageObjectData { Name = "test2" });
53 | var log = Assert.Single(test.GetFunctionLogEntries());
54 | Assert.Equal("Name: test2", log.Message);
55 | }
56 |
57 | // FunctionTestBase is abstract; this class just allows us to test multiple functions
58 | // easily in one class.
59 | private class Test : FunctionTestBase
60 | {
61 | }
62 |
63 | private class StorageObjectFunction : ICloudEventFunction
64 | {
65 | private ILogger _logger;
66 |
67 | public StorageObjectFunction(ILogger logger) =>
68 | _logger = logger;
69 |
70 | public Task HandleAsync(CloudEvent cloudEvent, StorageObjectData data, CancellationToken cancellationToken)
71 | {
72 | _logger.LogInformation($"Name: {data.Name}");
73 | return Task.CompletedTask;
74 | }
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/OpenFunction.Testing.Tests/OpenFunction.Testing.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net6.0
4 | false
5 | true
6 | ../OpenFunction.snk
7 | true
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/src/OpenFunction.Testing/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using System.Runtime.CompilerServices;
16 |
17 | [assembly: InternalsVisibleTo("OpenFunction.Hosting.Tests,PublicKey=0024000004800000940000000602000000240000525341310004000001000100afab79952ee22215f12b4e09337e65509c943fbc22d7006bc371d581d0f0ebf0da5d8039aab2607fb68a138a5d80a71bc02b7ebf586dbe1f2493c0ab20423ababfd15ce74d2264a6b37745f3658f016abaad662182aaef634a60f1346fcc45343acab5b6781535a3134818e13fac895a6c106c0480e34bbb06cb123e5583d8d2")]
18 |
--------------------------------------------------------------------------------
/src/OpenFunction.Testing/MemoryLoggerProvider.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using Microsoft.Extensions.Logging;
16 | using System.Collections.Concurrent;
17 | using System.Collections.Generic;
18 |
19 | namespace OpenFunction.Testing
20 | {
21 | ///
22 | /// A logger provider that creates instances of .
23 | ///
24 | internal sealed class MemoryLoggerProvider : ILoggerProvider
25 | {
26 | private readonly ConcurrentDictionary _loggersByCategory =
27 | new ConcurrentDictionary();
28 |
29 | public ILogger CreateLogger(string categoryName) =>
30 | _loggersByCategory.GetOrAdd(categoryName, name => new MemoryLogger(name));
31 |
32 | internal void Clear() => _loggersByCategory.Clear();
33 |
34 | ///
35 | /// Returns a list of log entries for the given category name. If no logs have been
36 | /// written for the given category, an empty list is returned.
37 | ///
38 | /// The category name for which to get log entries.
39 | /// A list of log entries for the given category name.
40 | internal List GetLogEntries(string categoryName) =>
41 | _loggersByCategory.TryGetValue(categoryName, out var logger)
42 | ? logger.ListLogEntries() : new List();
43 |
44 | public void Dispose()
45 | {
46 | // No-op
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/OpenFunction.Testing/OpenFunction.Testing.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | OpenFunction Framework Testing
6 | Simplifies tests for OpenFunction Framework functions.
7 | openfunction
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/OpenFunction.Testing/Preconditions.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2020, Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using System;
16 |
17 | namespace OpenFunction.Testing
18 | {
19 | ///
20 | /// Simple preconditions class, internal to avoid any conflicts and compatibility issues.
21 | ///
22 | internal static class Preconditions
23 | {
24 | internal static T CheckNotNull(T value, string paramName) =>
25 | value ?? throw new ArgumentNullException(paramName);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/OpenFunction.snk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenFunction/functions-framework-dotnet/89267fa0a726ed8fbebdcdf522e9c227347e3e53/src/OpenFunction.snk
--------------------------------------------------------------------------------
/update-googleevents-references.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | if [[ -z "$1" ]]
6 | then
7 | echo 'Please specify the Google.Events.* version to update to.'
8 | exit 1
9 | fi
10 |
11 | VERSION=$1
12 |
13 | for proj in $(find src -name '*proj') $(find examples -name '*proj')
14 | do
15 | sed -i -e "s/Include=\"Google\.Events\" Version=\".*\"/Include=\"Google.Events\" Version=\"$VERSION\"/g" $proj
16 | sed -i -e "s/Include=\"Google\.Events\.SystemTextJson\" Version=\".*\"/Include=\"Google.Events.SystemTextJson\" Version=\"$VERSION\"/g" $proj
17 | sed -i -e "s/Include=\"Google\.Events\.Protobuf\" Version=\".*\"/Include=\"Google.Events.Protobuf\" Version=\"$VERSION\"/g" $proj
18 | done
19 |
--------------------------------------------------------------------------------
/update-project-references.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | VERSION=$(grep '.*' src/CommonProperties.xml | sed -e 's/<\/\?Version>//g' | sed 's/ //g')
6 |
7 | for proj in $(find src/OpenFunction.Templates/templates -name '*proj')
8 | do
9 | sed -i -e "s/Include=\"OpenFunction.Hosting\" Version=\".*\"/Include=\"OpenFunction.Hosting\" Version=\"$VERSION\"/g" $proj
10 | done
11 |
12 | for proj in $(find examples -name '*proj')
13 | do
14 | sed -i -e "s/Include=\"OpenFunction.Hosting\" Version=\".*\"/Include=\"OpenFunction.Hosting\" Version=\"$VERSION\"/g" $proj
15 | sed -i -e "s/Include=\"OpenFunction.Testing\" Version=\".*\"/Include=\"OpenFunction.Testing\" Version=\"$VERSION\"/g" $proj
16 | done
17 |
--------------------------------------------------------------------------------