├── .github ├── CODEOWNERS ├── dependabot.template.yml ├── dependabot.yml ├── snippet-bot.yml ├── tests.sh └── workflows │ ├── generate_dependabot.yml │ ├── gradle-wrapper-validation.yml │ ├── lint.yml │ └── tests.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SECURITY.md ├── appengine ├── README.md ├── ktor │ ├── .editorconfig │ ├── README.md │ ├── build.gradle.kts │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle.kts │ └── src │ │ └── main │ │ ├── kotlin │ │ └── HelloApplication.kt │ │ ├── resources │ │ ├── application.conf │ │ └── logback.xml │ │ └── webapp │ │ └── WEB-INF │ │ ├── appengine-web.xml │ │ ├── logging.properties │ │ └── web.xml └── springboot │ ├── .gitignore │ ├── .mvn │ └── wrapper │ │ └── maven-wrapper.properties │ ├── README.md │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ ├── main │ ├── kotlin │ │ ├── DemoApplication.kt │ │ └── MessageController.kt │ ├── resources │ │ └── application.properties │ └── webapp │ │ └── WEB-INF │ │ └── appengine-web.xml │ └── test │ └── kotlin │ └── DemoApplicationTests.kt ├── firestore ├── README.md ├── build.gradle.kts ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle.kts └── src │ ├── main │ └── kotlin │ │ ├── Firestore.kt │ │ └── Quickstart.kt │ └── test │ └── kotlin │ └── FirestoreTest.kt ├── functions ├── README.md ├── build.gradle.kts ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle.kts └── src │ ├── main │ └── kotlin │ │ ├── EventExample.kt │ │ └── HttpExample.kt │ └── test │ └── kotlin │ ├── EventExampleTest.kt │ └── HttpExampleTest.kt ├── getting-started └── android-with-appengine │ ├── .gitignore │ ├── README.md │ ├── backend │ ├── .gitignore │ ├── .mvn │ │ └── wrapper │ │ │ └── maven-wrapper.properties │ ├── README.md │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── google │ │ │ │ └── cloud │ │ │ │ └── kotlin │ │ │ │ └── emojify │ │ │ │ ├── EmojifyApplication.kt │ │ │ │ └── EmojifyController.kt │ │ ├── resources │ │ │ ├── application.properties │ │ │ └── emojis │ │ │ │ ├── anger.png │ │ │ │ ├── joy.png │ │ │ │ ├── none.png │ │ │ │ ├── sorrow.png │ │ │ │ └── surprise.png │ │ └── webapp │ │ │ └── WEB-INF │ │ │ ├── appengine-web.xml │ │ │ └── logging.properties │ │ └── test │ │ └── kotlin │ │ └── com │ │ └── google │ │ └── cloud │ │ └── kotlin │ │ └── emojify │ │ └── EmojifyApplicationTests.kt │ ├── frontend │ ├── README.md │ ├── build.gradle.kts │ ├── emojify │ │ ├── .gitignore │ │ ├── build.gradle.kts │ │ └── src │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── assets │ │ │ └── application.properties │ │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── google │ │ │ │ └── cloud │ │ │ │ └── kotlin │ │ │ │ └── emojify │ │ │ │ ├── Adapter.kt │ │ │ │ ├── Application.kt │ │ │ │ ├── ImageActivity.kt │ │ │ │ └── MediaLoader.kt │ │ │ └── res │ │ │ ├── drawable-hdpi │ │ │ ├── ic_back_white.png │ │ │ ├── ic_camera_white.png │ │ │ ├── ic_eye_white.png │ │ │ ├── ic_image_white.png │ │ │ ├── ic_logo_white.png │ │ │ ├── ic_video_white.png │ │ │ └── tag_video_white.png │ │ │ ├── drawable-xhdpi │ │ │ ├── ic_back_white.png │ │ │ ├── ic_camera_white.png │ │ │ ├── ic_eye_white.png │ │ │ ├── ic_image_white.png │ │ │ ├── ic_logo_white.png │ │ │ └── ic_video_white.png │ │ │ ├── drawable-xxhdpi │ │ │ ├── ic_back_white.png │ │ │ ├── ic_camera_white.png │ │ │ ├── ic_eye_white.png │ │ │ ├── ic_image_white.png │ │ │ ├── ic_logo_white.png │ │ │ ├── ic_video_white.png │ │ │ └── tag_video_white.png │ │ │ ├── drawable-xxxhdpi │ │ │ ├── ic_back_white.png │ │ │ ├── ic_camera_white.png │ │ │ ├── ic_eye_white.png │ │ │ ├── ic_image_white.png │ │ │ ├── ic_logo_white.png │ │ │ ├── ic_video_white.png │ │ │ ├── placeholder.jpg │ │ │ └── tag_video_white.png │ │ │ ├── drawable │ │ │ └── ic_geek.xml │ │ │ ├── layout │ │ │ ├── activity_album.xml │ │ │ ├── activity_list_content.xml │ │ │ ├── item_content_image.xml │ │ │ ├── toolbar.xml │ │ │ └── toolbar_scroll.xml │ │ │ ├── menu │ │ │ ├── menu_album.xml │ │ │ └── menu_album_image.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ └── values │ │ │ ├── colors.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle.kts │ └── screenshots │ ├── emojified-meetup.jpg │ ├── meetup.jpg │ ├── placeholder-image-1.png │ ├── placeholder-image-2.png │ ├── result.png │ └── welcome.png ├── pubsub ├── README.md ├── build.gradle.kts ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle.kts └── src │ ├── main │ └── kotlin │ │ └── PubSub.kt │ └── test │ └── kotlin │ └── PubSubExampleTest.kt ├── run ├── README.md ├── grpc-hello-world-bidi-streaming │ ├── .editorconfig │ ├── .gitignore │ ├── Procfile │ ├── README.md │ ├── app.json │ ├── build.gradle.kts │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle.kts │ └── src │ │ └── main │ │ ├── kotlin │ │ └── io │ │ │ └── grpc │ │ │ └── examples │ │ │ └── helloworld │ │ │ ├── HelloWorldClient.kt │ │ │ └── HelloWorldServer.kt │ │ └── proto │ │ └── hello_world.proto ├── grpc-hello-world-gradle │ ├── .editorconfig │ ├── .gitignore │ ├── Procfile │ ├── README.md │ ├── build.gradle.kts │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle.kts │ └── src │ │ └── main │ │ ├── kotlin │ │ └── io │ │ │ └── grpc │ │ │ └── examples │ │ │ └── helloworld │ │ │ ├── HelloWorldClient.kt │ │ │ └── HelloWorldServer.kt │ │ └── proto │ │ └── hello_world.proto ├── grpc-hello-world-mvn │ ├── .gitignore │ ├── .mvn │ │ ├── jvm.config │ │ └── wrapper │ │ │ └── maven-wrapper.properties │ ├── Procfile │ ├── README.md │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ │ └── main │ │ ├── kotlin │ │ └── io │ │ │ └── grpc │ │ │ └── examples │ │ │ └── helloworld │ │ │ ├── HelloWorldClient.kt │ │ │ └── HelloWorldServer.kt │ │ └── proto │ │ └── hello_world.proto ├── grpc-hello-world-streaming │ ├── .editorconfig │ ├── .gitignore │ ├── Procfile │ ├── README.md │ ├── build.gradle.kts │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle.kts │ └── src │ │ └── main │ │ ├── kotlin │ │ └── io │ │ │ └── grpc │ │ │ └── examples │ │ │ └── helloworld │ │ │ ├── HelloWorldClient.kt │ │ │ └── HelloWorldServer.kt │ │ └── proto │ │ └── hello_world.proto ├── http4k-hello-world │ ├── Procfile │ ├── README.md │ ├── build.gradle.kts │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle.kts │ └── src │ │ └── main │ │ └── kotlin │ │ └── WebApp.kt ├── ktor-hello-world │ ├── .gitignore │ ├── Procfile │ ├── README.md │ ├── build.gradle.kts │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle.kts │ └── src │ │ └── main │ │ ├── kotlin │ │ └── WebApp.kt │ │ └── resources │ │ └── logback.xml ├── micronaut-hello-world │ ├── .gitignore │ ├── Procfile │ ├── README.md │ ├── build.gradle.kts │ ├── gradle │ │ ├── libs.versions.toml │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle.kts │ └── src │ │ └── main │ │ ├── kotlin │ │ └── hello │ │ │ └── WebApp.kt │ │ └── resources │ │ ├── application.properties │ │ └── logback.xml ├── plain-hello-world │ ├── .gitattributes │ ├── .gitignore │ ├── .mvn │ │ └── wrapper │ │ │ └── maven-wrapper.properties │ ├── README.md │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ │ └── main │ │ └── java │ │ └── com │ │ └── google │ │ └── WebApp.kt ├── quarkus-hello-world │ ├── .gitignore │ ├── .mvn │ │ └── wrapper │ │ │ └── maven-wrapper.properties │ ├── README.md │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ │ └── main │ │ └── kotlin │ │ └── com │ │ └── google │ │ └── App.kt ├── springboot-cloudsql │ ├── .gitignore │ ├── README.md │ ├── build.gradle.kts │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── project.toml │ ├── settings.gradle.kts │ └── src │ │ ├── main │ │ ├── kotlin │ │ │ └── kotlinbars │ │ │ │ └── Main.kt │ │ └── resources │ │ │ ├── application.properties │ │ │ └── schema.sql │ │ └── test │ │ ├── kotlin │ │ └── kotlinbars │ │ │ └── BarRepoTest.kt │ │ └── resources │ │ └── application.properties └── springboot-hello-world │ ├── .gitignore │ ├── Procfile │ ├── README.md │ ├── app.json │ ├── build.gradle.kts │ ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── project.toml │ ├── settings.gradle.kts │ └── src │ └── main │ ├── kotlin │ └── DemoApplication.kt │ └── resources │ └── application.properties ├── storage ├── README.md ├── build.gradle.kts ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── resources │ └── upload │ │ └── dog.jpg ├── settings.gradle.kts └── src │ ├── main │ └── kotlin │ │ ├── Quickstart.kt │ │ └── Storage.kt │ └── test │ └── kotlin │ ├── QuickstartTest.kt │ └── StorageTest.kt └── vision ├── README.md ├── build.gradle.kts ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── resources └── doggo.jpg ├── settings.gradle.kts └── src ├── main └── kotlin │ └── Quickstart.kt └── test └── kotlin └── QuickstartTest.kt /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Code owners file. 2 | # This file controls who is tagged for review for any given pull request. 3 | # 4 | # For syntax help see: 5 | # https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners#codeowners-syntax 6 | 7 | * @GoogleCloudPlatform/kotlin-eng 8 | -------------------------------------------------------------------------------- /.github/dependabot.template.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | 8 | - package-ecosystem: "gradle" 9 | directory: "gradlew" 10 | schedule: 11 | interval: "weekly" 12 | 13 | - package-ecosystem: "maven" 14 | directory: "pom.xml" 15 | schedule: 16 | interval: "weekly" 17 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # This file was generated by the "Generate Dependabot Glob" action. Do not edit it directly. 2 | # Make changes to `.github/dependabot.template.yml` and a PR will be automatically created. 3 | version: 2 4 | updates: 5 | - package-ecosystem: github-actions 6 | directory: / 7 | schedule: 8 | interval: weekly 9 | - package-ecosystem: gradle 10 | directory: appengine/ktor 11 | schedule: 12 | interval: weekly 13 | - package-ecosystem: gradle 14 | directory: firestore 15 | schedule: 16 | interval: weekly 17 | - package-ecosystem: gradle 18 | directory: functions 19 | schedule: 20 | interval: weekly 21 | - package-ecosystem: gradle 22 | directory: getting-started/android-with-appengine/frontend 23 | schedule: 24 | interval: weekly 25 | - package-ecosystem: gradle 26 | directory: pubsub 27 | schedule: 28 | interval: weekly 29 | - package-ecosystem: gradle 30 | directory: run/grpc-hello-world-bidi-streaming 31 | schedule: 32 | interval: weekly 33 | - package-ecosystem: gradle 34 | directory: run/grpc-hello-world-gradle 35 | schedule: 36 | interval: weekly 37 | - package-ecosystem: gradle 38 | directory: run/grpc-hello-world-streaming 39 | schedule: 40 | interval: weekly 41 | - package-ecosystem: gradle 42 | directory: run/http4k-hello-world 43 | schedule: 44 | interval: weekly 45 | - package-ecosystem: gradle 46 | directory: run/ktor-hello-world 47 | schedule: 48 | interval: weekly 49 | - package-ecosystem: gradle 50 | directory: run/micronaut-hello-world 51 | schedule: 52 | interval: weekly 53 | - package-ecosystem: gradle 54 | directory: run/springboot-cloudsql 55 | schedule: 56 | interval: weekly 57 | - package-ecosystem: gradle 58 | directory: run/springboot-hello-world 59 | schedule: 60 | interval: weekly 61 | - package-ecosystem: gradle 62 | directory: storage 63 | schedule: 64 | interval: weekly 65 | - package-ecosystem: gradle 66 | directory: vision 67 | schedule: 68 | interval: weekly 69 | - package-ecosystem: maven 70 | directory: appengine/springboot 71 | schedule: 72 | interval: weekly 73 | - package-ecosystem: maven 74 | directory: getting-started/android-with-appengine/backend 75 | schedule: 76 | interval: weekly 77 | - package-ecosystem: maven 78 | directory: run/grpc-hello-world-mvn 79 | schedule: 80 | interval: weekly 81 | - package-ecosystem: maven 82 | directory: run/plain-hello-world 83 | schedule: 84 | interval: weekly 85 | - package-ecosystem: maven 86 | directory: run/quarkus-hello-world 87 | schedule: 88 | interval: weekly 89 | -------------------------------------------------------------------------------- /.github/snippet-bot.yml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.github/workflows/generate_dependabot.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | repository_dispatch: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | generate: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v4 11 | 12 | - name: Generate dependabot.yml 13 | uses: Makeshift/generate-dependabot-glob-action@master 14 | 15 | - name: Create Pull Request 16 | uses: peter-evans/create-pull-request@v6 17 | -------------------------------------------------------------------------------- /.github/workflows/gradle-wrapper-validation.yml: -------------------------------------------------------------------------------- 1 | name: "Validate Gradle Wrapper" 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | jobs: 8 | validation: 9 | name: "Validation" 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4 13 | - uses: gradle/wrapper-validation-action@v2 14 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | jobs: 8 | lint: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4 12 | - name: Install ktlint 13 | run: | 14 | curl -sSLO https://github.com/shyiko/ktlint/releases/download/0.48.2/ktlint 15 | mkdir -p "${GITHUB_WORKSPACE}/bin/" 16 | mv ktlint "${GITHUB_WORKSPACE}/bin/ktlint" 17 | chmod +x "${GITHUB_WORKSPACE}/bin/ktlint" 18 | - name: Run ktlint 19 | run: | 20 | "${GITHUB_WORKSPACE}/bin/ktlint" "**/*".kt '!**/build/generated/**' --version 21 | "${GITHUB_WORKSPACE}/bin/ktlint" "**/*".kt '!**/build/generated/**' -l=info 22 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Unit Tests 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | env: 8 | GOOGLE_CLOUD_PROJECT: ${{ secrets.GOOGLE_CLOUD_PROJECT }} 9 | GOOGLE_STORAGE_BUCKET: ${{ secrets.GOOGLE_CLOUD_PROJECT }} 10 | jobs: 11 | sample_directories: 12 | name: Get sample directories 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4 16 | - id: set-testdirs 17 | run: echo "testdirs=[$(for i in $(ls -d */); do echo -n \"${i%%/}\",; done | sed 's/.$//')]" >> "$GITHUB_OUTPUT" 18 | outputs: 19 | testdirs: ${{ steps.set-testdirs.outputs.testdirs }} 20 | unit_tests: 21 | needs: sample_directories 22 | strategy: 23 | matrix: 24 | sample: ${{ fromJson(needs.sample_directories.outputs.testdirs) }} 25 | runs-on: ubuntu-latest 26 | name: ${{ matrix.sample }} 27 | steps: 28 | - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4 29 | - uses: actions/setup-java@v4 30 | with: 31 | distribution: 'temurin' 32 | java-version: '17' 33 | - name: Set Application Default Credentials Environment Variable 34 | run: echo "GOOGLE_APPLICATION_CREDENTIALS=${HOME}/credentials.json" >> $GITHUB_ENV 35 | - name: Set Application Default Credentials File 36 | run: echo ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS_B64 }} | base64 --decode >> $GOOGLE_APPLICATION_CREDENTIALS 37 | - name: Set config files for android-with-appengine sample 38 | if: matrix.sample == 'getting-started' && (github.ref == 'refs/heads/main' || github.event.pull_request.head.repo.full_name == github.repository) 39 | run : | 40 | echo ${{ secrets.GOOGLE_SERVICES_B64 }} | base64 --decode >> ${GITHUB_WORKSPACE}/getting-started/android-with-appengine/frontend/emojify/google-services.json 41 | echo "storage.bucket.name = $GOOGLE_CLOUD_PROJECT.appspot.com" >> ${GITHUB_WORKSPACE}/getting-started/android-with-appengine/backend/src/main/resources/application.properties 42 | - name: Build ${{ matrix.sample }} samples 43 | run: ./.github/tests.sh ${{ matrix.sample }} 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | out 3 | test-output 4 | target/ 5 | classes/ 6 | /var 7 | test-output/ 8 | temp-testng-customsuite.xml 9 | /atlassian-ide-plugin.xml 10 | hotspot.log 11 | pom.xml.versionsBackup 12 | .gradle 13 | .idea 14 | *.iml 15 | .DS_Store 16 | *.ipr 17 | *.iws 18 | .gradle/ 19 | .classpath 20 | .settings 21 | .project 22 | .externalToolBuilders 23 | *~ 24 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | As contributors and maintainers of this project, 4 | and in the interest of fostering an open and welcoming community, 5 | we pledge to respect all people who contribute through reporting issues, 6 | posting feature requests, updating documentation, 7 | submitting pull requests or patches, and other activities. 8 | 9 | We are committed to making participation in this project 10 | a harassment-free experience for everyone, 11 | regardless of level of experience, gender, gender identity and expression, 12 | sexual orientation, disability, personal appearance, 13 | body size, race, ethnicity, age, religion, or nationality. 14 | 15 | Examples of unacceptable behavior by participants include: 16 | 17 | * The use of sexualized language or imagery 18 | * Personal attacks 19 | * Trolling or insulting/derogatory comments 20 | * Public or private harassment 21 | * Publishing other's private information, 22 | such as physical or electronic 23 | addresses, without explicit permission 24 | * Other unethical or unprofessional conduct. 25 | 26 | Project maintainers have the right and responsibility to remove, edit, or reject 27 | comments, commits, code, wiki edits, issues, and other contributions 28 | that are not aligned to this Code of Conduct. 29 | By adopting this Code of Conduct, 30 | project maintainers commit themselves to fairly and consistently 31 | applying these principles to every aspect of managing this project. 32 | Project maintainers who do not follow or enforce the Code of Conduct 33 | may be permanently removed from the project team. 34 | 35 | This code of conduct applies both within project spaces and in public spaces 36 | when an individual is representing the project or its community. 37 | 38 | Instances of abusive, harassing, or otherwise unacceptable behavior 39 | may be reported by opening an issue 40 | or contacting one or more of the project maintainers. 41 | 42 | This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.2.0, 43 | available at [http://contributor-covenant.org/version/1/2/0/](http://contributor-covenant.org/version/1/2/0/) 44 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to become a contributor and submit your own code 2 | 3 | ## Contributor License Agreements 4 | 5 | We'd love to accept your patches! Before we can take them, we 6 | have to jump a couple of legal hurdles. 7 | 8 | Please fill out either the individual or corporate Contributor License Agreement 9 | (CLA). 10 | 11 | * If you are an individual writing original source code and you're sure you 12 | own the intellectual property, then you'll need to sign an [individual CLA] 13 | (https://developers.google.com/open-source/cla/individual). 14 | * If you work for a company that wants to allow you to contribute your work, 15 | then you'll need to sign a [corporate CLA] 16 | (https://developers.google.com/open-source/cla/corporate). 17 | 18 | Follow either of the two links above to access the appropriate CLA and 19 | instructions for how to sign and return it. Once we receive it, we'll be able to 20 | accept your pull requests. 21 | 22 | ## Contributing A Patch 23 | 24 | 1. Submit an issue describing your proposed change to the repo in question. 25 | 1. The repo owner will respond to your issue promptly. 26 | 1. If your proposed change is accepted, and you haven't already done so, sign a 27 | Contributor License Agreement (see details above). 28 | 1. Fork the desired repo, develop and test your code changes. 29 | 1. Ensure that your code adheres to the existing style in the sample to which 30 | you are contributing. 31 | 1. Ensure that your code has an appropriate set of unit tests which all pass. 32 | 1. Submit a pull request. 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Google Cloud Platform Kotlin Samples 2 | 3 | This repository holds samples written in Kotlin that demonstrate the Google 4 | Cloud Platform. 5 | 6 | ## Index 7 | 8 | |Path|Description| 9 | |---|---| 10 | |[appengine](appengine)|Basic examples for deploying Kotlin applications to [App Engine for Java 8][appengine].| 11 | |[firestore](firestore)|This sample demonstrates the [Google Cloud Firestore API][firestore-api].| 12 | |[functions](functions)|This sample demonstrates deploying to [Google Cloud Functions][functions].| 13 | |[pubsub](pubsub)|This sample demonstrates the [Google Cloud Pub/Sub API][pubsub-api].| 14 | |[run](run)|Basic examples for deploying Kotlin applications to [Cloud Run][run].| 15 | |[storage](storage)|This sample demonstrates the [Google Cloud Storage API][storage-api].| 16 | |[vision](vision)|This sample demonstrates the [Google Cloud Vision API][vision-api].| 17 | |[emojify](getting-started/android-with-appengine)|Getting started with Server side Kotlin? This app demonstrates an Android frontend written in Kotlin that communicates with a Kotlin backend running on [App Engine for Java 8][appengine].| 18 | 19 | The samples use the [Google Cloud Client Library for Java][google-cloud-java]. 20 | 21 | ## Contributing changes. 22 | 23 | Entirely new samples and bug fixes are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute. 24 | 25 | ## Licensing 26 | 27 | Code in this repository is licensed under the Apache 2.0. See [LICENSE](LICENSE). 28 | 29 | [appengine]: https://cloud.google.com/appengine/docs/standard/java/runtime-java8 30 | [storage-api]: https://cloud.google.com/storage/ 31 | [vision-api]: https://cloud.google.com/vision/ 32 | [pubsub-api]: https://cloud.google.com/pubsub/ 33 | [run]: https://cloud.google.com/run/ 34 | [firestore-api]: https://cloud.google.com/firestore/ 35 | [functions]: https://cloud.google.com/functions/ 36 | [google-cloud-java]: https://googlecloudplatform.github.io/google-cloud-java 37 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | To report a security issue, please use [g.co/vulnz](https://g.co/vulnz). 4 | 5 | The Google Security Team will respond within 5 working days of your report on g.co/vulnz. 6 | 7 | We use g.co/vulnz for our intake, and do coordination and disclosure here using GitHub Security Advisory to privately discuss and fix the issue. 8 | -------------------------------------------------------------------------------- /appengine/README.md: -------------------------------------------------------------------------------- 1 | Kotlin Samples for App Engine 2 | ============================= 3 | 4 | These sample Kotlin applications demonstrate how to deploy Kotlin backends to [App Engine for Java 8](https://cloud.google.com/appengine/docs/standard/java/runtime-java8). 5 | 6 | |Link|Description| 7 | |---|---| 8 | |[Simple Kotlin app - no framework](https://github.com/GoogleCloudPlatform/getting-started-java/tree/master/appengine-standard-java8/kotlin-appengine-standard)|Deploy a basic Kotlin application to App Engine for Java 8. This sample does not use any framework| 9 | |[Kotlin on Ktor](ktor)|Deploy a Kotlin application built with [Ktor](ktor) to App Engine for Java 8| 10 | |[Kotlin on Spring Boot](springboot)|Deploy a Kotlin application built with [Spring Boot](springboot) to App Engine for Java 8| 11 | |[Kotlin Spark Sample](https://github.com/GoogleCloudPlatform/getting-started-java/tree/master/appengine-standard-java8/kotlin-spark-appengine-standard)|Deploy a Kotlin application built with Spark to App Engine for Java 8| 12 | |[Android app with Kotlin Backend](https://github.com/GoogleCloudPlatform/kotlin-samples/tree/main/getting-started/android-with-appengine)|Sample of a Kotlin Spring Boot backend that runs on App Engine Standard for Java8 and communicates with an Android app written in Kotlin.| 13 | -------------------------------------------------------------------------------- /appengine/ktor/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | trim_trailing_whitespace = true 5 | insert_final_newline = true 6 | charset = utf-8 7 | indent_style = space 8 | 9 | [{*.sh,gradlew}] 10 | end_of_line = lf 11 | 12 | [{*.bat,*.cmd}] 13 | end_of_line = crlf 14 | 15 | [{*.kts,*.kt}] 16 | max_line_length = 100 17 | -------------------------------------------------------------------------------- /appengine/ktor/README.md: -------------------------------------------------------------------------------- 1 | # Ktor on Google App Engine Standard 2 | 3 | To download and run this sample, download the code with the commands below, and 4 | then follow the steps in the [Community Tutorial][tutorial]. 5 | 6 | ```sh 7 | git clone git@github.com:GoogleCloudPlatform/kotlin-samples 8 | cd kotlin-samples/appengine/ktor 9 | ``` 10 | 11 | [tutorial]: https://cloud.google.com/community/tutorials/kotlin-ktor-app-engine-java8 12 | -------------------------------------------------------------------------------- /appengine/ktor/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | kotlin("jvm") version "1.9.23" 3 | war 4 | id("com.google.cloud.tools.appengine") version "2.4.5" 5 | } 6 | 7 | repositories { 8 | mavenCentral() 9 | } 10 | 11 | kotlin { 12 | jvmToolchain(17) 13 | } 14 | 15 | dependencies { 16 | implementation(platform("io.ktor:ktor-bom:2.3.9")) 17 | implementation("io.ktor:ktor-server-servlet") 18 | implementation("io.ktor:ktor-server-html-builder") 19 | implementation("io.ktor:ktor-server-call-logging:2.3.9") 20 | implementation("io.ktor:ktor-server-default-headers:2.3.9") 21 | implementation("com.google.cloud:google-cloud-logging-logback:0.131.3-alpha") 22 | 23 | runtimeOnly("com.google.appengine:appengine:1.9.98") 24 | } 25 | 26 | appengine { 27 | deploy { 28 | projectId = "GCLOUD_CONFIG" 29 | version = "GCLOUD_CONFIG" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /appengine/ktor/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.parallel=true 2 | 3 | kotlin.code.style=official 4 | -------------------------------------------------------------------------------- /appengine/ktor/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/appengine/ktor/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /appengine/ktor/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /appengine/ktor/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "appengine-ktor" 2 | 3 | pluginManagement { 4 | repositories { 5 | mavenCentral() 6 | gradlePluginPortal() 7 | } 8 | resolutionStrategy { 9 | eachPlugin { 10 | if (requested.id.id == "com.google.cloud.tools.appengine") { 11 | useModule("com.google.cloud.tools:appengine-gradle-plugin:${requested.version}") 12 | } 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /appengine/ktor/src/main/kotlin/HelloApplication.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google LLC. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.example.demo 18 | 19 | import io.ktor.server.application.Application 20 | import io.ktor.server.application.call 21 | import io.ktor.server.application.install 22 | import io.ktor.server.html.respondHtml 23 | import io.ktor.server.plugins.callloging.CallLogging 24 | import io.ktor.server.plugins.defaultheaders.DefaultHeaders 25 | import io.ktor.server.routing.get 26 | import io.ktor.server.routing.routing 27 | import kotlinx.html.body 28 | import kotlinx.html.head 29 | import kotlinx.html.p 30 | import kotlinx.html.title 31 | 32 | // Entry Point of the application as defined in resources/application.conf. 33 | // @see https://ktor.io/servers/configuration.html#hocon-file 34 | fun Application.main() { 35 | // This adds Date and Server headers to each response, and allows custom additional headers 36 | install(DefaultHeaders) 37 | // This uses use the logger to log every call (request/response) 38 | install(CallLogging) 39 | 40 | routing { 41 | // Here we use a DSL for building HTML on the route "/" 42 | // @see https://github.com/Kotlin/kotlinx.html 43 | get("/") { 44 | call.respondHtml { 45 | head { 46 | title { +"Ktor on Google App Engine Standard" } 47 | } 48 | body { 49 | p { 50 | +"Hello there! This is Ktor running on Google Appengine Standard" 51 | } 52 | } 53 | } 54 | } 55 | get("/demo") { 56 | call.respondHtml { 57 | head { 58 | title { +"Ktor on Google App Engine Standard" } 59 | } 60 | body { 61 | p { 62 | +"It's another route!" 63 | } 64 | } 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /appengine/ktor/src/main/resources/application.conf: -------------------------------------------------------------------------------- 1 | ktor { 2 | application { 3 | modules = [ com.example.demo.HelloApplicationKt.main ] 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /appengine/ktor/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | INFO 8 | 9 | application.log 10 | WARN 11 | 12 | 13 | 14 | %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /appengine/ktor/src/main/webapp/WEB-INF/appengine-web.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | java8 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /appengine/ktor/src/main/webapp/WEB-INF/logging.properties: -------------------------------------------------------------------------------- 1 | .level = INFO 2 | -------------------------------------------------------------------------------- /appengine/ktor/src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | KtorServlet 5 | KtorServlet 6 | io.ktor.server.servlet.ServletApplicationEngine 7 | 8 | 9 | io.ktor.config 10 | application.conf 11 | 12 | 13 | 14 | KtorServlet 15 | / 16 | 17 | 18 | -------------------------------------------------------------------------------- /appengine/springboot/.gitignore: -------------------------------------------------------------------------------- 1 | /.mvn/wrapper/maven-wrapper.jar 2 | -------------------------------------------------------------------------------- /appengine/springboot/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.5/apache-maven-3.9.5-bin.zip 18 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar 19 | -------------------------------------------------------------------------------- /appengine/springboot/README.md: -------------------------------------------------------------------------------- 1 | # Kotlin Spring Boot on App Engine Standard 2 | 3 | To download and run this sample, download the code with the commands below, and 4 | then follow the steps in the [Community Tutorial][tutorial]. 5 | 6 | ```sh 7 | git clone git@github.com:GoogleCloudPlatform/kotlin-samples 8 | cd kotlin-samples/appengine/springboot 9 | ``` 10 | 11 | [tutorial]: https://cloud.google.com/community/tutorials/kotlin-springboot-app-engine-java8 12 | -------------------------------------------------------------------------------- /appengine/springboot/src/main/kotlin/DemoApplication.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google LLC. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.example.demo 18 | 19 | import org.springframework.boot.autoconfigure.SpringBootApplication 20 | import org.springframework.boot.runApplication 21 | import org.springframework.boot.web.servlet.support.SpringBootServletInitializer 22 | 23 | @SpringBootApplication 24 | class DemoApplication : SpringBootServletInitializer() 25 | 26 | fun main(args: Array) { 27 | runApplication(*args) 28 | } 29 | -------------------------------------------------------------------------------- /appengine/springboot/src/main/kotlin/MessageController.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google LLC. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.example.demo 18 | 19 | import org.springframework.web.bind.annotation.RequestMapping 20 | import org.springframework.web.bind.annotation.RestController 21 | 22 | data class Message(val text: String, val priority: String) 23 | 24 | @RestController 25 | class MessageController { 26 | @RequestMapping("/message") 27 | fun message(): Message { 28 | return Message("Hello from Google Cloud", "High") 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /appengine/springboot/src/main/resources/application.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/appengine/springboot/src/main/resources/application.properties -------------------------------------------------------------------------------- /appengine/springboot/src/main/webapp/WEB-INF/appengine-web.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | java8 5 | 6 | -------------------------------------------------------------------------------- /appengine/springboot/src/test/kotlin/DemoApplicationTests.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google LLC. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.example.demo 18 | 19 | import org.junit.Test 20 | import org.junit.runner.RunWith 21 | import org.springframework.boot.test.context.SpringBootTest 22 | import org.springframework.test.context.junit4.SpringRunner 23 | 24 | @RunWith(SpringRunner::class) 25 | @SpringBootTest 26 | class DemoApplicationTests { 27 | 28 | @Test 29 | fun contextLoads() { 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /firestore/README.md: -------------------------------------------------------------------------------- 1 | # Firestore Kotlin Sample 2 | 3 | [![Open in Cloud Shell][shell_img]][shell_link] 4 | 5 | [shell_img]: http://gstatic.com/cloudssh/images/open-btn.svg 6 | [shell_link]: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googlecloudplatform/kotlin-samples&page=editor&working_dir=firestore 7 | 8 | ## Description 9 | 10 | This simple command-line application demonstrates how invoke the Google [Cloud Firestore API][firestore-api] from a Kotlin application. 11 | 12 | **Check out the sample code in** [quickstart.kt](src/main/kotlin/quickstart.kt) **and** 13 | [firestore.kt](src/main/kotlin/firestore.kt). 14 | 15 | ## Quickstart 16 | 17 | #### Setup 18 | - Configure your project to use [Cloud Firestore](https://console.cloud.google.com/firestore) in Native mode. 19 | - [Enable Cloud Firestore API][enable-firestore-api] inside your Google Cloud project. 20 | - Set up [authentication](https://cloud.google.com/docs/authentication/getting-started). 21 | 22 | #### Build 23 | - Clone the repository 24 | ```sh 25 | git clone https://github.com/GoogleCloudPlatform/kotlin-samples 26 | cd kotlin-samples/firestore 27 | ``` 28 | - Build the project with Gradle Wrapper: 29 | ```sh 30 | # run with "-info" flag to print potential errors 31 | ./gradlew installDist -info 32 | ``` 33 | You should now have a **'firestore.jar'** file under **build/libs/** 34 | 35 | #### Running the sample 36 | 37 | Usage: ```build/install/firestore/bin/firestore YOUR_COLLECTION_NAME [KEY] [VALUE]``` 38 | 39 | * Running with a collection name will print all keys and values in the collection. 40 | * Running with a collection name and key will print the key/value pair. 41 | * Running with a collection name, key, and value will set the key to that value. 42 | 43 | ## Contributing changes 44 | 45 | * See [CONTRIBUTING.md](../CONTRIBUTING.md) 46 | 47 | ## Licensing 48 | 49 | * See [LICENSE](../LICENSE) 50 | 51 | [firestore-api]: https://cloud.google.com/firestore 52 | [enable-firestore-api]: https://console.cloud.google.com/flows/enableapi?apiid=firestore.googleapis.com 53 | -------------------------------------------------------------------------------- /firestore/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | application 3 | kotlin("jvm") version "1.9.23" 4 | } 5 | 6 | repositories { 7 | mavenCentral() 8 | } 9 | 10 | dependencies { 11 | implementation(kotlin("reflect")) 12 | implementation("com.google.cloud:google-cloud-firestore:3.18.0") 13 | testImplementation("junit:junit:4.13.2") 14 | testImplementation(kotlin("test")) 15 | // see: https://github.com/googleapis/sdk-platform-java/pull/1832 16 | modules { 17 | module("com.google.guava:listenablefuture") { 18 | replacedBy("com.google.guava:guava", "listenablefuture is part of guava") 19 | } 20 | } 21 | } 22 | 23 | kotlin { 24 | jvmToolchain(17) 25 | } 26 | 27 | application { 28 | mainClass.set("com.google.firestore.FirestoreKt") 29 | } 30 | -------------------------------------------------------------------------------- /firestore/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/firestore/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /firestore/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /firestore/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenCentral() 4 | gradlePluginPortal() 5 | } 6 | } 7 | 8 | rootProject.name = "firestore" 9 | 10 | plugins { 11 | id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" 12 | } 13 | -------------------------------------------------------------------------------- /firestore/src/main/kotlin/Firestore.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2018 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 | // http://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 | package com.google.firestore 16 | 17 | import com.google.cloud.firestore.FirestoreOptions 18 | 19 | fun main(vararg args: String) { 20 | // validate the arguments 21 | if (args.isEmpty() || args.size > 3) { 22 | throw Exception("Usage: java -jar firestore.jar YOUR_COLLECTION_ID [KEY] [VALUE]") 23 | } 24 | 25 | // create the client 26 | val db = FirestoreOptions.newBuilder() 27 | .build() 28 | .service 29 | 30 | // create the docRef and data object 31 | val docRef = db.collection(args[0]).document("samples") 32 | val data = docRef 33 | .get() // future 34 | .get() // snapshot 35 | .data // MutableMap 36 | 37 | // If no arguments are supplied, call the quickstart. Fetch the key value if only one argument is supplied. 38 | // Set the key to the supplied value if two arguments are supplied. 39 | when (args.size) { 40 | 1 -> quickstart(args[0], "samples") 41 | 2 -> println("${args[1]}: ${data?.get(args[1]) ?: "not found"}") 42 | else -> { 43 | val future = docRef.update(args[1], args[2]) 44 | println("Updated collection: ${future.get()}") 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /firestore/src/main/kotlin/Quickstart.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2018 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 | // http://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 | package com.google.firestore 16 | 17 | import com.google.cloud.firestore.FirestoreOptions 18 | 19 | fun quickstart(collectionName: String, documentName: String) { 20 | // [START firestore_quickstart] 21 | // Create the client. 22 | val db = FirestoreOptions.newBuilder() 23 | .build() 24 | .service 25 | 26 | // Fetch the document reference and data object. 27 | val docRef = db.collection(collectionName).document(documentName) 28 | val data = docRef 29 | .get() // future 30 | .get() // snapshot 31 | .data ?: error("Document $collectionName:$documentName not found") // MutableMap 32 | 33 | // Print the retrieved data. 34 | data.forEach { (key, value) -> println("$key: $value") } 35 | // [END firestore_quickstart] 36 | } 37 | -------------------------------------------------------------------------------- /firestore/src/test/kotlin/FirestoreTest.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2018 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 | // http://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 | package com.google.firestore 16 | 17 | import org.hamcrest.CoreMatchers.containsString 18 | import org.hamcrest.MatcherAssert.assertThat 19 | import org.junit.After 20 | import org.junit.Assert 21 | import org.junit.Before 22 | import org.junit.Test 23 | import java.io.ByteArrayOutputStream 24 | import java.io.PrintStream 25 | 26 | internal class FirestoreTest { 27 | 28 | private val outContent = ByteArrayOutputStream() 29 | private val originalOut = System.out!! 30 | private val collection = "test-collection" 31 | 32 | @Before 33 | fun `setup streams`() { 34 | System.setOut(PrintStream(outContent)) 35 | } 36 | 37 | @After 38 | fun `restore streams`() { 39 | System.setOut(originalOut) 40 | } 41 | 42 | @Test 43 | fun `fetch non-existing`() { 44 | main(collection, "non-existing") 45 | Assert.assertEquals("non-existing: not found\n", outContent.toString()) 46 | } 47 | 48 | @Test 49 | fun `set and fetch value`() { 50 | // Generate a key based on the current time (so it shouldn't exist) 51 | val key = System.currentTimeMillis().toString() 52 | 53 | // ensure key doesn't currently exist 54 | main(collection, key) 55 | Assert.assertEquals("$key: not found\n", outContent.toString()) 56 | 57 | // set the key to "some value" 58 | main(collection, key, "some value") 59 | assertThat(outContent.toString(), containsString("Updated collection: ")) 60 | 61 | // ensure key exists now (and reset the output stream) 62 | outContent.reset() 63 | main(collection, key) 64 | Assert.assertEquals("$key: some value\n", outContent.toString()) 65 | } 66 | 67 | @Test(expected = Exception::class) 68 | fun `too few arguments`() { 69 | main() 70 | } 71 | 72 | @Test(expected = Exception::class) 73 | fun `too many arguments`() { 74 | main("arg1", "arg2", "arg3", "arg4") 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /functions/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.gradle.api.tasks.testing.logging.TestLogEvent.* 2 | 3 | plugins { 4 | kotlin("jvm") version "1.9.23" 5 | id("com.github.johnrengelman.shadow") version "8.1.1" 6 | } 7 | 8 | repositories { 9 | mavenCentral() 10 | } 11 | 12 | dependencies { 13 | implementation("javax.servlet:javax.servlet-api:4.0.1") 14 | testImplementation("org.mockito:mockito-core:5.+") 15 | testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.2") 16 | testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.10.2") 17 | } 18 | 19 | kotlin { 20 | jvmToolchain(17) 21 | } 22 | 23 | tasks.withType { 24 | useJUnitPlatform() 25 | 26 | testLogging { 27 | showStandardStreams = true 28 | exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL 29 | events(STARTED, PASSED, SKIPPED, FAILED) 30 | } 31 | } 32 | 33 | 34 | //application { 35 | // mainClass.set("WebAppKt") 36 | //} 37 | 38 | //tasks.replace("assemble").dependsOn("installDist") 39 | 40 | //tasks.create("stage").dependsOn("installDist") 41 | /* 42 | buildscript { 43 | ext.kotlin_version = '1.3.20' 44 | 45 | repositories { 46 | jcenter() 47 | } 48 | dependencies { 49 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 50 | classpath "com.github.jengelman.gradle.plugins:shadow:5.0.0" 51 | } 52 | } 53 | 54 | plugins { 55 | id 'java' 56 | id "org.jetbrains.kotlin.jvm" version "1.2.61" 57 | } 58 | 59 | apply plugin: 'com.github.johnrengelman.shadow' 60 | tasks.build.dependsOn tasks.shadowJar 61 | 62 | shadowJar { 63 | mergeServiceFiles() 64 | } 65 | 66 | dependencies { 67 | compile "org.jetbrains.kotlin:kotlin-stdlib" 68 | compile "javax.servlet:javax.servlet-api:3.1.0" 69 | testCompile 'junit:junit:4.12' 70 | testImplementation 'org.mockito:mockito-core:5.+' 71 | } 72 | 73 | sourceSets { 74 | main.java.srcDirs += 'src/main/kotlin' 75 | test.java.srcDirs += 'src/test/kotlin' 76 | } 77 | 78 | repositories { 79 | mavenCentral() 80 | } 81 | */ 82 | -------------------------------------------------------------------------------- /functions/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/functions/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /functions/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /functions/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenCentral() 4 | gradlePluginPortal() 5 | } 6 | } 7 | 8 | rootProject.name = "gcloud-functions" 9 | 10 | plugins { 11 | id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" 12 | } 13 | -------------------------------------------------------------------------------- /functions/src/main/kotlin/EventExample.kt: -------------------------------------------------------------------------------- 1 | // [START functions_helloworld_pubsub] 2 | import java.util.Base64 3 | import java.util.logging.Logger 4 | 5 | class EventExample { 6 | companion object { 7 | var log: Logger = Logger.getLogger(EventExample::class.java.name) 8 | } 9 | 10 | fun helloPubSub(message: PubSubMessage) { 11 | val data = String(Base64.getDecoder().decode(message.data)) 12 | log.info(data) 13 | } 14 | } 15 | 16 | data class PubSubMessage( 17 | val data: String, 18 | val messageId: String, 19 | val publishTime: String, 20 | val attributes: Map, 21 | ) 22 | // [END functions_helloworld_pubsub] 23 | -------------------------------------------------------------------------------- /functions/src/main/kotlin/HttpExample.kt: -------------------------------------------------------------------------------- 1 | import javax.servlet.http.HttpServletRequest 2 | import javax.servlet.http.HttpServletResponse 3 | 4 | class HttpExample { 5 | fun helloWorld(req: HttpServletRequest, resp: HttpServletResponse) { 6 | with(resp.writer) { 7 | println("Hello World!") 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /functions/src/test/kotlin/EventExampleTest.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2018 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 | // http://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 | import org.junit.jupiter.api.Test 16 | import org.mockito.Mockito.mock 17 | import org.mockito.Mockito.times 18 | import org.mockito.Mockito.verify 19 | import java.util.logging.Logger 20 | 21 | internal class EventExampleTest { 22 | 23 | @Test 24 | fun `pubsub trigger`() { 25 | val message = PubSubMessage( 26 | data = "SGVsbG8sIFB1Yi9TdWIh", 27 | messageId = "", 28 | publishTime = "", 29 | attributes = mapOf(), 30 | ) 31 | 32 | val log = mock(Logger::class.java) 33 | EventExample.log = log 34 | EventExample().helloPubSub(message) 35 | 36 | verify(log, times(1)).info("Hello, Pub/Sub!") 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /functions/src/test/kotlin/HttpExampleTest.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2018 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 | // http://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 | import org.junit.jupiter.api.Test 16 | import org.mockito.Mockito.mock 17 | import org.mockito.Mockito.times 18 | import org.mockito.Mockito.verify 19 | import org.mockito.Mockito.`when` 20 | import java.io.PrintWriter 21 | import javax.servlet.http.HttpServletRequest 22 | import javax.servlet.http.HttpServletResponse 23 | 24 | internal class HttpExampleTest { 25 | 26 | @Test 27 | fun `http trigger`() { 28 | val req = mock(HttpServletRequest::class.java) 29 | val res = mock(HttpServletResponse::class.java) 30 | val writer = mock(PrintWriter::class.java) 31 | `when`(res.writer).thenReturn(writer) 32 | 33 | HttpExample().helloWorld(req, res) 34 | 35 | verify(writer, times(1)).println("Hello World!") 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /getting-started/android-with-appengine/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /**/local.properties 3 | google-services.json 4 | 5 | 6 | ### STS ### 7 | .apt_generated 8 | .classpath 9 | .factorypath 10 | .project 11 | .settings 12 | .springBeans 13 | .sts4-cache 14 | 15 | ### IntelliJ IDEA ### 16 | .idea 17 | *.iws 18 | *.iml 19 | *.ipr 20 | 21 | ### NetBeans ### 22 | /nbproject/private/ 23 | /build/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | -------------------------------------------------------------------------------- /getting-started/android-with-appengine/README.md: -------------------------------------------------------------------------------- 1 | Serverless Kotlin: Getting Started App 2 | === 3 | 4 | This sample application, called **Emojify**, demonstrates a Kotlin backend which communicates with a mobile frontend. The backend receives an input image from the frontend and overlays an emoji on each of the detected faces in the image based on facial expressions. 5 | 6 | Follow the [Extend an Android frontend with a Kotlin backend running on App Engine](https://g.co/codelabs/emojify) codelab to build and run it yourself! 7 | 8 | Example of source and emojified images: 9 | 10 | 11 | 12 | Picture taken at [DroidCon NYC Extended](https://dcnyc-extended-2018.splashthat.com/). 13 | -------------------------------------------------------------------------------- /getting-started/android-with-appengine/backend/.gitignore: -------------------------------------------------------------------------------- 1 | /.mvn/wrapper/maven-wrapper.jar 2 | -------------------------------------------------------------------------------- /getting-started/android-with-appengine/backend/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.5/apache-maven-3.9.5-bin.zip 18 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar 19 | -------------------------------------------------------------------------------- /getting-started/android-with-appengine/backend/README.md: -------------------------------------------------------------------------------- 1 | Emojify Backend 2 | === 3 | 4 | ## Sample SpringBoot application written in Kotlin for use with App Engine Java8 Standard. 5 | This folder contains the source code of Emojify backend. When deployed on App Engine, the backend acts as a cloud endpoint: 6 | * Input: name of source image in configured bucket (see setup) 7 | * Output: object of class [EmojifyResponse](src/main/kotlin/com/google/cloud/kotlin/emojify/EmojifyController.kt) 8 | 9 | ## Setup 10 | 11 | * Download and initialize the [Cloud SDK](https://cloud.google.com/sdk/) 12 | 13 | `gcloud init` 14 | 15 | * Edit value of *storage.bucket.name* in **application.properties** (src/main/resources): `storage.bucket.name = REPLACE_THIS_WITH_YOUR_BUCKET`. 16 | 17 | ## Maven 18 | ### Running locally 19 | 20 | `./mvnw package appengine:run` 21 | 22 | To use visit: http://localhost:8080/ 23 | 24 | ### Deploying to App Engine 25 | 26 | `./mvnw appengine:deploy` 27 | 28 | See the [Google App Engine standard environment documentation][ae-docs] for more 29 | detailed instructions. 30 | 31 | [ae-docs]: https://cloud.google.com/appengine/docs/java/ 32 | 33 | * [Java 8](http://www.oracle.com/technetwork/java/javase/downloads/index.html) 34 | * [Maven](https://maven.apache.org/download.cgi) (at least 3.5) 35 | * [Google Cloud SDK](https://cloud.google.com/sdk/) (aka gcloud command line tool) 36 | 37 | ## Testing 38 | 39 | `./mvnw verify` 40 | -------------------------------------------------------------------------------- /getting-started/android-with-appengine/backend/src/main/kotlin/com/google/cloud/kotlin/emojify/EmojifyApplication.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.cloud.kotlin.emojify 18 | 19 | import com.google.cloud.storage.Storage 20 | import com.google.cloud.storage.StorageOptions 21 | import com.google.cloud.vision.v1.ImageAnnotatorClient 22 | import org.springframework.boot.autoconfigure.SpringBootApplication 23 | import org.springframework.boot.runApplication 24 | import org.springframework.boot.web.servlet.support.SpringBootServletInitializer 25 | import org.springframework.context.annotation.Bean 26 | 27 | @SpringBootApplication 28 | class EmojifyApplication : SpringBootServletInitializer() { 29 | @Bean 30 | fun storage(): Storage = StorageOptions.getDefaultInstance().service 31 | 32 | @Bean 33 | fun vision(): ImageAnnotatorClient = ImageAnnotatorClient.create() 34 | } 35 | 36 | fun main(args: Array) { 37 | runApplication(*args) 38 | } 39 | -------------------------------------------------------------------------------- /getting-started/android-with-appengine/backend/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Google LLC 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # Unless required by applicable law or agreed to in writing, software 7 | # distributed under the License is distributed on an "AS IS" BASIS, 8 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | # See the License for the specific language governing permissions and 10 | # limitations under the License. 11 | 12 | # Your bucket name must be of the format YOUR_PROJECT_ID.appspot.com for the 13 | # Emojify frontend to work as expected 14 | storage.bucket.name = YOUR_PROJECT_ID.appspot.com 15 | -------------------------------------------------------------------------------- /getting-started/android-with-appengine/backend/src/main/resources/emojis/anger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/backend/src/main/resources/emojis/anger.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/backend/src/main/resources/emojis/joy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/backend/src/main/resources/emojis/joy.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/backend/src/main/resources/emojis/none.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/backend/src/main/resources/emojis/none.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/backend/src/main/resources/emojis/sorrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/backend/src/main/resources/emojis/sorrow.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/backend/src/main/resources/emojis/surprise.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/backend/src/main/resources/emojis/surprise.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/backend/src/main/webapp/WEB-INF/appengine-web.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | true 16 | java8 17 | 18 | 19 | 20 | 21 | 1 22 | 23 | -------------------------------------------------------------------------------- /getting-started/android-with-appengine/backend/src/main/webapp/WEB-INF/logging.properties: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Google LLC 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # Unless required by applicable law or agreed to in writing, software 7 | # distributed under the License is distributed on an "AS IS" BASIS, 8 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | # See the License for the specific language governing permissions and 10 | # limitations under the License. 11 | 12 | # A default java.util.logging configuration. 13 | # (All App Engine logging is through java.util.logging by default). 14 | # 15 | # To use this configuration, copy it into your application's WEB-INF 16 | # folder and add the following to your appengine-web.xml: 17 | # 18 | # 19 | # 20 | # 21 | # 22 | 23 | # Set the default logging level for all loggers to WARNING 24 | .level = WARNING 25 | -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/README.md: -------------------------------------------------------------------------------- 1 | Emojify Frontend 2 | === 3 | 4 | ## Description 5 | 6 | Android app written in Kotlin 7 | 8 | * Allows User to select an existing picture or take a new picture 9 | * Uploads selected image to bucket in Cloud Storage (see Setup for how to configure bucket) 10 | * Makes a url call to Emojify backend (https://YOUR-PROJECT-ID.appspot.com/emojify?objectName=IMAGE_NAME) 11 | * Processes backend response (parses emojified image public url) 12 | * Downloads and draws emojified image 13 | 14 | A few screenshots: 15 | 16 | < 17 | < 18 | ## Setup 19 | 20 | This app uses Firebase. You will need to: 21 | * Configure Firebase for your Google Cloud Project. Note that it is **REQUIRED** that your android app and the backend are connected to the same Google Cloud Project and that you deploy the backend first! 22 | * Connect the app to Firebase ([Android Studio](https://developer.android.com/studio/write/firebase); [Manually](https://firebase.google.com/docs/android/setup#manually_add_firebase)). After this step, you should have your Firebase configuration file (google-services.json) present at `frontend/emojify/` 23 | * Edit value of `cloud.project.id` in `application.properties` (src/main/assets): `cloud.project.id = REPLACE_THIS_WITH_YOUR_PROJECT_ID` 24 | * You're all set! 25 | -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.android.application") version "8.3.0" apply false 3 | kotlin("android") version "1.9.23" apply false 4 | } 5 | 6 | allprojects { 7 | repositories { 8 | mavenCentral() 9 | google() 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/.gitignore: -------------------------------------------------------------------------------- 1 | /build/ -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.android.application") 3 | kotlin("android") 4 | } 5 | 6 | kotlin { 7 | jvmToolchain(11) 8 | } 9 | 10 | dependencies { 11 | implementation("androidx.constraintlayout:constraintlayout:2.1.4") 12 | implementation("com.yanzhenjie:album:2.1.3") 13 | implementation("com.yanzhenjie:mediascanner:1.0.3") 14 | implementation("com.google.android.material:material:1.11.0") 15 | implementation("androidx.appcompat:appcompat:1.6.1") 16 | implementation("androidx.recyclerview:recyclerview:1.3.2") 17 | implementation("androidx.cardview:cardview:1.0.0") 18 | implementation("com.github.bumptech.glide:glide:4.16.0") 19 | implementation("com.android.volley:volley:1.2.1") 20 | implementation("com.google.firebase:firebase-core:21.1.1") 21 | implementation("com.google.firebase:firebase-storage:20.3.0") 22 | implementation("com.google.firebase:firebase-auth:22.3.1") 23 | implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0") 24 | } 25 | 26 | android { 27 | compileSdk = 34 28 | namespace = "com.google.cloud.kotlin.emojify" 29 | 30 | defaultConfig { 31 | minSdk = 28 32 | } 33 | 34 | compileOptions { 35 | sourceCompatibility = JavaVersion.VERSION_11 36 | targetCompatibility = JavaVersion.VERSION_11 37 | } 38 | 39 | buildFeatures { 40 | viewBinding = true 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 31 | 32 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/assets/application.properties: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Google LLC 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # Unless required by applicable law or agreed to in writing, software 7 | # distributed under the License is distributed on an "AS IS" BASIS, 8 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | # See the License for the specific language governing permissions and 10 | # limitations under the License. 11 | 12 | cloud.project.id = REPLACE_THIS_WITH_YOUR_PROJECT_ID -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/kotlin/com/google/cloud/kotlin/emojify/Adapter.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.cloud.kotlin.emojify 18 | 19 | import android.view.LayoutInflater 20 | import android.view.View 21 | import android.view.ViewGroup 22 | import androidx.recyclerview.widget.DiffUtil 23 | import androidx.recyclerview.widget.ListAdapter 24 | import androidx.recyclerview.widget.RecyclerView 25 | import com.google.cloud.kotlin.emojify.databinding.ItemContentImageBinding 26 | import com.yanzhenjie.album.Album 27 | import com.yanzhenjie.album.AlbumFile 28 | 29 | class AlbumFileDiffCallback : DiffUtil.ItemCallback() { 30 | 31 | override fun areItemsTheSame(oldItem: AlbumFile, newItem: AlbumFile): Boolean { 32 | return oldItem.path == newItem.path 33 | } 34 | 35 | override fun areContentsTheSame(oldItem: AlbumFile, newItem: AlbumFile): Boolean { 36 | return oldItem == newItem 37 | } 38 | } 39 | 40 | class Adapter(private val clickListener: (AlbumFile) -> Unit) : ListAdapter(AlbumFileDiffCallback()) { 41 | 42 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { 43 | val inflater = LayoutInflater.from(parent.context) 44 | return ViewHolder(inflater.inflate(R.layout.item_content_image, parent, false)) 45 | } 46 | 47 | override fun onBindViewHolder(holder: ViewHolder, position: Int) = holder.bind(getItem(position), clickListener) 48 | 49 | class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 50 | private lateinit var itemContentImageBinding: ItemContentImageBinding 51 | 52 | fun bind(albumFile: AlbumFile, clickListener: (AlbumFile) -> Unit) { 53 | Album.getAlbumConfig().albumLoader.load(itemContentImageBinding.ivAlbumContentImage, albumFile) 54 | itemView.setOnClickListener { clickListener(albumFile) } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/kotlin/com/google/cloud/kotlin/emojify/Application.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.cloud.kotlin.emojify 18 | 19 | import com.yanzhenjie.album.Album 20 | import com.yanzhenjie.album.AlbumConfig 21 | import java.util.Locale 22 | 23 | class Application : android.app.Application() { 24 | 25 | override fun onCreate() { 26 | super.onCreate() 27 | Album.initialize( 28 | AlbumConfig.newBuilder(this) 29 | .setAlbumLoader(MediaLoader()) 30 | .setLocale(Locale.getDefault()) 31 | .build(), 32 | ) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/kotlin/com/google/cloud/kotlin/emojify/MediaLoader.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.cloud.kotlin.emojify 18 | 19 | import android.widget.ImageView 20 | import com.bumptech.glide.Glide 21 | import com.bumptech.glide.load.engine.DiskCacheStrategy 22 | import com.bumptech.glide.request.RequestOptions 23 | import com.yanzhenjie.album.AlbumFile 24 | import com.yanzhenjie.album.AlbumLoader 25 | 26 | class MediaLoader : AlbumLoader { 27 | 28 | override fun load(imageView: ImageView, albumFile: AlbumFile) { 29 | load(imageView, albumFile.path) 30 | } 31 | 32 | override fun load(imageView: ImageView, url: String) { 33 | Glide.with(imageView.context) 34 | .load(url) 35 | .apply( 36 | RequestOptions() 37 | .diskCacheStrategy(DiskCacheStrategy.NONE) 38 | .skipMemoryCache(true) 39 | .error(R.drawable.placeholder) 40 | .placeholder(R.drawable.placeholder), 41 | ) 42 | .into(imageView) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-hdpi/ic_back_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-hdpi/ic_back_white.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-hdpi/ic_camera_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-hdpi/ic_camera_white.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-hdpi/ic_eye_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-hdpi/ic_eye_white.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-hdpi/ic_image_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-hdpi/ic_image_white.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-hdpi/ic_logo_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-hdpi/ic_logo_white.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-hdpi/ic_video_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-hdpi/ic_video_white.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-hdpi/tag_video_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-hdpi/tag_video_white.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-xhdpi/ic_back_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-xhdpi/ic_back_white.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-xhdpi/ic_camera_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-xhdpi/ic_camera_white.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-xhdpi/ic_eye_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-xhdpi/ic_eye_white.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-xhdpi/ic_image_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-xhdpi/ic_image_white.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-xhdpi/ic_logo_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-xhdpi/ic_logo_white.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-xhdpi/ic_video_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-xhdpi/ic_video_white.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-xxhdpi/ic_back_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-xxhdpi/ic_back_white.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-xxhdpi/ic_camera_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-xxhdpi/ic_camera_white.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-xxhdpi/ic_eye_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-xxhdpi/ic_eye_white.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-xxhdpi/ic_image_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-xxhdpi/ic_image_white.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-xxhdpi/ic_logo_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-xxhdpi/ic_logo_white.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-xxhdpi/ic_video_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-xxhdpi/ic_video_white.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-xxhdpi/tag_video_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-xxhdpi/tag_video_white.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-xxxhdpi/ic_back_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-xxxhdpi/ic_back_white.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-xxxhdpi/ic_camera_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-xxxhdpi/ic_camera_white.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-xxxhdpi/ic_eye_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-xxxhdpi/ic_eye_white.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-xxxhdpi/ic_image_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-xxxhdpi/ic_image_white.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-xxxhdpi/ic_logo_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-xxxhdpi/ic_logo_white.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-xxxhdpi/ic_video_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-xxxhdpi/ic_video_white.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-xxxhdpi/placeholder.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-xxxhdpi/placeholder.jpg -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-xxxhdpi/tag_video_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/frontend/emojify/src/main/res/drawable-xxxhdpi/tag_video_white.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/layout/activity_album.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/layout/activity_list_content.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 19 | 20 | 26 | 27 | 33 | 34 | 38 | 39 | -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/layout/item_content_image.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/layout/toolbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 17 | 18 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/layout/toolbar_scroll.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 18 | 19 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/menu/menu_album.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 19 | 20 | 26 | 27 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/menu/menu_album_image.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 17 | 18 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/frontend/emojify/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/frontend/emojify/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/frontend/emojify/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/frontend/emojify/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/frontend/emojify/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/frontend/emojify/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/frontend/emojify/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/frontend/emojify/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/frontend/emojify/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/frontend/emojify/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | #2196F3 16 | #1E88E5 17 | #dc572e 18 | #FF2B2B2B 19 | -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/emojify/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | Emojify 16 | + 17 | Media Album 18 | Album 19 | Original image 20 | Checkable Options 21 | Canceled 22 | There is no picture selected! 23 | Your shiny emojified picture is ready! 24 | Crossing fingers… 25 | Something is coming… 26 | Your shiny emojified picture is ready! 27 | There was an error calling the backend! 28 | There was an error with Cloud Storage! 29 | Image View 30 | -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | android.enableJetifier=true 13 | android.useAndroidX=true 14 | org.gradle.jvmargs=-Xmx1536m 15 | 16 | # When configured, Gradle will run in incubating parallel mode. 17 | # This option should only be used with decoupled projects. More details, visit 18 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 19 | # org.gradle.parallel=true 20 | -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/frontend/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /getting-started/android-with-appengine/frontend/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | google() 5 | mavenCentral() 6 | } 7 | } 8 | 9 | plugins { 10 | id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" 11 | } 12 | 13 | rootProject.name = "android-with-appengine-frontend" 14 | 15 | include(":emojify") 16 | -------------------------------------------------------------------------------- /getting-started/android-with-appengine/screenshots/emojified-meetup.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/screenshots/emojified-meetup.jpg -------------------------------------------------------------------------------- /getting-started/android-with-appengine/screenshots/meetup.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/screenshots/meetup.jpg -------------------------------------------------------------------------------- /getting-started/android-with-appengine/screenshots/placeholder-image-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/screenshots/placeholder-image-1.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/screenshots/placeholder-image-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/screenshots/placeholder-image-2.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/screenshots/result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/screenshots/result.png -------------------------------------------------------------------------------- /getting-started/android-with-appengine/screenshots/welcome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/getting-started/android-with-appengine/screenshots/welcome.png -------------------------------------------------------------------------------- /pubsub/README.md: -------------------------------------------------------------------------------- 1 | # Google Cloud Pub/Sub Kotlin Sample 2 | 3 | [![Open in Cloud Shell][shell_img]][shell_link] 4 | 5 | [shell_img]: http://gstatic.com/cloudssh/images/open-btn.svg 6 | [shell_link]: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googlecloudplatform/kotlin-samples&page=editor&working_dir=pubsub 7 | 8 | ## Description 9 | 10 | [Google Cloud Pub/Sub][pubsub] is a fully-managed real-time messaging service that allows you to send and receive messages between independent applications. This simple Kotlin command-line application demonstrates how to access the Pub/Sub API using 11 | the [Google Cloud Client Library for Java][google-cloud-java]. 12 | 13 | [pubsub]: https://cloud.google.com/pubsub/ 14 | [google-cloud-java]: https://github.com/GoogleCloudPlatform/google-cloud-java 15 | 16 | 17 | ## Quickstart 18 | 19 | #### Setup 20 | - [Enable](https://console.cloud.google.com/apis/api/pubsub.googleapis.com/overview) Pub/Sub API. 21 | - Set up [authentication](https://cloud.google.com/docs/authentication/getting-started). 22 | 23 | #### Build 24 | - Clone the repository 25 | ```sh 26 | git clone https://github.com/GoogleCloudPlatform/kotlin-samples 27 | cd kotlin-samples/pubsub 28 | ``` 29 | - Build the project with Gradle Wrapper: 30 | ```sh 31 | # run with "--info" flag to print potential errors 32 | ./gradlew build --info 33 | ``` 34 | You should now have a **'pubsub-kotlin-sample-all.jar'** file under **build/libs/** 35 | 36 | #### Create a new topic 37 | 38 | `java -jar build/libs/pubsub-kotlin-sample-all.jar create ` 39 | 40 | #### Create a subscription 41 | 42 | `java -jar build/libs/pubsub-kotlin-sample-all.jar sub ` 43 | 44 | #### Publish messages 45 | 46 | `java -jar build/libs/pubsub-kotlin-sample-all.jar pub ` 47 | 48 | #### Receive messages 49 | 50 | `java -jar build/libs/pubsub-kotlin-sample-all.jar listen ` 51 | 52 | Subscriber will continue to listen on the topic for 5 minutes and print out message id and data as messages are received. 53 | 54 | #### Delete a topic 55 | 56 | `java -jar build/libs/pubsub-kotlin-sample-all.jar del-topic ` 57 | 58 | #### Delete a subscription 59 | 60 | `java -jar build/libs/pubsub-kotlin-sample-all.jar del-sub ` 61 | 62 | #### Testing 63 | Run the test with Gradle Wrapper 64 | 65 | `./gradlew test` 66 | 67 | ## Contributing changes 68 | 69 | * See [CONTRIBUTING.md](../CONTRIBUTING.md) 70 | 71 | ## Licensing 72 | 73 | * See [LICENSE](../LICENSE) 74 | -------------------------------------------------------------------------------- /pubsub/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.gradle.api.tasks.testing.logging.TestLogEvent.* 2 | 3 | plugins { 4 | application 5 | kotlin("jvm") version "1.9.23" 6 | id("com.github.johnrengelman.shadow") version "8.1.1" 7 | } 8 | 9 | repositories { 10 | mavenCentral() 11 | } 12 | 13 | dependencies { 14 | implementation(platform("com.google.cloud:libraries-bom:26.34.0")) 15 | implementation("com.google.cloud:google-cloud-core") 16 | implementation("com.google.cloud:google-cloud-pubsub") 17 | implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0") 18 | testImplementation("com.google.truth:truth:1.4.2") 19 | testImplementation("org.jetbrains.kotlin:kotlin-test-junit") 20 | // see: https://github.com/googleapis/sdk-platform-java/pull/1832 21 | modules { 22 | module("com.google.guava:listenablefuture") { 23 | replacedBy("com.google.guava:guava", "listenablefuture is part of guava") 24 | } 25 | } 26 | } 27 | 28 | kotlin { 29 | jvmToolchain(17) 30 | } 31 | 32 | tasks.withType { 33 | testLogging { 34 | showStandardStreams = true 35 | exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL 36 | events(STARTED, PASSED, SKIPPED, FAILED) 37 | } 38 | } 39 | 40 | application { 41 | mainClass.set("pubsubKt") 42 | } 43 | -------------------------------------------------------------------------------- /pubsub/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/pubsub/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /pubsub/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /pubsub/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenCentral() 4 | gradlePluginPortal() 5 | } 6 | } 7 | 8 | rootProject.name = "pubsub-kotlin-sample" 9 | 10 | plugins { 11 | id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" 12 | } 13 | -------------------------------------------------------------------------------- /run/README.md: -------------------------------------------------------------------------------- 1 | Kotlin Samples for Cloud Run 2 | ============================ 3 | 4 | These sample Kotlin applications demonstrate how to deploy Kotlin to [Google Cloud Run](https://cloud.google.com/run/docs). 5 | 6 | |Link|Description|Deploy| 7 | |---|---|---| 8 | |[Plain Kotlin](plain-hello-world)|Deploy a basic HTTP server Cloud Run (uses Maven)|[![Run on Google Cloud](https://deploy.cloud.run/button.svg)](https://deploy.cloud.run/?dir=run/plain-hello-world)| 9 | |[Spring Boot Kotlin](springboot-hello-world)|Deploy a basic Spring Boot service on Cloud Run (uses Gradle)|[![Run on Google Cloud](https://deploy.cloud.run/button.svg)](https://deploy.cloud.run/?dir=run/springboot-hello-world)| 10 | |[Ktor Kotlin](ktor-hello-world)|Deploy a basic Ktor service on Cloud Run (uses Gradle)|[![Run on Google Cloud](https://deploy.cloud.run/button.svg)](https://deploy.cloud.run/?dir=run/ktor-hello-world)| 11 | |[http4k Kotlin](http4k-hello-world)|Deploy a basic http4k service on Cloud Run (uses Gradle)|[![Run on Google Cloud](https://deploy.cloud.run/button.svg)](https://deploy.cloud.run/?dir=run/http4k-hello-world)| 12 | |[Micronaut Kotlin](micronaut-hello-world)|Deploy a basic Micronaut service on Cloud Run (uses Gradle)|[![Run on Google Cloud](https://deploy.cloud.run/button.svg)](https://deploy.cloud.run/?dir=run/micronaut-hello-world)| 13 | |[Quarkus Kotlin](quarkus-hello-world)|Deploy a basic Quarkus service on Cloud Run (uses Maven)|[![Run on Google Cloud](https://deploy.cloud.run/button.svg)](https://deploy.cloud.run/?dir=run/quarkus-hello-world)| 14 | |[gRPC Kotlin Maven](grpc-hello-world-mvn)|Deploy a unary gRPC service on Cloud Run (uses Maven)|[![Run on Google Cloud](https://deploy.cloud.run/button.svg)](https://deploy.cloud.run/?dir=run/grpc-hello-world-mvn)| 15 | |[gRPC Kotlin Gradle](grpc-hello-world-gradle)|Deploy a unary gRPC service on Cloud Run (uses Gradle)|[![Run on Google Cloud](https://deploy.cloud.run/button.svg)](https://deploy.cloud.run/?dir=run/grpc-hello-world-gradle)| 16 | |[gRPC Kotlin Streaming](grpc-hello-world-streaming)|Deploy a unary streaming gRPC service on Cloud Run|[![Run on Google Cloud](https://deploy.cloud.run/button.svg)](https://deploy.cloud.run/?dir=run/grpc-hello-world-streaming)| 17 | |[gRPC Kotlin Bidi Streaming](grpc-hello-world-bidi-streaming)|Deploy a bidirectional streaming gRPC service on Cloud Run|[![Run on Google Cloud](https://deploy.cloud.run/button.svg)](https://deploy.cloud.run/?dir=run/grpc-hello-world-bidi-streaming)| 18 | |[Spring Boot CloudSQL](springboot-cloudsql)|A Spring Boot REST server connected to CloudSQL Postgres using R2DBC|TODO: Button Support| 19 | -------------------------------------------------------------------------------- /run/grpc-hello-world-bidi-streaming/.editorconfig: -------------------------------------------------------------------------------- 1 | [build/generated/source/proto/main/**] 2 | ktlint = disabled 3 | -------------------------------------------------------------------------------- /run/grpc-hello-world-bidi-streaming/.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /.idea/ 3 | /.gradle/ 4 | -------------------------------------------------------------------------------- /run/grpc-hello-world-bidi-streaming/Procfile: -------------------------------------------------------------------------------- 1 | web: build/install/grpc-hello-world-bidi-streaming/bin/grpc-hello-world-bidi-streaming 2 | client: build/install/grpc-hello-world-streaming/bin/HelloWorldClientKt $HOST 3 | -------------------------------------------------------------------------------- /run/grpc-hello-world-bidi-streaming/README.md: -------------------------------------------------------------------------------- 1 | gRPC Kotlin BiDi Streaming Cloud Run Example 2 | -------------------------------------------- 3 | 4 | Run Locally: 5 | 1. In one shell / terminal window, start the server: 6 | ``` 7 | ./gradlew run 8 | ``` 9 | 1. In another shell / terminal window, run the client: 10 | ``` 11 | ./gradlew HelloWorldClient 12 | ``` 13 | 14 | You should continually see: `hello, world` 15 | 16 | Deploy on Cloud Run: 17 | 18 | 1. [![Run on Google Cloud](https://deploy.cloud.run/button.svg)](https://deploy.cloud.run) 19 | 20 | *This will take a few minutes to build and deploy.* 21 | 22 | 1. From within Cloud Shell, run the client against the service you just deployed on Cloud Run, replacing `YOUR_CLOUD_RUN_DOMAIN_NAME` with your service's domain name and replacing `YOUR_PROJECT_ID` with your GCP project: 23 | ``` 24 | export PROJECT_ID=YOUR_PROJECT_ID 25 | docker run -it --entrypoint=/cnb/lifecycle/launcher gcr.io/$PROJECT_ID/grpc-hello-world-bidi-streaming \ 26 | "build/install/grpc-hello-world-bidi-streaming/bin/HelloWorldClientKt YOUR_CLOUD_RUN_DOMAIN_NAME" 27 | ``` 28 | 29 | You should continually see output like: `hello, YOUR_CLOUD_RUN_DOMAIN_NAME` 30 | -------------------------------------------------------------------------------- /run/grpc-hello-world-bidi-streaming/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "options": { 3 | "http2": true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /run/grpc-hello-world-bidi-streaming/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | application 3 | kotlin("jvm") version "1.9.23" 4 | id("com.google.protobuf") version "0.9.4" 5 | id("org.jlleitschuh.gradle.ktlint") version "12.1.0" 6 | } 7 | 8 | repositories { 9 | mavenLocal() 10 | google() 11 | mavenCentral() 12 | } 13 | 14 | java { 15 | toolchain { 16 | languageVersion.set(JavaLanguageVersion.of(8)) 17 | } 18 | } 19 | 20 | kotlin.sourceSets.all { 21 | languageSettings.optIn("kotlin.RequiresOptIn") 22 | } 23 | 24 | val grpcVersion = "1.62.2" 25 | val grpcKotlinVersion = "1.4.1" 26 | val protobufVersion = "3.25.3" 27 | val coroutinesVersion = "1.8.0" 28 | 29 | dependencies { 30 | implementation(kotlin("stdlib")) 31 | implementation("javax.annotation:javax.annotation-api:1.3.2") 32 | implementation("io.grpc:grpc-kotlin-stub:$grpcKotlinVersion") 33 | implementation("io.grpc:grpc-protobuf:$grpcVersion") 34 | implementation("com.google.protobuf:protobuf-kotlin:$protobufVersion") 35 | implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion") 36 | runtimeOnly("io.grpc:grpc-netty-shaded:$grpcVersion") 37 | } 38 | 39 | protobuf { 40 | protoc { 41 | artifact = "com.google.protobuf:protoc:$protobufVersion" 42 | } 43 | 44 | plugins { 45 | create("grpc") { 46 | artifact = "io.grpc:protoc-gen-grpc-java:$grpcVersion" 47 | } 48 | create("grpckt") { 49 | artifact = "io.grpc:protoc-gen-grpc-kotlin:$grpcKotlinVersion:jdk8@jar" 50 | } 51 | } 52 | 53 | generateProtoTasks { 54 | all().forEach { 55 | it.plugins { 56 | create("grpc") 57 | create("grpckt") 58 | } 59 | it.builtins { 60 | create("kotlin") 61 | } 62 | } 63 | } 64 | } 65 | 66 | application { 67 | mainClass.set("io.grpc.examples.helloworld.HelloWorldServerKt") 68 | } 69 | 70 | ktlint { 71 | filter { 72 | // this doesn't work: https://github.com/JLLeitschuh/ktlint-gradle/issues/746 73 | exclude("**/generated/**") 74 | } 75 | } 76 | 77 | tasks.register("HelloWorldClient") { 78 | dependsOn("classes") 79 | classpath = sourceSets["main"].runtimeClasspath 80 | mainClass.set("io.grpc.examples.helloworld.HelloWorldClientKt") 81 | } 82 | 83 | val otherStartScripts = 84 | tasks.register("otherStartScripts") { 85 | mainClass.set("io.grpc.examples.helloworld.HelloWorldClientKt") 86 | applicationName = "HelloWorldClientKt" 87 | outputDir = tasks.named("startScripts").get().outputDir 88 | classpath = tasks.named("startScripts").get().classpath 89 | } 90 | 91 | tasks.named("startScripts") { 92 | dependsOn(otherStartScripts) 93 | } 94 | 95 | task("stage").dependsOn("installDist") 96 | 97 | tasks.replace("assemble").dependsOn(":installDist") 98 | -------------------------------------------------------------------------------- /run/grpc-hello-world-bidi-streaming/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/run/grpc-hello-world-bidi-streaming/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /run/grpc-hello-world-bidi-streaming/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /run/grpc-hello-world-bidi-streaming/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenCentral() 4 | gradlePluginPortal() 5 | } 6 | } 7 | 8 | rootProject.name = "grpc-hello-world-bidi-streaming" 9 | 10 | plugins { 11 | id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" 12 | } 13 | -------------------------------------------------------------------------------- /run/grpc-hello-world-bidi-streaming/src/main/kotlin/io/grpc/examples/helloworld/HelloWorldClient.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 gRPC authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.grpc.examples.helloworld 18 | 19 | import io.grpc.ManagedChannel 20 | import io.grpc.ManagedChannelBuilder 21 | import io.grpc.examples.helloworld.GreeterGrpcKt.GreeterCoroutineStub 22 | import kotlinx.coroutines.Dispatchers 23 | import kotlinx.coroutines.asExecutor 24 | import kotlinx.coroutines.coroutineScope 25 | import kotlinx.coroutines.delay 26 | import kotlinx.coroutines.flow.Flow 27 | import kotlinx.coroutines.flow.collect 28 | import kotlinx.coroutines.flow.flow 29 | import kotlinx.coroutines.runBlocking 30 | import java.io.Closeable 31 | import java.util.concurrent.TimeUnit 32 | 33 | class HelloWorldClient(private val channel: ManagedChannel) : Closeable { 34 | private val stub: GreeterCoroutineStub = GreeterCoroutineStub(channel) 35 | 36 | suspend fun sayHello(producer: Flow): Unit = 37 | coroutineScope { 38 | stub.sayHelloStream(producer).collect { helloResponse -> 39 | println(helloResponse.message) 40 | } 41 | } 42 | 43 | override fun close() { 44 | channel.shutdown().awaitTermination(5, TimeUnit.SECONDS) 45 | } 46 | } 47 | 48 | fun main(args: Array) = 49 | runBlocking { 50 | val isRemote = args.size == 1 51 | 52 | val builder = 53 | if (isRemote) { 54 | ManagedChannelBuilder.forTarget(args[0].removePrefix("https://") + ":443").useTransportSecurity() 55 | } else { 56 | ManagedChannelBuilder.forTarget("localhost:50051").usePlaintext() 57 | } 58 | 59 | val client = HelloWorldClient(builder.executor(Dispatchers.Default.asExecutor()).build()) 60 | 61 | val user = args.singleOrNull() ?: "world" 62 | 63 | val helloFlow = 64 | flow { 65 | while (true) { 66 | delay(1000) 67 | emit(helloRequest { name = user }) 68 | } 69 | } 70 | 71 | client.sayHello(helloFlow) 72 | } 73 | -------------------------------------------------------------------------------- /run/grpc-hello-world-bidi-streaming/src/main/kotlin/io/grpc/examples/helloworld/HelloWorldServer.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 gRPC authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.grpc.examples.helloworld 18 | 19 | import io.grpc.Server 20 | import io.grpc.ServerBuilder 21 | import kotlinx.coroutines.flow.Flow 22 | import kotlinx.coroutines.flow.map 23 | 24 | class HelloWorldServer(private val port: Int) { 25 | val server: Server = 26 | ServerBuilder 27 | .forPort(port) 28 | .addService(HelloWorldService()) 29 | .build() 30 | 31 | fun start() { 32 | server.start() 33 | println("Server started, listening on $port") 34 | val thread = 35 | Thread { 36 | println("*** shutting down gRPC server since JVM is shutting down") 37 | stop() 38 | println("*** server shut down") 39 | } 40 | Runtime.getRuntime().addShutdownHook(thread) 41 | } 42 | 43 | private fun stop() { 44 | server.shutdown() 45 | } 46 | 47 | fun blockUntilShutdown() { 48 | server.awaitTermination() 49 | } 50 | 51 | private class HelloWorldService : GreeterGrpcKt.GreeterCoroutineImplBase() { 52 | override fun sayHelloStream(requests: Flow): Flow { 53 | return requests.map { request -> 54 | println(request) 55 | helloReply { message = "hello, ${request.name}" } 56 | } 57 | } 58 | } 59 | } 60 | 61 | fun main() { 62 | val port = System.getenv("PORT")?.toInt() ?: 50051 63 | val server = HelloWorldServer(port) 64 | server.start() 65 | server.blockUntilShutdown() 66 | } 67 | -------------------------------------------------------------------------------- /run/grpc-hello-world-bidi-streaming/src/main/proto/hello_world.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2015 gRPC authors. 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 | // http://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 | syntax = "proto3"; 16 | 17 | package io.grpc.examples.helloworld; 18 | 19 | option java_multiple_files = true; 20 | 21 | // The greeting service definition. 22 | service Greeter { 23 | // Continually sends a greeting 24 | rpc SayHelloStream (stream HelloRequest) returns (stream HelloReply) {} 25 | } 26 | 27 | // The request message containing the user's name. 28 | message HelloRequest { 29 | string name = 1; 30 | } 31 | 32 | // The response message containing the greetings 33 | message HelloReply { 34 | string message = 1; 35 | } 36 | -------------------------------------------------------------------------------- /run/grpc-hello-world-gradle/.editorconfig: -------------------------------------------------------------------------------- 1 | [build/generated/source/proto/main/**] 2 | ktlint = disabled 3 | -------------------------------------------------------------------------------- /run/grpc-hello-world-gradle/.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /.idea/ 3 | /.gradle/ 4 | -------------------------------------------------------------------------------- /run/grpc-hello-world-gradle/Procfile: -------------------------------------------------------------------------------- 1 | web: build/install/grpc-hello-world-gradle/bin/grpc-hello-world-gradle 2 | client: build/install/grpc-hello-world-gradle/bin/HelloWorldClientKt $HOST 3 | -------------------------------------------------------------------------------- /run/grpc-hello-world-gradle/README.md: -------------------------------------------------------------------------------- 1 | gRPC Kotlin Cloud Run Example 2 | ----------------------------- 3 | 4 | Run Locally: 5 | 1. In one shell / terminal window, start the server: 6 | ``` 7 | ./gradlew run 8 | ``` 9 | 1. In another shell / terminal window, run the client: 10 | ``` 11 | ./gradlew HelloWorldClient 12 | ``` 13 | 14 | You should see output like: `Greeter client received: Hello world` 15 | 16 | Deploy on Cloud Run: 17 | 18 | 1. [![Run on Google Cloud](https://deploy.cloud.run/button.svg)](https://deploy.cloud.run) 19 | 20 | *This will take a few minutes to build and deploy.* 21 | 22 | 1. From within Cloud Shell, run the client against the service you just deployed on Cloud Run, replacing `YOUR_CLOUD_RUN_DOMAIN_NAME` with your service's domain name and replacing `YOUR_PROJECT_ID` with your GCP project: 23 | ``` 24 | export PROJECT_ID=YOUR_PROJECT_ID 25 | docker run -it --entrypoint=/cnb/lifecycle/launcher gcr.io/$PROJECT_ID/grpc-hello-world-gradle \ 26 | "build/install/grpc-hello-world-gradle/bin/HelloWorldClientKt YOUR_CLOUD_RUN_DOMAIN_NAME" 27 | ``` 28 | 29 | You should see output like: `Greeter client received: Hello YOUR_CLOUD_RUN_DOMAIN_NAME` 30 | -------------------------------------------------------------------------------- /run/grpc-hello-world-gradle/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | application 3 | kotlin("jvm") version "1.9.23" 4 | id("com.google.protobuf") version "0.9.4" 5 | id("org.jlleitschuh.gradle.ktlint") version "12.1.0" 6 | } 7 | 8 | repositories { 9 | google() 10 | mavenCentral() 11 | } 12 | 13 | kotlin { 14 | jvmToolchain(17) 15 | } 16 | 17 | kotlin.sourceSets.all { 18 | languageSettings.optIn("kotlin.RequiresOptIn") 19 | } 20 | 21 | val grpcVersion = "1.62.2" 22 | val grpcKotlinVersion = "1.4.1" 23 | val protobufVersion = "3.25.3" 24 | val coroutinesVersion = "1.8.0" 25 | 26 | dependencies { 27 | implementation("javax.annotation:javax.annotation-api:1.3.2") 28 | implementation("io.grpc:grpc-kotlin-stub:$grpcKotlinVersion") 29 | implementation("io.grpc:grpc-protobuf:$grpcVersion") 30 | implementation("com.google.protobuf:protobuf-kotlin:$protobufVersion") 31 | implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion") 32 | runtimeOnly("io.grpc:grpc-netty-shaded:$grpcVersion") 33 | } 34 | 35 | protobuf { 36 | protoc { 37 | artifact = "com.google.protobuf:protoc:$protobufVersion" 38 | } 39 | 40 | plugins { 41 | create("grpc") { 42 | artifact = "io.grpc:protoc-gen-grpc-java:$grpcVersion" 43 | } 44 | create("grpckt") { 45 | artifact = "io.grpc:protoc-gen-grpc-kotlin:$grpcKotlinVersion:jdk8@jar" 46 | } 47 | } 48 | 49 | generateProtoTasks { 50 | all().forEach { 51 | it.plugins { 52 | create("grpc") 53 | create("grpckt") 54 | } 55 | it.builtins { 56 | create("kotlin") 57 | } 58 | } 59 | } 60 | } 61 | 62 | application { 63 | mainClass.set("io.grpc.examples.helloworld.HelloWorldServerKt") 64 | } 65 | 66 | ktlint { 67 | filter { 68 | // this doesn't work: https://github.com/JLLeitschuh/ktlint-gradle/issues/746 69 | exclude("**/generated/**") 70 | } 71 | } 72 | 73 | tasks.register("HelloWorldClient") { 74 | dependsOn("classes") 75 | javaLauncher.set(javaToolchains.launcherFor(java.toolchain)) 76 | classpath = sourceSets["main"].runtimeClasspath 77 | mainClass.set("io.grpc.examples.helloworld.HelloWorldClientKt") 78 | } 79 | 80 | val otherStartScripts = 81 | tasks.register("otherStartScripts") { 82 | mainClass.set("io.grpc.examples.helloworld.HelloWorldClientKt") 83 | applicationName = "HelloWorldClientKt" 84 | outputDir = tasks.named("startScripts").get().outputDir 85 | classpath = tasks.named("startScripts").get().classpath 86 | } 87 | 88 | tasks.named("startScripts") { 89 | dependsOn(otherStartScripts) 90 | } 91 | 92 | task("stage").dependsOn("installDist") 93 | 94 | tasks.replace("assemble").dependsOn(":installDist") 95 | -------------------------------------------------------------------------------- /run/grpc-hello-world-gradle/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/run/grpc-hello-world-gradle/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /run/grpc-hello-world-gradle/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /run/grpc-hello-world-gradle/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenCentral() 4 | gradlePluginPortal() 5 | } 6 | } 7 | 8 | rootProject.name = "grpc-hello-world-gradle" 9 | 10 | plugins { 11 | id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" 12 | } 13 | -------------------------------------------------------------------------------- /run/grpc-hello-world-gradle/src/main/kotlin/io/grpc/examples/helloworld/HelloWorldClient.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 gRPC authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.grpc.examples.helloworld 18 | 19 | import io.grpc.ManagedChannel 20 | import io.grpc.ManagedChannelBuilder 21 | import io.grpc.StatusException 22 | import io.grpc.examples.helloworld.GreeterGrpcKt.GreeterCoroutineStub 23 | import kotlinx.coroutines.asCoroutineDispatcher 24 | import kotlinx.coroutines.asExecutor 25 | import kotlinx.coroutines.runBlocking 26 | import java.io.Closeable 27 | import java.util.concurrent.Executors 28 | import java.util.concurrent.TimeUnit 29 | 30 | class HelloWorldClient(val channel: ManagedChannel) : Closeable { 31 | private val stub: GreeterCoroutineStub = GreeterCoroutineStub(channel) 32 | 33 | fun greet(s: String) = 34 | runBlocking { 35 | val request = helloRequest { name = s } 36 | try { 37 | val response = stub.sayHello(request) 38 | println("Greeter client received: ${response.message}") 39 | } catch (e: StatusException) { 40 | println("RPC failed: ${e.status}") 41 | } 42 | } 43 | 44 | override fun close() { 45 | channel.shutdown().awaitTermination(5, TimeUnit.SECONDS) 46 | } 47 | } 48 | 49 | /** 50 | * Greeter, uses first argument as name to greet if present; 51 | * greets "world" otherwise. 52 | */ 53 | fun main(args: Array) { 54 | val isRemote = args.size == 1 55 | 56 | Executors.newFixedThreadPool(10).asCoroutineDispatcher().use { dispatcher -> 57 | val builder = 58 | if (isRemote) { 59 | ManagedChannelBuilder.forTarget(args[0].removePrefix("https://") + ":443").useTransportSecurity() 60 | } else { 61 | ManagedChannelBuilder.forTarget("localhost:50051").usePlaintext() 62 | } 63 | 64 | val channel = builder.executor(dispatcher.asExecutor()).build() 65 | HelloWorldClient(channel).use { 66 | val user = args.singleOrNull() ?: "world" 67 | it.greet(user) 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /run/grpc-hello-world-gradle/src/main/kotlin/io/grpc/examples/helloworld/HelloWorldServer.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 gRPC authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.grpc.examples.helloworld 18 | 19 | import io.grpc.Server 20 | import io.grpc.ServerBuilder 21 | 22 | class HelloWorldServer(val port: Int) { 23 | val server: Server = 24 | ServerBuilder 25 | .forPort(port) 26 | .addService(HelloWorldService()) 27 | .build() 28 | 29 | fun start() { 30 | server.start() 31 | println("Server started, listening on $port") 32 | val thread = 33 | Thread { 34 | println("*** shutting down gRPC server since JVM is shutting down") 35 | stop() 36 | println("*** server shut down") 37 | } 38 | Runtime.getRuntime().addShutdownHook(thread) 39 | } 40 | 41 | private fun stop() { 42 | server.shutdown() 43 | } 44 | 45 | fun blockUntilShutdown() { 46 | server.awaitTermination() 47 | } 48 | 49 | private class HelloWorldService : GreeterGrpcKt.GreeterCoroutineImplBase() { 50 | override suspend fun sayHello(request: HelloRequest) = 51 | helloReply { 52 | message = "Hello ${request.name}" 53 | } 54 | } 55 | } 56 | 57 | fun main() { 58 | val port = System.getenv("PORT")?.toInt() ?: 50051 59 | val server = HelloWorldServer(port) 60 | server.start() 61 | server.blockUntilShutdown() 62 | } 63 | -------------------------------------------------------------------------------- /run/grpc-hello-world-gradle/src/main/proto/hello_world.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2015 gRPC authors. 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 | // http://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 | syntax = "proto3"; 16 | 17 | package io.grpc.examples.helloworld; 18 | 19 | option java_multiple_files = true; 20 | 21 | // The greeting service definition. 22 | service Greeter { 23 | // Sends a greeting 24 | rpc SayHello (HelloRequest) returns (HelloReply) {} 25 | } 26 | 27 | // The request message containing the user's name. 28 | message HelloRequest { 29 | string name = 1; 30 | } 31 | 32 | // The response message containing the greetings 33 | message HelloReply { 34 | string message = 1; 35 | } 36 | -------------------------------------------------------------------------------- /run/grpc-hello-world-mvn/.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /.idea/ 3 | /.gradle/ 4 | /.mvn/wrapper/maven-wrapper.jar 5 | -------------------------------------------------------------------------------- /run/grpc-hello-world-mvn/.mvn/jvm.config: -------------------------------------------------------------------------------- 1 | --add-opens java.base/java.lang=ALL-UNNAMED 2 | -------------------------------------------------------------------------------- /run/grpc-hello-world-mvn/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.5/apache-maven-3.9.5-bin.zip 18 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar 19 | -------------------------------------------------------------------------------- /run/grpc-hello-world-mvn/Procfile: -------------------------------------------------------------------------------- 1 | web: java $JAVA_OPTS -cp target/classes:target/dependency/* io.grpc.examples.helloworld.HelloWorldServerKt 2 | client: java $JAVA_OPTS -cp target/classes:target/dependency/* io.grpc.examples.helloworld.HelloWorldClientKt $HOST 3 | -------------------------------------------------------------------------------- /run/grpc-hello-world-mvn/README.md: -------------------------------------------------------------------------------- 1 | gRPC Kotlin Cloud Run Example 2 | ----------------------------- 3 | 4 | Run Locally: 5 | 1. In one shell / terminal window, start the server: 6 | ``` 7 | ./mvnw compile exec:java -Dexec.mainClass="io.grpc.examples.helloworld.HelloWorldServerKt" 8 | ``` 9 | 1. In another shell / terminal window, run the client: 10 | ``` 11 | ./mvnw compile exec:java -Dexec.mainClass="io.grpc.examples.helloworld.HelloWorldClientKt" 12 | ``` 13 | 14 | You should see output like: `Greeter client received: Hello world` 15 | 16 | Deploy on Cloud Run: 17 | 18 | 1. [![Run on Google Cloud](https://deploy.cloud.run/button.svg)](https://deploy.cloud.run) 19 | 20 | *This will take a few minutes to build and deploy.* 21 | 22 | 1. From within Cloud Shell, run the client against the service you just deployed on Cloud Run, replacing `YOUR_CLOUD_RUN_DOMAIN_NAME` with your service's domain name and replacing `YOUR_PROJECT_ID` with your GCP project: 23 | ``` 24 | export PROJECT_ID=YOUR_PROJECT_ID 25 | docker run -it --entrypoint=/cnb/lifecycle/launcher gcr.io/$PROJECT_ID/grpc-hello-world-mvn \ 26 | "java -cp target/classes:target/dependency/* io.grpc.examples.helloworld.HelloWorldClientKt YOUR_CLOUD_RUN_DOMAIN_NAME" 27 | ``` 28 | 29 | You should see output like: `Greeter client received: Hello YOUR_CLOUD_RUN_DOMAIN_NAME` 30 | -------------------------------------------------------------------------------- /run/grpc-hello-world-mvn/src/main/kotlin/io/grpc/examples/helloworld/HelloWorldClient.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 gRPC authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.grpc.examples.helloworld 18 | 19 | import io.grpc.ManagedChannel 20 | import io.grpc.ManagedChannelBuilder 21 | import io.grpc.StatusException 22 | import io.grpc.examples.helloworld.GreeterGrpcKt.GreeterCoroutineStub 23 | import kotlinx.coroutines.asCoroutineDispatcher 24 | import kotlinx.coroutines.asExecutor 25 | import kotlinx.coroutines.runBlocking 26 | import java.io.Closeable 27 | import java.util.concurrent.Executors 28 | import java.util.concurrent.TimeUnit 29 | 30 | class HelloWorldClient(val channel: ManagedChannel) : Closeable { 31 | private val stub: GreeterCoroutineStub = GreeterCoroutineStub(channel) 32 | 33 | fun greet(s: String) = runBlocking { 34 | val request = helloRequest { name = s } 35 | try { 36 | val response = stub.sayHello(request) 37 | println("Greeter client received: ${response.message}") 38 | } catch (e: StatusException) { 39 | println("RPC failed: ${e.status}") 40 | } 41 | } 42 | 43 | override fun close() { 44 | channel.shutdown().awaitTermination(5, TimeUnit.SECONDS) 45 | } 46 | } 47 | 48 | /** 49 | * Greeter, uses first argument as name to greet if present; 50 | * greets "world" otherwise. 51 | */ 52 | fun main(args: Array) { 53 | val isRemote = args.size == 1 54 | 55 | Executors.newFixedThreadPool(10).asCoroutineDispatcher().use { dispatcher -> 56 | val builder = if (isRemote) { 57 | ManagedChannelBuilder.forTarget(args[0].removePrefix("https://") + ":443").useTransportSecurity() 58 | } else { 59 | ManagedChannelBuilder.forTarget("localhost:50051").usePlaintext() 60 | } 61 | 62 | HelloWorldClient(builder.executor(dispatcher.asExecutor()).build()).use { 63 | val user = args.singleOrNull() ?: "world" 64 | it.greet(user) 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /run/grpc-hello-world-mvn/src/main/kotlin/io/grpc/examples/helloworld/HelloWorldServer.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 gRPC authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.grpc.examples.helloworld 18 | 19 | import io.grpc.Server 20 | import io.grpc.ServerBuilder 21 | 22 | class HelloWorldServer(val port: Int) { 23 | val server: Server = ServerBuilder 24 | .forPort(port) 25 | .addService(HelloWorldService()) 26 | .build() 27 | 28 | fun start() { 29 | server.start() 30 | println("Server started, listening on $port") 31 | Runtime.getRuntime().addShutdownHook( 32 | Thread { 33 | println("*** shutting down gRPC server since JVM is shutting down") 34 | stop() 35 | println("*** server shut down") 36 | }, 37 | ) 38 | } 39 | 40 | private fun stop() { 41 | server.shutdown() 42 | } 43 | 44 | fun blockUntilShutdown() { 45 | server.awaitTermination() 46 | } 47 | 48 | private class HelloWorldService : GreeterGrpcKt.GreeterCoroutineImplBase() { 49 | override suspend fun sayHello(request: HelloRequest) = helloReply { 50 | message = "Hello ${request.name}" 51 | } 52 | } 53 | } 54 | 55 | fun main() { 56 | val port = System.getenv("PORT")?.toInt() ?: 50051 57 | val server = HelloWorldServer(port) 58 | server.start() 59 | server.blockUntilShutdown() 60 | } 61 | -------------------------------------------------------------------------------- /run/grpc-hello-world-mvn/src/main/proto/hello_world.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2015 gRPC authors. 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 | // http://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 | syntax = "proto3"; 16 | 17 | package io.grpc.examples.helloworld; 18 | 19 | option java_multiple_files = true; 20 | 21 | // The greeting service definition. 22 | service Greeter { 23 | // Sends a greeting 24 | rpc SayHello (HelloRequest) returns (HelloReply) {} 25 | } 26 | 27 | // The request message containing the user's name. 28 | message HelloRequest { 29 | string name = 1; 30 | } 31 | 32 | // The response message containing the greetings 33 | message HelloReply { 34 | string message = 1; 35 | } 36 | -------------------------------------------------------------------------------- /run/grpc-hello-world-streaming/.editorconfig: -------------------------------------------------------------------------------- 1 | [build/generated/source/proto/main/**] 2 | ktlint = disabled 3 | -------------------------------------------------------------------------------- /run/grpc-hello-world-streaming/.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /.idea/ 3 | /.gradle/ 4 | -------------------------------------------------------------------------------- /run/grpc-hello-world-streaming/Procfile: -------------------------------------------------------------------------------- 1 | web: build/install/grpc-hello-world-streaming/bin/grpc-hello-world-streaming 2 | client: build/install/grpc-hello-world-streaming/bin/HelloWorldClientKt $HOST 3 | -------------------------------------------------------------------------------- /run/grpc-hello-world-streaming/README.md: -------------------------------------------------------------------------------- 1 | gRPC Kotlin Server Streaming Cloud Run Example 2 | --------------------------------------- 3 | 4 | Run Locally: 5 | 1. In one shell / terminal window, start the server: 6 | ``` 7 | ./gradlew run 8 | ``` 9 | 1. In another shell / terminal window, run the client: 10 | ``` 11 | ./gradlew HelloWorldClient 12 | ``` 13 | 14 | You should continually see: `hello, world` 15 | 16 | Deploy on Cloud Run: 17 | 18 | 1. [![Run on Google Cloud](https://deploy.cloud.run/button.svg)](https://deploy.cloud.run) 19 | 20 | *This will take a few minutes to build and deploy.* 21 | 22 | 1. From within Cloud Shell, run the client against the service you just deployed on Cloud Run, replacing `YOUR_CLOUD_RUN_DOMAIN_NAME` with your service's domain name and replacing `YOUR_PROJECT_ID` with your GCP project: 23 | ``` 24 | export PROJECT_ID=YOUR_PROJECT_ID 25 | docker run -it --entrypoint=/cnb/lifecycle/launcher gcr.io/$PROJECT_ID/grpc-hello-world-streaming \ 26 | "build/install/grpc-hello-world-streaming/bin/HelloWorldClientKt YOUR_CLOUD_RUN_DOMAIN_NAME" 27 | ``` 28 | 29 | You should continually see output like: `Greeter client received: Hello YOUR_CLOUD_RUN_DOMAIN_NAME` 30 | -------------------------------------------------------------------------------- /run/grpc-hello-world-streaming/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | application 3 | kotlin("jvm") version "1.9.23" 4 | id("com.google.protobuf") version "0.9.4" 5 | id("org.jlleitschuh.gradle.ktlint") version "12.1.0" 6 | } 7 | 8 | repositories { 9 | mavenLocal() 10 | google() 11 | mavenCentral() 12 | } 13 | 14 | java { 15 | toolchain { 16 | languageVersion.set(JavaLanguageVersion.of(8)) 17 | } 18 | } 19 | 20 | kotlin.sourceSets.all { 21 | languageSettings.optIn("kotlin.RequiresOptIn") 22 | } 23 | 24 | val grpcVersion = "1.62.2" 25 | val grpcKotlinVersion = "1.4.1" 26 | val protobufVersion = "3.25.3" 27 | val coroutinesVersion = "1.8.0" 28 | 29 | dependencies { 30 | implementation(kotlin("stdlib")) 31 | implementation("javax.annotation:javax.annotation-api:1.3.2") 32 | implementation("io.grpc:grpc-kotlin-stub:$grpcKotlinVersion") 33 | implementation("io.grpc:grpc-protobuf:$grpcVersion") 34 | implementation("com.google.protobuf:protobuf-kotlin:$protobufVersion") 35 | implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion") 36 | runtimeOnly("io.grpc:grpc-netty-shaded:$grpcVersion") 37 | } 38 | 39 | protobuf { 40 | protoc { 41 | artifact = "com.google.protobuf:protoc:$protobufVersion" 42 | } 43 | 44 | plugins { 45 | create("grpc") { 46 | artifact = "io.grpc:protoc-gen-grpc-java:$grpcVersion" 47 | } 48 | create("grpckt") { 49 | artifact = "io.grpc:protoc-gen-grpc-kotlin:$grpcKotlinVersion:jdk8@jar" 50 | } 51 | } 52 | 53 | generateProtoTasks { 54 | all().forEach { 55 | it.plugins { 56 | create("grpc") 57 | create("grpckt") 58 | } 59 | it.builtins { 60 | create("kotlin") 61 | } 62 | } 63 | } 64 | } 65 | 66 | application { 67 | mainClass.set("io.grpc.examples.helloworld.HelloWorldServerKt") 68 | } 69 | 70 | ktlint { 71 | filter { 72 | // this doesn't work: https://github.com/JLLeitschuh/ktlint-gradle/issues/746 73 | exclude("**/generated/**") 74 | } 75 | } 76 | 77 | tasks.register("HelloWorldClient") { 78 | dependsOn("classes") 79 | classpath = sourceSets["main"].runtimeClasspath 80 | mainClass.set("io.grpc.examples.helloworld.HelloWorldClientKt") 81 | } 82 | 83 | val otherStartScripts = 84 | tasks.register("otherStartScripts") { 85 | mainClass.set("io.grpc.examples.helloworld.HelloWorldClientKt") 86 | applicationName = "HelloWorldClientKt" 87 | outputDir = tasks.named("startScripts").get().outputDir 88 | classpath = tasks.named("startScripts").get().classpath 89 | } 90 | 91 | tasks.named("startScripts") { 92 | dependsOn(otherStartScripts) 93 | } 94 | 95 | task("stage").dependsOn("installDist") 96 | 97 | tasks.replace("assemble").dependsOn(":installDist") 98 | -------------------------------------------------------------------------------- /run/grpc-hello-world-streaming/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/run/grpc-hello-world-streaming/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /run/grpc-hello-world-streaming/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /run/grpc-hello-world-streaming/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenCentral() 4 | gradlePluginPortal() 5 | } 6 | } 7 | 8 | rootProject.name = "grpc-hello-world-streaming" 9 | 10 | plugins { 11 | id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" 12 | } 13 | -------------------------------------------------------------------------------- /run/grpc-hello-world-streaming/src/main/kotlin/io/grpc/examples/helloworld/HelloWorldClient.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 gRPC authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.grpc.examples.helloworld 18 | 19 | import io.grpc.ManagedChannel 20 | import io.grpc.ManagedChannelBuilder 21 | import io.grpc.examples.helloworld.GreeterGrpcKt.GreeterCoroutineStub 22 | import kotlinx.coroutines.asCoroutineDispatcher 23 | import kotlinx.coroutines.asExecutor 24 | import kotlinx.coroutines.flow.collect 25 | import kotlinx.coroutines.runBlocking 26 | import java.io.Closeable 27 | import java.util.concurrent.Executors 28 | import java.util.concurrent.TimeUnit 29 | 30 | class HelloWorldClient(val channel: ManagedChannel) : Closeable { 31 | private val stub: GreeterCoroutineStub = GreeterCoroutineStub(channel) 32 | 33 | fun greet(s: String) = 34 | runBlocking { 35 | val request = helloRequest { name = s } 36 | val flow = stub.sayHelloStream(request) 37 | flow.collect { response -> 38 | println(response.message) 39 | } 40 | } 41 | 42 | override fun close() { 43 | channel.shutdown().awaitTermination(5, TimeUnit.SECONDS) 44 | } 45 | } 46 | 47 | /** 48 | * Greeter, uses first argument as name to greet if present; 49 | * greets "world" otherwise. 50 | */ 51 | fun main(args: Array) { 52 | val isRemote = args.size == 1 53 | 54 | Executors.newFixedThreadPool(10).asCoroutineDispatcher().use { dispatcher -> 55 | val builder = 56 | if (isRemote) { 57 | ManagedChannelBuilder.forTarget(args[0].removePrefix("https://") + ":443").useTransportSecurity() 58 | } else { 59 | ManagedChannelBuilder.forTarget("localhost:50051").usePlaintext() 60 | } 61 | 62 | val channel = builder.executor(dispatcher.asExecutor()).build() 63 | HelloWorldClient(channel).use { 64 | val user = args.singleOrNull() ?: "world" 65 | it.greet(user) 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /run/grpc-hello-world-streaming/src/main/kotlin/io/grpc/examples/helloworld/HelloWorldServer.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 gRPC authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.grpc.examples.helloworld 18 | 19 | import io.grpc.Server 20 | import io.grpc.ServerBuilder 21 | import kotlinx.coroutines.delay 22 | import kotlinx.coroutines.flow.Flow 23 | import kotlinx.coroutines.flow.flow 24 | 25 | class HelloWorldServer(val port: Int) { 26 | val server: Server = 27 | ServerBuilder 28 | .forPort(port) 29 | .addService(HelloWorldService()) 30 | .build() 31 | 32 | fun start() { 33 | server.start() 34 | println("Server started, listening on $port") 35 | val thread = 36 | Thread { 37 | println("*** shutting down gRPC server since JVM is shutting down") 38 | stop() 39 | println("*** server shut down") 40 | } 41 | Runtime.getRuntime().addShutdownHook(thread) 42 | } 43 | 44 | private fun stop() { 45 | server.shutdown() 46 | } 47 | 48 | fun blockUntilShutdown() { 49 | server.awaitTermination() 50 | } 51 | 52 | private class HelloWorldService : GreeterGrpcKt.GreeterCoroutineImplBase() { 53 | override fun sayHelloStream(request: HelloRequest): Flow = 54 | flow { 55 | while (true) { 56 | delay(1000) 57 | emit(helloReply { message = "hello, ${request.name}" }) 58 | } 59 | } 60 | } 61 | } 62 | 63 | fun main() { 64 | val port = System.getenv("PORT")?.toInt() ?: 50051 65 | val server = HelloWorldServer(port) 66 | server.start() 67 | server.blockUntilShutdown() 68 | } 69 | -------------------------------------------------------------------------------- /run/grpc-hello-world-streaming/src/main/proto/hello_world.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2015 gRPC authors. 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 | // http://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 | syntax = "proto3"; 16 | 17 | package io.grpc.examples.helloworld; 18 | 19 | option java_multiple_files = true; 20 | 21 | // The greeting service definition. 22 | service Greeter { 23 | // Continually sends a greeting 24 | rpc SayHelloStream (HelloRequest) returns (stream HelloReply) {} 25 | } 26 | 27 | // The request message containing the user's name. 28 | message HelloRequest { 29 | string name = 1; 30 | } 31 | 32 | // The response message containing the greetings 33 | message HelloReply { 34 | string message = 1; 35 | } 36 | -------------------------------------------------------------------------------- /run/http4k-hello-world/Procfile: -------------------------------------------------------------------------------- 1 | web: build/install/hello-kotlin-http4k/bin/hello-kotlin-http4k 2 | -------------------------------------------------------------------------------- /run/http4k-hello-world/README.md: -------------------------------------------------------------------------------- 1 | Hello Kotlin http4k 2 | ----------------- 3 | 4 | ## Run Locally: 5 | 1. Start the local server: 6 | ``` 7 | ./gradlew run 8 | ``` 9 | 1. Open: [localhost:8080](http://localhost:8080) 10 | 11 | ## Click-to-Deploy on Google Cloud Run 12 | [![Run on Google Cloud](https://deploy.cloud.run/button.svg)](https://deploy.cloud.run) 13 | 14 | ## CLI Deploy on Google Cloud Run 15 | 1. [Install & setup gcloud](https://cloud.google.com/sdk/install) 16 | 17 | 1. Enable the Container, Container Registry, Cloud Build, and Cloud Run APIs: 18 | ``` 19 | gcloud services enable container.googleapis.com containerregistry.googleapis.com cloudbuild.googleapis.com run.googleapis.com 20 | ``` 21 | 22 | 1. Build the container image on Cloud Build using Buildpacks, storing the image on Google Container Registry: 23 | ``` 24 | export PROJECT_ID=YOUR_GCP_PROJECT 25 | gcloud builds submit --pack=image=gcr.io/$PROJECT_ID/http4k-hello-world 26 | ``` 27 | 28 | 1. Deploy the container on Cloud Run: 29 | ``` 30 | gcloud run deploy \ 31 | --project=$PROJECT_ID \ 32 | --region=us-central1 \ 33 | --platform=managed \ 34 | --allow-unauthenticated \ 35 | --image=gcr.io/$PROJECT_ID/http4k-hello-world \ 36 | http4k-hello-world 37 | ``` 38 | 39 | ## Local Docker Build & Run 40 | 41 | 1. [Install Docker](https://docs.docker.com/get-docker/) 42 | 43 | 1. [Install pack](https://buildpacks.io/docs/install-pack/) 44 | 45 | 1. Build the image using Buildpacks: 46 | ``` 47 | pack build --builder=gcr.io/buildpacks/builder:v1 http4k-hello-world 48 | ``` 49 | 50 | 1. Run image: 51 | ``` 52 | docker run -p8080:8080 http4k-hello-world 53 | ``` 54 | 55 | 1. Open: [localhost:8080](http://localhost:8080) 56 | -------------------------------------------------------------------------------- /run/http4k-hello-world/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | application 3 | kotlin("jvm") version "1.9.23" 4 | } 5 | 6 | repositories { 7 | mavenCentral() 8 | } 9 | 10 | dependencies { 11 | implementation("org.http4k:http4k-core:5.14.0.0") 12 | implementation("org.http4k:http4k-server-undertow:5.14.0.0") 13 | } 14 | 15 | kotlin { 16 | jvmToolchain(17) 17 | } 18 | 19 | application { 20 | mainClass.set("WebAppKt") 21 | } 22 | 23 | tasks.replace("assemble").dependsOn("installDist") 24 | 25 | tasks.create("stage").dependsOn("installDist") 26 | -------------------------------------------------------------------------------- /run/http4k-hello-world/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/run/http4k-hello-world/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /run/http4k-hello-world/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /run/http4k-hello-world/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenCentral() 4 | gradlePluginPortal() 5 | } 6 | } 7 | 8 | rootProject.name = "hello-kotlin-http4k" 9 | 10 | plugins { 11 | id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" 12 | } 13 | -------------------------------------------------------------------------------- /run/http4k-hello-world/src/main/kotlin/WebApp.kt: -------------------------------------------------------------------------------- 1 | import org.http4k.core.Method.GET 2 | import org.http4k.core.Response 3 | import org.http4k.core.Status.Companion.OK 4 | import org.http4k.core.then 5 | import org.http4k.filter.DebuggingFilters.PrintRequest 6 | import org.http4k.routing.bind 7 | import org.http4k.routing.routes 8 | import org.http4k.server.Undertow 9 | import org.http4k.server.asServer 10 | 11 | val app = routes( 12 | "/" bind GET to { 13 | Response(OK).body("hello, world") 14 | }, 15 | ) 16 | 17 | fun main() { 18 | val port = System.getenv("PORT")?.toInt() ?: 8080 19 | 20 | val server = PrintRequest() 21 | .then(app) 22 | .asServer(Undertow(port)).start() 23 | 24 | println("Server started on " + server.port()) 25 | } 26 | -------------------------------------------------------------------------------- /run/ktor-hello-world/.gitignore: -------------------------------------------------------------------------------- 1 | /.gradle/ 2 | /.idea/ 3 | /build/ 4 | *.iml 5 | -------------------------------------------------------------------------------- /run/ktor-hello-world/Procfile: -------------------------------------------------------------------------------- 1 | web: build/install/hello-kotlin-ktor/bin/hello-kotlin-ktor 2 | -------------------------------------------------------------------------------- /run/ktor-hello-world/README.md: -------------------------------------------------------------------------------- 1 | Hello Kotlin Ktor 2 | ----------------- 3 | 4 | ## Run Locally: 5 | 1. Start the local server: 6 | ``` 7 | ./gradlew run 8 | ``` 9 | 1. Open: [localhost:8080](http://localhost:8080) 10 | 11 | ## Click-to-Deploy on Google Cloud Run 12 | [![Run on Google Cloud](https://deploy.cloud.run/button.svg)](https://deploy.cloud.run) 13 | 14 | ## CLI Deploy on Google Cloud Run 15 | 1. [Install & setup gcloud](https://cloud.google.com/sdk/install) 16 | 17 | 1. Enable the Container, Container Registry, Cloud Build, and Cloud Run APIs: 18 | ``` 19 | gcloud services enable container.googleapis.com containerregistry.googleapis.com cloudbuild.googleapis.com run.googleapis.com 20 | ``` 21 | 22 | 1. Build the container image on Cloud Build using Buildpacks, storing the image on Google Container Registry: 23 | ``` 24 | export PROJECT_ID=YOUR_GCP_PROJECT 25 | gcloud builds submit --pack=image=gcr.io/$PROJECT_ID/ktor-hello-world 26 | ``` 27 | 28 | 1. Deploy the container on Cloud Run: 29 | ``` 30 | gcloud run deploy \ 31 | --project=$PROJECT_ID \ 32 | --region=us-central1 \ 33 | --platform=managed \ 34 | --allow-unauthenticated \ 35 | --image=gcr.io/$PROJECT_ID/ktor-hello-world \ 36 | ktor-hello-world 37 | ``` 38 | 39 | ## Local Docker Build & Run 40 | 41 | 1. [Install Docker](https://docs.docker.com/get-docker/) 42 | 43 | 1. [Install pack](https://buildpacks.io/docs/install-pack/) 44 | 45 | 1. Build the image using Buildpacks: 46 | ``` 47 | pack build --builder=gcr.io/buildpacks/builder:v1 ktor-hello-world 48 | ``` 49 | 50 | 1. Run image: 51 | ``` 52 | docker run -p8080:8080 ktor-hello-world 53 | ``` 54 | 55 | 1. Open: [localhost:8080](http://localhost:8080) 56 | -------------------------------------------------------------------------------- /run/ktor-hello-world/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | application 3 | kotlin("jvm") version "1.9.23" 4 | } 5 | 6 | repositories { 7 | mavenCentral() 8 | } 9 | 10 | dependencies { 11 | implementation(kotlin("reflect")) 12 | implementation("io.ktor:ktor-server-core:2.3.9") 13 | implementation("io.ktor:ktor-server-call-logging:2.3.9") 14 | implementation("io.ktor:ktor-server-default-headers:2.3.8") 15 | implementation("io.ktor:ktor-server-cio:2.3.9") 16 | runtimeOnly("ch.qos.logback:logback-classic:1.5.3") 17 | } 18 | 19 | kotlin { 20 | jvmToolchain(17) 21 | } 22 | 23 | application { 24 | mainClass.set("WebAppKt") 25 | } 26 | 27 | tasks.replace("assemble").dependsOn("installDist") 28 | 29 | tasks.create("stage").dependsOn("installDist") 30 | -------------------------------------------------------------------------------- /run/ktor-hello-world/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/run/ktor-hello-world/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /run/ktor-hello-world/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /run/ktor-hello-world/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenCentral() 4 | gradlePluginPortal() 5 | } 6 | } 7 | 8 | rootProject.name = "hello-kotlin-ktor" 9 | 10 | plugins { 11 | id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" 12 | } 13 | -------------------------------------------------------------------------------- /run/ktor-hello-world/src/main/kotlin/WebApp.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google LLC. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import io.ktor.server.application.Application 18 | import io.ktor.server.application.call 19 | import io.ktor.server.application.install 20 | import io.ktor.server.cio.CIO 21 | import io.ktor.server.engine.embeddedServer 22 | import io.ktor.server.plugins.callloging.CallLogging 23 | import io.ktor.server.plugins.defaultheaders.DefaultHeaders 24 | import io.ktor.server.response.respondText 25 | import io.ktor.server.routing.Routing 26 | import io.ktor.server.routing.get 27 | 28 | fun Application.module() { 29 | install(DefaultHeaders) 30 | install(CallLogging) 31 | install(Routing) { 32 | get("/") { 33 | call.respondText("hello, world") 34 | } 35 | } 36 | } 37 | 38 | fun main() { 39 | val port = System.getenv("PORT")?.toInt() ?: 8080 40 | embeddedServer(CIO, port, watchPaths = listOf("build"), module = Application::module).start(true) 41 | } 42 | -------------------------------------------------------------------------------- /run/ktor-hello-world/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /run/micronaut-hello-world/.gitignore: -------------------------------------------------------------------------------- 1 | /.gradle/ 2 | /.idea/ 3 | /build/ 4 | *.iml 5 | -------------------------------------------------------------------------------- /run/micronaut-hello-world/Procfile: -------------------------------------------------------------------------------- 1 | web: build/install/micronaut-hello-world/bin/micronaut-hello-world 2 | -------------------------------------------------------------------------------- /run/micronaut-hello-world/README.md: -------------------------------------------------------------------------------- 1 | Micronaut + Kotlin Hello, World 2 | --------------------------------- 3 | 4 | ## Run Locally: 5 | 1. Start the local server: `./gradlew -t run` 6 | 1. Open: [localhost:8080](http://localhost:8080) 7 | 8 | ## Deploy on Cloud Run (with a couple clicks): 9 | [![Run on Google Cloud](https://deploy.cloud.run/button.svg)](https://deploy.cloud.run) 10 | 11 | ## Run on Google Cloud Run (with the command line): 12 | 13 | 1. [Install & setup gcloud](https://cloud.google.com/sdk/install) 14 | 15 | 1. Enable the Container, Container Registry, Cloud Build, and Cloud Run APIs: 16 | ``` 17 | gcloud services enable container.googleapis.com containerregistry.googleapis.com cloudbuild.googleapis.com run.googleapis.com 18 | ``` 19 | 20 | 1. Build the container image on Cloud Build using Buildpacks, storing the image on Google Container Registry: 21 | ``` 22 | export PROJECT_ID=YOUR_GCP_PROJECT_ID 23 | gcloud builds submit --pack=image=gcr.io/$PROJECT_ID/micronaut-hello-world 24 | ``` 25 | 26 | 1. Deploy on Google Cloud Run: 27 | ``` 28 | gcloud run deploy \ 29 | --image=gcr.io/$PROJECT_ID/micronaut-hello-world \ 30 | --platform=managed \ 31 | --allow-unauthenticated \ 32 | --project=$PROJECT_ID \ 33 | --region=us-central1 \ 34 | micronaut-hello-world 35 | ``` 36 | 37 | ## Local Docker Build & Run 38 | 39 | 1. [Install Docker](https://docs.docker.com/get-docker/) 40 | 41 | 1. [Install pack](https://buildpacks.io/docs/install-pack/) 42 | 43 | 1. Build the image using Buildpacks: 44 | ``` 45 | pack build --builder=gcr.io/buildpacks/builder:v1 micronaut-hello-world 46 | ``` 47 | 48 | 1. Run image: 49 | ``` 50 | docker run -p8080:8080 micronaut-hello-world 51 | ``` 52 | 53 | 1. Open: [localhost:8080](http://localhost:8080) 54 | -------------------------------------------------------------------------------- /run/micronaut-hello-world/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | kotlin("jvm") version "1.9.23" 3 | kotlin("plugin.allopen") version "1.9.23" 4 | id("com.google.devtools.ksp") version "1.9.23-1.0.19" 5 | id("io.micronaut.application") version "4.3.4" 6 | } 7 | 8 | repositories { 9 | mavenCentral() 10 | } 11 | 12 | micronaut { 13 | processing { 14 | incremental(true) 15 | annotations("hello.*") 16 | } 17 | } 18 | 19 | dependencies { 20 | ksp("io.micronaut.serde:micronaut-serde-processor") 21 | implementation(kotlin("reflect")) 22 | implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0") 23 | implementation("io.micronaut:micronaut-http-server-netty") 24 | implementation("io.micronaut.kotlin:micronaut-kotlin-runtime") 25 | implementation("io.micronaut.serde:micronaut-serde-jackson") 26 | runtimeOnly("ch.qos.logback:logback-classic") 27 | runtimeOnly("com.fasterxml.jackson.module:jackson-module-kotlin") 28 | } 29 | 30 | kotlin { 31 | jvmToolchain(17) 32 | } 33 | 34 | application { 35 | mainClass = "hello.WebAppKt" 36 | } 37 | 38 | tasks.replace("assemble").dependsOn("installDist") 39 | 40 | tasks.create("stage").dependsOn("installDist") 41 | -------------------------------------------------------------------------------- /run/micronaut-hello-world/gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | micronaut="4.0.0" 3 | -------------------------------------------------------------------------------- /run/micronaut-hello-world/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/run/micronaut-hello-world/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /run/micronaut-hello-world/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /run/micronaut-hello-world/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenCentral() 4 | gradlePluginPortal() 5 | } 6 | } 7 | 8 | rootProject.name = "micronaut-hello-world" 9 | 10 | plugins { 11 | id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" 12 | id("io.micronaut.platform.catalog") version "4.3.4" 13 | } 14 | -------------------------------------------------------------------------------- /run/micronaut-hello-world/src/main/kotlin/hello/WebApp.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google LLC. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package hello 18 | 19 | import io.micronaut.http.HttpResponse 20 | import io.micronaut.http.annotation.Controller 21 | import io.micronaut.http.annotation.Get 22 | import io.micronaut.runtime.Micronaut.build 23 | import kotlinx.coroutines.async 24 | import kotlinx.coroutines.coroutineScope 25 | 26 | fun main(args: Array) { 27 | build() 28 | .args(*args) 29 | .packages("hello") 30 | .start() 31 | } 32 | 33 | @Controller 34 | class WebApp { 35 | @Get("/") 36 | // it is silly to use coroutines here, but we do it as an example 37 | suspend fun index(): HttpResponse = coroutineScope { 38 | val futureResponse = async { 39 | HttpResponse.ok("hello, world") 40 | } 41 | futureResponse.await() 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /run/micronaut-hello-world/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | micronaut.router.static-resources.assets.enabled=true 2 | micronaut.router.static-resources.assets.paths=classpath:assets 3 | micronaut.router.static-resources.assets.mapping=/assets/** 4 | -------------------------------------------------------------------------------- /run/micronaut-hello-world/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /run/plain-hello-world/.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | mvnw text eol=lf 3 | -------------------------------------------------------------------------------- /run/plain-hello-world/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /.idea/ 3 | /*.iml 4 | /.mvn/wrapper/maven-wrapper.jar 5 | -------------------------------------------------------------------------------- /run/plain-hello-world/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.5/apache-maven-3.9.5-bin.zip 18 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar 19 | -------------------------------------------------------------------------------- /run/plain-hello-world/README.md: -------------------------------------------------------------------------------- 1 | Hello World 2 | ----------- 3 | 4 | ## Run Locally: 5 | 1. Start the local server: 6 | ``` 7 | ./mvnw compile exec:java 8 | ``` 9 | 1. Open: [localhost:8080](http://localhost:8080) 10 | 11 | ## Click-to-Deploy on Google Cloud Run 12 | [![Run on Google Cloud](https://deploy.cloud.run/button.svg)](https://deploy.cloud.run) 13 | 14 | ## CLI Deploy on Google Cloud Run 15 | 1. [Install & setup gcloud](https://cloud.google.com/sdk/install) 16 | 17 | 1. Enable the Container, Container Registry, Cloud Build, and Cloud Run APIs: 18 | ``` 19 | gcloud services enable container.googleapis.com containerregistry.googleapis.com cloudbuild.googleapis.com run.googleapis.com 20 | ``` 21 | 22 | 1. Build the container image and store it on Google Container Registry: 23 | ``` 24 | export PROJECT_ID=YOUR_GCP_PROJECT 25 | ./mvnw compile jib:build -Dimage=gcr.io/$PROJECT_ID/plain-hello-world 26 | ``` 27 | 28 | 1. Deploy the container on Cloud Run: 29 | ``` 30 | gcloud run deploy \ 31 | --project=$PROJECT_ID \ 32 | --region=us-central1 \ 33 | --platform=managed \ 34 | --allow-unauthenticated \ 35 | --image=gcr.io/$PROJECT_ID/plain-hello-world \ 36 | plain-hello-world 37 | ``` 38 | 39 | ## Local Docker Build & Run 40 | 41 | 1. [Install Docker](https://docs.docker.com/get-docker/) 42 | 43 | 1. Build the image using Jib: 44 | ``` 45 | ./mvnw compile jib:dockerBuild -Dimage=plain-hello-world 46 | ``` 47 | 48 | 1. Run image: 49 | ``` 50 | docker run -p8080:8080 plain-hello-world 51 | ``` 52 | 53 | 1. Open: [localhost:8080](http://localhost:8080) 54 | -------------------------------------------------------------------------------- /run/plain-hello-world/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.google 7 | plain-hello-world 8 | jar 9 | 0.1.0-SNAPSHOT 10 | 11 | 12 | 8 13 | 8 14 | 15 | 16 | 17 | 18 | org.jetbrains.kotlin 19 | kotlin-stdlib-jdk8 20 | 1.9.23 21 | 22 | 23 | 24 | 25 | 26 | 27 | org.jetbrains.kotlin 28 | kotlin-maven-plugin 29 | 1.9.23 30 | 31 | 32 | compile 33 | process-sources 34 | 35 | compile 36 | 37 | 38 | 39 | 40 | 41 | 42 | org.codehaus.mojo 43 | exec-maven-plugin 44 | 3.2.0 45 | 46 | 47 | 48 | java 49 | 50 | 51 | 52 | 53 | com.google.WebAppKt 54 | 55 | 56 | 57 | 58 | com.google.cloud.tools 59 | jib-maven-plugin 60 | 3.4.1 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /run/plain-hello-world/src/main/java/com/google/WebApp.kt: -------------------------------------------------------------------------------- 1 | package com.google 2 | 3 | import com.sun.net.httpserver.HttpServer 4 | import java.net.InetSocketAddress 5 | 6 | fun main() { 7 | val port = Integer.parseInt(System.getenv().getOrDefault("PORT", "8080")) 8 | val server = HttpServer.create(InetSocketAddress(port), 0) 9 | 10 | server.createContext("/") { handler -> 11 | val response = "hello, world".toByteArray() 12 | handler.sendResponseHeaders(200, response.size.toLong()) 13 | handler.responseBody.write(response) 14 | } 15 | 16 | println("Listening at http://localhost:$port") 17 | 18 | server.start() 19 | } 20 | -------------------------------------------------------------------------------- /run/quarkus-hello-world/.gitignore: -------------------------------------------------------------------------------- 1 | # Eclipse 2 | .project 3 | .classpath 4 | .settings/ 5 | bin/ 6 | 7 | # IntelliJ 8 | .idea 9 | *.ipr 10 | *.iml 11 | *.iws 12 | 13 | # NetBeans 14 | nb-configuration.xml 15 | 16 | # Visual Studio Code 17 | .vscode 18 | 19 | # OSX 20 | .DS_Store 21 | 22 | # Vim 23 | *.swp 24 | *.swo 25 | 26 | # patch 27 | *.orig 28 | *.rej 29 | 30 | # Maven 31 | target/ 32 | pom.xml.tag 33 | pom.xml.releaseBackup 34 | pom.xml.versionsBackup 35 | release.properties 36 | 37 | /.mvn/wrapper/maven-wrapper.jar 38 | -------------------------------------------------------------------------------- /run/quarkus-hello-world/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.5/apache-maven-3.9.5-bin.zip 18 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar 19 | -------------------------------------------------------------------------------- /run/quarkus-hello-world/README.md: -------------------------------------------------------------------------------- 1 | Quarkus + Kotlin Hello, World 2 | --------------------------------- 3 | 4 | ## Run Locally: 5 | 1. Start the local server: `./mvnw quarkus:dev` 6 | 1. Open: [localhost:8080](http://localhost:8080) 7 | 8 | ## Deploy on Cloud Run (with a couple clicks): 9 | [![Run on Google Cloud](https://deploy.cloud.run/button.svg)](https://deploy.cloud.run) 10 | 11 | ## Run on Google Cloud Run (with the command line): 12 | 13 | 1. [Install & setup gcloud](https://cloud.google.com/sdk/install) 14 | 15 | 1. Enable the Container, Container Registry, Cloud Build, and Cloud Run APIs: 16 | ``` 17 | gcloud services enable container.googleapis.com containerregistry.googleapis.com cloudbuild.googleapis.com run.googleapis.com 18 | ``` 19 | 20 | 1. Build the container image on Cloud Build using Buildpacks, storing the image on Google Container Registry: 21 | ``` 22 | export PROJECT_ID=YOUR_GCP_PROJECT_ID 23 | gcloud builds submit --pack=image=gcr.io/$PROJECT_ID/quarkus-hello-world 24 | ``` 25 | 26 | 1. Deploy on Google Cloud Run: 27 | ``` 28 | gcloud run deploy \ 29 | --image=gcr.io/$PROJECT_ID/quarkus-hello-world \ 30 | --platform=managed \ 31 | --allow-unauthenticated \ 32 | --project=$PROJECT_ID \ 33 | --region=us-central1 \ 34 | quarkus-hello-world 35 | ``` 36 | 37 | ## Local Docker Build & Run 38 | 39 | 1. [Install Docker](https://docs.docker.com/get-docker/) 40 | 41 | 1. [Install pack](https://buildpacks.io/docs/install-pack/) 42 | 43 | 1. Build the image using Buildpacks: 44 | ``` 45 | pack build --builder=gcr.io/buildpacks/builder:v1 quarkus-hello-world 46 | ``` 47 | 48 | 1. Run image: 49 | ``` 50 | docker run -p8080:8080 quarkus-hello-world 51 | ``` 52 | 53 | 1. Open: [localhost:8080](http://localhost:8080) 54 | -------------------------------------------------------------------------------- /run/quarkus-hello-world/src/main/kotlin/com/google/App.kt: -------------------------------------------------------------------------------- 1 | package com.google 2 | 3 | import jakarta.ws.rs.GET 4 | import jakarta.ws.rs.Path 5 | import jakarta.ws.rs.Produces 6 | import jakarta.ws.rs.core.MediaType 7 | 8 | @Path("/") 9 | class App { 10 | @GET 11 | @Produces(MediaType.TEXT_PLAIN) 12 | fun index(): String { 13 | return "hello, world" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /run/springboot-cloudsql/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle/ 2 | .idea/ 3 | build/ 4 | *.iml 5 | /local.properties 6 | -------------------------------------------------------------------------------- /run/springboot-cloudsql/README.md: -------------------------------------------------------------------------------- 1 | # Spring Boot CloudSQL 2 | 3 | 4 | 5 | ## Local Dev (Requires JDK 17 or higher) 6 | 7 | Run the server: 8 | ``` 9 | ./gradlew bootRun 10 | ``` 11 | 12 | Use the server: 13 | ``` 14 | # Create a "bar" 15 | curl -v -X POST localhost:8080/bars -H 'Content-Type: application/json' -d '{"name": "Test"}' 16 | 17 | # Get the "bars" 18 | curl localhost:8080/bars 19 | ``` 20 | 21 | 22 | Run the tests: 23 | ``` 24 | ./gradlew test 25 | ``` 26 | 27 | 28 | Create container & run with docker: 29 | ``` 30 | ./gradlew bootBuildImage 31 | 32 | docker run --rm -ePOSTGRES_PASSWORD=password -p5432:5432 --name my-postgres postgres:13.3 33 | 34 | # psql 35 | docker exec -it my-postgres psql -U postgres 36 | 37 | docker run -it --network host \ 38 | -eSPRING_R2DBC_URL=r2dbc:postgresql://localhost/postgres \ 39 | -eSPRING_R2DBC_USERNAME=postgres \ 40 | -eSPRING_R2DBC_PASSWORD=password \ 41 | springboot-cloudsql 42 | ``` 43 | 44 | [http://localhost:8080/bars](http://localhost:8080/bars) 45 | 46 | 47 | ## Google Cloud 48 | 49 | 1. Set project: 50 | ``` 51 | export PROJECT_ID=YOUR_PROJECT_ID 52 | ``` 53 | 2. Create the Cloud SQL instance, VPC, and VPC Connector 54 | 3. Create & push the container: 55 | ``` 56 | ./gradlew bootBuildImage --imageName=gcr.io/$PROJECT_ID/springboot-cloudsql 57 | docker push gcr.io/$PROJECT_ID/springboot-cloudsql 58 | ``` 59 | 4. Init the schema: 60 | TODO: Need to run the container with an arg `init` and the env vars: `SPRING_R2DBC_URL`, `SPRING_R2DBC_USERNAME`, `SPRING_R2DBC_PASSWORD` in a place that can access the db 61 | 5. Deploy the Cloud Run service: 62 | ``` 63 | gcloud run deploy \ 64 | --image=gcr.io/$PROJECT_ID/springboot-cloudsql \ 65 | --platform=managed \ 66 | --allow-unauthenticated \ 67 | --project=$PROJECT_ID \ 68 | --region=us-central1 \ 69 | --set-env-vars=SPRING_R2DBC_URL=r2dbc:postgresql://YOUR_DB_IP/postgres \ 70 | --set-env-vars=SPRING_R2DBC_USERNAME=YOUR_DB_USERNAME \ 71 | --set-env-vars=SPRING_R2DBC_PASSWORD=YOUR_DB_PASSWORD \ 72 | springboot-cloudsql 73 | ``` 74 | -------------------------------------------------------------------------------- /run/springboot-cloudsql/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.gradle.api.tasks.testing.logging.TestLogEvent.* 2 | 3 | plugins { 4 | application 5 | kotlin("jvm") version "1.9.23" 6 | kotlin("plugin.spring") version "1.9.23" 7 | id("org.springframework.boot") version "3.2.3" 8 | id("io.spring.dependency-management") version "1.1.4" 9 | } 10 | 11 | repositories { 12 | mavenCentral() 13 | } 14 | 15 | dependencies { 16 | implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core") 17 | 18 | implementation("org.springframework.boot:spring-boot-starter-webflux") 19 | 20 | implementation("com.fasterxml.jackson.module:jackson-module-kotlin") 21 | implementation("io.projectreactor.kotlin:reactor-kotlin-extensions") 22 | implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor") 23 | 24 | implementation("org.springframework.boot:spring-boot-starter-data-r2dbc") 25 | runtimeOnly("org.postgresql:r2dbc-postgresql") 26 | 27 | testImplementation("org.springframework.boot:spring-boot-starter-test") 28 | 29 | testImplementation("org.testcontainers:postgresql:1.19.7") 30 | testImplementation("org.testcontainers:r2dbc:1.19.7") 31 | 32 | developmentOnly("org.springframework.boot:spring-boot-devtools") 33 | } 34 | 35 | kotlin { 36 | jvmToolchain(17) 37 | } 38 | 39 | application { 40 | mainClass.set("kotlinbars.MainKt") 41 | } 42 | 43 | tasks.withType { 44 | classpath = sourceSets["test"].runtimeClasspath + classpath 45 | } 46 | 47 | tasks.withType { 48 | useJUnitPlatform() 49 | 50 | testLogging { 51 | showStandardStreams = true 52 | exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL 53 | events(STARTED, PASSED, SKIPPED, FAILED) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /run/springboot-cloudsql/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/run/springboot-cloudsql/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /run/springboot-cloudsql/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /run/springboot-cloudsql/project.toml: -------------------------------------------------------------------------------- 1 | [[build.env]] 2 | name = "GOOGLE_RUNTIME_VERSION" 3 | value = "17" 4 | -------------------------------------------------------------------------------- /run/springboot-cloudsql/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenCentral() 4 | gradlePluginPortal() 5 | } 6 | } 7 | 8 | rootProject.name = "springboot-cloudsql" 9 | 10 | plugins { 11 | id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" 12 | } 13 | -------------------------------------------------------------------------------- /run/springboot-cloudsql/src/main/kotlin/kotlinbars/Main.kt: -------------------------------------------------------------------------------- 1 | package kotlinbars 2 | 3 | import kotlinx.coroutines.reactive.awaitFirst 4 | import kotlinx.coroutines.reactive.awaitFirstOrNull 5 | import org.slf4j.LoggerFactory 6 | import org.springframework.boot.autoconfigure.SpringBootApplication 7 | import org.springframework.boot.runApplication 8 | import org.springframework.data.annotation.Id 9 | import org.springframework.data.repository.reactive.ReactiveCrudRepository 10 | import org.springframework.http.HttpStatus 11 | import org.springframework.http.ResponseEntity 12 | import org.springframework.web.bind.annotation.GetMapping 13 | import org.springframework.web.bind.annotation.PostMapping 14 | import org.springframework.web.bind.annotation.RequestBody 15 | import org.springframework.web.bind.annotation.RestController 16 | import java.net.URI 17 | import java.util.Properties 18 | 19 | data class Bar(@Id val id: Long?, val name: String) 20 | 21 | interface BarRepo : ReactiveCrudRepository 22 | 23 | @SpringBootApplication 24 | @RestController 25 | class WebApp(val barRepo: BarRepo) { 26 | 27 | val logger = LoggerFactory.getLogger(WebApp::class.java) 28 | 29 | @GetMapping("/bars") 30 | suspend fun getBars(): List { 31 | return barRepo.findAll().collectList().awaitFirst() 32 | } 33 | 34 | @PostMapping("/bars") 35 | suspend fun addBar(@RequestBody bar: Bar) = run { 36 | barRepo.save(bar).awaitFirstOrNull()?.let { 37 | ResponseEntity(HttpStatus.NO_CONTENT) 38 | } ?: ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR) 39 | } 40 | 41 | @GetMapping("/error") 42 | fun error() = run { 43 | logger.error("An Error") 44 | } 45 | } 46 | 47 | fun main(args: Array) { 48 | val props = Properties() 49 | 50 | System.getenv()["DATABASE_URL"]?.let { 51 | val dbUri = URI(it) 52 | props["spring.r2dbc.url"] = "r2dbc:postgresql://" + dbUri.host + dbUri.path 53 | props["spring.r2dbc.username"] = dbUri.userInfo.split(":")[0] 54 | props["spring.r2dbc.password"] = dbUri.userInfo.split(":")[1] 55 | } 56 | 57 | runApplication(*args) 58 | } 59 | -------------------------------------------------------------------------------- /run/springboot-cloudsql/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.sql.init.mode=always 2 | -------------------------------------------------------------------------------- /run/springboot-cloudsql/src/main/resources/schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE bar ( 2 | id SERIAL PRIMARY KEY, 3 | name VARCHAR(255) NOT NULL 4 | ); -------------------------------------------------------------------------------- /run/springboot-cloudsql/src/test/kotlin/kotlinbars/BarRepoTest.kt: -------------------------------------------------------------------------------- 1 | package kotlinbars 2 | 3 | import kotlinx.coroutines.reactive.awaitFirst 4 | import kotlinx.coroutines.runBlocking 5 | import org.assertj.core.api.Assertions.assertThat 6 | import org.junit.jupiter.api.Test 7 | import org.springframework.beans.factory.annotation.Autowired 8 | import org.springframework.boot.test.context.SpringBootTest 9 | 10 | @SpringBootTest 11 | class BarRepoTest(@Autowired val barRepo: BarRepo) { 12 | 13 | @Test 14 | fun `barRepo works`(): Unit = runBlocking { 15 | barRepo.save(Bar(null, "foo")).awaitFirst() 16 | 17 | val bars = barRepo.findAll().collectList().awaitFirst() 18 | assertThat(bars.size).isEqualTo(1) 19 | assertThat(bars.first().id).isNotNull 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /run/springboot-cloudsql/src/test/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.sql.init.mode=always 2 | spring.r2dbc.url=r2dbc:tc:postgresql:///bars?TC_IMAGE_TAG=13.3 3 | -------------------------------------------------------------------------------- /run/springboot-hello-world/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/** 6 | !**/src/test/** 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | 17 | ### IntelliJ IDEA ### 18 | .idea 19 | *.iws 20 | *.iml 21 | *.ipr 22 | out/ 23 | 24 | ### NetBeans ### 25 | /nbproject/private/ 26 | /nbbuild/ 27 | /dist/ 28 | /nbdist/ 29 | /.nb-gradle/ 30 | 31 | ### VS Code ### 32 | .vscode/ 33 | -------------------------------------------------------------------------------- /run/springboot-hello-world/Procfile: -------------------------------------------------------------------------------- 1 | web: java -jar build/libs/springboot-hello-world.jar 2 | -------------------------------------------------------------------------------- /run/springboot-hello-world/README.md: -------------------------------------------------------------------------------- 1 | Spring Boot + Kotlin Hello, World 2 | --------------------------------- 3 | 4 | ## Run Locally (Requires JDK 17 or higher): 5 | 1. Start the local server: `./gradlew bootRun` 6 | 1. (Optional) To enable auto-reload, in another terminal / shell: `./gradlew -t classes` 7 | 1. Open: [localhost:8080](http://localhost:8080) 8 | 9 | ## Deploy on Cloud Run (with a couple clicks): 10 | [![Run on Google Cloud](https://deploy.cloud.run/button.svg)](https://deploy.cloud.run) 11 | 12 | ## Run on Google Cloud Run (with the command line): 13 | 14 | 1. [Install & setup gcloud](https://cloud.google.com/sdk/install) 15 | 16 | 1. Enable the Container, Container Registry, Cloud Build, and Cloud Run APIs: 17 | ``` 18 | gcloud services enable container.googleapis.com containerregistry.googleapis.com cloudbuild.googleapis.com run.googleapis.com 19 | ``` 20 | 21 | 1. Build the container image on Cloud Build using Buildpacks, storing the image on Google Container Registry: 22 | ``` 23 | export PROJECT_ID=YOUR_GCP_PROJECT_ID 24 | gcloud builds submit --pack=image=gcr.io/$PROJECT_ID/springboot-hello-world 25 | ``` 26 | 27 | 1. Deploy on Google Cloud Run: 28 | ``` 29 | gcloud run deploy \ 30 | --image=gcr.io/$PROJECT_ID/springboot-hello-world \ 31 | --platform=managed \ 32 | --allow-unauthenticated \ 33 | --project=$PROJECT_ID \ 34 | --region=us-central1 \ 35 | --memory=1Gi \ 36 | springboot-hello-world 37 | ``` 38 | 39 | ## Local Docker Build & Run 40 | 41 | 1. [Install Docker](https://docs.docker.com/get-docker/) 42 | 43 | 1. Build the image 44 | ``` 45 | ./gradlew bootBuildImage --imageName=springboot-hello-world 46 | ``` 47 | 48 | 1. Run image: 49 | ``` 50 | docker run -p8080:8080 springboot-hello-world 51 | ``` 52 | 53 | 1. Open: [localhost:8080](http://localhost:8080) 54 | -------------------------------------------------------------------------------- /run/springboot-hello-world/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "options": { 3 | "memory": "1Gi" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /run/springboot-hello-world/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.springframework.boot") version "3.2.3" 3 | id("io.spring.dependency-management") version "1.1.4" 4 | kotlin("jvm") version "1.9.23" 5 | kotlin("plugin.spring") version "1.9.23" 6 | } 7 | 8 | repositories { 9 | mavenCentral() 10 | } 11 | 12 | kotlin { 13 | jvmToolchain(17) 14 | } 15 | 16 | dependencies { 17 | implementation(kotlin("reflect")) 18 | implementation("org.springframework.boot:spring-boot-starter-webflux") 19 | implementation("com.fasterxml.jackson.module:jackson-module-kotlin") 20 | implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm") 21 | implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor") 22 | developmentOnly("org.springframework.boot:spring-boot-devtools") 23 | } 24 | -------------------------------------------------------------------------------- /run/springboot-hello-world/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/run/springboot-hello-world/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /run/springboot-hello-world/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /run/springboot-hello-world/project.toml: -------------------------------------------------------------------------------- 1 | [[build.env]] 2 | name = "GOOGLE_RUNTIME_VERSION" 3 | value = "17" 4 | -------------------------------------------------------------------------------- /run/springboot-hello-world/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenCentral() 4 | gradlePluginPortal() 5 | } 6 | } 7 | 8 | rootProject.name = "springboot-hello-world" 9 | 10 | plugins { 11 | id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" 12 | } 13 | -------------------------------------------------------------------------------- /run/springboot-hello-world/src/main/kotlin/DemoApplication.kt: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | import kotlinx.coroutines.delay 4 | import org.springframework.boot.autoconfigure.SpringBootApplication 5 | import org.springframework.boot.runApplication 6 | import org.springframework.web.bind.annotation.GetMapping 7 | import org.springframework.web.bind.annotation.RestController 8 | 9 | @SpringBootApplication 10 | @RestController 11 | class DemoApplication { 12 | 13 | @GetMapping("/") 14 | suspend fun index() = run { 15 | delay(10) 16 | "hello, world" 17 | } 18 | } 19 | 20 | fun main(args: Array) { 21 | runApplication(*args) 22 | } 23 | -------------------------------------------------------------------------------- /run/springboot-hello-world/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=${PORT:8080} 2 | -------------------------------------------------------------------------------- /storage/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | application 3 | kotlin("jvm") version "1.9.23" 4 | } 5 | 6 | repositories { 7 | mavenCentral() 8 | } 9 | 10 | dependencies { 11 | implementation(kotlin("reflect")) 12 | implementation("com.google.cloud:google-cloud-storage:2.35.0") 13 | testImplementation("junit:junit:4.13.2") 14 | // see: https://github.com/googleapis/sdk-platform-java/pull/1832 15 | modules { 16 | module("com.google.guava:listenablefuture") { 17 | replacedBy("com.google.guava:guava", "listenablefuture is part of guava") 18 | } 19 | } 20 | } 21 | 22 | kotlin { 23 | jvmToolchain(17) 24 | } 25 | 26 | application { 27 | mainClass.set("com.google.storage.StorageKt") 28 | } 29 | -------------------------------------------------------------------------------- /storage/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/storage/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /storage/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /storage/resources/upload/dog.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/storage/resources/upload/dog.jpg -------------------------------------------------------------------------------- /storage/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenCentral() 4 | gradlePluginPortal() 5 | } 6 | } 7 | 8 | rootProject.name = "storage" 9 | 10 | plugins { 11 | id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" 12 | } 13 | -------------------------------------------------------------------------------- /storage/src/main/kotlin/Quickstart.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.storage 18 | 19 | /** 20 | * A class which instantiates a Cloud Storage client and lists all objects in a 21 | * bucket. 22 | */ 23 | 24 | import com.google.cloud.storage.StorageOptions 25 | 26 | fun quickstart(bucketName: String) { 27 | // [START storage_quickstart] 28 | // import com.google.cloud.storage.StorageOptions 29 | val storage = StorageOptions.getDefaultInstance().service 30 | val bucket = storage.get(bucketName) ?: error("Bucket $bucketName does not exist.") 31 | 32 | println("Listing all blobs in bucket $bucketName:") 33 | bucket.list().iterateAll().forEach { blob -> 34 | println("${blob.name} (content-type: ${blob.contentType}, size: ${blob.size})") 35 | } 36 | // [END storage_quickstart] 37 | } 38 | -------------------------------------------------------------------------------- /storage/src/test/kotlin/QuickstartTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.storage 18 | 19 | import org.junit.Test 20 | 21 | internal class QuickstartTest { 22 | @Test 23 | fun quickstartTest() { 24 | quickstart(System.getenv("GOOGLE_STORAGE_BUCKET")) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /storage/src/test/kotlin/StorageTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.storage 18 | 19 | import com.google.cloud.Timestamp 20 | import org.junit.FixMethodOrder 21 | import org.junit.Test 22 | import org.junit.runners.MethodSorters 23 | import java.util.Random 24 | 25 | @FixMethodOrder(MethodSorters.NAME_ASCENDING) 26 | internal class StorageTest { 27 | 28 | companion object { 29 | val timestamp = Timestamp.now().seconds 30 | val rand = Random().nextInt(10000) 31 | val bucketName = "my_kotlin_sample_bucket_${timestamp}_$rand" 32 | } 33 | 34 | @Test(expected = IllegalStateException::class) 35 | fun t01_mainNoArgTest() { 36 | main() 37 | } 38 | 39 | @Test(expected = IllegalStateException::class) 40 | fun t02_badActionName() { 41 | main("feedMyDog") 42 | } 43 | 44 | @Test 45 | fun t03_usage() { 46 | main("usage") 47 | } 48 | 49 | @Test(expected = IllegalStateException::class) 50 | fun t04_createWithoutBucketName() { 51 | main("create") 52 | } 53 | 54 | @Test 55 | fun t05_createBucket() { 56 | main("create", bucketName) 57 | } 58 | 59 | @Test 60 | fun t06_infoStorage() { 61 | main("info") 62 | } 63 | 64 | @Test(expected = IllegalStateException::class) 65 | fun t07_infoBucketNameNotExist() { 66 | main("info", "i_bet_you_do_not_have_a_bucket_with_this_name") 67 | } 68 | 69 | fun t08_infoBucket() { 70 | main("info", bucketName) 71 | } 72 | 73 | @Test 74 | fun t09_upload() { 75 | main("upload", "resources/upload/dog.jpg", bucketName, "dog.jpg") 76 | } 77 | 78 | @Test 79 | fun t10_uploadNoBlobName() { 80 | main("upload", "resources/upload/dog.jpg", bucketName) 81 | } 82 | 83 | @Test 84 | fun t11_download() { 85 | main("download", bucketName, "dog.jpg", "resources/dog-downloaded.jpg") 86 | } 87 | 88 | @Test 89 | fun t12_deleteBlob() { 90 | main("delete", bucketName, "dog.jpg") 91 | } 92 | 93 | @Test 94 | fun t13_deleteBucket() { 95 | main("delete", bucketName) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /vision/README.md: -------------------------------------------------------------------------------- 1 | # Google Cloud Vision Kotlin Sample 2 | 3 | [![Open in Cloud Shell][shell_img]][shell_link] 4 | 5 | [shell_img]: http://gstatic.com/cloudssh/images/open-btn.svg 6 | [shell_link]: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googlecloudplatform/kotlin-samples&page=editor&working_dir=vision 7 | 8 | ## Description 9 | 10 | This simple command-line application demonstrates how to invoke the [Google 11 | Cloud Vision API][vision-api-docs] from Kotlin. The sample calls the Vision API on an input image. 12 | 13 | Example: 14 | ```sh 15 | build/install/vision/bin/vision ./resources/doggo.jpg 16 | ``` 17 | 18 | ## Quickstart 19 | 20 | #### Setup 21 | - [Enable][enable-vision-api] Cloud Vision API. 22 | - Set up [authentication](https://cloud.google.com/docs/authentication/getting-started). 23 | 24 | #### Build 25 | - Clone the repository 26 | ```sh 27 | git clone https://github.com/GoogleCloudPlatform/kotlin-samples 28 | cd kotlin-samples/vision 29 | ``` 30 | - Build the project with Gradle Wrapper: 31 | ```sh 32 | # run with "-info" flag to print potential errors 33 | ./gradlew installDist -info 34 | ``` 35 | #### Running the sample! 36 | 37 | ```sh 38 | # Call the Vision API with the default image file in "resources/doggo.jpg" 39 | build/install/vision/bin/vision 40 | # Call the Vision API with your own image file 41 | build/install/vision/bin/vision path/to/your-image.jpg 42 | ``` 43 | ## Contributing changes 44 | 45 | * See [CONTRIBUTING.md](../CONTRIBUTING.md) 46 | 47 | ## Licensing 48 | 49 | * See [LICENSE](../LICENSE) 50 | 51 | [vision-api-docs]: https://cloud.google.com/vision/ 52 | [enable-vision-api]: https://console.cloud.google.com/flows/enableapi?apiid=vision.googleapis.com 53 | [google-cloud-java]: https://googlecloudplatform.github.io/google-cloud-java 54 | -------------------------------------------------------------------------------- /vision/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | application 3 | kotlin("jvm") version "1.9.23" 4 | } 5 | 6 | repositories { 7 | mavenCentral() 8 | } 9 | 10 | dependencies { 11 | implementation(kotlin("reflect")) 12 | implementation("com.google.cloud:google-cloud-vision:3.35.0") 13 | testImplementation("junit:junit:4.13.2") 14 | testImplementation(kotlin("test")) 15 | // see: https://github.com/googleapis/sdk-platform-java/pull/1832 16 | modules { 17 | module("com.google.guava:listenablefuture") { 18 | replacedBy("com.google.guava:guava", "listenablefuture is part of guava") 19 | } 20 | } 21 | } 22 | 23 | kotlin { 24 | jvmToolchain(17) 25 | } 26 | 27 | application { 28 | mainClass.set("com.google.vision.QuickstartKt") 29 | } 30 | -------------------------------------------------------------------------------- /vision/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/vision/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /vision/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /vision/resources/doggo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/kotlin-samples/fabbf5b5dcee3f239d9e1c60e049dca7713d2694/vision/resources/doggo.jpg -------------------------------------------------------------------------------- /vision/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenCentral() 4 | gradlePluginPortal() 5 | } 6 | } 7 | 8 | rootProject.name = "vision" 9 | 10 | plugins { 11 | id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" 12 | } 13 | -------------------------------------------------------------------------------- /vision/src/main/kotlin/Quickstart.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google Inc. 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 | // http://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 | package com.google.vision 15 | 16 | import com.google.cloud.vision.v1.AnnotateImageRequest 17 | import com.google.cloud.vision.v1.Feature 18 | import com.google.cloud.vision.v1.Feature.Type 19 | import com.google.cloud.vision.v1.Image 20 | import com.google.cloud.vision.v1.ImageAnnotatorClient 21 | import com.google.protobuf.ByteString 22 | import java.io.File 23 | import java.io.IOException 24 | 25 | fun main(args: Array) { 26 | val imageFileName = if (args.isEmpty()) { 27 | "./resources/doggo.jpg" // Image file path 28 | } else { 29 | args[0] // grab args[0] for file 30 | } 31 | 32 | val imageFile = File(imageFileName) 33 | if (!imageFile.exists()) { 34 | throw NoSuchFileException(file = imageFile, reason = "The file you specified does not exist") 35 | } 36 | 37 | try { 38 | quickstart(imageFileName) 39 | } catch (e: IOException) { 40 | println("Image annotation failed:") 41 | println(e.message) 42 | } 43 | } 44 | 45 | fun quickstart(imageFileName: String) { 46 | // [START vision_quickstart] 47 | // import com.google.cloud.vision.v1.ImageAnnotatorClient 48 | // import java.io.File 49 | val imgProto = ByteString.copyFrom(File(imageFileName).readBytes()) 50 | val vision = ImageAnnotatorClient.create() 51 | 52 | // Set up the Cloud Vision API request. 53 | val img = Image.newBuilder().setContent(imgProto).build() 54 | val feat = Feature.newBuilder().setType(Type.LABEL_DETECTION).build() 55 | val request = AnnotateImageRequest.newBuilder() 56 | .addFeatures(feat) 57 | .setImage(img) 58 | .build() 59 | 60 | // Call the Cloud Vision API and perform label detection on the image. 61 | val result = vision.batchAnnotateImages(arrayListOf(request)) 62 | 63 | // Print the label annotations for the first response. 64 | result.responsesList[0].labelAnnotationsList.forEach { label -> 65 | println("${label.description} (${(label.score * 100).toInt()}%)") 66 | } 67 | // [END vision_quickstart] 68 | } 69 | -------------------------------------------------------------------------------- /vision/src/test/kotlin/QuickstartTest.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google Inc. 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 | // http://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 | package com.google.vision 16 | 17 | import org.junit.Test 18 | 19 | internal class QuickstartTest { 20 | 21 | @Test 22 | fun mainTest() { 23 | /* 24 | * Pass the image to the Vision API, expect a non-error response. 25 | */ 26 | val args = arrayOf("./resources/doggo.jpg") 27 | main(args) 28 | } 29 | 30 | @Test(expected = NoSuchFileException::class) 31 | fun mainNoImageTest() { 32 | /* 33 | * Pass invalid image path to the Vision API, expect an exception. 34 | */ 35 | val args = arrayOf("does/not/exist.jpg") 36 | main(args) 37 | } 38 | } 39 | --------------------------------------------------------------------------------