diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..da2a58b3 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,73 @@ +name: run tests + +on: [push] + +jobs: + test: + runs-on: ubuntu-latest + permissions: + contents: 'read' + id-token: 'write' + + steps: + - uses: actions/checkout@v4 + - name: Authenticate to Google Cloud + id: auth + uses: google-github-actions/auth@v1 + with: + token_format: access_token + workload_identity_provider: projects/949875736540/locations/global/workloadIdentityPools/external-pool/providers/github-provider + service_account: artifact-pusher@talon-artifacts.iam.gserviceaccount.com + - name: Login to GAR + uses: docker/login-action@v3 + with: + registry: europe-west3-docker.pkg.dev + username: oauth2accesstoken + password: ${{ steps.auth.outputs.access_token }} + - uses: hoverkraft-tech/compose-action@v2.0.2 + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version: 23 + - name: Install dependencies + run: | + npm install; + npm run build; + sudo apt-get install jq curl + - name: Run example + run: | + ACCOUNT_RESPONSE=$(curl -s --location "http://localhost:9000/v1/accounts" \ + --header "Content-Type: application/json" \ + --data-raw '{ + "companyName": "demo", + "email": "integrationtest@talon.one", + "password": "Password1234!" + }'); + export TALON_USER_ID=$(echo $ACCOUNT_RESPONSE | jq ".userId"); + export TALON_USER_TOKEN=$(echo $ACCOUNT_RESPONSE | jq ".token" | tr -d '"'); + USER_RESPONSE=$(curl -s --location "http://localhost:9000/v1/users/$TALON_USER_ID" \ + --header "Authorization: Bearer $TALON_USER_TOKEN"); + export TALON_ACCOUNT_ID=$(echo $USER_RESPONSE | jq ".accountId"); + echo "User with ID $TALON_USER_ID and Token $TALON_USER_TOKEN was created for application $TALON_ACCOUNT_ID"; + APPLICATION_RESPONSE=$(curl -s --location "http://localhost:9000/v1/applications" \ + --header "Content-Type: application/json" \ + --header "Authorization: Bearer $TALON_USER_TOKEN" \ + --data-raw '{ + "name": "demo", + "currency": "EUR", + "timezone": "Europe/Berlin", + "enableFlattenedCartItems": false + }'); + export TALON_APPLICATION_ID=$(echo $USER_RESPONSE | jq ".id"); + echo "Application with ID $TALON_APPLICATION_ID was created" + API_KEY_RESPONSE=$(curl -s -v --location "http://localhost:9000/v1/applications/$TALON_APPLICATION_ID/apikeys" \ + --header "Content-Type: application/json" \ + --header "Authorization: Bearer $TALON_USER_TOKEN" \ + --data-raw '{ + "title": "Application HIT KEY", + "expires": "2099-01-01T0:00:00Z" + }'); + echo "Api-Key-Response: $API_KEY_RESPONSE"; + export TALON_API_KEY=$(echo $API_KEY_RESPONSE | jq ".key" | tr -d '"'); + echo "Api-Key $TALON_API_KEY created" + node example.js; diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..1378a67b --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,47 @@ +services: + api-service: + image: europe-west3-docker.pkg.dev/talon-artifacts/talon-images/talon-service:master + depends_on: + - database-service + ports: + - "9000:9000" + environment: + - TALON_DB_NAME=talon + - TALON_DB_USER=talon + - TALON_DB_PASSWORD=talon.one.9000 + - TALON_DB_HOST=database-service + - TALON_DB_PORT=5432 + - TALON_ENABLE_WEBHOOK_WORKER_POOL=false + - TZ=UTC + - RELEASE_STAGE=ci + - TALON_CH_ENABLED=false + - TALON_DISABLE_PROFILER=true + - USE_REPLICA_DB=false + command: + - /talon/talon + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:9000/v1/status/health"] + interval: 10s + timeout: 5s + retries: 10 + restart: "on-failure:10" + + database-service: + image: docker.io/bitnami/postgresql:15 + volumes: + - 'postgresql_master_data:/bitnami/postgresql' + ports: + - "5433:5432" + environment: + - POSTGRESQL_DATABASE=talon + - POSTGRESQL_USERNAME=talon + - POSTGRESQL_PASSWORD=talon.one.9000 + healthcheck: + test: ["CMD-SHELL", "pg_isready -U talon -d talon"] + interval: 10s + timeout: 5s + retries: 5 + restart: "on-failure:10" + +volumes: + postgresql_master_data: \ No newline at end of file diff --git a/example.js b/example.js new file mode 100644 index 00000000..515f63da --- /dev/null +++ b/example.js @@ -0,0 +1,90 @@ +const TalonOne = require("./dist"); + +const defaultClient = TalonOne.ApiClient.instance; +defaultClient.basePath = "http://localhost:9000"; + +// Configure API key authorization: api_key_v1 +const api_key_v1 = defaultClient.authentications["api_key_v1"]; +api_key_v1.apiKey = process.env.TALON_API_KEY; +api_key_v1.apiKeyPrefix = "ApiKey-v1"; + +// Integration API example to send a session update +const integrationApi = new TalonOne.IntegrationApi(); + +// Initializing a customer session object +const customerSession = TalonOne.NewCustomerSessionV2.constructFromObject({ + profileId: 'example_prof_id', + cartItems: [ + { + name: 'Döner King', + sku: 'kd-100', + quantity: 1, + price: 2.00, + category: 'pizzas' + }, + { + name: 'Spezi 500ml', + sku: 'sp-50', + quantity: 1, + price: 2, + category: 'beverages' + }, + { + name: 'Queen Döner', + sku: 'qd-100', + quantity: 1, + price: 2.50, + category: 'pizzas' + }, + { + name: 'Club Mate 330ml', + sku: 'cm-33', + quantity: 1, + price: 1.80, + category: 'beverages' + } + ], + couponCodes: [ + 'Cool-Summer!' + ] +}); + +//Initializing an integration request wrapping the customer session +const integrationRequest = new TalonOne.IntegrationRequest(customerSession); + +// Optional list of requested information to be present on the response. +// See src/model/IntegrationRequest#ResponseContentEnum for full list of supported values +// integrationRequest.responseContent = [ +// TalonOne.IntegrationRequest.ResponseContentEnum.customerSession, +// TalonOne.IntegrationRequest.ResponseContentEnum.customerProfile +// ] + +integrationApi + .updateCustomerSessionV2("example_integration_v2_id", integrationRequest) + .then( + data => { + console.log(JSON.stringify(data, null, 2)); + + // Parsing the returned effects list, please consult https://developers.talon.one/Integration-API/handling-effects-v2 for the full list of effects and their corresponding properties + data.effects.forEach(effect => { + switch (effect.effectType) { + case 'setDiscount': + // Initiating right props instance according to the effect type + const setDiscountProps = TalonOne.SetDiscountEffectProps.constructFromObject(effect.props) + // Initiating the right props class is not a necessity, it is only a suggestion here that could help in case of unexpected returned values from the API + + // Access the specific effect's properties + console.log(`Set a discount '${setDiscountProps.name}' of ${setDiscountProps.value}`) + break + case 'rejectCoupon': + // Work with AcceptCouponEffectProps' properties + // ... + default: + throw new Error(`Unhandled effect type from Talon.One integration: ${effect.effectType}`) + } + }) + }, + err => { + console.error(err); + } + ); \ No newline at end of file