Skip to content

add total limit and offset for pagination #8

add total limit and offset for pagination

add total limit and offset for pagination #8

# Deploys a developer Feeds API (Cloud Run) and Operations API (Cloud Function) to the DEV project.
#
# Completely isolated — no Terraform, no shared infrastructure changes.
# Only two new named resources are created/updated (both scoped to your suffix):
# - Cloud Run: developer-feed-api-<name_suffix>
# - Cloud Function: operations-api-developer-<name_suffix>
#
# Both use the same dev secrets/databases as the shared dev environment (read-safe).
#
# Prerequisites: none — reuses all existing dev environment vars and secrets:
# Secrets (already set for api-dev.yml): DEV_GCP_MOBILITY_FEEDS_SA_KEY, OP_SERVICE_ACCOUNT_TOKEN
# Variables (already set for api-dev.yml): DEV_MOBILITY_FEEDS_PROJECT_ID,
# DEV_MOBILITY_FEEDS_DEPLOYER_SERVICE_ACCOUNT, MOBILITY_FEEDS_REGION
#
# Usage: Actions → "Deploy Developer Feeds API" → Run workflow → fill in name_suffix
name: Deploy Developer Feeds API
on:
# PR for testing
push:
branches:
- 'user_feature_flag'
workflow_dispatch:
inputs:
name_suffix:
description: >
Developer identifier suffix. Creates:
Cloud Run: mobility-feed-api-developer-<suffix>
Cloud Function: operations-api-developer-<suffix>
Use your name or branch (e.g. "david", "feat-1723").
required: true
default: 'x'
deploy_feeds_api:
description: 'Deploy Feeds API (Cloud Run)'
required: false
default: 'true'
deploy_operations_api:
description: 'Deploy Operations API (Cloud Function)'
required: false
default: 'true'
env:
python_version: '3.11'
java_version: '11'
REGION: ${{ vars.MOBILITY_FEEDS_REGION }}
PROJECT_ID: ${{ vars.DEV_MOBILITY_FEEDS_PROJECT_ID }}
ARTIFACT_REPO: feeds-dev
# Dev VPC connector lives in the QA project (matches Terraform logic in infra/main.tf)
VPC_CONNECTOR: vpc-connector-qa
VPC_CONNECTOR_PROJECT: mobility-feeds-qa
jobs:
# ── Shared setup: generate code stubs needed by both deploy jobs ──────────
generate-and-build:
runs-on: ubuntu-latest
permissions: write-all
needs: []
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Extract commit hash and version from git
run: ./scripts/extract-hash-and-version.sh
- name: Set up JDK ${{ env.java_version }}
uses: actions/setup-java@v4
with:
java-version: ${{ env.java_version }}
distribution: 'temurin'
- uses: actions/setup-python@v5
with:
python-version: ${{ env.python_version }}
- name: Start PostgreSQL (needed for db-gen)
run: docker compose --env-file ./config/.env.local up -d --wait postgres postgres-test
- name: Install Liquibase
env:
LIQUIBASE_VERSION: 4.33.0
run: |
curl -sSL https://github.com/liquibase/liquibase/releases/download/v${LIQUIBASE_VERSION}/liquibase-${LIQUIBASE_VERSION}.tar.gz -o liquibase.tar.gz
mkdir liquibase-dist && tar -xzf liquibase.tar.gz -C liquibase-dist
sudo mv liquibase-dist /usr/local/liquibase
sudo ln -sf /usr/local/liquibase/liquibase /usr/local/bin/liquibase
- name: Run Liquibase migrations
working-directory: ${{ github.workspace }}/liquibase
run: |
export LIQUIBASE_COMMAND_USERNAME=postgres
export LIQUIBASE_COMMAND_PASSWORD=postgres
export LIQUIBASE_COMMAND_URL=jdbc:postgresql://localhost:54320/MobilityDatabaseTest
export LIQUIBASE_COMMAND_CHANGELOG_FILE=changelog.xml
liquibase update
export LIQUIBASE_COMMAND_URL=jdbc:postgresql://localhost:54320/MobilityDatabaseUsersTest
export LIQUIBASE_COMMAND_CHANGELOG_FILE=changelog_user.xml
liquibase update
- name: Generate API stubs and DB models
run: |
scripts/setup-openapi-generator.sh
scripts/api-gen.sh
scripts/api-user-service-gen.sh
scripts/api-operations-gen.sh
export USE_TEST_DB=true
scripts/db-gen.sh
scripts/db-gen-user.sh
- name: Build operations API function zip
if: ${{ github.event.inputs.deploy_operations_api != 'false' }}
run: scripts/function-python-build.sh --function_name operations_api
- name: Upload generated feeds_gen
uses: actions/upload-artifact@v4
with:
name: feeds_gen
path: api/src/feeds_gen/
- name: Upload generated database_gen
uses: actions/upload-artifact@v4
with:
name: database_gen
path: api/src/shared/database_gen/
- name: Upload generated users_database_gen
uses: actions/upload-artifact@v4
with:
name: users_database_gen
path: api/src/shared/users_database_gen/
- name: Upload generated user_service_gen
uses: actions/upload-artifact@v4
with:
name: user_service_gen
path: api/src/user_service_gen/
- name: Upload operations API build
if: ${{ github.event.inputs.deploy_operations_api != 'false' }}
uses: actions/upload-artifact@v4
with:
name: operations_api_build
path: functions-python/operations_api/.dist/
include-hidden-files: true
# ── Feeds API: build Docker image and deploy to developer Cloud Run ─────────
deploy-feeds-api:
if: ${{ github.event.inputs.deploy_feeds_api != 'false' }}
runs-on: ubuntu-latest
permissions: write-all
needs: [generate-and-build]
outputs:
service_name: ${{ steps.names.outputs.service_name }}
service_url: ${{ steps.get_url.outputs.url }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set service name and image tag
id: names
run: |
echo "service_name=mobility-feed-api-developer${{ github.event.inputs.name_suffix || 'x' }}" >> "$GITHUB_OUTPUT"
echo "image_tag=developer-$(date +%s)-${GITHUB_SHA::8}" >> "$GITHUB_OUTPUT"
- name: Download generated artifacts
uses: actions/download-artifact@v4
with:
name: feeds_gen
path: api/src/feeds_gen/
- uses: actions/download-artifact@v4
with:
name: database_gen
path: api/src/shared/database_gen/
- uses: actions/download-artifact@v4
with:
name: users_database_gen
path: api/src/shared/users_database_gen/
- uses: actions/download-artifact@v4
with:
name: user_service_gen
path: api/src/user_service_gen/
- name: Create local .env (required by Dockerfile)
run: |
echo "ENVIRONMENT=dev" > config/.env.local
- name: Authenticate to Google Cloud
uses: google-github-actions/auth@v2
with:
credentials_json: ${{ secrets.DEV_GCP_MOBILITY_FEEDS_SA_KEY }}
- name: Login to Artifact Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGION }}-docker.pkg.dev
username: _json_key_base64
password: ${{ secrets.DEV_GCP_MOBILITY_FEEDS_SA_KEY }}
- name: Build & Push Docker image
run: |
scripts/docker-build-push.sh \
-project_id "${{ env.PROJECT_ID }}" \
-repo_name "${{ env.ARTIFACT_REPO }}" \
-service "${{ steps.names.outputs.service_name }}" \
-region "${{ env.REGION }}" \
-version "${{ steps.names.outputs.image_tag }}"
- name: GCloud Setup
uses: google-github-actions/setup-gcloud@v2
- name: Deploy to developer Cloud Run
# Creates/updates ONLY developer-feed-api-<suffix> — shared dev Cloud Run is untouched.
# Secret names follow the same DEV_<KEY> pattern used by Terraform (infra/feed-api/main.tf).
run: |
IMAGE="${{ env.REGION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.ARTIFACT_REPO }}/${{ steps.names.outputs.service_name }}:${{ steps.names.outputs.image_tag }}"
gcloud run deploy "${{ steps.names.outputs.service_name }}" \
--image "$IMAGE" \
--platform managed \
--region "${{ env.REGION }}" \
--allow-unauthenticated \
--project "${{ env.PROJECT_ID }}" \
--service-account "${{ vars.DEV_MOBILITY_FEEDS_DEPLOYER_SERVICE_ACCOUNT }}" \
--update-secrets "FEEDS_DATABASE_URL=DEV_FEEDS_DATABASE_URL:latest,USERS_DATABASE_URL=DEV_USERS_DATABASE_URL:latest,S2S_JWT_SECRET=DEV_S2S_JWT_SECRET:latest" \
--set-env-vars "PROJECT_ID=${{ env.PROJECT_ID }}" \
--vpc-connector "projects/${{ env.VPC_CONNECTOR_PROJECT }}/locations/${{ env.REGION }}/connectors/${{ env.VPC_CONNECTOR }}" \
--vpc-egress all
- name: Get service URL
id: get_url
run: |
URL=$(gcloud run services describe "${{ steps.names.outputs.service_name }}" \
--platform managed \
--region "${{ env.REGION }}" \
--project "${{ env.PROJECT_ID }}" \
--format 'value(status.url)')
echo "url=${URL}" >> "$GITHUB_OUTPUT"
# ── Operations API: build zip and deploy to developer Cloud Function ────────
deploy-operations-api:
if: ${{ github.event.inputs.deploy_operations_api != 'false' }}
runs-on: ubuntu-latest
permissions: write-all
needs: [generate-and-build]
outputs:
function_name: ${{ steps.names.outputs.function_name }}
function_url: ${{ steps.get_url.outputs.url }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set function name
id: names
run: |
echo "function_name=operations-api-developer-${{ github.event.inputs.name_suffix || 'dev' }}" >> "$GITHUB_OUTPUT"
- name: Download operations API build
uses: actions/download-artifact@v4
with:
name: operations_api_build
path: functions-python/operations_api/.dist/
- name: Authenticate to Google Cloud
uses: google-github-actions/auth@v2
with:
credentials_json: ${{ secrets.DEV_GCP_MOBILITY_FEEDS_SA_KEY }}
- name: Load OPERATIONS_OAUTH2_CLIENT_ID from 1Password
uses: 1password/load-secrets-action@v2
with:
export-env: true
env:
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
OPERATIONS_OAUTH2_CLIENT_ID: "op://rbiv7rvkkrsdlpcrz3bmv7nmcu/GCP_RETOOL_OAUTH2_CREDS/username"
- name: GCloud Setup
uses: google-github-actions/setup-gcloud@v2
- name: Deploy to developer Cloud Function
# Creates/updates ONLY operations-api-developer-<suffix> — shared operations-api is untouched.
# Secret names follow the same DEV_<KEY> pattern used by Terraform (infra/functions-python/main.tf).
run: |
gcloud functions deploy "${{ steps.names.outputs.function_name }}" \
--gen2 \
--project "${{ env.PROJECT_ID }}" \
--region "${{ env.REGION }}" \
--runtime python311 \
--entry-point main \
--source functions-python/operations_api/.dist/build \
--service-account "${{ vars.DEV_MOBILITY_FEEDS_DEPLOYER_SERVICE_ACCOUNT }}" \
--memory 1Gi \
--timeout 540s \
--max-instances 10 \
--min-instances 0 \
--concurrency 100 \
--cpu 1 \
--ingress-settings all \
--trigger-http \
--allow-unauthenticated \
--set-env-vars "ENVIRONMENT=dev,PROJECT_ID=${{ env.PROJECT_ID }},GCP_REGION=${{ env.REGION }},GOOGLE_CLIENT_ID=${OPERATIONS_OAUTH2_CLIENT_ID}" \
--set-secrets "FEEDS_DATABASE_URL=DEV_FEEDS_DATABASE_URL:latest,USERS_DATABASE_URL=DEV_USERS_DATABASE_URL:latest"
- name: Get function URL
id: get_url
run: |
URL=$(gcloud functions describe "${{ steps.names.outputs.function_name }}" \
--gen2 \
--region "${{ env.REGION }}" \
--project "${{ env.PROJECT_ID }}" \
--format 'value(serviceConfig.uri)')
echo "url=${URL}" >> "$GITHUB_OUTPUT"
# ── Summary ───────────────────────────────────────────────────────────────
summary:
runs-on: ubuntu-latest
needs: [deploy-feeds-api, deploy-operations-api]
if: always()
steps:
- name: Write job summary
run: |
echo "## 🚀 Developer API deployment" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "| Service | Name | URL |" >> "$GITHUB_STEP_SUMMARY"
echo "|---------|------|-----|" >> "$GITHUB_STEP_SUMMARY"
FEEDS_URL="${{ needs.deploy-feeds-api.outputs.service_url }}"
FEEDS_NAME="${{ needs.deploy-feeds-api.outputs.service_name }}"
if [ -n "$FEEDS_URL" ]; then
echo "| Feeds API | \`${FEEDS_NAME}\` | [${FEEDS_URL}/docs/](${FEEDS_URL}/docs/) |" >> "$GITHUB_STEP_SUMMARY"
fi
OPS_URL="${{ needs.deploy-operations-api.outputs.function_url }}"
OPS_NAME="${{ needs.deploy-operations-api.outputs.function_name }}"
if [ -n "$OPS_URL" ]; then
echo "| Operations API | \`${OPS_NAME}\` | [${OPS_URL}](${OPS_URL}) |" >> "$GITHUB_STEP_SUMMARY"
fi
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "> ⚠️ These are developer services sharing the **dev databases** (read/write)." >> "$GITHUB_STEP_SUMMARY"
echo "> No shared Cloud Run, Terraform state, load balancer, or IAM was modified." >> "$GITHUB_STEP_SUMMARY"