├── .lycheeignore ├── .azdo └── pipelines │ └── azure-dev.yml ├── .devcontainer ├── azd-bicep │ └── devcontainer.json ├── azd-terraform │ └── devcontainer.json ├── btpsa │ └── devcontainer.json └── devcontainer.json ├── .gitattributes ├── .github ├── CODE_OF_CONDUCT.md ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ └── feature_request.yml ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml └── workflows │ ├── azure-dev.yml │ ├── codeql.yml │ └── links-watcher.yml ├── .gitignore ├── .vscode ├── launch.json └── settings.json ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── assets ├── apim-backoff-delay.png ├── app-auth-principal-propagation.svg ├── azd-app-auth-flow.svg ├── bupa-create-vsc-rest.png ├── bupa-delete-vsc-rest.png ├── bupa-get-vsc-rest.png ├── bupa-update-vsc-rest.png ├── drawings.pptx ├── project-overview-azd-style.png └── sap-cloud-sdk-auth.drawio ├── azure.yaml ├── documentation ├── ADDITIONAL-RESOURCES.md ├── AUTHENTICATION.md ├── AZD-CICD-SETUP.md ├── DEPLOYMENT-AZD-TERRAFORM.md ├── DEPLOYMENT-AZD.md ├── DEPLOYMENT-BTPSA.md ├── DEPLOYMENT-VSCODE.md └── WHATS-NEXT.md ├── hooks ├── createAppRegistrations.ps1 ├── deleteAppRegistrations.ps1 └── fireworks.sh ├── infra-btpsa ├── parameters.json └── usecase.json ├── infra-terraform ├── main.tf ├── main.tfvars.json ├── modules │ ├── apim-api │ │ ├── apim-api-policy.xml │ │ ├── apim-api.tf │ │ ├── apim-api_output.tf │ │ └── apim-api_variables.tf │ ├── apim │ │ ├── apim.tf │ │ ├── apim_output.tf │ │ └── apim_variables.tf │ ├── applicationinsights │ │ ├── applicationinsights.tf │ │ ├── applicationinsights_output.tf │ │ └── applicationinsights_variables.tf │ ├── appservicenode │ │ ├── appservicenode.tf │ │ ├── appservicenode_output.tf │ │ └── appservicenode_variables.tf │ ├── appserviceplan │ │ ├── appserviceplan.tf │ │ ├── appserviceplan_output.tf │ │ └── appserviceplan_variables.tf │ ├── cosmos │ │ ├── cosmos.tf │ │ ├── cosmos_output.tf │ │ └── cosmos_variables.tf │ ├── keyvault │ │ ├── keyvault.tf │ │ ├── keyvault_output.tf │ │ └── keyvault_variables.tf │ └── loganalytics │ │ ├── loganalytics.tf │ │ ├── loganalytics_output.tf │ │ └── loganalytics_variables.tf ├── modules_local │ ├── apim-api │ │ ├── apim-api-policy.xml │ │ ├── apim-api.tf │ │ ├── apim-api_output.tf │ │ └── apim-api_variables.tf │ └── appservicenode │ │ ├── appservicenode.tf │ │ ├── appservicenode_output.tf │ │ └── appservicenode_variables.tf ├── output.tf ├── provider.tf └── variables.tf ├── infra ├── abbreviations.json ├── app │ ├── api.bicep │ ├── apim-api-policy.xml │ └── apim-api.bicep ├── core │ ├── ai │ │ └── cognitiveservices.bicep │ ├── config │ │ └── configstore.bicep │ ├── database │ │ ├── cosmos │ │ │ ├── cosmos-account.bicep │ │ │ ├── mongo │ │ │ │ ├── cosmos-mongo-account.bicep │ │ │ │ └── cosmos-mongo-db.bicep │ │ │ └── sql │ │ │ │ ├── cosmos-sql-account.bicep │ │ │ │ ├── cosmos-sql-db.bicep │ │ │ │ ├── cosmos-sql-role-assign.bicep │ │ │ │ └── cosmos-sql-role-def.bicep │ │ ├── mysql │ │ │ └── flexibleserver.bicep │ │ ├── postgresql │ │ │ └── flexibleserver.bicep │ │ └── sqlserver │ │ │ └── sqlserver.bicep │ ├── gateway │ │ └── apim.bicep │ ├── host │ │ ├── aks-agent-pool.bicep │ │ ├── aks-managed-cluster.bicep │ │ ├── aks.bicep │ │ ├── appservice-appsettings.bicep │ │ ├── appservice.bicep │ │ ├── appserviceplan.bicep │ │ ├── container-app-upsert.bicep │ │ ├── container-app.bicep │ │ ├── container-apps-environment.bicep │ │ ├── container-apps.bicep │ │ ├── container-registry.bicep │ │ ├── functions.bicep │ │ └── staticwebapp.bicep │ ├── monitor │ │ ├── applicationinsights-dashboard.bicep │ │ ├── applicationinsights.bicep │ │ ├── loganalytics.bicep │ │ └── monitoring.bicep │ ├── networking │ │ ├── cdn-endpoint.bicep │ │ ├── cdn-profile.bicep │ │ └── cdn.bicep │ ├── search │ │ └── search-services.bicep │ ├── security │ │ ├── aks-managed-cluster-access.bicep │ │ ├── configstore-access.bicep │ │ ├── keyvault-access.bicep │ │ ├── keyvault-secret.bicep │ │ ├── keyvault.bicep │ │ ├── registry-access.bicep │ │ └── role.bicep │ ├── storage │ │ └── storage-account.bicep │ └── testing │ │ └── loadtesting.bicep ├── core_local │ └── host │ │ └── appservice.bicep ├── main.bicep └── main.parameters.json ├── package-lock.json ├── package.json ├── sample-http-requests ├── SAP-Cloud-SDK-on-Azure-App-Service-Quickstart.postman_collection.json └── business-partner-requests.http ├── src └── api │ ├── .deployment │ ├── .gitignore │ ├── API_BUSINESS_PARTNER.edmx │ ├── API_BUSINESS_PARTNER.openapi.json │ ├── manifest.yaml │ ├── nest-cli.json │ ├── package-lock.json │ ├── package.json │ ├── rome.json │ ├── service-specifications │ ├── ZAPI_BUSINESS_PARTNER.edmx │ └── config.json │ ├── services │ └── business-partner-service-1 │ │ ├── AddressEmailAddress.ts │ │ ├── AddressEmailAddressApi.ts │ │ ├── AddressEmailAddressRequestBuilder.ts │ │ ├── AddressFaxNumber.ts │ │ ├── AddressFaxNumberApi.ts │ │ ├── AddressFaxNumberRequestBuilder.ts │ │ ├── AddressHomePageUrl.ts │ │ ├── AddressHomePageUrlApi.ts │ │ ├── AddressHomePageUrlRequestBuilder.ts │ │ ├── AddressPhoneNumber.ts │ │ ├── AddressPhoneNumberApi.ts │ │ ├── AddressPhoneNumberRequestBuilder.ts │ │ ├── BatchRequest.ts │ │ ├── BpContactToAddress.ts │ │ ├── BpContactToAddressApi.ts │ │ ├── BpContactToAddressRequestBuilder.ts │ │ ├── BpContactToFuncAndDept.ts │ │ ├── BpContactToFuncAndDeptApi.ts │ │ ├── BpContactToFuncAndDeptRequestBuilder.ts │ │ ├── BuPaAddressUsage.ts │ │ ├── BuPaAddressUsageApi.ts │ │ ├── BuPaAddressUsageRequestBuilder.ts │ │ ├── BuPaIdentification.ts │ │ ├── BuPaIdentificationApi.ts │ │ ├── BuPaIdentificationRequestBuilder.ts │ │ ├── BuPaIndustry.ts │ │ ├── BuPaIndustryApi.ts │ │ ├── BuPaIndustryRequestBuilder.ts │ │ ├── BusinessPartner.ts │ │ ├── BusinessPartnerAddress.ts │ │ ├── BusinessPartnerAddressApi.ts │ │ ├── BusinessPartnerAddressRequestBuilder.ts │ │ ├── BusinessPartnerApi.ts │ │ ├── BusinessPartnerBank.ts │ │ ├── BusinessPartnerBankApi.ts │ │ ├── BusinessPartnerBankRequestBuilder.ts │ │ ├── BusinessPartnerContact.ts │ │ ├── BusinessPartnerContactApi.ts │ │ ├── BusinessPartnerContactRequestBuilder.ts │ │ ├── BusinessPartnerRequestBuilder.ts │ │ ├── BusinessPartnerRole.ts │ │ ├── BusinessPartnerRoleApi.ts │ │ ├── BusinessPartnerRoleRequestBuilder.ts │ │ ├── BusinessPartnerTaxNumber.ts │ │ ├── BusinessPartnerTaxNumberApi.ts │ │ ├── BusinessPartnerTaxNumberRequestBuilder.ts │ │ ├── CustSalesPartnerFunc.ts │ │ ├── CustSalesPartnerFuncApi.ts │ │ ├── CustSalesPartnerFuncRequestBuilder.ts │ │ ├── Customer.ts │ │ ├── CustomerApi.ts │ │ ├── CustomerCompany.ts │ │ ├── CustomerCompanyApi.ts │ │ ├── CustomerCompanyRequestBuilder.ts │ │ ├── CustomerCompanyText.ts │ │ ├── CustomerCompanyTextApi.ts │ │ ├── CustomerCompanyTextRequestBuilder.ts │ │ ├── CustomerDunning.ts │ │ ├── CustomerDunningApi.ts │ │ ├── CustomerDunningRequestBuilder.ts │ │ ├── CustomerRequestBuilder.ts │ │ ├── CustomerSalesArea.ts │ │ ├── CustomerSalesAreaApi.ts │ │ ├── CustomerSalesAreaRequestBuilder.ts │ │ ├── CustomerSalesAreaTax.ts │ │ ├── CustomerSalesAreaTaxApi.ts │ │ ├── CustomerSalesAreaTaxRequestBuilder.ts │ │ ├── CustomerSalesAreaText.ts │ │ ├── CustomerSalesAreaTextApi.ts │ │ ├── CustomerSalesAreaTextRequestBuilder.ts │ │ ├── CustomerTaxGrouping.ts │ │ ├── CustomerTaxGroupingApi.ts │ │ ├── CustomerTaxGroupingRequestBuilder.ts │ │ ├── CustomerText.ts │ │ ├── CustomerTextApi.ts │ │ ├── CustomerTextRequestBuilder.ts │ │ ├── CustomerUnloadingPoint.ts │ │ ├── CustomerUnloadingPointApi.ts │ │ ├── CustomerUnloadingPointRequestBuilder.ts │ │ ├── CustomerWithHoldingTax.ts │ │ ├── CustomerWithHoldingTaxApi.ts │ │ ├── CustomerWithHoldingTaxRequestBuilder.ts │ │ ├── Supplier.ts │ │ ├── SupplierApi.ts │ │ ├── SupplierCompany.ts │ │ ├── SupplierCompanyApi.ts │ │ ├── SupplierCompanyRequestBuilder.ts │ │ ├── SupplierCompanyText.ts │ │ ├── SupplierCompanyTextApi.ts │ │ ├── SupplierCompanyTextRequestBuilder.ts │ │ ├── SupplierDunning.ts │ │ ├── SupplierDunningApi.ts │ │ ├── SupplierDunningRequestBuilder.ts │ │ ├── SupplierPartnerFunc.ts │ │ ├── SupplierPartnerFuncApi.ts │ │ ├── SupplierPartnerFuncRequestBuilder.ts │ │ ├── SupplierPurchasingOrg.ts │ │ ├── SupplierPurchasingOrgApi.ts │ │ ├── SupplierPurchasingOrgRequestBuilder.ts │ │ ├── SupplierPurchasingOrgText.ts │ │ ├── SupplierPurchasingOrgTextApi.ts │ │ ├── SupplierPurchasingOrgTextRequestBuilder.ts │ │ ├── SupplierRequestBuilder.ts │ │ ├── SupplierText.ts │ │ ├── SupplierTextApi.ts │ │ ├── SupplierTextRequestBuilder.ts │ │ ├── SupplierWithHoldingTax.ts │ │ ├── SupplierWithHoldingTaxApi.ts │ │ ├── SupplierWithHoldingTaxRequestBuilder.ts │ │ ├── index.ts │ │ └── service.ts │ ├── src │ ├── app.controller.spec.ts │ ├── app.controller.ts │ ├── app.module.ts │ ├── app.service.ts │ ├── business-partner │ │ ├── business-partner.controller.spec.ts │ │ ├── business-partner.controller.ts │ │ ├── business-partner.service.spec.ts │ │ └── business-partner.service.ts │ ├── main.ts │ ├── public │ │ ├── github-logo.svg │ │ ├── sap-cloud-sdk-logo.svg │ │ └── stylesheets │ │ │ └── style.css │ └── views │ │ ├── bupa.pug │ │ └── index.pug │ ├── tsconfig.build.json │ └── tsconfig.json ├── templates ├── .env ├── azuredeploy.json └── azuredeploy.parameters.json ├── test ├── app.e2e-spec.ts └── jest-e2e.json └── workspaces ├── azd-bicep.code-workspace ├── azd-terraform.code-workspace └── btpsa.code-workspace / .lycheeignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/app-service-javascript-sap-cloud-sdk-quickstart/0675ad44767ad29593e6a6f62ee442bdd4169b57/ .lycheeignore -------------------------------------------------------------------------------- /.azdo/pipelines/azure-dev.yml: -------------------------------------------------------------------------------- 1 | trigger: 2 | - main 3 | 4 | pool: 5 | vmImage: ubuntu-latest 6 | 7 | steps: 8 | - task: setup-azd@0 9 | displayName: Install azd 10 | 11 | - pwsh: | 12 | $info = $Env:AZURE_CREDENTIALS | ConvertFrom-Json -AsHashtable; 13 | 14 | azd auth login ` 15 | --client-id "$($info.clientId)" ` 16 | --client-secret "$($info.clientSecret)" ` 17 | --tenant-id "$($info.tenantId)" 18 | displayName: azd login 19 | env: 20 | AZURE_CREDENTIALS: $(AZURE_CREDENTIALS) 21 | 22 | - task: AzureCLI@2 23 | displayName: Azure Dev Provision 24 | inputs: 25 | azureSubscription: azconnection 26 | scriptType: bash 27 | scriptLocation: inlineScript 28 | inlineScript: | 29 | azd provision --no-prompt 30 | env: 31 | AZURE_SUBSCRIPTION_ID: $(AZURE_SUBSCRIPTION_ID) 32 | AZURE_ENV_NAME: $(AZURE_ENV_NAME) 33 | AZURE_LOCATION: $(AZURE_LOCATION) 34 | ARM_TENANT_ID: $(ARM_TENANT_ID) 35 | ARM_CLIENT_ID: $(ARM_CLIENT_ID) 36 | ARM_CLIENT_SECRET: $(ARM_CLIENT_SECRET) 37 | RS_RESOURCE_GROUP: $(RS_RESOURCE_GROUP) 38 | RS_STORAGE_ACCOUNT: $(RS_STORAGE_ACCOUNT) 39 | RS_CONTAINER_NAME: $(RS_CONTAINER_NAME) 40 | ODATA_URL: $(ODATA_URL) 41 | ODATA_USERNAME: $(ODATA_USERNAME) 42 | ODATA_USERPWD: $(ODATA_USERPWD) 43 | APIKEY: $(APIKEY) 44 | APIKEY_HEADERNAME: $(APIKEY_HEADERNAME) 45 | 46 | - task: AzureCLI@2 47 | displayName: Azure Dev Deploy 48 | inputs: 49 | azureSubscription: azconnection 50 | scriptType: bash 51 | scriptLocation: inlineScript 52 | inlineScript: | 53 | azd deploy --no-prompt 54 | env: 55 | AZURE_SUBSCRIPTION_ID: $(AZURE_SUBSCRIPTION_ID) 56 | AZURE_ENV_NAME: $(AZURE_ENV_NAME) 57 | AZURE_LOCATION: $(AZURE_LOCATION) 58 | ODATA_URL: $(ODATA_URL) 59 | ODATA_USERNAME: $(ODATA_USERNAME) 60 | ODATA_USERPWD: $(ODATA_USERPWD) 61 | APIKEY: $(APIKEY) 62 | APIKEY_HEADERNAME: $(APIKEY_HEADERNAME) 63 | -------------------------------------------------------------------------------- /.devcontainer/azd-bicep/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. 2 | { 3 | "name": "Azure Developer CLI (Bicep)", 4 | "image": "mcr.microsoft.com/devcontainers/typescript-node:20-bullseye", 5 | "features": { 6 | "ghcr.io/devcontainers-contrib/features/nestjs-cli:2": {}, 7 | "ghcr.io/devcontainers/features/docker-in-docker:2": {}, 8 | "ghcr.io/devcontainers/features/azure-cli:1": {}, 9 | "ghcr.io/azure/azure-dev/azd:latest": {}, 10 | "ghcr.io/devcontainers/features/github-cli:1": { 11 | "version": "2" 12 | }, 13 | "ghcr.io/devcontainers/features/powershell:1": {} 14 | }, 15 | "customizations": { 16 | "vscode": { 17 | "extensions": [ 18 | "GitHub.vscode-github-actions", 19 | "ms-azuretools.azure-dev", 20 | "ms-azuretools.vscode-azurefunctions", 21 | "ms-azuretools.vscode-bicep", 22 | "ms-azuretools.vscode-docker", 23 | "ms-vscode.js-debug", 24 | "ms-vscode.vscode-node-azure-pack", 25 | "humao.rest-client", 26 | "rome.rome" 27 | ] 28 | } 29 | }, 30 | "forwardPorts": [ 31 | 8080 32 | ], 33 | "postCreateCommand": "cd src/api && npm install && cd ../.. && chmod -R a+x /workspaces/app-service-javascript-sap-cloud-sdk-quickstart/hooks", 34 | "remoteUser": "node", 35 | "hostRequirements": { 36 | "memory": "8gb" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /.devcontainer/azd-terraform/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. 2 | { 3 | "name": "Azure Developer CLI (Terraform)", 4 | "image": "mcr.microsoft.com/devcontainers/typescript-node:20-bullseye", 5 | "features": { 6 | "ghcr.io/devcontainers-contrib/features/nestjs-cli:2": {}, 7 | "ghcr.io/devcontainers/features/azure-cli:1": {}, 8 | "ghcr.io/devcontainers/features/docker-in-docker:2": {}, 9 | "ghcr.io/azure/azure-dev/azd:latest": {}, 10 | "ghcr.io/devcontainers/features/terraform:1": { 11 | "version": "latest" 12 | }, 13 | "ghcr.io/devcontainers/features/powershell:1": {} 14 | }, 15 | "customizations": { 16 | "vscode": { 17 | "extensions": [ 18 | "GitHub.vscode-github-actions", 19 | "ms-azuretools.azure-dev", 20 | "ms-azuretools.vscode-azurefunctions", 21 | "ms-azuretools.vscode-bicep", 22 | "ms-azuretools.vscode-docker", 23 | "ms-vscode.js-debug", 24 | "ms-vscode.vscode-node-azure-pack", 25 | "humao.rest-client", 26 | "rome.rome" 27 | ] 28 | } 29 | }, 30 | "forwardPorts": [ 31 | 8080 32 | ], 33 | "postCreateCommand": "cd src/api && npm install && cd ../.. && azd config set auth.useAzCliAuth true", 34 | "remoteUser": "node", 35 | "hostRequirements": { 36 | "memory": "8gb" 37 | } 38 | } -------------------------------------------------------------------------------- /.devcontainer/btpsa/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "BTP Setup Automator (Stable)", 3 | "image": "ghcr.io/sap-samples/btp-setup-automator:latest", 4 | "customizations": { 5 | "vscode": { 6 | "settings": { 7 | "python.defaultInterpreterPath": "/usr/local/bin/python3", 8 | "python.linting.enabled": true, 9 | "python.formatting.provider": "black", 10 | "python.linting.flake8Enabled": true, 11 | "python.linting.flake8Args": [ 12 | "--ignore=E501,F405,W504" 13 | ], 14 | "python.linting.pylintEnabled": false 15 | }, 16 | "extensions": [ 17 | "ms-python.python", 18 | "ms-python.vscode-pylance" 19 | ] 20 | }, 21 | "codespaces": { 22 | "openFiles": [ 23 | "parameters.json", 24 | "usecase.json" 25 | ] 26 | } 27 | }, 28 | "remoteUser": "user", 29 | "postCreateCommand": "cp -f /workspaces/app-service-javascript-sap-cloud-sdk-quickstart/infra-btpsa/parameters.json /home/user/parameters.json && cp -f /workspaces/app-service-javascript-sap-cloud-sdk-quickstart/infra-btpsa/usecase.json /home/user/usecase.json && cd /home/user && echo 'export PATH=\"$HOME/.local/bin:$PATH\"' >> ~/.profile && pip install flake8 black && source ~/.profile", 30 | "workspaceFolder": "/home/user", 31 | "hostRequirements": { 32 | "cpus": 2, 33 | "memory": "4gb", 34 | "storage": "32gb" 35 | } 36 | } -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. 2 | { 3 | "name": "Azure Developer CLI (Default - Bicep)", 4 | "image": "mcr.microsoft.com/devcontainers/typescript-node:20-bullseye", 5 | "features": { 6 | "ghcr.io/devcontainers-contrib/features/nestjs-cli:2": {}, 7 | "ghcr.io/devcontainers/features/docker-in-docker:2": {}, 8 | "ghcr.io/devcontainers/features/azure-cli:1": {}, 9 | "ghcr.io/azure/azure-dev/azd:latest": {}, 10 | "ghcr.io/devcontainers/features/github-cli:1": { 11 | "version": "2" 12 | }, 13 | "ghcr.io/devcontainers/features/powershell:1": {} 14 | }, 15 | "customizations": { 16 | "vscode": { 17 | "extensions": [ 18 | "GitHub.vscode-github-actions", 19 | "ms-azuretools.azure-dev", 20 | "ms-azuretools.vscode-azurefunctions", 21 | "ms-azuretools.vscode-bicep", 22 | "ms-azuretools.vscode-docker", 23 | "ms-vscode.js-debug", 24 | "ms-vscode.vscode-node-azure-pack", 25 | "humao.rest-client", 26 | "rome.rome" 27 | ] 28 | } 29 | }, 30 | "forwardPorts": [ 31 | 8080 32 | ], 33 | "postCreateCommand": "cd src/api && npm install && cd ../.. && chmod -R a+x /workspaces/app-service-javascript-sap-cloud-sdk-quickstart/hooks", 34 | "remoteUser": "node", 35 | "hostRequirements": { 36 | "memory": "8gb" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | *.{cmd,[cC][mM][dD]} text eol=crlf 3 | *.{bat,[bB][aA][tT]} text eol=crlf -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: 🐞 Bug report 2 | description: File a bug/issue to help us improve 3 | title: "[BUG]" 4 | labels: [bug] 5 | assignees: [MartinPankraz] 6 | body: 7 | - type: checkboxes 8 | attributes: 9 | label: Is there an existing issue for this? 10 | description: Please search to see if an issue already exists for the bug you encountered. 11 | options: 12 | - label: I have searched the existing issues 13 | required: true 14 | - type: dropdown 15 | id: area 16 | attributes: 17 | label: What type of issue are you facing 18 | description: What type of issue are you facing? 19 | options: 20 | - bug report 21 | - documentation issue or request 22 | - regression (a behavior that used to work and stopped in a new version) 23 | validations: 24 | required: true 25 | - type: textarea 26 | attributes: 27 | label: Describe the bug 28 | description: Provide a clear and concise description of what the bug is. 29 | validations: 30 | required: true 31 | - type: textarea 32 | attributes: 33 | label: Expected Behavior 34 | description: A concise description of what you expected to happen. 35 | validations: 36 | required: false 37 | - type: textarea 38 | attributes: 39 | label: Steps To Reproduce 40 | description: Steps to reproduce the behavior. 41 | placeholder: | 42 | 1. Go to '...' 43 | 2. Click on '....' 44 | 3. Scroll down to '....' 45 | 4. See error 46 | validations: 47 | required: false 48 | - type: textarea 49 | attributes: 50 | label: Add screenshots to help explain your problem 51 | description: | 52 | If applicable, add screenshots to help explain your problem. 53 | 54 | Tip: You can attach images or files by clicking this area to highlight it and then dragging files in. 55 | validations: 56 | required: false 57 | - type: textarea 58 | attributes: 59 | label: Additional context 60 | description: | 61 | Add any other context like links or references about the problem here. Anything that will give us more context about the issue you are encountering! 62 | 63 | Tip: You can attach images or files by clicking this area to highlight it and then dragging files in. 64 | validations: 65 | required: false 66 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: 💡Feature request 2 | description: Suggest an idea for this project 3 | title: "[FEATURE]" 4 | labels: [enhancement] 5 | assignees: [MartinPankraz] 6 | body: 7 | - type: dropdown 8 | attributes: 9 | label: What area do you want to see improved? 10 | description: Specify the main area that you want to see improved. 11 | options: 12 | - app code 13 | - documentation 14 | - infrastructure setup 15 | - developer experience (GitHub CodeSpaces etc.) 16 | - other 17 | validations: 18 | required: true 19 | - type: textarea 20 | id: area 21 | attributes: 22 | label: Is your feature request related to a problem? Please describe. 23 | description: Provide a clear and concise description of what the problem is e.g., I'm always frustrated when [...] 24 | validations: 25 | required: true 26 | - type: textarea 27 | attributes: 28 | label: Describe the solution you'd like 29 | description: A clear and concise description of what you want to happen. 30 | validations: 31 | required: true 32 | - type: textarea 33 | attributes: 34 | label: Describe alternatives you've considered 35 | description: A clear and concise description of any alternative solutions or features you've considered. 36 | validations: 37 | required: false 38 | - type: textarea 39 | attributes: 40 | label: Additional context 41 | description: | 42 | Add any other context like links or references about the problem here. Anything that will give us more context about the issue you are encountering! 43 | 44 | Tip: You can attach images or files by clicking this area to highlight it and then dragging files in. 45 | validations: 46 | required: false 47 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Purpose 2 | 3 | * ... 4 | 5 | ## Does this introduce a breaking change? 6 | 7 | ``` 8 | [ ] Yes 9 | [ ] No 10 | ``` 11 | 12 | ## Pull Request Type 13 | What kind of change does this Pull Request introduce? 14 | 15 | 16 | ``` 17 | [ ] Bugfix 18 | [ ] Feature 19 | [ ] Code style update (formatting, local variables) 20 | [ ] Refactoring (no functional changes, no api changes) 21 | [ ] Documentation content changes 22 | [ ] Other... Please describe: 23 | ``` 24 | 25 | ## How to Test 26 | * Get the code 27 | 28 | ``` 29 | git clone [repo-address] 30 | cd [repo-name] 31 | git checkout [branch-name] 32 | npm install 33 | ``` 34 | 35 | * Test the code 36 | 37 | ``` 38 | ``` 39 | 40 | ## What to Check 41 | Verify that the following are valid 42 | * ... 43 | 44 | ## Other Information 45 | 46 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | # Check for updates to GitHub Actions once a week on Monday 7 | interval: "weekly" 8 | day: "monday" 9 | - package-ecosystem: "npm" 10 | directory: "/" 11 | schedule: 12 | interval: "weekly" 13 | day: "monday" 14 | - package-ecosystem: "devcontainers" 15 | directory: "/" 16 | schedule: 17 | interval: "weekly" 18 | day: "monday" 19 | -------------------------------------------------------------------------------- /.github/workflows/azure-dev.yml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_dispatch: 3 | 4 | # Remove comment of this section once you want to use the setup via GH Actions 5 | # push: 6 | # branches: 7 | # - main 8 | # pull_request: 9 | # branches: 10 | # - main 11 | 12 | # https://learn.microsoft.com/en-us/azure/developer/github/connect-from-azure?tabs=azure-portal%2Clinux#set-up-azure-login-with-openid-connect-authentication 13 | 14 | jobs: 15 | build: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v4 20 | 21 | - name: Install azd 22 | uses: Azure/setup-azd@v2.0.0 23 | 24 | - name: Install Nodejs 25 | uses: actions/setup-node@v4 26 | with: 27 | node-version: 18 28 | 29 | - name: Login az 30 | uses: azure/login@v2 31 | with: 32 | creds: ${{ secrets.AZURE_CREDENTIALS }} 33 | 34 | - name: Set az account 35 | uses: azure/CLI@v2 36 | with: 37 | inlineScript: | 38 | az account set --subscription ${{vars.AZURE_SUBSCRIPTION_ID}} 39 | 40 | - name: Log in with Azure 41 | run: | 42 | $info = $Env:AZURE_CREDENTIALS | ConvertFrom-Json -AsHashtable; 43 | Write-Host "::add-mask::$($info.clientSecret)" 44 | 45 | azd auth login ` 46 | --client-id "$($info.clientId)" ` 47 | --client-secret "$($info.clientSecret)" ` 48 | --tenant-id "$($info.tenantId)" 49 | shell: pwsh 50 | env: 51 | AZURE_CREDENTIALS: ${{ secrets.AZURE_CREDENTIALS }} 52 | 53 | - name: Azure Dev Provision 54 | run: azd provision --no-prompt 55 | env: 56 | AZURE_ENV_NAME: ${{ vars.AZURE_ENV_NAME }} 57 | AZURE_LOCATION: ${{ vars.AZURE_LOCATION }} 58 | AZURE_SUBSCRIPTION_ID: ${{ vars.AZURE_SUBSCRIPTION_ID }} 59 | ARM_TENANT_ID: ${{ vars.ARM_TENANT_ID }} 60 | ARM_CLIENT_ID: ${{ vars.ARM_CLIENT_ID }} 61 | ARM_CLIENT_SECRET: ${{ secrets.ARM_CLIENT_SECRET }} 62 | RS_RESOURCE_GROUP: ${{ vars.RS_RESOURCE_GROUP }} 63 | RS_STORAGE_ACCOUNT: ${{ vars.RS_STORAGE_ACCOUNT }} 64 | RS_CONTAINER_NAME: ${{ vars.RS_CONTAINER_NAME }} 65 | ODATA_URL: ${{ secrets.ODATA_URL }} 66 | ODATA_USERNAME: ${{ secrets.ODATA_USERNAME }} 67 | ODATA_USERPWD: ${{ secrets.ODATA_USERPWD }} 68 | APIKEY: ${{ secrets.APIKEY }} 69 | APIKEY_HEADERNAME: ${{ secrets.APIKEY_HEADERNAME }} 70 | 71 | - name: Azure Dev Deploy 72 | run: azd deploy --no-prompt 73 | env: 74 | AZURE_ENV_NAME: ${{ secrets.AZURE_ENV_NAME }} 75 | AZURE_LOCATION: ${{ secrets.AZURE_LOCATION }} 76 | AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} 77 | ODATA_URL: ${{ secrets.ODATA_URL }} 78 | ODATA_USERNAME: ${{ secrets.ODATA_USERNAME }} 79 | ODATA_USERPWD: ${{ secrets.ODATA_USERPWD }} 80 | APIKEY: ${{ secrets.APIKEY }} 81 | APIKEY_HEADERNAME: ${{ secrets.APIKEY_HEADERNAME }} 82 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ "main" ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ "main" ] 20 | schedule: 21 | - cron: '37 7 * * 6' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'javascript' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 37 | # Use only 'java' to analyze code written in Java, Kotlin or both 38 | # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both 39 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 40 | 41 | steps: 42 | - name: Checkout repository 43 | uses: actions/checkout@v4 44 | 45 | # Initializes the CodeQL tools for scanning. 46 | - name: Initialize CodeQL 47 | uses: github/codeql-action/init@v3 48 | with: 49 | languages: ${{ matrix.language }} 50 | # If you wish to specify custom queries, you can do so here or in a config file. 51 | # By default, queries listed here will override any specified in a config file. 52 | # Prefix the list here with "+" to use these queries and those in the config file. 53 | 54 | # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 55 | # queries: security-extended,security-and-quality 56 | 57 | 58 | # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). 59 | # If this step fails, then you should remove it and run the build manually (see below) 60 | - name: Autobuild 61 | uses: github/codeql-action/autobuild@v3 62 | 63 | # ℹ️ Command-line programs to run using the OS shell. 64 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 65 | 66 | # If the Autobuild fails above, remove it and uncomment the following three lines. 67 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 68 | 69 | # - run: | 70 | # echo "Run, Build Application using script" 71 | # ./location_of_script_within_repo/buildscript.sh 72 | 73 | - name: Perform CodeQL Analysis 74 | uses: github/codeql-action/analyze@v3 75 | with: 76 | category: "/language:${{matrix.language}}" 77 | -------------------------------------------------------------------------------- /.github/workflows/links-watcher.yml: -------------------------------------------------------------------------------- 1 | name: Periodic Link Checker 2 | 3 | on: 4 | schedule: 5 | - cron: "0 23 * * *" 6 | workflow_dispatch: 7 | 8 | permissions: 9 | contents: read 10 | issues: write 11 | 12 | jobs: 13 | link-checker: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: "Checkout source code" 17 | uses: actions/checkout@v4 18 | - name: Link Checker 19 | id: lychee 20 | uses: lycheeverse/lychee-action@v2.2.0 21 | with: 22 | args: --verbose --no-progress --max-concurrency 2 --exclude-link-local --exclude-loopback './**/*.md' --exclude portal.azure.com --exclude developers.sap.com 23 | output: ./lychee/out.md 24 | fail: true 25 | env: 26 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 27 | - name: Find existing issue 28 | id: find_issue 29 | uses: micalevisk/last-issue-action@v2 30 | if: failure() 31 | with: 32 | state: open 33 | labels: | 34 | broken link 35 | automated issue 36 | - name: Create or update issue for broken links 37 | uses: peter-evans/create-issue-from-file@v5 38 | if: failure() 39 | with: 40 | title: Link Checker Report 41 | # If issue number is empty a new issue gets created 42 | issue-number: ${{ steps.find_issue.outputs.issue-number }} 43 | content-filepath: ./lychee/out.md 44 | labels: broken link, automated issue -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .azure 2 | node_modules 3 | dist 4 | infra-terraform/.terraform.lock.hcl 5 | .$*.bkp 6 | .$*.dtmp 7 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node-terminal", 9 | "name": "Run npm start:dev", 10 | "request": "launch", 11 | "command": "npm run start:dev", 12 | "cwd": "${workspaceFolder}/src/api" 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "appService.zipIgnorePattern": [ 3 | "node_modules{,/**}", 4 | ".vscode{,/**}", 5 | "img{,/**}", 6 | "templates{,/**}", 7 | ".env" 8 | ], 9 | "appService.deploySubpath": "src/api" 10 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [project-title] Changelog 2 | 3 | 4 | # x.y.z (yyyy-mm-dd) 5 | 6 | *Features* 7 | * ... 8 | 9 | *Bug Fixes* 10 | * ... 11 | 12 | *Breaking Changes* 13 | * ... 14 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE -------------------------------------------------------------------------------- /assets/apim-backoff-delay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/app-service-javascript-sap-cloud-sdk-quickstart/0675ad44767ad29593e6a6f62ee442bdd4169b57/assets/apim-backoff-delay.png -------------------------------------------------------------------------------- /assets/bupa-create-vsc-rest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/app-service-javascript-sap-cloud-sdk-quickstart/0675ad44767ad29593e6a6f62ee442bdd4169b57/assets/bupa-create-vsc-rest.png -------------------------------------------------------------------------------- /assets/bupa-delete-vsc-rest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/app-service-javascript-sap-cloud-sdk-quickstart/0675ad44767ad29593e6a6f62ee442bdd4169b57/assets/bupa-delete-vsc-rest.png -------------------------------------------------------------------------------- /assets/bupa-get-vsc-rest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/app-service-javascript-sap-cloud-sdk-quickstart/0675ad44767ad29593e6a6f62ee442bdd4169b57/assets/bupa-get-vsc-rest.png -------------------------------------------------------------------------------- /assets/bupa-update-vsc-rest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/app-service-javascript-sap-cloud-sdk-quickstart/0675ad44767ad29593e6a6f62ee442bdd4169b57/assets/bupa-update-vsc-rest.png -------------------------------------------------------------------------------- /assets/drawings.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/app-service-javascript-sap-cloud-sdk-quickstart/0675ad44767ad29593e6a6f62ee442bdd4169b57/assets/drawings.pptx -------------------------------------------------------------------------------- /assets/project-overview-azd-style.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/app-service-javascript-sap-cloud-sdk-quickstart/0675ad44767ad29593e6a6f62ee442bdd4169b57/assets/project-overview-azd-style.png -------------------------------------------------------------------------------- /azure.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/main/schemas/v1.0/azure.yaml.json 2 | 3 | name: app-service-javascript-sap-cloud-sdk-quickstart 4 | metadata: 5 | template: app-service-javascript-sap-cloud-sdk-quickstart 6 | requiredVersions: 7 | azd: ">= 1.4.0" 8 | hooks: 9 | postup: 10 | posix: 11 | shell: sh 12 | run: ./hooks/fireworks.sh 13 | continueOnError: true 14 | interactive: true 15 | postprovision: 16 | posix: 17 | shell: pwsh 18 | run: ./hooks/createAppRegistrations.ps1 19 | continueOnError: false 20 | interactive: true 21 | windows: 22 | shell: pwsh 23 | run: ./hooks/createAppRegistrations.ps1 24 | continueOnError: false 25 | interactive: true 26 | #postdown: 27 | #posix: 28 | # shell: pwsh 29 | # run: ./hooks/deleteAppRegistrations.ps1 30 | # continueOnError: true 31 | # interactive: true 32 | #windows: 33 | # shell: pwsh 34 | # run: ./hooks/deleteAppRegistrations.ps1 35 | # continueOnError: false 36 | # interactive: true 37 | services: 38 | sap-cloud-sdk-api: 39 | project: ./src/api 40 | language: ts 41 | host: appservice 42 | # Remove the comments from the lines below to enable the deployment of the infrastructure via Terraform 43 | #infra: 44 | # provider: terraform 45 | # path: ./infra-terraform 46 | -------------------------------------------------------------------------------- /documentation/ADDITIONAL-RESOURCES.md: -------------------------------------------------------------------------------- 1 | # Additional resources 2 | 3 | ## Details 4 | 5 | * [SAP Cloud SDK documentation for JavaScript](https://sap.github.io/cloud-sdk/docs/js/tutorials/getting-started/introduction) 6 | 7 | * [Access SAP Business Application Studio as a remote from Visual Studio Code / Codespaces](https://blogs.sap.com/2023/04/28/access-sap-business-application-studio-as-a-remote-from-visual-studio-code) 8 | 9 | * [SAP Cloud SDK repos for JavaScript](https://github.com/SAP/cloud-sdk-js) 10 | 11 | * [SAP Cloud SDK for JavaScript API documentation](https://sap.github.io/cloud-sdk/api/v2/index.html) 12 | 13 | * [.NET speaks OData too – how to implement Azure App Service with SAP Gateway](https://github.com/MartinPankraz/AzureSAPODataReader) 14 | 15 | * [Azure API Management policy for SAP Principal Propagation](https://github.com/Azure/api-management-policy-snippets/blob/master/examples/Request%20OAuth2%20access%20token%20from%20SAP%20using%20AAD%20JWT%20token.xml) 16 | 17 | ## Related efforts and repos🖇️ 18 | 19 | * [Azure SDK for SAP OData](https://github.com/Azure/azure-sdk-for-sap-odata) 20 | 21 | * [Visual Studio extension for generating client code for OData Services](https://learn.microsoft.com/odata/connectedservice/getting-started) 22 | 23 | * [OData CLI](https://learn.microsoft.com/odata/odatacli/getting-started) 24 | 25 | * [.NET project showcasing integration of Azure AD with Azure API Management for SAP OData consumption leveraging Principal Propagation](https://github.com/MartinPankraz/AzureSAPODataReader) 26 | 27 | * [OData to OpenAPI converter](https://aka.ms/ODataOpenAPI) 28 | 29 | * [SAP ABAP OpenAPI UI](https://blogs.sap.com/2022/03/31/abap-openapi-ui-v2-a-long-overdue-update/) 30 | 31 | * [Azure Developer CLI](https://github.com/Azure/azure-dev) 32 | -------------------------------------------------------------------------------- /documentation/DEPLOYMENT-VSCODE.md: -------------------------------------------------------------------------------- 1 | # Deployment via VS Code Extension 2 | 3 | In this example we use the [Azure App Service extension](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-azureappservice) for VS Code to deploy the project. Learn more about [this process on Microsoft learn](https://learn.microsoft.com/training/modules/create-publish-webapp-app-service-vs-code/5-exercise-publish-app-azure-app-service-vs-code?pivots=nodeexpress). 4 | 5 | 1. Create an Azure App Service with Node.js 18 LTS and Linux using the [VS Code extension for Azure](https://code.visualstudio.com/docs/azure/extensions) or use below button. 6 | 2. Maintain or upload environment variables in the [Azure App Service configuration](https://learn.microsoft.com/azure/app-service/configure-common?tabs=portal#configure-app-settings) - just like you did for the `.env` file for local execution in the previous section. 7 | 3. Deploy to Web App from VS Code or GitHub Codespaces (right click in the explorer on the project folder and select "Deploy to Web App..."). 8 | 4. Browse your new app powered by the SAP Cloud SDK (it takes a while the first time). 9 | 10 | [![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure-Samples%2Fapp-service-javascript-sap-cloud-sdk-quickstart%2Fmain%2Ftemplates%2Fazuredeploy.json) 11 | -------------------------------------------------------------------------------- /hooks/deleteAppRegistrations.ps1: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script is part of the sample's workflow for configuring App Registrations 4 | # in Azure AD and saving the appropriate values in Key Vault, and Azure App Config Service 5 | # so that the application can authenticate users. Note that an app registration is 6 | # something you'll want to set up once, and reuse for every version of the web app 7 | # that you deploy. You can learn more about app registrations at 8 | # https://learn.microsoft.com/en-us/azure/active-directory/develop/application-model 9 | # 10 | # If you do not have permission to create App Registrations consider 11 | # sharing this script, or something similar, with your administrators to help them 12 | # set up the variables you need to integrate with Azure AD 13 | # 14 | # This code may be repurposed for your scenario as desired 15 | # but is not covered by the guidance in this content. 16 | 17 | # Using Azure CLI command to create an app registration on Entra ID: https://learn.microsoft.com/cli/azure/ad/app?view=azure-cli-latest 18 | if ($env:USE_EntraIDAuthentication -eq "false") { 19 | 20 | Write-Output "Skipping app registration delete because USE_EntraIDAuthentication is set to false" 21 | exit 0 22 | 23 | } 24 | 25 | Write-Output "Deleting app registration for web app using object id" 26 | 27 | # Get Object ID of app registration 28 | 29 | ###### Figure out how to get the object id of the app registration when AZD clears the .ENV file before this script runs 30 | #$APP_METADATA = az ad app list --display-name $env:WEB_APP_NAME | ConvertFrom-Json 31 | #$OBJECT_ID = $APP_METADATA.id 32 | #az ad app delete --id $OBJECT_ID 33 | 34 | # all done 35 | exit 0 -------------------------------------------------------------------------------- /infra-btpsa/parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/SAP-samples/btp-setup-automator/main/libs/btpsa-parameters.json", 3 | "usecasefile": "usecase.json", 4 | "region": "eu20", 5 | "globalaccount": "ID of your Global Account", 6 | "myemail": "Enter your email address here", 7 | "loginmethod": "basicAuthentication", 8 | "subaccountname": "SAP-Cloud-SDK-Quickstart", 9 | "cfspacename": "development", 10 | "prunesubaccount": false 11 | } -------------------------------------------------------------------------------- /infra-btpsa/usecase.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/SAP-samples/btp-setup-automator/main/libs/btpsa-usecase.json", 3 | "aboutThisUseCase": { 4 | "name": "SAP Cloud SDK Quickstart", 5 | "description": "This usecase provides all necessary information to create and deploy a SAP Cloud SDK based Node.js app a SAP BTP account to Cloud Foundry.", 6 | "author": "christian.lechner@sap.com", 7 | "testStatus": "tested successfully", 8 | "usageStatus": "READY TO BE USED", 9 | "relatedLinks": [ 10 | "https://github.com/Azure-Samples/app-service-javascript-sap-cloud-sdk-quickstart" 11 | ] 12 | }, 13 | "services": [ 14 | { 15 | "name": "cloudfoundry", 16 | "plan": "free", 17 | "category": "ENVIRONMENT" 18 | }, 19 | { 20 | "name": "xsuaa", 21 | "instancename": "xsuaaquickstart", 22 | "plan": "application", 23 | "category": "SERVICE" 24 | }, 25 | { 26 | "name": "destination", 27 | "instancename": "destinationquickstart", 28 | "plan": "lite", 29 | "category": "SERVICE", 30 | "parameters": { 31 | "HTML5Runtime_enabled": "true", 32 | "init_data": { 33 | "subaccount": { 34 | "existing_destinations_policy": "fail", 35 | "destinations": [ 36 | { 37 | "Name": "S4HANACloudSandbox", 38 | "Description": "SAP S/4HANA Cloud Sandbox", 39 | "Type": "HTTP", 40 | "URL": "https://sandbox.api.sap.com/s4hanacloud", 41 | "Authentication": "BasicAuthentication", 42 | "ProxyType": "Internet", 43 | "User": "ODATA_USERNAME", 44 | "Password": "ODATA_USERPWD" 45 | } 46 | ] 47 | } 48 | } 49 | } 50 | }, 51 | { 52 | "category": "SERVICE", 53 | "name": "privatelink", 54 | "plan": "standard", 55 | "entitleonly": true 56 | } 57 | ], 58 | "executeAfterAccountSetup": [ 59 | { 60 | "description": "Building application assets", 61 | "command": "cd /workspaces/app-service-javascript-sap-cloud-sdk-quickstart/src/api && npm ci && npm run build " 62 | }, 63 | { 64 | "description": "Deploying the application", 65 | "command": "cd /workspaces/app-service-javascript-sap-cloud-sdk-quickstart/src/api && cf push cloudsdkapp" 66 | }, 67 | { 68 | "description": "YOUR MANUAL TODO (Private Link Configuration)", 69 | "command": "echo '- Create service instance for Private Link service'" 70 | } 71 | ], 72 | "executeToPruneUseCase": [ 73 | { 74 | "description": "Delete the application", 75 | "command": "cf delete cloudsdkapp -r -f" 76 | } 77 | ] 78 | } -------------------------------------------------------------------------------- /infra-terraform/main.tfvars.json: -------------------------------------------------------------------------------- 1 | { 2 | "location": "${AZURE_LOCATION}", 3 | "environment_name": "${AZURE_ENV_NAME}", 4 | "principal_id": "${AZURE_PRINCIPAL_ID}" 5 | } 6 | -------------------------------------------------------------------------------- /infra-terraform/modules/apim-api/apim-api.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | azurerm = { 4 | version = "~>3.47.0" 5 | source = "hashicorp/azurerm" 6 | } 7 | azurecaf = { 8 | source = "aztfmod/azurecaf" 9 | version = "~>1.2.15" 10 | } 11 | } 12 | } 13 | 14 | data "azurerm_api_management" "apim" { 15 | name = var.name 16 | resource_group_name = var.rg_name 17 | } 18 | 19 | # ------------------------------------------------------------------------------------------------------ 20 | # Deploy apim-api service 21 | # ------------------------------------------------------------------------------------------------------ 22 | resource "azurerm_api_management_api" "api" { 23 | name = var.api_name 24 | resource_group_name = var.rg_name 25 | api_management_name = data.azurerm_api_management.apim.name 26 | revision = "1" 27 | display_name = var.api_display_name 28 | path = var.api_path 29 | protocols = ["https"] 30 | service_url = var.api_backend_url 31 | subscription_required = false 32 | 33 | import { 34 | content_format = "openapi" 35 | content_value = file("${path.module}/../../../src/api/openapi.yaml") 36 | } 37 | } 38 | 39 | resource "azurerm_api_management_api_policy" "policies" { 40 | api_name = azurerm_api_management_api.api.name 41 | api_management_name = azurerm_api_management_api.api.api_management_name 42 | resource_group_name = var.rg_name 43 | 44 | xml_content = replace(file("${path.module}/apim-api-policy.xml"), "{origin}", var.web_front_end_url) 45 | } 46 | 47 | resource "azurerm_api_management_api_diagnostic" "diagnostics" { 48 | identifier = "applicationinsights" 49 | resource_group_name = var.rg_name 50 | api_management_name = azurerm_api_management_api.api.api_management_name 51 | api_name = azurerm_api_management_api.api.name 52 | api_management_logger_id = var.api_management_logger_id 53 | 54 | sampling_percentage = 100.0 55 | always_log_errors = true 56 | log_client_ip = true 57 | verbosity = "verbose" 58 | http_correlation_protocol = "W3C" 59 | 60 | frontend_request { 61 | body_bytes = 1024 62 | headers_to_log = [ 63 | "content-type", 64 | "accept", 65 | "origin", 66 | ] 67 | } 68 | 69 | frontend_response { 70 | body_bytes = 1024 71 | headers_to_log = [ 72 | "content-type", 73 | "content-length", 74 | "origin", 75 | ] 76 | } 77 | 78 | backend_request { 79 | body_bytes = 1024 80 | headers_to_log = [ 81 | "content-type", 82 | "accept", 83 | "origin", 84 | ] 85 | } 86 | 87 | backend_response { 88 | body_bytes = 1024 89 | headers_to_log = [ 90 | "content-type", 91 | "content-length", 92 | "origin", 93 | ] 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /infra-terraform/modules/apim-api/apim-api_output.tf: -------------------------------------------------------------------------------- 1 | output "SERVICE_API_URI" { 2 | value = "${data.azurerm_api_management.apim.gateway_url}/${var.api_path}" 3 | } 4 | -------------------------------------------------------------------------------- /infra-terraform/modules/apim-api/apim-api_variables.tf: -------------------------------------------------------------------------------- 1 | variable "name" { 2 | type = string 3 | } 4 | 5 | variable "rg_name" { 6 | description = "The name of the resource group to deploy resources into" 7 | type = string 8 | } 9 | 10 | variable "api_management_logger_id" { 11 | description = "The name of the resource application insights" 12 | type = string 13 | } 14 | 15 | variable "web_front_end_url" { 16 | description = "The url of the web" 17 | type = string 18 | } 19 | 20 | variable "api_backend_url" { 21 | description = "Absolute URL of the backend service implementing this API." 22 | type = string 23 | } 24 | 25 | variable "api_name" { 26 | description = "Resource name to uniquely identify this API within the API Management service instance" 27 | type = string 28 | } 29 | 30 | variable "api_display_name" { 31 | 32 | description = "The Display Name of the API" 33 | type = string 34 | } 35 | 36 | variable "api_path" { 37 | description = "Relative URL uniquely identifying this API and all of its resource paths within the API Management service instance. It is appended to the API endpoint base URL specified during the service instance creation to form a public URL for this API." 38 | type = string 39 | } 40 | -------------------------------------------------------------------------------- /infra-terraform/modules/apim/apim.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | azurerm = { 4 | version = "~>3.47.0" 5 | source = "hashicorp/azurerm" 6 | } 7 | azurecaf = { 8 | source = "aztfmod/azurecaf" 9 | version = "~>1.2.15" 10 | } 11 | } 12 | } 13 | 14 | data "azurerm_application_insights" "appinsights"{ 15 | name = var.application_insights_name 16 | resource_group_name = var.rg_name 17 | } 18 | # ------------------------------------------------------------------------------------------------------ 19 | # Deploy api management service 20 | # ------------------------------------------------------------------------------------------------------ 21 | 22 | # Create a new APIM instance 23 | resource "azurerm_api_management" "apim" { 24 | name = var.name 25 | location = var.location 26 | resource_group_name = var.rg_name 27 | publisher_name = var.publisher_name 28 | publisher_email = var.publisher_email 29 | tags = var.tags 30 | sku_name = "${var.sku}_${(var.sku == "Consumption") ? 0 : ((var.sku == "Developer") ? 1 : var.skuCount)}" 31 | identity { 32 | type = "SystemAssigned" 33 | } 34 | } 35 | 36 | # Create Logger 37 | resource "azurerm_api_management_logger" "logger" { 38 | name = "app-insights-logger" 39 | api_management_name = azurerm_api_management.apim.name 40 | resource_group_name = var.rg_name 41 | 42 | application_insights { 43 | instrumentation_key = data.azurerm_application_insights.appinsights.instrumentation_key 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /infra-terraform/modules/apim/apim_output.tf: -------------------------------------------------------------------------------- 1 | output "APIM_SERVICE_NAME" { 2 | value = azurerm_api_management.apim.name 3 | } 4 | 5 | output "API_MANAGEMENT_LOGGER_ID" { 6 | value = azurerm_api_management_logger.logger.id 7 | } 8 | -------------------------------------------------------------------------------- /infra-terraform/modules/apim/apim_variables.tf: -------------------------------------------------------------------------------- 1 | variable "location" { 2 | description = "The supported Azure location where the resource deployed" 3 | type = string 4 | } 5 | 6 | variable "rg_name" { 7 | description = "The name of the resource group to deploy resources into" 8 | type = string 9 | } 10 | 11 | variable "tags" { 12 | description = "A list of tags used for deployed services." 13 | type = map(string) 14 | } 15 | 16 | variable "sku" { 17 | description = "The pricing tier of this API Management service." 18 | type = string 19 | default = "Consumption" 20 | } 21 | 22 | variable "application_insights_name" { 23 | description = "Azure Application Insights Name." 24 | type = string 25 | } 26 | 27 | variable "skuCount" { 28 | description = "The instance size of this API Management service. @allowed([ 0, 1, 2 ])" 29 | type = string 30 | default = "0" 31 | } 32 | 33 | variable "name" { 34 | type = string 35 | } 36 | 37 | variable "publisher_email" { 38 | description = "The email address of the owner of the service." 39 | type = string 40 | default = "noreply@microsoft.com" 41 | } 42 | 43 | variable "publisher_name" { 44 | description = "The name of the owner of the service" 45 | type = string 46 | default = "n/a" 47 | } 48 | -------------------------------------------------------------------------------- /infra-terraform/modules/applicationinsights/applicationinsights_output.tf: -------------------------------------------------------------------------------- 1 | output "APPLICATIONINSIGHTS_CONNECTION_STRING" { 2 | value = azurerm_application_insights.applicationinsights.connection_string 3 | sensitive = true 4 | } 5 | 6 | output "APPLICATIONINSIGHTS_NAME" { 7 | value = azurerm_application_insights.applicationinsights.name 8 | sensitive = false 9 | } 10 | 11 | output "APPLICATIONINSIGHTS_INSTRUMENTATION_KEY" { 12 | value = azurerm_application_insights.applicationinsights.instrumentation_key 13 | sensitive = true 14 | } 15 | -------------------------------------------------------------------------------- /infra-terraform/modules/applicationinsights/applicationinsights_variables.tf: -------------------------------------------------------------------------------- 1 | variable "location" { 2 | description = "The supported Azure location where the resource deployed" 3 | type = string 4 | } 5 | 6 | variable "rg_name" { 7 | description = "The name of the resource group to deploy resources into" 8 | type = string 9 | } 10 | 11 | variable "environment_name" { 12 | description = "The name of the environment to be deployed" 13 | type = string 14 | } 15 | 16 | variable "workspace_id" { 17 | description = "The name of the Azure log analytics workspace" 18 | type = string 19 | } 20 | 21 | variable "tags" { 22 | description = "A list of tags used for deployed services." 23 | type = map(string) 24 | } 25 | 26 | variable "resource_token" { 27 | description = "A suffix string to centrally mitigate resource name collisions." 28 | type = string 29 | } -------------------------------------------------------------------------------- /infra-terraform/modules/appservicenode/appservicenode.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | azurerm = { 4 | version = "~>3.47.0" 5 | source = "hashicorp/azurerm" 6 | } 7 | azurecaf = { 8 | source = "aztfmod/azurecaf" 9 | version = "~>1.2.15" 10 | } 11 | } 12 | } 13 | # ------------------------------------------------------------------------------------------------------ 14 | # Deploy app service web app 15 | # ------------------------------------------------------------------------------------------------------ 16 | resource "azurecaf_name" "web_name" { 17 | name = "${var.service_name}-${var.resource_token}" 18 | resource_type = "azurerm_app_service" 19 | random_length = 0 20 | clean_input = true 21 | } 22 | 23 | resource "azurerm_linux_web_app" "web" { 24 | name = azurecaf_name.web_name.result 25 | location = var.location 26 | resource_group_name = var.rg_name 27 | service_plan_id = var.appservice_plan_id 28 | https_only = true 29 | tags = var.tags 30 | 31 | site_config { 32 | always_on = true 33 | ftps_state = "FtpsOnly" 34 | app_command_line = var.app_command_line 35 | application_stack { 36 | node_version = var.node_version 37 | } 38 | } 39 | 40 | app_settings = var.app_settings 41 | 42 | dynamic "identity" { 43 | for_each = { for k, v in var.identity : k => v if var.identity != [] } 44 | content { 45 | type = identity.value["type"] 46 | } 47 | } 48 | 49 | logs { 50 | application_logs { 51 | file_system_level = "Verbose" 52 | } 53 | detailed_error_messages = true 54 | failed_request_tracing = true 55 | http_logs { 56 | file_system { 57 | retention_in_days = 1 58 | retention_in_mb = 35 59 | } 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /infra-terraform/modules/appservicenode/appservicenode_output.tf: -------------------------------------------------------------------------------- 1 | output "URI" { 2 | value = "https://${azurerm_linux_web_app.web.default_hostname}" 3 | } 4 | 5 | output "IDENTITY_PRINCIPAL_ID" { 6 | value = length(azurerm_linux_web_app.web.identity) == 0 ? "" : azurerm_linux_web_app.web.identity.0.principal_id 7 | sensitive = true 8 | } -------------------------------------------------------------------------------- /infra-terraform/modules/appservicenode/appservicenode_variables.tf: -------------------------------------------------------------------------------- 1 | variable "location" { 2 | description = "The supported Azure location where the resource deployed" 3 | type = string 4 | } 5 | 6 | variable "rg_name" { 7 | description = "The name of the resource group to deploy resources into" 8 | type = string 9 | } 10 | 11 | variable "appservice_plan_id" { 12 | description = "The id of the appservice plan to use." 13 | type = string 14 | } 15 | 16 | variable "service_name" { 17 | description = "A name to reflect the type of the app service e.g: web, api." 18 | type = string 19 | } 20 | 21 | variable "app_settings" { 22 | description = "A list of app settings pairs to be assigned to the app service" 23 | type = map(string) 24 | } 25 | 26 | variable "identity" { 27 | description = "A list of application identity" 28 | type = list(any) 29 | default = [] 30 | } 31 | 32 | variable "app_command_line" { 33 | description = "The cmd line to configure the app to run." 34 | type = string 35 | } 36 | 37 | variable "tags" { 38 | description = "A list of tags used for deployed services." 39 | type = map(string) 40 | } 41 | 42 | variable "resource_token" { 43 | description = "A suffix string to centrally mitigate resource name collisions." 44 | type = string 45 | } 46 | 47 | variable "node_version" { 48 | description = "the application stack node version to set for the app service." 49 | type = string 50 | default = "16-lts" 51 | } -------------------------------------------------------------------------------- /infra-terraform/modules/appserviceplan/appserviceplan.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | azurerm = { 4 | version = "~>3.47.0" 5 | source = "hashicorp/azurerm" 6 | } 7 | azurecaf = { 8 | source = "aztfmod/azurecaf" 9 | version = "~>1.2.15" 10 | } 11 | } 12 | } 13 | # ------------------------------------------------------------------------------------------------------ 14 | # Deploy app service plan 15 | # ------------------------------------------------------------------------------------------------------ 16 | resource "azurecaf_name" "plan_name" { 17 | name = var.resource_token 18 | resource_type = "azurerm_app_service_plan" 19 | random_length = 0 20 | clean_input = true 21 | } 22 | 23 | resource "azurerm_service_plan" "plan" { 24 | name = azurecaf_name.plan_name.result 25 | location = var.location 26 | resource_group_name = var.rg_name 27 | os_type = var.os_type 28 | sku_name = var.sku_name 29 | 30 | tags = var.tags 31 | } 32 | -------------------------------------------------------------------------------- /infra-terraform/modules/appserviceplan/appserviceplan_output.tf: -------------------------------------------------------------------------------- 1 | output "APPSERVICE_PLAN_ID" { 2 | value = azurerm_service_plan.plan.id 3 | sensitive = true 4 | } -------------------------------------------------------------------------------- /infra-terraform/modules/appserviceplan/appserviceplan_variables.tf: -------------------------------------------------------------------------------- 1 | variable "location" { 2 | description = "The supported Azure location where the resource deployed" 3 | type = string 4 | } 5 | 6 | variable "rg_name" { 7 | description = "The name of the resource group to deploy resources into" 8 | type = string 9 | } 10 | 11 | variable "tags" { 12 | description = "A list of tags used for deployed services." 13 | type = map(string) 14 | } 15 | 16 | variable "resource_token" { 17 | description = "A suffix string to centrally mitigate resource name collisions." 18 | type = string 19 | } 20 | 21 | variable "sku_name" { 22 | description = "The SKU for the plan." 23 | type = string 24 | default = "B1" 25 | } 26 | 27 | variable "os_type" { 28 | description = "The O/S type for the App Services to be hosted in this plan." 29 | type = string 30 | default = "Linux" 31 | } -------------------------------------------------------------------------------- /infra-terraform/modules/cosmos/cosmos.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | azurerm = { 4 | version = "~>3.47.0" 5 | source = "hashicorp/azurerm" 6 | } 7 | azurecaf = { 8 | source = "aztfmod/azurecaf" 9 | version = "~>1.2.15" 10 | } 11 | } 12 | } 13 | # ------------------------------------------------------------------------------------------------------ 14 | # Deploy cosmos db account 15 | # ------------------------------------------------------------------------------------------------------ 16 | resource "azurecaf_name" "db_acc_name" { 17 | name = var.resource_token 18 | resource_type = "azurerm_cosmosdb_account" 19 | random_length = 0 20 | clean_input = true 21 | } 22 | 23 | resource "azurerm_cosmosdb_account" "db" { 24 | name = azurecaf_name.db_acc_name.result 25 | location = var.location 26 | resource_group_name = var.rg_name 27 | offer_type = "Standard" 28 | kind = "MongoDB" 29 | enable_automatic_failover = false 30 | enable_multiple_write_locations = false 31 | mongo_server_version = "4.0" 32 | tags = var.tags 33 | 34 | capabilities { 35 | name = "EnableServerless" 36 | } 37 | 38 | lifecycle { 39 | ignore_changes = [capabilities] 40 | } 41 | consistency_policy { 42 | consistency_level = "Session" 43 | } 44 | 45 | geo_location { 46 | location = var.location 47 | failover_priority = 0 48 | zone_redundant = false 49 | } 50 | } 51 | 52 | # ------------------------------------------------------------------------------------------------------ 53 | # Deploy cosmos mongo db and collections 54 | # ------------------------------------------------------------------------------------------------------ 55 | resource "azurerm_cosmosdb_mongo_database" "mongodb" { 56 | name = "Todo" 57 | resource_group_name = azurerm_cosmosdb_account.db.resource_group_name 58 | account_name = azurerm_cosmosdb_account.db.name 59 | } 60 | 61 | resource "azurerm_cosmosdb_mongo_collection" "list" { 62 | name = "TodoList" 63 | resource_group_name = azurerm_cosmosdb_account.db.resource_group_name 64 | account_name = azurerm_cosmosdb_account.db.name 65 | database_name = azurerm_cosmosdb_mongo_database.mongodb.name 66 | shard_key = "_id" 67 | 68 | 69 | index { 70 | keys = ["_id"] 71 | unique = true 72 | } 73 | } 74 | 75 | resource "azurerm_cosmosdb_mongo_collection" "item" { 76 | name = "TodoItem" 77 | resource_group_name = azurerm_cosmosdb_account.db.resource_group_name 78 | account_name = azurerm_cosmosdb_account.db.name 79 | database_name = azurerm_cosmosdb_mongo_database.mongodb.name 80 | shard_key = "_id" 81 | 82 | index { 83 | keys = ["_id"] 84 | unique = true 85 | } 86 | } -------------------------------------------------------------------------------- /infra-terraform/modules/cosmos/cosmos_output.tf: -------------------------------------------------------------------------------- 1 | output "AZURE_COSMOS_CONNECTION_STRING" { 2 | value = azurerm_cosmosdb_account.db.connection_strings[0] 3 | sensitive = true 4 | } 5 | 6 | output "AZURE_COSMOS_DATABASE_NAME" { 7 | value = azurerm_cosmosdb_mongo_database.mongodb.name 8 | } -------------------------------------------------------------------------------- /infra-terraform/modules/cosmos/cosmos_variables.tf: -------------------------------------------------------------------------------- 1 | variable "location" { 2 | description = "The supported Azure location where the resource deployed" 3 | type = string 4 | } 5 | 6 | variable "rg_name" { 7 | description = "The name of the resource group to deploy resources into" 8 | type = string 9 | } 10 | 11 | variable "tags" { 12 | description = "A list of tags used for deployed services." 13 | type = map(string) 14 | } 15 | 16 | variable "resource_token" { 17 | description = "A suffix string to centrally mitigate resource name collisions." 18 | type = string 19 | } -------------------------------------------------------------------------------- /infra-terraform/modules/keyvault/keyvault.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | azurerm = { 4 | version = "~>3.47.0" 5 | source = "hashicorp/azurerm" 6 | } 7 | azurecaf = { 8 | source = "aztfmod/azurecaf" 9 | version = "~>1.2.15" 10 | } 11 | } 12 | } 13 | 14 | data "azurerm_client_config" "current" {} 15 | # ------------------------------------------------------------------------------------------------------ 16 | # DEPLOY AZURE KEYVAULT 17 | # ------------------------------------------------------------------------------------------------------ 18 | resource "azurecaf_name" "kv_name" { 19 | name = var.resource_token 20 | resource_type = "azurerm_key_vault" 21 | random_length = 0 22 | clean_input = true 23 | } 24 | 25 | resource "azurerm_key_vault" "kv" { 26 | name = azurecaf_name.kv_name.result 27 | location = var.location 28 | resource_group_name = var.rg_name 29 | tenant_id = data.azurerm_client_config.current.tenant_id 30 | purge_protection_enabled = false 31 | sku_name = "standard" 32 | 33 | tags = var.tags 34 | } 35 | 36 | resource "azurerm_key_vault_access_policy" "app" { 37 | count = length(var.access_policy_object_ids) 38 | key_vault_id = azurerm_key_vault.kv.id 39 | tenant_id = data.azurerm_client_config.current.tenant_id 40 | object_id = var.access_policy_object_ids[count.index] 41 | 42 | secret_permissions = [ 43 | "Get", 44 | "Set", 45 | "List", 46 | "Delete", 47 | ] 48 | } 49 | 50 | resource "azurerm_key_vault_access_policy" "user" { 51 | count = var.principal_id == "" ? 0 : 1 52 | key_vault_id = azurerm_key_vault.kv.id 53 | tenant_id = data.azurerm_client_config.current.tenant_id 54 | object_id = var.principal_id 55 | 56 | secret_permissions = [ 57 | "Get", 58 | "Set", 59 | "List", 60 | "Delete", 61 | "Purge" 62 | ] 63 | } 64 | 65 | resource "azurerm_key_vault_secret" "secrets" { 66 | count = length(var.secrets) 67 | name = var.secrets[count.index].name 68 | value = var.secrets[count.index].value 69 | key_vault_id = azurerm_key_vault.kv.id 70 | depends_on = [ 71 | azurerm_key_vault_access_policy.user, 72 | azurerm_key_vault_access_policy.app 73 | ] 74 | } -------------------------------------------------------------------------------- /infra-terraform/modules/keyvault/keyvault_output.tf: -------------------------------------------------------------------------------- 1 | output "AZURE_KEY_VAULT_ENDPOINT" { 2 | value = azurerm_key_vault.kv.vault_uri 3 | sensitive = true 4 | } -------------------------------------------------------------------------------- /infra-terraform/modules/keyvault/keyvault_variables.tf: -------------------------------------------------------------------------------- 1 | variable "location" { 2 | description = "The supported Azure location where the resource deployed" 3 | type = string 4 | } 5 | 6 | variable "rg_name" { 7 | description = "The name of the resource group to deploy resources into" 8 | type = string 9 | } 10 | 11 | variable "tags" { 12 | description = "A list of tags used for deployed services." 13 | type = map(string) 14 | } 15 | 16 | variable "resource_token" { 17 | description = "A suffix string to centrally mitigate resource name collisions." 18 | type = string 19 | } 20 | 21 | variable "principal_id" { 22 | description = "The Id of the service principal to add to deployed keyvault access policies" 23 | sensitive = true 24 | type = string 25 | } 26 | 27 | variable "access_policy_object_ids" { 28 | description = "A list of object ids to be be added to the keyvault access policies" 29 | type = list(string) 30 | sensitive = true 31 | default = [] 32 | } 33 | 34 | variable "secrets" { 35 | description = "A list of secrets to be added to the keyvault" 36 | type = list(object({ 37 | name = string 38 | value = string 39 | })) 40 | sensitive = true 41 | } -------------------------------------------------------------------------------- /infra-terraform/modules/loganalytics/loganalytics.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | azurerm = { 4 | version = "~>3.47.0" 5 | source = "hashicorp/azurerm" 6 | } 7 | azurecaf = { 8 | source = "aztfmod/azurecaf" 9 | version = "~>1.2.15" 10 | } 11 | } 12 | } 13 | # ------------------------------------------------------------------------------------------------------ 14 | # Deploy log analytics workspace 15 | # ------------------------------------------------------------------------------------------------------ 16 | resource "azurecaf_name" "workspace_name" { 17 | name = var.resource_token 18 | resource_type = "azurerm_log_analytics_workspace" 19 | random_length = 0 20 | clean_input = true 21 | } 22 | 23 | resource "azurerm_log_analytics_workspace" "workspace" { 24 | name = azurecaf_name.workspace_name.result 25 | location = var.location 26 | resource_group_name = var.rg_name 27 | sku = "PerGB2018" 28 | retention_in_days = 30 29 | tags = var.tags 30 | } 31 | -------------------------------------------------------------------------------- /infra-terraform/modules/loganalytics/loganalytics_output.tf: -------------------------------------------------------------------------------- 1 | output "LOGANALYTICS_WORKSPACE_ID" { 2 | value = azurerm_log_analytics_workspace.workspace.id 3 | } -------------------------------------------------------------------------------- /infra-terraform/modules/loganalytics/loganalytics_variables.tf: -------------------------------------------------------------------------------- 1 | variable "location" { 2 | description = "The supported Azure location where the resource deployed" 3 | type = string 4 | } 5 | 6 | variable "rg_name" { 7 | description = "The name of the resource group to deploy resources into" 8 | type = string 9 | } 10 | 11 | variable "resource_token" { 12 | description = "A suffix string to centrally mitigate resource name collisions." 13 | type = string 14 | } 15 | 16 | variable "tags" { 17 | description = "A list of tags used for deployed services." 18 | type = map(string) 19 | } -------------------------------------------------------------------------------- /infra-terraform/modules_local/apim-api/apim-api.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | azurerm = { 4 | version = "~>3.47.0" 5 | source = "hashicorp/azurerm" 6 | } 7 | azurecaf = { 8 | source = "aztfmod/azurecaf" 9 | version = "~>1.2.15" 10 | } 11 | # azapi = { 12 | # source = "Azure/azapi" 13 | # version = "~>1.6.0" 14 | # } 15 | } 16 | } 17 | 18 | locals { 19 | appNameforAppProperties = var.api_app_name != "" ? var.api_app_name : "placeholdername" 20 | } 21 | 22 | # ------------------------------------------------------------------------------------------------------ 23 | # Read existing resources from different resource groups 24 | # ------------------------------------------------------------------------------------------------------ 25 | data "azurerm_application_insights" "appinsights" { 26 | name = var.application_insights_name 27 | resource_group_name = var.rg_name_app 28 | } 29 | 30 | data "azurerm_api_management" "apim" { 31 | name = var.name 32 | resource_group_name = var.rg_name_apim 33 | } 34 | 35 | # ------------------------------------------------------------------------------------------------------ 36 | # Deploy apim-api service 37 | # ------------------------------------------------------------------------------------------------------ 38 | resource "azurerm_api_management_api" "api" { 39 | name = var.api_name 40 | resource_group_name = var.rg_name_apim 41 | api_management_name = data.azurerm_api_management.apim.name 42 | revision = "1" 43 | display_name = var.api_display_name 44 | path = var.api_path 45 | protocols = ["https"] 46 | service_url = var.api_backend_url 47 | subscription_required = false 48 | api_type = "http" 49 | description = var.api_description 50 | 51 | import { 52 | content_format = "openapi" 53 | content_value = file("${path.module}/../../../src/api/API_BUSINESS_PARTNER.openapi.json") 54 | } 55 | } 56 | 57 | resource "azurerm_api_management_api_policy" "policies" { 58 | api_name = azurerm_api_management_api.api.name 59 | api_management_name = azurerm_api_management_api.api.api_management_name 60 | resource_group_name = var.rg_name_apim 61 | 62 | xml_content = file("${path.module}/apim-api-policy.xml") 63 | } 64 | 65 | resource "azurerm_api_management_logger" "apimLogger" { 66 | name = "apimlogger" 67 | api_management_name = data.azurerm_api_management.apim.name 68 | resource_group_name = var.rg_name_apim 69 | resource_id = data.azurerm_application_insights.appinsights.id 70 | 71 | application_insights { 72 | instrumentation_key = data.azurerm_application_insights.appinsights.instrumentation_key 73 | } 74 | } 75 | 76 | #resource "azapi_resource" "api_app_properties" { 77 | # type = "Microsoft.Web/sites/config@2022-03-01" 78 | # name = "${local.appNameforAppProperties}/web" 79 | # parent_id = data.azurerm_api_management.apim.id 80 | # body = jsonencode({ 81 | # properties = { 82 | # apiManagementConfig = { 83 | # id = "${data.azurerm_api_management.apim.id}/apis/${var.api_name}" 84 | # } 85 | # } 86 | # }) 87 | #} 88 | -------------------------------------------------------------------------------- /infra-terraform/modules_local/apim-api/apim-api_output.tf: -------------------------------------------------------------------------------- 1 | output "SERVICE_API_URI" { 2 | value = "${data.azurerm_api_management.apim.gateway_url}/${var.api_path}" 3 | } 4 | -------------------------------------------------------------------------------- /infra-terraform/modules_local/apim-api/apim-api_variables.tf: -------------------------------------------------------------------------------- 1 | variable "name" { 2 | description = "The name of the API Management Service" 3 | type = string 4 | } 5 | 6 | variable "rg_name_apim" { 7 | description = "The name of the resource group of the API management" 8 | type = string 9 | } 10 | 11 | variable "rg_name_app" { 12 | description = "The name of the resource group of the application" 13 | type = string 14 | } 15 | 16 | variable "api_name" { 17 | description = "Resource name to uniquely identify this API within the API Management service instance" 18 | type = string 19 | } 20 | 21 | variable "api_display_name" { 22 | description = "The Display Name of the API" 23 | type = string 24 | } 25 | 26 | variable "api_description" { 27 | description = "Description of the API. May include HTML formatting tags." 28 | type = string 29 | } 30 | 31 | variable "api_path" { 32 | description = "Relative URL uniquely identifying this API and all of its resource paths within the API Management service instance. It is appended to the API endpoint base URL specified during the service instance creation to form a public URL for this API." 33 | type = string 34 | } 35 | 36 | variable "api_backend_url" { 37 | description = "Absolute URL of the backend service implementing this API." 38 | type = string 39 | } 40 | 41 | variable "application_insights_name" { 42 | description = "Azure Application Insights Name." 43 | type = string 44 | } 45 | 46 | variable "api_app_name" { 47 | description = "Resource name for backend Web App or Function App" 48 | type = string 49 | default = "" 50 | } 51 | -------------------------------------------------------------------------------- /infra-terraform/modules_local/appservicenode/appservicenode.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | azurerm = { 4 | version = "~>3.47.0" 5 | source = "hashicorp/azurerm" 6 | } 7 | azurecaf = { 8 | source = "aztfmod/azurecaf" 9 | version = "~>1.2.15" 10 | } 11 | } 12 | } 13 | # ------------------------------------------------------------------------------------------------------ 14 | # Deploy app service web app 15 | # ------------------------------------------------------------------------------------------------------ 16 | resource "azurecaf_name" "web_name" { 17 | name = "${var.service_name}-${var.resource_token}" 18 | resource_type = "azurerm_app_service" 19 | random_length = 0 20 | clean_input = true 21 | } 22 | 23 | resource "azurerm_linux_web_app" "web" { 24 | name = azurecaf_name.web_name.result 25 | location = var.location 26 | resource_group_name = var.rg_name 27 | service_plan_id = var.appservice_plan_id 28 | https_only = true 29 | tags = var.tags 30 | 31 | site_config { 32 | ftps_state = "FtpsOnly" 33 | app_command_line = var.app_command_line 34 | application_stack { 35 | node_version = var.node_version 36 | } 37 | always_on = var.always_on 38 | health_check_path = var.health_check_path 39 | use_32_bit_worker = var.use_32_bit_worker 40 | } 41 | 42 | app_settings = var.app_settings 43 | 44 | dynamic "identity" { 45 | for_each = { for k, v in var.identity : k => v if var.identity != [] } 46 | content { 47 | type = identity.value["type"] 48 | } 49 | } 50 | 51 | logs { 52 | application_logs { 53 | file_system_level = "Verbose" 54 | } 55 | detailed_error_messages = true 56 | failed_request_tracing = true 57 | http_logs { 58 | file_system { 59 | retention_in_days = 1 60 | retention_in_mb = 35 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /infra-terraform/modules_local/appservicenode/appservicenode_output.tf: -------------------------------------------------------------------------------- 1 | output "URI" { 2 | value = "https://${azurerm_linux_web_app.web.default_hostname}" 3 | } 4 | 5 | output "IDENTITY_PRINCIPAL_ID" { 6 | value = length(azurerm_linux_web_app.web.identity) == 0 ? "" : azurerm_linux_web_app.web.identity.0.principal_id 7 | sensitive = true 8 | } -------------------------------------------------------------------------------- /infra-terraform/modules_local/appservicenode/appservicenode_variables.tf: -------------------------------------------------------------------------------- 1 | variable "location" { 2 | description = "The supported Azure location where the resource deployed" 3 | type = string 4 | } 5 | 6 | variable "rg_name" { 7 | description = "The name of the resource group to deploy resources into" 8 | type = string 9 | } 10 | 11 | variable "appservice_plan_id" { 12 | description = "The id of the appservice plan to use." 13 | type = string 14 | } 15 | 16 | variable "service_name" { 17 | description = "A name to reflect the type of the app service e.g: web, api." 18 | type = string 19 | } 20 | 21 | variable "app_settings" { 22 | description = "A list of app settings pairs to be assigned to the app service" 23 | type = map(string) 24 | } 25 | 26 | variable "identity" { 27 | description = "A list of application identity" 28 | type = list(any) 29 | default = [] 30 | } 31 | 32 | variable "app_command_line" { 33 | description = "The cmd line to configure the app to run." 34 | type = string 35 | } 36 | 37 | variable "tags" { 38 | description = "A list of tags used for deployed services." 39 | type = map(string) 40 | } 41 | 42 | variable "resource_token" { 43 | description = "A suffix string to centrally mitigate resource name collisions." 44 | type = string 45 | } 46 | 47 | variable "node_version" { 48 | description = "the application stack node version to set for the app service." 49 | type = string 50 | default = "18-lts" 51 | } 52 | 53 | variable "always_on" { 54 | description = "The always on setting for the app service." 55 | type = bool 56 | default = true 57 | } 58 | 59 | variable "health_check_path" { 60 | description = "The path to the health check endpoint" 61 | type = string 62 | default = "" 63 | } 64 | 65 | variable "use_32_bit_worker" { 66 | description = "The use 32 bit worker setting for the app service." 67 | type = bool 68 | default = false 69 | } -------------------------------------------------------------------------------- /infra-terraform/output.tf: -------------------------------------------------------------------------------- 1 | output "APPLICATIONINSIGHTS_CONNECTION_STRING" { 2 | value = module.applicationinsights.APPLICATIONINSIGHTS_CONNECTION_STRING 3 | sensitive = true 4 | } 5 | 6 | output "AZURE_KEY_VAULT_ENDPOINT" { 7 | value = module.keyvault.AZURE_KEY_VAULT_ENDPOINT 8 | sensitive = true 9 | } 10 | 11 | output "AZURE_LOCATION" { 12 | value = var.location 13 | } 14 | 15 | output "USE_APIM" { 16 | value = var.useAPIM 17 | } 18 | 19 | output "SAP_CLOUD_SDK_API_URL" { 20 | value = module.api.URI 21 | } 22 | 23 | output "SAP_CLOUD_SDK_API_APPLICATIONINSIGHTS_CONNECTION_STRING" { 24 | value = module.applicationinsights.APPLICATIONINSIGHTS_CONNECTION_STRING 25 | sensitive = true 26 | } 27 | -------------------------------------------------------------------------------- /infra-terraform/provider.tf: -------------------------------------------------------------------------------- 1 | #Set the terraform required version, and Configure the Azure Provider.Use local storage 2 | 3 | # Configure the Azure Provider 4 | terraform { 5 | required_version = ">= 1.1.7, < 2.0.0" 6 | required_providers { 7 | azurerm = { 8 | version = "~>3.47.0" 9 | source = "hashicorp/azurerm" 10 | } 11 | azurecaf = { 12 | source = "aztfmod/azurecaf" 13 | version = "~>1.2.15" 14 | } 15 | } 16 | } 17 | 18 | provider "azurerm" { 19 | features { 20 | key_vault { 21 | purge_soft_delete_on_destroy = false 22 | } 23 | resource_group { 24 | prevent_deletion_if_contains_resources = false 25 | } 26 | } 27 | } 28 | 29 | # Make client_id, tenant_id, subscription_id and object_id variables 30 | data "azurerm_client_config" "current" {} -------------------------------------------------------------------------------- /infra-terraform/variables.tf: -------------------------------------------------------------------------------- 1 | variable "location" { 2 | description = "The supported Azure location where the resource deployed" 3 | type = string 4 | } 5 | 6 | variable "environment_name" { 7 | description = "The name of the azd environment to be deployed" 8 | type = string 9 | } 10 | 11 | variable "principal_id" { 12 | description = "The Id of the azd service principal to add to deployed keyvault access policies" 13 | type = string 14 | default = "" 15 | } 16 | 17 | // App specific parameters - provide the values via the main.parameters.json referencing e.g. environment parameters 18 | variable "sku_name" { 19 | description = "The name of the SKU used to create the key vault" 20 | type = string 21 | default = "F1" 22 | } 23 | 24 | variable "health_check_path" { 25 | description = "The path to the health check endpoint" 26 | type = string 27 | default = "/health" 28 | } 29 | 30 | 31 | variable "oDataUrl" { 32 | description = "SAP OData service URL" 33 | type = string 34 | default = "https://sandbox.api.sap.com/s4hanacloud" 35 | } 36 | 37 | variable "oDataUsername" { 38 | description = "SAP OData user name" 39 | type = string 40 | default = "" 41 | } 42 | 43 | variable "oDataUserpwd" { 44 | description = "SAP OData user password" 45 | type = string 46 | default = "" 47 | sensitive = true 48 | } 49 | 50 | variable "_APIKey" { 51 | description = "API Key" 52 | type = string 53 | default = "" 54 | sensitive = true 55 | } 56 | 57 | variable "ApiKeyHeaderName" { 58 | description = "API Key Header Name" 59 | type = string 60 | default = "APIKey" 61 | } 62 | 63 | variable "useAPIM" { 64 | description = "Flag to use Azure API Management to mediate the calls between the Web frontend and the SAP backend API" 65 | type = bool 66 | default = false 67 | } 68 | 69 | variable "apimResourceGroupName" { 70 | description = "Resource Group containing the existing API Management instance" 71 | type = string 72 | default = "DEMO-NEU-SAP-PM1" 73 | } 74 | 75 | variable "apimServiceName" { 76 | description = "Name of the existing API Management instance" 77 | type = string 78 | default = "demo-sap-apim" 79 | } 80 | 81 | variable "apimApiSAPBackendURL" { 82 | description = "Target URL of the SAP backend API fronted by the existing API Management" 83 | type = string 84 | default = "https://sandbox.api.sap.com/s4hanacloud/sap/opu/odata/sap/API_BUSINESS_PARTNER" 85 | } 86 | -------------------------------------------------------------------------------- /infra/app/api.bicep: -------------------------------------------------------------------------------- 1 | param name string 2 | param location string = resourceGroup().location 3 | param tags object = {} 4 | 5 | param allowedOrigins array = [] 6 | param appCommandLine string = '' 7 | param applicationInsightsName string = '' 8 | param appServicePlanId string 9 | param appSettings object = {} 10 | param useAuthSettingsv2 bool = false 11 | param keyVaultName string 12 | param serviceName string = 'sap-cloud-sdk-api' 13 | param healthCheckPath string = '/health' 14 | param use32BitWorkerProcess bool = false 15 | 16 | param alwaysOn bool = true 17 | 18 | module api '../core_local/host/appservice.bicep' = { 19 | name: '${name}-app-module' 20 | params: { 21 | name: name 22 | location: location 23 | tags: union(tags, { 'azd-service-name': serviceName }) 24 | allowedOrigins: allowedOrigins 25 | appCommandLine: appCommandLine 26 | applicationInsightsName: applicationInsightsName 27 | appServicePlanId: appServicePlanId 28 | appSettings: appSettings 29 | useAuthSettingsv2: useAuthSettingsv2 30 | keyVaultName: keyVaultName 31 | runtimeName: 'node' 32 | runtimeVersion: '20-lts' 33 | scmDoBuildDuringDeployment: true 34 | healthCheckPath: healthCheckPath 35 | use32BitWorkerProcess: use32BitWorkerProcess 36 | alwaysOn: alwaysOn 37 | } 38 | } 39 | 40 | output SERVICE_API_IDENTITY_PRINCIPAL_ID string = api.outputs.identityPrincipalId 41 | output SERVICE_API_NAME string = api.outputs.name 42 | output SERVICE_API_URI string = api.outputs.uri 43 | -------------------------------------------------------------------------------- /infra/app/apim-api-policy.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 28 | 29 | 30 | 31 | 32 | 33 | @(context.LastError.Source) 34 | 35 | 36 | @(context.LastError.Reason) 37 | 38 | 39 | @(context.LastError.Message) 40 | 41 | 42 | @(context.LastError.Scope) 43 | 44 | 45 | @(context.LastError.Section) 46 | 47 | 48 | @(context.LastError.Path) 49 | 50 | 51 | @(context.LastError.PolicyId) 52 | 53 | 54 | @(context.Response.StatusCode.ToString()) 55 | 56 | 57 | -------------------------------------------------------------------------------- /infra/core/ai/cognitiveservices.bicep: -------------------------------------------------------------------------------- 1 | metadata description = 'Creates an Azure Cognitive Services instance.' 2 | param name string 3 | param location string = resourceGroup().location 4 | param tags object = {} 5 | @description('The custom subdomain name used to access the API. Defaults to the value of the name parameter.') 6 | param customSubDomainName string = name 7 | param deployments array = [] 8 | param kind string = 'OpenAI' 9 | 10 | @allowed([ 'Enabled', 'Disabled' ]) 11 | param publicNetworkAccess string = 'Enabled' 12 | param sku object = { 13 | name: 'S0' 14 | } 15 | 16 | param allowedIpRules array = [] 17 | param networkAcls object = empty(allowedIpRules) ? { 18 | defaultAction: 'Allow' 19 | } : { 20 | ipRules: allowedIpRules 21 | defaultAction: 'Deny' 22 | } 23 | 24 | resource account 'Microsoft.CognitiveServices/accounts@2023-05-01' = { 25 | name: name 26 | location: location 27 | tags: tags 28 | kind: kind 29 | properties: { 30 | customSubDomainName: customSubDomainName 31 | publicNetworkAccess: publicNetworkAccess 32 | networkAcls: networkAcls 33 | } 34 | sku: sku 35 | } 36 | 37 | @batchSize(1) 38 | resource deployment 'Microsoft.CognitiveServices/accounts/deployments@2023-05-01' = [for deployment in deployments: { 39 | parent: account 40 | name: deployment.name 41 | properties: { 42 | model: deployment.model 43 | raiPolicyName: contains(deployment, 'raiPolicyName') ? deployment.raiPolicyName : null 44 | } 45 | sku: contains(deployment, 'sku') ? deployment.sku : { 46 | name: 'Standard' 47 | capacity: 20 48 | } 49 | }] 50 | 51 | output endpoint string = account.properties.endpoint 52 | output id string = account.id 53 | output name string = account.name 54 | -------------------------------------------------------------------------------- /infra/core/config/configstore.bicep: -------------------------------------------------------------------------------- 1 | metadata description = 'Creates an Azure App Configuration store.' 2 | 3 | @description('The name for the Azure App Configuration store') 4 | param name string 5 | 6 | @description('The Azure region/location for the Azure App Configuration store') 7 | param location string = resourceGroup().location 8 | 9 | @description('Custom tags to apply to the Azure App Configuration store') 10 | param tags object = {} 11 | 12 | @description('Specifies the names of the key-value resources. The name is a combination of key and label with $ as delimiter. The label is optional.') 13 | param keyValueNames array = [] 14 | 15 | @description('Specifies the values of the key-value resources.') 16 | param keyValueValues array = [] 17 | 18 | @description('The principal ID to grant access to the Azure App Configuration store') 19 | param principalId string 20 | 21 | resource configStore 'Microsoft.AppConfiguration/configurationStores@2023-03-01' = { 22 | name: name 23 | location: location 24 | sku: { 25 | name: 'standard' 26 | } 27 | tags: tags 28 | } 29 | 30 | resource configStoreKeyValue 'Microsoft.AppConfiguration/configurationStores/keyValues@2023-03-01' = [for (item, i) in keyValueNames: { 31 | parent: configStore 32 | name: item 33 | properties: { 34 | value: keyValueValues[i] 35 | tags: tags 36 | } 37 | }] 38 | 39 | module configStoreAccess '../security/configstore-access.bicep' = { 40 | name: 'app-configuration-access' 41 | params: { 42 | configStoreName: name 43 | principalId: principalId 44 | } 45 | dependsOn: [configStore] 46 | } 47 | 48 | output endpoint string = configStore.properties.endpoint 49 | -------------------------------------------------------------------------------- /infra/core/database/cosmos/cosmos-account.bicep: -------------------------------------------------------------------------------- 1 | metadata description = 'Creates an Azure Cosmos DB account.' 2 | param name string 3 | param location string = resourceGroup().location 4 | param tags object = {} 5 | 6 | param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' 7 | param keyVaultName string 8 | 9 | @allowed([ 'GlobalDocumentDB', 'MongoDB', 'Parse' ]) 10 | param kind string 11 | 12 | resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' = { 13 | name: name 14 | kind: kind 15 | location: location 16 | tags: tags 17 | properties: { 18 | consistencyPolicy: { defaultConsistencyLevel: 'Session' } 19 | locations: [ 20 | { 21 | locationName: location 22 | failoverPriority: 0 23 | isZoneRedundant: false 24 | } 25 | ] 26 | databaseAccountOfferType: 'Standard' 27 | enableAutomaticFailover: false 28 | enableMultipleWriteLocations: false 29 | apiProperties: (kind == 'MongoDB') ? { serverVersion: '4.2' } : {} 30 | capabilities: [ { name: 'EnableServerless' } ] 31 | } 32 | } 33 | 34 | resource cosmosConnectionString 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { 35 | parent: keyVault 36 | name: connectionStringKey 37 | properties: { 38 | value: cosmos.listConnectionStrings().connectionStrings[0].connectionString 39 | } 40 | } 41 | 42 | resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { 43 | name: keyVaultName 44 | } 45 | 46 | output connectionStringKey string = connectionStringKey 47 | output endpoint string = cosmos.properties.documentEndpoint 48 | output id string = cosmos.id 49 | output name string = cosmos.name 50 | -------------------------------------------------------------------------------- /infra/core/database/cosmos/mongo/cosmos-mongo-account.bicep: -------------------------------------------------------------------------------- 1 | metadata description = 'Creates an Azure Cosmos DB for MongoDB account.' 2 | param name string 3 | param location string = resourceGroup().location 4 | param tags object = {} 5 | 6 | param keyVaultName string 7 | param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' 8 | 9 | module cosmos '../../cosmos/cosmos-account.bicep' = { 10 | name: 'cosmos-account' 11 | params: { 12 | name: name 13 | location: location 14 | connectionStringKey: connectionStringKey 15 | keyVaultName: keyVaultName 16 | kind: 'MongoDB' 17 | tags: tags 18 | } 19 | } 20 | 21 | output connectionStringKey string = cosmos.outputs.connectionStringKey 22 | output endpoint string = cosmos.outputs.endpoint 23 | output id string = cosmos.outputs.id 24 | -------------------------------------------------------------------------------- /infra/core/database/cosmos/mongo/cosmos-mongo-db.bicep: -------------------------------------------------------------------------------- 1 | metadata description = 'Creates an Azure Cosmos DB for MongoDB account with a database.' 2 | param accountName string 3 | param databaseName string 4 | param location string = resourceGroup().location 5 | param tags object = {} 6 | 7 | param collections array = [] 8 | param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' 9 | param keyVaultName string 10 | 11 | module cosmos 'cosmos-mongo-account.bicep' = { 12 | name: 'cosmos-mongo-account' 13 | params: { 14 | name: accountName 15 | location: location 16 | keyVaultName: keyVaultName 17 | tags: tags 18 | connectionStringKey: connectionStringKey 19 | } 20 | } 21 | 22 | resource database 'Microsoft.DocumentDB/databaseAccounts/mongodbDatabases@2022-08-15' = { 23 | name: '${accountName}/${databaseName}' 24 | tags: tags 25 | properties: { 26 | resource: { id: databaseName } 27 | } 28 | 29 | resource list 'collections' = [for collection in collections: { 30 | name: collection.name 31 | properties: { 32 | resource: { 33 | id: collection.id 34 | shardKey: { _id: collection.shardKey } 35 | indexes: [ { key: { keys: [ collection.indexKey ] } } ] 36 | } 37 | } 38 | }] 39 | 40 | dependsOn: [ 41 | cosmos 42 | ] 43 | } 44 | 45 | output connectionStringKey string = connectionStringKey 46 | output databaseName string = databaseName 47 | output endpoint string = cosmos.outputs.endpoint 48 | -------------------------------------------------------------------------------- /infra/core/database/cosmos/sql/cosmos-sql-account.bicep: -------------------------------------------------------------------------------- 1 | metadata description = 'Creates an Azure Cosmos DB for NoSQL account.' 2 | param name string 3 | param location string = resourceGroup().location 4 | param tags object = {} 5 | 6 | param keyVaultName string 7 | 8 | module cosmos '../../cosmos/cosmos-account.bicep' = { 9 | name: 'cosmos-account' 10 | params: { 11 | name: name 12 | location: location 13 | tags: tags 14 | keyVaultName: keyVaultName 15 | kind: 'GlobalDocumentDB' 16 | } 17 | } 18 | 19 | output connectionStringKey string = cosmos.outputs.connectionStringKey 20 | output endpoint string = cosmos.outputs.endpoint 21 | output id string = cosmos.outputs.id 22 | output name string = cosmos.outputs.name 23 | -------------------------------------------------------------------------------- /infra/core/database/cosmos/sql/cosmos-sql-db.bicep: -------------------------------------------------------------------------------- 1 | metadata description = 'Creates an Azure Cosmos DB for NoSQL account with a database.' 2 | param accountName string 3 | param databaseName string 4 | param location string = resourceGroup().location 5 | param tags object = {} 6 | 7 | param containers array = [] 8 | param keyVaultName string 9 | param principalIds array = [] 10 | 11 | module cosmos 'cosmos-sql-account.bicep' = { 12 | name: 'cosmos-sql-account' 13 | params: { 14 | name: accountName 15 | location: location 16 | tags: tags 17 | keyVaultName: keyVaultName 18 | } 19 | } 20 | 21 | resource database 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases@2022-05-15' = { 22 | name: '${accountName}/${databaseName}' 23 | properties: { 24 | resource: { id: databaseName } 25 | } 26 | 27 | resource list 'containers' = [for container in containers: { 28 | name: container.name 29 | properties: { 30 | resource: { 31 | id: container.id 32 | partitionKey: { paths: [ container.partitionKey ] } 33 | } 34 | options: {} 35 | } 36 | }] 37 | 38 | dependsOn: [ 39 | cosmos 40 | ] 41 | } 42 | 43 | module roleDefinition 'cosmos-sql-role-def.bicep' = { 44 | name: 'cosmos-sql-role-definition' 45 | params: { 46 | accountName: accountName 47 | } 48 | dependsOn: [ 49 | cosmos 50 | database 51 | ] 52 | } 53 | 54 | // We need batchSize(1) here because sql role assignments have to be done sequentially 55 | @batchSize(1) 56 | module userRole 'cosmos-sql-role-assign.bicep' = [for principalId in principalIds: if (!empty(principalId)) { 57 | name: 'cosmos-sql-user-role-${uniqueString(principalId)}' 58 | params: { 59 | accountName: accountName 60 | roleDefinitionId: roleDefinition.outputs.id 61 | principalId: principalId 62 | } 63 | dependsOn: [ 64 | cosmos 65 | database 66 | ] 67 | }] 68 | 69 | output accountId string = cosmos.outputs.id 70 | output accountName string = cosmos.outputs.name 71 | output connectionStringKey string = cosmos.outputs.connectionStringKey 72 | output databaseName string = databaseName 73 | output endpoint string = cosmos.outputs.endpoint 74 | output roleDefinitionId string = roleDefinition.outputs.id 75 | -------------------------------------------------------------------------------- /infra/core/database/cosmos/sql/cosmos-sql-role-assign.bicep: -------------------------------------------------------------------------------- 1 | metadata description = 'Creates a SQL role assignment under an Azure Cosmos DB account.' 2 | param accountName string 3 | 4 | param roleDefinitionId string 5 | param principalId string = '' 6 | 7 | resource role 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2022-05-15' = { 8 | parent: cosmos 9 | name: guid(roleDefinitionId, principalId, cosmos.id) 10 | properties: { 11 | principalId: principalId 12 | roleDefinitionId: roleDefinitionId 13 | scope: cosmos.id 14 | } 15 | } 16 | 17 | resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' existing = { 18 | name: accountName 19 | } 20 | -------------------------------------------------------------------------------- /infra/core/database/cosmos/sql/cosmos-sql-role-def.bicep: -------------------------------------------------------------------------------- 1 | metadata description = 'Creates a SQL role definition under an Azure Cosmos DB account.' 2 | param accountName string 3 | 4 | resource roleDefinition 'Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions@2022-08-15' = { 5 | parent: cosmos 6 | name: guid(cosmos.id, accountName, 'sql-role') 7 | properties: { 8 | assignableScopes: [ 9 | cosmos.id 10 | ] 11 | permissions: [ 12 | { 13 | dataActions: [ 14 | 'Microsoft.DocumentDB/databaseAccounts/readMetadata' 15 | 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/*' 16 | 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/*' 17 | ] 18 | notDataActions: [] 19 | } 20 | ] 21 | roleName: 'Reader Writer' 22 | type: 'CustomRole' 23 | } 24 | } 25 | 26 | resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' existing = { 27 | name: accountName 28 | } 29 | 30 | output id string = roleDefinition.id 31 | -------------------------------------------------------------------------------- /infra/core/database/mysql/flexibleserver.bicep: -------------------------------------------------------------------------------- 1 | metadata description = 'Creates an Azure Database for MySQL - Flexible Server.' 2 | param name string 3 | param location string = resourceGroup().location 4 | param tags object = {} 5 | 6 | param sku object 7 | param storage object 8 | param administratorLogin string 9 | @secure() 10 | param administratorLoginPassword string 11 | param highAvailabilityMode string = 'Disabled' 12 | param databaseNames array = [] 13 | param allowAzureIPsFirewall bool = false 14 | param allowAllIPsFirewall bool = false 15 | param allowedSingleIPs array = [] 16 | 17 | // MySQL version 18 | param version string 19 | 20 | resource mysqlServer 'Microsoft.DBforMySQL/flexibleServers@2023-06-30' = { 21 | location: location 22 | tags: tags 23 | name: name 24 | sku: sku 25 | properties: { 26 | version: version 27 | administratorLogin: administratorLogin 28 | administratorLoginPassword: administratorLoginPassword 29 | storage: storage 30 | highAvailability: { 31 | mode: highAvailabilityMode 32 | } 33 | } 34 | 35 | resource database 'databases' = [for name in databaseNames: { 36 | name: name 37 | }] 38 | 39 | resource firewall_all 'firewallRules' = if (allowAllIPsFirewall) { 40 | name: 'allow-all-IPs' 41 | properties: { 42 | startIpAddress: '0.0.0.0' 43 | endIpAddress: '255.255.255.255' 44 | } 45 | } 46 | 47 | resource firewall_azure 'firewallRules' = if (allowAzureIPsFirewall) { 48 | name: 'allow-all-azure-internal-IPs' 49 | properties: { 50 | startIpAddress: '0.0.0.0' 51 | endIpAddress: '0.0.0.0' 52 | } 53 | } 54 | 55 | resource firewall_single 'firewallRules' = [for ip in allowedSingleIPs: { 56 | name: 'allow-single-${replace(ip, '.', '')}' 57 | properties: { 58 | startIpAddress: ip 59 | endIpAddress: ip 60 | } 61 | }] 62 | 63 | } 64 | 65 | output MYSQL_DOMAIN_NAME string = mysqlServer.properties.fullyQualifiedDomainName 66 | -------------------------------------------------------------------------------- /infra/core/database/postgresql/flexibleserver.bicep: -------------------------------------------------------------------------------- 1 | metadata description = 'Creates an Azure Database for PostgreSQL - Flexible Server.' 2 | param name string 3 | param location string = resourceGroup().location 4 | param tags object = {} 5 | 6 | param sku object 7 | param storage object 8 | param administratorLogin string 9 | @secure() 10 | param administratorLoginPassword string 11 | param databaseNames array = [] 12 | param allowAzureIPsFirewall bool = false 13 | param allowAllIPsFirewall bool = false 14 | param allowedSingleIPs array = [] 15 | 16 | // PostgreSQL version 17 | param version string 18 | 19 | // Latest official version 2022-12-01 does not have Bicep types available 20 | resource postgresServer 'Microsoft.DBforPostgreSQL/flexibleServers@2022-12-01' = { 21 | location: location 22 | tags: tags 23 | name: name 24 | sku: sku 25 | properties: { 26 | version: version 27 | administratorLogin: administratorLogin 28 | administratorLoginPassword: administratorLoginPassword 29 | storage: storage 30 | highAvailability: { 31 | mode: 'Disabled' 32 | } 33 | } 34 | 35 | resource database 'databases' = [for name in databaseNames: { 36 | name: name 37 | }] 38 | 39 | resource firewall_all 'firewallRules' = if (allowAllIPsFirewall) { 40 | name: 'allow-all-IPs' 41 | properties: { 42 | startIpAddress: '0.0.0.0' 43 | endIpAddress: '255.255.255.255' 44 | } 45 | } 46 | 47 | resource firewall_azure 'firewallRules' = if (allowAzureIPsFirewall) { 48 | name: 'allow-all-azure-internal-IPs' 49 | properties: { 50 | startIpAddress: '0.0.0.0' 51 | endIpAddress: '0.0.0.0' 52 | } 53 | } 54 | 55 | resource firewall_single 'firewallRules' = [for ip in allowedSingleIPs: { 56 | name: 'allow-single-${replace(ip, '.', '')}' 57 | properties: { 58 | startIpAddress: ip 59 | endIpAddress: ip 60 | } 61 | }] 62 | 63 | } 64 | 65 | output POSTGRES_DOMAIN_NAME string = postgresServer.properties.fullyQualifiedDomainName 66 | -------------------------------------------------------------------------------- /infra/core/host/aks-agent-pool.bicep: -------------------------------------------------------------------------------- 1 | metadata description = 'Adds an agent pool to an Azure Kubernetes Service (AKS) cluster.' 2 | param clusterName string 3 | 4 | @description('The agent pool name') 5 | param name string 6 | 7 | @description('The agent pool configuration') 8 | param config object 9 | 10 | resource aksCluster 'Microsoft.ContainerService/managedClusters@2023-10-02-preview' existing = { 11 | name: clusterName 12 | } 13 | 14 | resource nodePool 'Microsoft.ContainerService/managedClusters/agentPools@2023-10-02-preview' = { 15 | parent: aksCluster 16 | name: name 17 | properties: config 18 | } 19 | -------------------------------------------------------------------------------- /infra/core/host/appservice-appsettings.bicep: -------------------------------------------------------------------------------- 1 | metadata description = 'Updates app settings for an Azure App Service.' 2 | @description('The name of the app service resource within the current resource group scope') 3 | param name string 4 | 5 | @description('The app settings to be applied to the app service') 6 | @secure() 7 | param appSettings object 8 | 9 | resource appService 'Microsoft.Web/sites@2022-03-01' existing = { 10 | name: name 11 | } 12 | 13 | resource settings 'Microsoft.Web/sites/config@2022-03-01' = { 14 | name: 'appsettings' 15 | parent: appService 16 | properties: appSettings 17 | } 18 | -------------------------------------------------------------------------------- /infra/core/host/appserviceplan.bicep: -------------------------------------------------------------------------------- 1 | metadata description = 'Creates an Azure App Service plan.' 2 | param name string 3 | param location string = resourceGroup().location 4 | param tags object = {} 5 | 6 | param kind string = '' 7 | param reserved bool = true 8 | param sku object 9 | 10 | resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = { 11 | name: name 12 | location: location 13 | tags: tags 14 | sku: sku 15 | kind: kind 16 | properties: { 17 | reserved: reserved 18 | } 19 | } 20 | 21 | output id string = appServicePlan.id 22 | output name string = appServicePlan.name 23 | -------------------------------------------------------------------------------- /infra/core/host/container-apps-environment.bicep: -------------------------------------------------------------------------------- 1 | metadata description = 'Creates an Azure Container Apps environment.' 2 | param name string 3 | param location string = resourceGroup().location 4 | param tags object = {} 5 | 6 | @description('Name of the Application Insights resource') 7 | param applicationInsightsName string = '' 8 | 9 | @description('Specifies if Dapr is enabled') 10 | param daprEnabled bool = false 11 | 12 | @description('Name of the Log Analytics workspace') 13 | param logAnalyticsWorkspaceName string 14 | 15 | resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-05-01' = { 16 | name: name 17 | location: location 18 | tags: tags 19 | properties: { 20 | appLogsConfiguration: { 21 | destination: 'log-analytics' 22 | logAnalyticsConfiguration: { 23 | customerId: logAnalyticsWorkspace.properties.customerId 24 | sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey 25 | } 26 | } 27 | daprAIInstrumentationKey: daprEnabled && !empty(applicationInsightsName) ? applicationInsights.properties.InstrumentationKey : '' 28 | } 29 | } 30 | 31 | resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = { 32 | name: logAnalyticsWorkspaceName 33 | } 34 | 35 | resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (daprEnabled && !empty(applicationInsightsName)) { 36 | name: applicationInsightsName 37 | } 38 | 39 | output defaultDomain string = containerAppsEnvironment.properties.defaultDomain 40 | output id string = containerAppsEnvironment.id 41 | output name string = containerAppsEnvironment.name 42 | -------------------------------------------------------------------------------- /infra/core/host/container-apps.bicep: -------------------------------------------------------------------------------- 1 | metadata description = 'Creates an Azure Container Registry and an Azure Container Apps environment.' 2 | param name string 3 | param location string = resourceGroup().location 4 | param tags object = {} 5 | 6 | param containerAppsEnvironmentName string 7 | param containerRegistryName string 8 | param containerRegistryResourceGroupName string = '' 9 | param containerRegistryAdminUserEnabled bool = false 10 | param logAnalyticsWorkspaceName string 11 | param applicationInsightsName string = '' 12 | 13 | module containerAppsEnvironment 'container-apps-environment.bicep' = { 14 | name: '${name}-container-apps-environment' 15 | params: { 16 | name: containerAppsEnvironmentName 17 | location: location 18 | tags: tags 19 | logAnalyticsWorkspaceName: logAnalyticsWorkspaceName 20 | applicationInsightsName: applicationInsightsName 21 | } 22 | } 23 | 24 | module containerRegistry 'container-registry.bicep' = { 25 | name: '${name}-container-registry' 26 | scope: !empty(containerRegistryResourceGroupName) ? resourceGroup(containerRegistryResourceGroupName) : resourceGroup() 27 | params: { 28 | name: containerRegistryName 29 | location: location 30 | adminUserEnabled: containerRegistryAdminUserEnabled 31 | tags: tags 32 | } 33 | } 34 | 35 | output defaultDomain string = containerAppsEnvironment.outputs.defaultDomain 36 | output environmentName string = containerAppsEnvironment.outputs.name 37 | output environmentId string = containerAppsEnvironment.outputs.id 38 | 39 | output registryLoginServer string = containerRegistry.outputs.loginServer 40 | output registryName string = containerRegistry.outputs.name 41 | -------------------------------------------------------------------------------- /infra/core/host/functions.bicep: -------------------------------------------------------------------------------- 1 | metadata description = 'Creates an Azure Function in an existing Azure App Service plan.' 2 | param name string 3 | param location string = resourceGroup().location 4 | param tags object = {} 5 | 6 | // Reference Properties 7 | param applicationInsightsName string = '' 8 | param appServicePlanId string 9 | param keyVaultName string = '' 10 | param managedIdentity bool = !empty(keyVaultName) 11 | param storageAccountName string 12 | 13 | // Runtime Properties 14 | @allowed([ 15 | 'dotnet', 'dotnetcore', 'dotnet-isolated', 'node', 'python', 'java', 'powershell', 'custom' 16 | ]) 17 | param runtimeName string 18 | param runtimeNameAndVersion string = '${runtimeName}|${runtimeVersion}' 19 | param runtimeVersion string 20 | 21 | // Function Settings 22 | @allowed([ 23 | '~4', '~3', '~2', '~1' 24 | ]) 25 | param extensionVersion string = '~4' 26 | 27 | // Microsoft.Web/sites Properties 28 | param kind string = 'functionapp,linux' 29 | 30 | // Microsoft.Web/sites/config 31 | param allowedOrigins array = [] 32 | param alwaysOn bool = true 33 | param appCommandLine string = '' 34 | @secure() 35 | param appSettings object = {} 36 | param clientAffinityEnabled bool = false 37 | param enableOryxBuild bool = contains(kind, 'linux') 38 | param functionAppScaleLimit int = -1 39 | param linuxFxVersion string = runtimeNameAndVersion 40 | param minimumElasticInstanceCount int = -1 41 | param numberOfWorkers int = -1 42 | param scmDoBuildDuringDeployment bool = true 43 | param use32BitWorkerProcess bool = false 44 | param healthCheckPath string = '' 45 | 46 | module functions 'appservice.bicep' = { 47 | name: '${name}-functions' 48 | params: { 49 | name: name 50 | location: location 51 | tags: tags 52 | allowedOrigins: allowedOrigins 53 | alwaysOn: alwaysOn 54 | appCommandLine: appCommandLine 55 | applicationInsightsName: applicationInsightsName 56 | appServicePlanId: appServicePlanId 57 | appSettings: union(appSettings, { 58 | AzureWebJobsStorage: 'DefaultEndpointsProtocol=https;AccountName=${storage.name};AccountKey=${storage.listKeys().keys[0].value};EndpointSuffix=${environment().suffixes.storage}' 59 | FUNCTIONS_EXTENSION_VERSION: extensionVersion 60 | FUNCTIONS_WORKER_RUNTIME: runtimeName 61 | }) 62 | clientAffinityEnabled: clientAffinityEnabled 63 | enableOryxBuild: enableOryxBuild 64 | functionAppScaleLimit: functionAppScaleLimit 65 | healthCheckPath: healthCheckPath 66 | keyVaultName: keyVaultName 67 | kind: kind 68 | linuxFxVersion: linuxFxVersion 69 | managedIdentity: managedIdentity 70 | minimumElasticInstanceCount: minimumElasticInstanceCount 71 | numberOfWorkers: numberOfWorkers 72 | runtimeName: runtimeName 73 | runtimeVersion: runtimeVersion 74 | runtimeNameAndVersion: runtimeNameAndVersion 75 | scmDoBuildDuringDeployment: scmDoBuildDuringDeployment 76 | use32BitWorkerProcess: use32BitWorkerProcess 77 | } 78 | } 79 | 80 | resource storage 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { 81 | name: storageAccountName 82 | } 83 | 84 | output identityPrincipalId string = managedIdentity ? functions.outputs.identityPrincipalId : '' 85 | output name string = functions.outputs.name 86 | output uri string = functions.outputs.uri 87 | -------------------------------------------------------------------------------- /infra/core/host/staticwebapp.bicep: -------------------------------------------------------------------------------- 1 | metadata description = 'Creates an Azure Static Web Apps instance.' 2 | param name string 3 | param location string = resourceGroup().location 4 | param tags object = {} 5 | 6 | param sku object = { 7 | name: 'Free' 8 | tier: 'Free' 9 | } 10 | 11 | resource web 'Microsoft.Web/staticSites@2022-03-01' = { 12 | name: name 13 | location: location 14 | tags: tags 15 | sku: sku 16 | properties: { 17 | provider: 'Custom' 18 | } 19 | } 20 | 21 | output name string = web.name 22 | output uri string = 'https://${web.properties.defaultHostname}' 23 | -------------------------------------------------------------------------------- /infra/core/monitor/applicationinsights.bicep: -------------------------------------------------------------------------------- 1 | metadata description = 'Creates an Application Insights instance based on an existing Log Analytics workspace.' 2 | param name string 3 | param dashboardName string = '' 4 | param location string = resourceGroup().location 5 | param tags object = {} 6 | param logAnalyticsWorkspaceId string 7 | 8 | resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = { 9 | name: name 10 | location: location 11 | tags: tags 12 | kind: 'web' 13 | properties: { 14 | Application_Type: 'web' 15 | WorkspaceResourceId: logAnalyticsWorkspaceId 16 | } 17 | } 18 | 19 | module applicationInsightsDashboard 'applicationinsights-dashboard.bicep' = if (!empty(dashboardName)) { 20 | name: 'application-insights-dashboard' 21 | params: { 22 | name: dashboardName 23 | location: location 24 | applicationInsightsName: applicationInsights.name 25 | } 26 | } 27 | 28 | output connectionString string = applicationInsights.properties.ConnectionString 29 | output id string = applicationInsights.id 30 | output instrumentationKey string = applicationInsights.properties.InstrumentationKey 31 | output name string = applicationInsights.name 32 | -------------------------------------------------------------------------------- /infra/core/monitor/loganalytics.bicep: -------------------------------------------------------------------------------- 1 | metadata description = 'Creates a Log Analytics workspace.' 2 | param name string 3 | param location string = resourceGroup().location 4 | param tags object = {} 5 | 6 | resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' = { 7 | name: name 8 | location: location 9 | tags: tags 10 | properties: any({ 11 | retentionInDays: 30 12 | features: { 13 | searchVersion: 1 14 | } 15 | sku: { 16 | name: 'PerGB2018' 17 | } 18 | }) 19 | } 20 | 21 | output id string = logAnalytics.id 22 | output name string = logAnalytics.name 23 | -------------------------------------------------------------------------------- /infra/core/monitor/monitoring.bicep: -------------------------------------------------------------------------------- 1 | metadata description = 'Creates an Application Insights instance and a Log Analytics workspace.' 2 | param logAnalyticsName string 3 | param applicationInsightsName string 4 | param applicationInsightsDashboardName string = '' 5 | param location string = resourceGroup().location 6 | param tags object = {} 7 | 8 | module logAnalytics 'loganalytics.bicep' = { 9 | name: 'loganalytics' 10 | params: { 11 | name: logAnalyticsName 12 | location: location 13 | tags: tags 14 | } 15 | } 16 | 17 | module applicationInsights 'applicationinsights.bicep' = { 18 | name: 'applicationinsights' 19 | params: { 20 | name: applicationInsightsName 21 | location: location 22 | tags: tags 23 | dashboardName: applicationInsightsDashboardName 24 | logAnalyticsWorkspaceId: logAnalytics.outputs.id 25 | } 26 | } 27 | 28 | output applicationInsightsConnectionString string = applicationInsights.outputs.connectionString 29 | output applicationInsightsId string = applicationInsights.outputs.id 30 | output applicationInsightsInstrumentationKey string = applicationInsights.outputs.instrumentationKey 31 | output applicationInsightsName string = applicationInsights.outputs.name 32 | output logAnalyticsWorkspaceId string = logAnalytics.outputs.id 33 | output logAnalyticsWorkspaceName string = logAnalytics.outputs.name 34 | -------------------------------------------------------------------------------- /infra/core/networking/cdn-endpoint.bicep: -------------------------------------------------------------------------------- 1 | metadata description = 'Adds an endpoint to an Azure CDN profile.' 2 | param name string 3 | param location string = resourceGroup().location 4 | param tags object = {} 5 | 6 | @description('The name of the CDN profile resource') 7 | @minLength(1) 8 | param cdnProfileName string 9 | 10 | @description('Delivery policy rules') 11 | param deliveryPolicyRules array = [] 12 | 13 | @description('The origin URL for the endpoint') 14 | @minLength(1) 15 | param originUrl string 16 | 17 | resource endpoint 'Microsoft.Cdn/profiles/endpoints@2022-05-01-preview' = { 18 | parent: cdnProfile 19 | name: name 20 | location: location 21 | tags: tags 22 | properties: { 23 | originHostHeader: originUrl 24 | isHttpAllowed: false 25 | isHttpsAllowed: true 26 | queryStringCachingBehavior: 'UseQueryString' 27 | optimizationType: 'GeneralWebDelivery' 28 | origins: [ 29 | { 30 | name: replace(originUrl, '.', '-') 31 | properties: { 32 | hostName: originUrl 33 | originHostHeader: originUrl 34 | priority: 1 35 | weight: 1000 36 | enabled: true 37 | } 38 | } 39 | ] 40 | deliveryPolicy: { 41 | rules: deliveryPolicyRules 42 | } 43 | } 44 | } 45 | 46 | resource cdnProfile 'Microsoft.Cdn/profiles@2022-05-01-preview' existing = { 47 | name: cdnProfileName 48 | } 49 | 50 | output id string = endpoint.id 51 | output name string = endpoint.name 52 | output uri string = 'https://${endpoint.properties.hostName}' 53 | -------------------------------------------------------------------------------- /infra/core/networking/cdn-profile.bicep: -------------------------------------------------------------------------------- 1 | metadata description = 'Creates an Azure CDN profile.' 2 | param name string 3 | param location string = resourceGroup().location 4 | param tags object = {} 5 | 6 | @description('The pricing tier of this CDN profile') 7 | @allowed([ 8 | 'Custom_Verizon' 9 | 'Premium_AzureFrontDoor' 10 | 'Premium_Verizon' 11 | 'StandardPlus_955BandWidth_ChinaCdn' 12 | 'StandardPlus_AvgBandWidth_ChinaCdn' 13 | 'StandardPlus_ChinaCdn' 14 | 'Standard_955BandWidth_ChinaCdn' 15 | 'Standard_Akamai' 16 | 'Standard_AvgBandWidth_ChinaCdn' 17 | 'Standard_AzureFrontDoor' 18 | 'Standard_ChinaCdn' 19 | 'Standard_Microsoft' 20 | 'Standard_Verizon' 21 | ]) 22 | param sku string = 'Standard_Microsoft' 23 | 24 | resource profile 'Microsoft.Cdn/profiles@2022-05-01-preview' = { 25 | name: name 26 | location: location 27 | tags: tags 28 | sku: { 29 | name: sku 30 | } 31 | } 32 | 33 | output id string = profile.id 34 | output name string = profile.name 35 | -------------------------------------------------------------------------------- /infra/core/networking/cdn.bicep: -------------------------------------------------------------------------------- 1 | metadata description = 'Creates an Azure CDN profile with a single endpoint.' 2 | param location string = resourceGroup().location 3 | param tags object = {} 4 | 5 | @description('Name of the CDN endpoint resource') 6 | param cdnEndpointName string 7 | 8 | @description('Name of the CDN profile resource') 9 | param cdnProfileName string 10 | 11 | @description('Delivery policy rules') 12 | param deliveryPolicyRules array = [] 13 | 14 | @description('Origin URL for the CDN endpoint') 15 | param originUrl string 16 | 17 | module cdnProfile 'cdn-profile.bicep' = { 18 | name: 'cdn-profile' 19 | params: { 20 | name: cdnProfileName 21 | location: location 22 | tags: tags 23 | } 24 | } 25 | 26 | module cdnEndpoint 'cdn-endpoint.bicep' = { 27 | name: 'cdn-endpoint' 28 | params: { 29 | name: cdnEndpointName 30 | location: location 31 | tags: tags 32 | cdnProfileName: cdnProfile.outputs.name 33 | originUrl: originUrl 34 | deliveryPolicyRules: deliveryPolicyRules 35 | } 36 | } 37 | 38 | output endpointName string = cdnEndpoint.outputs.name 39 | output endpointId string = cdnEndpoint.outputs.id 40 | output profileName string = cdnProfile.outputs.name 41 | output profileId string = cdnProfile.outputs.id 42 | output uri string = cdnEndpoint.outputs.uri 43 | -------------------------------------------------------------------------------- /infra/core/search/search-services.bicep: -------------------------------------------------------------------------------- 1 | metadata description = 'Creates an Azure AI Search instance.' 2 | param name string 3 | param location string = resourceGroup().location 4 | param tags object = {} 5 | 6 | param sku object = { 7 | name: 'standard' 8 | } 9 | 10 | param authOptions object = {} 11 | param disableLocalAuth bool = false 12 | param disabledDataExfiltrationOptions array = [] 13 | param encryptionWithCmk object = { 14 | enforcement: 'Unspecified' 15 | } 16 | @allowed([ 17 | 'default' 18 | 'highDensity' 19 | ]) 20 | param hostingMode string = 'default' 21 | param networkRuleSet object = { 22 | bypass: 'None' 23 | ipRules: [] 24 | } 25 | param partitionCount int = 1 26 | @allowed([ 27 | 'enabled' 28 | 'disabled' 29 | ]) 30 | param publicNetworkAccess string = 'enabled' 31 | param replicaCount int = 1 32 | @allowed([ 33 | 'disabled' 34 | 'free' 35 | 'standard' 36 | ]) 37 | param semanticSearch string = 'disabled' 38 | 39 | var searchIdentityProvider = (sku.name == 'free') ? null : { 40 | type: 'SystemAssigned' 41 | } 42 | 43 | resource search 'Microsoft.Search/searchServices@2021-04-01-preview' = { 44 | name: name 45 | location: location 46 | tags: tags 47 | // The free tier does not support managed identity 48 | identity: searchIdentityProvider 49 | properties: { 50 | authOptions: authOptions 51 | disableLocalAuth: disableLocalAuth 52 | disabledDataExfiltrationOptions: disabledDataExfiltrationOptions 53 | encryptionWithCmk: encryptionWithCmk 54 | hostingMode: hostingMode 55 | networkRuleSet: networkRuleSet 56 | partitionCount: partitionCount 57 | publicNetworkAccess: publicNetworkAccess 58 | replicaCount: replicaCount 59 | semanticSearch: semanticSearch 60 | } 61 | sku: sku 62 | } 63 | 64 | output id string = search.id 65 | output endpoint string = 'https://${name}.search.windows.net/' 66 | output name string = search.name 67 | output principalId string = !empty(searchIdentityProvider) ? search.identity.principalId : '' 68 | 69 | -------------------------------------------------------------------------------- /infra/core/security/aks-managed-cluster-access.bicep: -------------------------------------------------------------------------------- 1 | metadata description = 'Assigns RBAC role to the specified AKS cluster and principal.' 2 | param clusterName string 3 | param principalId string 4 | 5 | var aksClusterAdminRole = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b1ff04bb-8a4e-4dc4-8eb5-8693973ce19b') 6 | 7 | resource aksRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = { 8 | scope: aksCluster // Use when specifying a scope that is different than the deployment scope 9 | name: guid(subscription().id, resourceGroup().id, principalId, aksClusterAdminRole) 10 | properties: { 11 | roleDefinitionId: aksClusterAdminRole 12 | principalType: 'User' 13 | principalId: principalId 14 | } 15 | } 16 | 17 | resource aksCluster 'Microsoft.ContainerService/managedClusters@2023-10-02-preview' existing = { 18 | name: clusterName 19 | } 20 | -------------------------------------------------------------------------------- /infra/core/security/configstore-access.bicep: -------------------------------------------------------------------------------- 1 | @description('Name of Azure App Configuration store') 2 | param configStoreName string 3 | 4 | @description('The principal ID of the service principal to assign the role to') 5 | param principalId string 6 | 7 | resource configStore 'Microsoft.AppConfiguration/configurationStores@2023-03-01' existing = { 8 | name: configStoreName 9 | } 10 | 11 | var configStoreDataReaderRole = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '516239f1-63e1-4d78-a4de-a74fb236a071') 12 | 13 | resource configStoreDataReaderRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { 14 | name: guid(subscription().id, resourceGroup().id, principalId, configStoreDataReaderRole) 15 | scope: configStore 16 | properties: { 17 | roleDefinitionId: configStoreDataReaderRole 18 | principalId: principalId 19 | principalType: 'ServicePrincipal' 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /infra/core/security/keyvault-access.bicep: -------------------------------------------------------------------------------- 1 | metadata description = 'Assigns an Azure Key Vault access policy.' 2 | param name string = 'add' 3 | 4 | param keyVaultName string 5 | param permissions object = { secrets: [ 'get', 'list' ] } 6 | param principalId string 7 | 8 | resource keyVaultAccessPolicies 'Microsoft.KeyVault/vaults/accessPolicies@2022-07-01' = { 9 | parent: keyVault 10 | name: name 11 | properties: { 12 | accessPolicies: [ { 13 | objectId: principalId 14 | tenantId: subscription().tenantId 15 | permissions: permissions 16 | } ] 17 | } 18 | } 19 | 20 | resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { 21 | name: keyVaultName 22 | } 23 | -------------------------------------------------------------------------------- /infra/core/security/keyvault-secret.bicep: -------------------------------------------------------------------------------- 1 | metadata description = 'Creates or updates a secret in an Azure Key Vault.' 2 | param name string 3 | param tags object = {} 4 | param keyVaultName string 5 | param contentType string = 'string' 6 | @description('The value of the secret. Provide only derived values like blob storage access, but do not hard code any secrets in your templates') 7 | @secure() 8 | param secretValue string 9 | 10 | param enabled bool = true 11 | param exp int = 0 12 | param nbf int = 0 13 | 14 | resource keyVaultSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { 15 | name: name 16 | tags: tags 17 | parent: keyVault 18 | properties: { 19 | attributes: { 20 | enabled: enabled 21 | exp: exp 22 | nbf: nbf 23 | } 24 | contentType: contentType 25 | value: secretValue 26 | } 27 | } 28 | 29 | resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { 30 | name: keyVaultName 31 | } 32 | -------------------------------------------------------------------------------- /infra/core/security/keyvault.bicep: -------------------------------------------------------------------------------- 1 | metadata description = 'Creates an Azure Key Vault.' 2 | param name string 3 | param location string = resourceGroup().location 4 | param tags object = {} 5 | 6 | param principalId string = '' 7 | 8 | resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { 9 | name: name 10 | location: location 11 | tags: tags 12 | properties: { 13 | tenantId: subscription().tenantId 14 | sku: { family: 'A', name: 'standard' } 15 | accessPolicies: !empty(principalId) ? [ 16 | { 17 | objectId: principalId 18 | permissions: { secrets: [ 'get', 'list' ] } 19 | tenantId: subscription().tenantId 20 | } 21 | ] : [] 22 | } 23 | } 24 | 25 | output endpoint string = keyVault.properties.vaultUri 26 | output id string = keyVault.id 27 | output name string = keyVault.name 28 | -------------------------------------------------------------------------------- /infra/core/security/registry-access.bicep: -------------------------------------------------------------------------------- 1 | metadata description = 'Assigns ACR Pull permissions to access an Azure Container Registry.' 2 | param containerRegistryName string 3 | param principalId string 4 | 5 | var acrPullRole = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d') 6 | 7 | resource aksAcrPull 'Microsoft.Authorization/roleAssignments@2022-04-01' = { 8 | scope: containerRegistry // Use when specifying a scope that is different than the deployment scope 9 | name: guid(subscription().id, resourceGroup().id, principalId, acrPullRole) 10 | properties: { 11 | roleDefinitionId: acrPullRole 12 | principalType: 'ServicePrincipal' 13 | principalId: principalId 14 | } 15 | } 16 | 17 | resource containerRegistry 'Microsoft.ContainerRegistry/registries@2023-01-01-preview' existing = { 18 | name: containerRegistryName 19 | } 20 | -------------------------------------------------------------------------------- /infra/core/security/role.bicep: -------------------------------------------------------------------------------- 1 | metadata description = 'Creates a role assignment for a service principal.' 2 | param principalId string 3 | 4 | @allowed([ 5 | 'Device' 6 | 'ForeignGroup' 7 | 'Group' 8 | 'ServicePrincipal' 9 | 'User' 10 | ]) 11 | param principalType string = 'ServicePrincipal' 12 | param roleDefinitionId string 13 | 14 | resource role 'Microsoft.Authorization/roleAssignments@2022-04-01' = { 15 | name: guid(subscription().id, resourceGroup().id, principalId, roleDefinitionId) 16 | properties: { 17 | principalId: principalId 18 | principalType: principalType 19 | roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', roleDefinitionId) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /infra/core/storage/storage-account.bicep: -------------------------------------------------------------------------------- 1 | metadata description = 'Creates an Azure storage account.' 2 | param name string 3 | param location string = resourceGroup().location 4 | param tags object = {} 5 | 6 | @allowed([ 7 | 'Cool' 8 | 'Hot' 9 | 'Premium' ]) 10 | param accessTier string = 'Hot' 11 | param allowBlobPublicAccess bool = true 12 | param allowCrossTenantReplication bool = true 13 | param allowSharedKeyAccess bool = true 14 | param containers array = [] 15 | param corsRules array = [] 16 | param defaultToOAuthAuthentication bool = false 17 | param deleteRetentionPolicy object = {} 18 | @allowed([ 'AzureDnsZone', 'Standard' ]) 19 | param dnsEndpointType string = 'Standard' 20 | param files array = [] 21 | param kind string = 'StorageV2' 22 | param minimumTlsVersion string = 'TLS1_2' 23 | param queues array = [] 24 | param shareDeleteRetentionPolicy object = {} 25 | param supportsHttpsTrafficOnly bool = true 26 | param tables array = [] 27 | param networkAcls object = { 28 | bypass: 'AzureServices' 29 | defaultAction: 'Allow' 30 | } 31 | @allowed([ 'Enabled', 'Disabled' ]) 32 | param publicNetworkAccess string = 'Enabled' 33 | param sku object = { name: 'Standard_LRS' } 34 | 35 | resource storage 'Microsoft.Storage/storageAccounts@2023-01-01' = { 36 | name: name 37 | location: location 38 | tags: tags 39 | kind: kind 40 | sku: sku 41 | properties: { 42 | accessTier: accessTier 43 | allowBlobPublicAccess: allowBlobPublicAccess 44 | allowCrossTenantReplication: allowCrossTenantReplication 45 | allowSharedKeyAccess: allowSharedKeyAccess 46 | defaultToOAuthAuthentication: defaultToOAuthAuthentication 47 | dnsEndpointType: dnsEndpointType 48 | minimumTlsVersion: minimumTlsVersion 49 | networkAcls: networkAcls 50 | publicNetworkAccess: publicNetworkAccess 51 | supportsHttpsTrafficOnly: supportsHttpsTrafficOnly 52 | } 53 | 54 | resource blobServices 'blobServices' = if (!empty(containers)) { 55 | name: 'default' 56 | properties: { 57 | cors: { 58 | corsRules: corsRules 59 | } 60 | deleteRetentionPolicy: deleteRetentionPolicy 61 | } 62 | resource container 'containers' = [for container in containers: { 63 | name: container.name 64 | properties: { 65 | publicAccess: contains(container, 'publicAccess') ? container.publicAccess : 'None' 66 | } 67 | }] 68 | } 69 | 70 | resource fileServices 'fileServices' = if (!empty(files)) { 71 | name: 'default' 72 | properties: { 73 | cors: { 74 | corsRules: corsRules 75 | } 76 | shareDeleteRetentionPolicy: shareDeleteRetentionPolicy 77 | } 78 | } 79 | 80 | resource queueServices 'queueServices' = if (!empty(queues)) { 81 | name: 'default' 82 | properties: { 83 | 84 | } 85 | resource queue 'queues' = [for queue in queues: { 86 | name: queue.name 87 | properties: { 88 | metadata: {} 89 | } 90 | }] 91 | } 92 | 93 | resource tableServices 'tableServices' = if (!empty(tables)) { 94 | name: 'default' 95 | properties: {} 96 | } 97 | } 98 | 99 | output id string = storage.id 100 | output name string = storage.name 101 | output primaryEndpoints object = storage.properties.primaryEndpoints 102 | -------------------------------------------------------------------------------- /infra/core/testing/loadtesting.bicep: -------------------------------------------------------------------------------- 1 | param name string 2 | param location string = resourceGroup().location 3 | param managedIdentity bool = false 4 | param tags object = {} 5 | 6 | resource loadTest 'Microsoft.LoadTestService/loadTests@2022-12-01' = { 7 | name: name 8 | location: location 9 | tags: tags 10 | identity: { type: managedIdentity ? 'SystemAssigned' : 'None' } 11 | properties: { 12 | } 13 | } 14 | 15 | output loadTestingName string = loadTest.name 16 | -------------------------------------------------------------------------------- /infra/main.parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "environmentName": { 6 | "value": "${AZURE_ENV_NAME}" 7 | }, 8 | "location": { 9 | "value": "${AZURE_LOCATION}" 10 | }, 11 | "principalId": { 12 | "value": "${AZURE_PRINCIPAL_ID}" 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "@sap-cloud-sdk/util": "^3.25.0" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /sample-http-requests/business-partner-requests.http: -------------------------------------------------------------------------------- 1 | GET http://localhost:8080/health HTTP/1.1 2 | content-type: application/json 3 | 4 | ### 5 | 6 | GET http://localhost:8080/business-partner HTTP/1.1 7 | content-type: application/json 8 | x-ms-token-aad-access-token: your-access-token-if-any 9 | 10 | ### 11 | 12 | GET http://localhost:8080/business-partner/1003764 HTTP/1.1 13 | content-type: application/json 14 | 15 | ### 16 | 17 | PUT http://localhost:8080/business-partner/1003764/address/28238 HTTP/1.1 18 | content-type: application/json 19 | 20 | { 21 | "houseNumber": "1" 22 | } 23 | 24 | ### 25 | 26 | DELETE http://localhost:8080/business-partner/1003764/address/28238 HTTP/1.1 27 | content-type: application/json 28 | 29 | ### 30 | 31 | POST http://localhost:8080/business-partner/1003764/address HTTP/1.1 32 | content-type: application/json 33 | 34 | { 35 | "businessPartner": "1003764", 36 | "cityName": "Bangalore", 37 | "country": "IN", 38 | "houseNumber": "", 39 | "postalCode": "560066", 40 | "streetName": "123", 41 | "toAddressUsage": 42 | [ 43 | { 44 | "addressUsage": "XXDEFAULT", 45 | "standardUsage": true 46 | } 47 | ] 48 | } 49 | -------------------------------------------------------------------------------- /src/api/.deployment: -------------------------------------------------------------------------------- 1 | [config] 2 | SCM_DO_BUILD_DURING_DEPLOYMENT=true -------------------------------------------------------------------------------- /src/api/manifest.yaml: -------------------------------------------------------------------------------- 1 | applications: 2 | - name: cloudsdkapp 3 | path: . 4 | buildpacks: 5 | - nodejs_buildpack 6 | memory: 1G 7 | command: npm run start:prod 8 | random-route: true 9 | build-parameters: 10 | builder: custom 11 | commands: 12 | - npm run build -------------------------------------------------------------------------------- /src/api/nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/nest-cli", 3 | "collection": "@nestjs/schematics", 4 | "sourceRoot": "src" 5 | } 6 | -------------------------------------------------------------------------------- /src/api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sap-cloud-sdk-on-azure", 3 | "version": "0.0.1", 4 | "description": "", 5 | "author": "", 6 | "private": true, 7 | "license": "UNLICENSED", 8 | "scripts": { 9 | "prebuild": "rimraf dist", 10 | "build": "nest build", 11 | "start": "nest start", 12 | "start:dev": "nest start --watch", 13 | "start:debug": "nest start --debug --watch", 14 | "start:prod": "node dist/src/main.js", 15 | "lint-check": "rome check .", 16 | "lint": "rome check --apply-suggested .", 17 | "format": "rome format --write .", 18 | "test": "jest", 19 | "test:watch": "jest --watch", 20 | "test:cov": "jest --coverage", 21 | "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", 22 | "test:e2e": "jest --config ./test/jest-e2e.json" 23 | }, 24 | "dependencies": { 25 | "@nestjs/common": "^9.3.9", 26 | "@nestjs/config": "^2.3.1", 27 | "@nestjs/core": "^10.3.7", 28 | "@nestjs/platform-express": "^10.3.7", 29 | "@sap-cloud-sdk/http-client": "^3.0.1", 30 | "@sap-cloud-sdk/odata-v2": "^3.0.1", 31 | "pug": "^3.0.3", 32 | "reflect-metadata": "^0.1.13", 33 | "rimraf": "^4.3.1", 34 | "rxjs": "^7.8.0" 35 | }, 36 | "devDependencies": { 37 | "@nestjs/cli": "^10.4.5", 38 | "@nestjs/schematics": "^9.0.4", 39 | "@nestjs/testing": "^10.3.7", 40 | "@sap-cloud-sdk/generator": "^3.0.1", 41 | "@types/express": "^4.17.17", 42 | "@types/jest": "29.4.0", 43 | "@types/node": "^18.14.6", 44 | "@types/supertest": "^2.0.12", 45 | "jest": "29.5.0", 46 | "prettier": "^2.8.4", 47 | "rome": "^11.0.0", 48 | "source-map-support": "^0.5.21", 49 | "supertest": "^6.3.3", 50 | "ts-jest": "29.0.5", 51 | "ts-loader": "^9.4.2", 52 | "ts-node": "^10.9.1", 53 | "tsconfig-paths": "4.1.2", 54 | "typescript": "^4.9.5" 55 | }, 56 | "jest": { 57 | "moduleFileExtensions": [ 58 | "js", 59 | "json", 60 | "ts" 61 | ], 62 | "rootDir": "./src", 63 | "testRegex": ".*\\.spec\\.ts$", 64 | "transform": { 65 | "^.+\\.(t|j)s$": "ts-jest" 66 | }, 67 | "collectCoverageFrom": [ 68 | "**/*.(t|j)s" 69 | ], 70 | "coverageDirectory": "../coverage", 71 | "testEnvironment": "node" 72 | }, 73 | "ts-standard": { 74 | "ignore": [ 75 | "dist", 76 | "service-specifications", 77 | "services/business-partner-service-1" 78 | ] 79 | } 80 | } -------------------------------------------------------------------------------- /src/api/rome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/rome/configuration_schema.json", 3 | "files": { 4 | "ignore": [ 5 | "./dist/*", 6 | "./node_modules/*", 7 | "./services/*" 8 | ] 9 | }, 10 | "linter": { 11 | "enabled": true, 12 | "rules": { 13 | "recommended": true 14 | } 15 | }, 16 | "formatter": { 17 | "enabled": true, 18 | "formatWithErrors": false, 19 | "indentStyle": "tab", 20 | "indentSize": 2, 21 | "lineWidth": 80, 22 | "ignore": [] 23 | } 24 | } -------------------------------------------------------------------------------- /src/api/service-specifications/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "service-specifications/ZAPI_BUSINESS_PARTNER.edmx": { 3 | "packageName": "business-partner-service-1", 4 | "directoryName": "business-partner-service-1", 5 | "basePath": "/sap/opu/odata/sap/API_BUSINESS_PARTNER" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/api/services/business-partner-service-1/AddressEmailAddress.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 SAP SE or an SAP affiliate company. All rights reserved. 3 | * 4 | * This is a generated file powered by the SAP Cloud SDK for JavaScript. 5 | */ 6 | import { 7 | Entity, 8 | DefaultDeSerializers, 9 | DeSerializers, 10 | DeserializedType 11 | } from '@sap-cloud-sdk/odata-v2'; 12 | import type { AddressEmailAddressApi } from './AddressEmailAddressApi'; 13 | 14 | /** 15 | * This class represents the entity "A_AddressEmailAddress" of service "API_BUSINESS_PARTNER". 16 | */ 17 | export class AddressEmailAddress 18 | extends Entity 19 | implements AddressEmailAddressType 20 | { 21 | /** 22 | * Technical entity name for AddressEmailAddress. 23 | */ 24 | static _entityName = 'A_AddressEmailAddress'; 25 | /** 26 | * Default url path for the according service. 27 | */ 28 | static _defaultBasePath = '/sap/opu/odata/sap/API_BUSINESS_PARTNER'; 29 | /** 30 | * All key fields of the AddressEmailAddress entity 31 | */ 32 | static _keys = ['AddressID', 'Person', 'OrdinalNumber']; 33 | /** 34 | * Address Number. 35 | * Maximum length: 10. 36 | */ 37 | addressId!: DeserializedType; 38 | /** 39 | * Person Number. 40 | * Maximum length: 10. 41 | */ 42 | person!: DeserializedType; 43 | /** 44 | * Sequence Number. 45 | * Maximum length: 3. 46 | */ 47 | ordinalNumber!: DeserializedType; 48 | /** 49 | * Flag: this address is the default address. 50 | * @nullable 51 | */ 52 | isDefaultEmailAddress?: DeserializedType | null; 53 | /** 54 | * E-Mail Address. 55 | * Maximum length: 241. 56 | * @nullable 57 | */ 58 | emailAddress?: DeserializedType | null; 59 | /** 60 | * E-Mail Address Search Field. 61 | * Maximum length: 20. 62 | * @nullable 63 | */ 64 | searchEmailAddress?: DeserializedType | null; 65 | /** 66 | * Communication link notes. 67 | * Maximum length: 50. 68 | * @nullable 69 | */ 70 | addressCommunicationRemarkText?: DeserializedType | null; 71 | 72 | constructor(readonly _entityApi: AddressEmailAddressApi) { 73 | super(_entityApi); 74 | } 75 | } 76 | 77 | export interface AddressEmailAddressType< 78 | T extends DeSerializers = DefaultDeSerializers 79 | > { 80 | addressId: DeserializedType; 81 | person: DeserializedType; 82 | ordinalNumber: DeserializedType; 83 | isDefaultEmailAddress?: DeserializedType | null; 84 | emailAddress?: DeserializedType | null; 85 | searchEmailAddress?: DeserializedType | null; 86 | addressCommunicationRemarkText?: DeserializedType | null; 87 | } 88 | -------------------------------------------------------------------------------- /src/api/services/business-partner-service-1/AddressFaxNumber.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 SAP SE or an SAP affiliate company. All rights reserved. 3 | * 4 | * This is a generated file powered by the SAP Cloud SDK for JavaScript. 5 | */ 6 | import { 7 | Entity, 8 | DefaultDeSerializers, 9 | DeSerializers, 10 | DeserializedType 11 | } from '@sap-cloud-sdk/odata-v2'; 12 | import type { AddressFaxNumberApi } from './AddressFaxNumberApi'; 13 | 14 | /** 15 | * This class represents the entity "A_AddressFaxNumber" of service "API_BUSINESS_PARTNER". 16 | */ 17 | export class AddressFaxNumber 18 | extends Entity 19 | implements AddressFaxNumberType 20 | { 21 | /** 22 | * Technical entity name for AddressFaxNumber. 23 | */ 24 | static _entityName = 'A_AddressFaxNumber'; 25 | /** 26 | * Default url path for the according service. 27 | */ 28 | static _defaultBasePath = '/sap/opu/odata/sap/API_BUSINESS_PARTNER'; 29 | /** 30 | * All key fields of the AddressFaxNumber entity 31 | */ 32 | static _keys = ['AddressID', 'Person', 'OrdinalNumber']; 33 | /** 34 | * Address Number. 35 | * Maximum length: 10. 36 | */ 37 | addressId!: DeserializedType; 38 | /** 39 | * Person Number. 40 | * Maximum length: 10. 41 | */ 42 | person!: DeserializedType; 43 | /** 44 | * Sequence Number. 45 | * Maximum length: 3. 46 | */ 47 | ordinalNumber!: DeserializedType; 48 | /** 49 | * Standard Sender Address in this Communication Type. 50 | * @nullable 51 | */ 52 | isDefaultFaxNumber?: DeserializedType | null; 53 | /** 54 | * Country/Region for Telephone/Fax Number. 55 | * Maximum length: 3. 56 | * @nullable 57 | */ 58 | faxCountry?: DeserializedType | null; 59 | /** 60 | * Fax number: dialling code+number. 61 | * Maximum length: 30. 62 | * @nullable 63 | */ 64 | faxNumber?: DeserializedType | null; 65 | /** 66 | * Fax no.: Extension. 67 | * Maximum length: 10. 68 | * @nullable 69 | */ 70 | faxNumberExtension?: DeserializedType | null; 71 | /** 72 | * Complete number: dialling code+number+extension. 73 | * Maximum length: 30. 74 | * @nullable 75 | */ 76 | internationalFaxNumber?: DeserializedType | null; 77 | /** 78 | * Communication link notes. 79 | * Maximum length: 50. 80 | * @nullable 81 | */ 82 | addressCommunicationRemarkText?: DeserializedType | null; 83 | 84 | constructor(readonly _entityApi: AddressFaxNumberApi) { 85 | super(_entityApi); 86 | } 87 | } 88 | 89 | export interface AddressFaxNumberType< 90 | T extends DeSerializers = DefaultDeSerializers 91 | > { 92 | addressId: DeserializedType; 93 | person: DeserializedType; 94 | ordinalNumber: DeserializedType; 95 | isDefaultFaxNumber?: DeserializedType | null; 96 | faxCountry?: DeserializedType | null; 97 | faxNumber?: DeserializedType | null; 98 | faxNumberExtension?: DeserializedType | null; 99 | internationalFaxNumber?: DeserializedType | null; 100 | addressCommunicationRemarkText?: DeserializedType | null; 101 | } 102 | -------------------------------------------------------------------------------- /src/api/services/business-partner-service-1/AddressHomePageUrl.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 SAP SE or an SAP affiliate company. All rights reserved. 3 | * 4 | * This is a generated file powered by the SAP Cloud SDK for JavaScript. 5 | */ 6 | import { 7 | Entity, 8 | DefaultDeSerializers, 9 | DeSerializers, 10 | DeserializedType 11 | } from '@sap-cloud-sdk/odata-v2'; 12 | import type { AddressHomePageUrlApi } from './AddressHomePageUrlApi'; 13 | 14 | /** 15 | * This class represents the entity "A_AddressHomePageURL" of service "API_BUSINESS_PARTNER". 16 | */ 17 | export class AddressHomePageUrl 18 | extends Entity 19 | implements AddressHomePageUrlType 20 | { 21 | /** 22 | * Technical entity name for AddressHomePageUrl. 23 | */ 24 | static _entityName = 'A_AddressHomePageURL'; 25 | /** 26 | * Default url path for the according service. 27 | */ 28 | static _defaultBasePath = '/sap/opu/odata/sap/API_BUSINESS_PARTNER'; 29 | /** 30 | * All key fields of the AddressHomePageUrl entity 31 | */ 32 | static _keys = [ 33 | 'AddressID', 34 | 'Person', 35 | 'OrdinalNumber', 36 | 'ValidityStartDate', 37 | 'IsDefaultURLAddress' 38 | ]; 39 | /** 40 | * Address Number. 41 | * Maximum length: 10. 42 | */ 43 | addressId!: DeserializedType; 44 | /** 45 | * Person Number. 46 | * Maximum length: 10. 47 | */ 48 | person!: DeserializedType; 49 | /** 50 | * Sequence Number. 51 | * Maximum length: 3. 52 | */ 53 | ordinalNumber!: DeserializedType; 54 | /** 55 | * Valid-from date - in current Release only 00010101 possible. 56 | */ 57 | validityStartDate!: DeserializedType; 58 | /** 59 | * Flag: this address is the default address. 60 | */ 61 | isDefaultUrlAddress!: DeserializedType; 62 | /** 63 | * URI address search field. 64 | * Maximum length: 50. 65 | * @nullable 66 | */ 67 | searchUrlAddress?: DeserializedType | null; 68 | /** 69 | * Communication link notes. 70 | * Maximum length: 50. 71 | * @nullable 72 | */ 73 | addressCommunicationRemarkText?: DeserializedType | null; 74 | /** 75 | * URI field length. 76 | * @nullable 77 | */ 78 | urlFieldLength?: DeserializedType | null; 79 | /** 80 | * Universal Resource Identifier (URI). 81 | * Maximum length: 2048. 82 | * @nullable 83 | */ 84 | websiteUrl?: DeserializedType | null; 85 | 86 | constructor(readonly _entityApi: AddressHomePageUrlApi) { 87 | super(_entityApi); 88 | } 89 | } 90 | 91 | export interface AddressHomePageUrlType< 92 | T extends DeSerializers = DefaultDeSerializers 93 | > { 94 | addressId: DeserializedType; 95 | person: DeserializedType; 96 | ordinalNumber: DeserializedType; 97 | validityStartDate: DeserializedType; 98 | isDefaultUrlAddress: DeserializedType; 99 | searchUrlAddress?: DeserializedType | null; 100 | addressCommunicationRemarkText?: DeserializedType | null; 101 | urlFieldLength?: DeserializedType | null; 102 | websiteUrl?: DeserializedType | null; 103 | } 104 | -------------------------------------------------------------------------------- /src/api/services/business-partner-service-1/BpContactToAddressRequestBuilder.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 SAP SE or an SAP affiliate company. All rights reserved. 3 | * 4 | * This is a generated file powered by the SAP Cloud SDK for JavaScript. 5 | */ 6 | import { Moment } from 'moment'; 7 | import { 8 | DefaultDeSerializers, 9 | DeSerializers, 10 | GetAllRequestBuilder, 11 | GetByKeyRequestBuilder, 12 | DeserializedType, 13 | RequestBuilder 14 | } from '@sap-cloud-sdk/odata-v2'; 15 | import { BpContactToAddress } from './BpContactToAddress'; 16 | 17 | /** 18 | * Request builder class for operations supported on the {@link BpContactToAddress} entity. 19 | */ 20 | export class BpContactToAddressRequestBuilder< 21 | T extends DeSerializers = DefaultDeSerializers 22 | > extends RequestBuilder, T> { 23 | /** 24 | * Returns a request builder for retrieving one `BpContactToAddress` entity based on its keys. 25 | * @param relationshipNumber Key property. See {@link BpContactToAddress.relationshipNumber}. 26 | * @param businessPartnerCompany Key property. See {@link BpContactToAddress.businessPartnerCompany}. 27 | * @param businessPartnerPerson Key property. See {@link BpContactToAddress.businessPartnerPerson}. 28 | * @param validityEndDate Key property. See {@link BpContactToAddress.validityEndDate}. 29 | * @param addressId Key property. See {@link BpContactToAddress.addressId}. 30 | * @returns A request builder for creating requests to retrieve one `BpContactToAddress` entity based on its keys. 31 | */ 32 | getByKey( 33 | relationshipNumber: DeserializedType, 34 | businessPartnerCompany: DeserializedType, 35 | businessPartnerPerson: DeserializedType, 36 | validityEndDate: DeserializedType, 37 | addressId: DeserializedType 38 | ): GetByKeyRequestBuilder, T> { 39 | return new GetByKeyRequestBuilder, T>( 40 | this.entityApi, 41 | { 42 | RelationshipNumber: relationshipNumber, 43 | BusinessPartnerCompany: businessPartnerCompany, 44 | BusinessPartnerPerson: businessPartnerPerson, 45 | ValidityEndDate: validityEndDate, 46 | AddressID: addressId 47 | } 48 | ); 49 | } 50 | 51 | /** 52 | * Returns a request builder for querying all `BpContactToAddress` entities. 53 | * @returns A request builder for creating requests to retrieve all `BpContactToAddress` entities. 54 | */ 55 | getAll(): GetAllRequestBuilder, T> { 56 | return new GetAllRequestBuilder, T>(this.entityApi); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/api/services/business-partner-service-1/BpContactToFuncAndDeptRequestBuilder.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 SAP SE or an SAP affiliate company. All rights reserved. 3 | * 4 | * This is a generated file powered by the SAP Cloud SDK for JavaScript. 5 | */ 6 | import { Moment } from 'moment'; 7 | import { 8 | DefaultDeSerializers, 9 | DeSerializers, 10 | GetAllRequestBuilder, 11 | GetByKeyRequestBuilder, 12 | UpdateRequestBuilder, 13 | DeserializedType, 14 | RequestBuilder 15 | } from '@sap-cloud-sdk/odata-v2'; 16 | import { BpContactToFuncAndDept } from './BpContactToFuncAndDept'; 17 | 18 | /** 19 | * Request builder class for operations supported on the {@link BpContactToFuncAndDept} entity. 20 | */ 21 | export class BpContactToFuncAndDeptRequestBuilder< 22 | T extends DeSerializers = DefaultDeSerializers 23 | > extends RequestBuilder, T> { 24 | /** 25 | * Returns a request builder for retrieving one `BpContactToFuncAndDept` entity based on its keys. 26 | * @param relationshipNumber Key property. See {@link BpContactToFuncAndDept.relationshipNumber}. 27 | * @param businessPartnerCompany Key property. See {@link BpContactToFuncAndDept.businessPartnerCompany}. 28 | * @param businessPartnerPerson Key property. See {@link BpContactToFuncAndDept.businessPartnerPerson}. 29 | * @param validityEndDate Key property. See {@link BpContactToFuncAndDept.validityEndDate}. 30 | * @returns A request builder for creating requests to retrieve one `BpContactToFuncAndDept` entity based on its keys. 31 | */ 32 | getByKey( 33 | relationshipNumber: DeserializedType, 34 | businessPartnerCompany: DeserializedType, 35 | businessPartnerPerson: DeserializedType, 36 | validityEndDate: DeserializedType 37 | ): GetByKeyRequestBuilder, T> { 38 | return new GetByKeyRequestBuilder, T>( 39 | this.entityApi, 40 | { 41 | RelationshipNumber: relationshipNumber, 42 | BusinessPartnerCompany: businessPartnerCompany, 43 | BusinessPartnerPerson: businessPartnerPerson, 44 | ValidityEndDate: validityEndDate 45 | } 46 | ); 47 | } 48 | 49 | /** 50 | * Returns a request builder for querying all `BpContactToFuncAndDept` entities. 51 | * @returns A request builder for creating requests to retrieve all `BpContactToFuncAndDept` entities. 52 | */ 53 | getAll(): GetAllRequestBuilder, T> { 54 | return new GetAllRequestBuilder, T>( 55 | this.entityApi 56 | ); 57 | } 58 | 59 | /** 60 | * Returns a request builder for updating an entity of type `BpContactToFuncAndDept`. 61 | * @param entity The entity to be updated 62 | * @returns A request builder for creating requests that update an entity of type `BpContactToFuncAndDept`. 63 | */ 64 | update( 65 | entity: BpContactToFuncAndDept 66 | ): UpdateRequestBuilder, T> { 67 | return new UpdateRequestBuilder, T>( 68 | this.entityApi, 69 | entity 70 | ); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/api/services/business-partner-service-1/BuPaAddressUsage.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 SAP SE or an SAP affiliate company. All rights reserved. 3 | * 4 | * This is a generated file powered by the SAP Cloud SDK for JavaScript. 5 | */ 6 | import { 7 | Entity, 8 | DefaultDeSerializers, 9 | DeSerializers, 10 | DeserializedType 11 | } from '@sap-cloud-sdk/odata-v2'; 12 | import type { BuPaAddressUsageApi } from './BuPaAddressUsageApi'; 13 | 14 | /** 15 | * This class represents the entity "A_BuPaAddressUsage" of service "API_BUSINESS_PARTNER". 16 | */ 17 | export class BuPaAddressUsage 18 | extends Entity 19 | implements BuPaAddressUsageType 20 | { 21 | /** 22 | * Technical entity name for BuPaAddressUsage. 23 | */ 24 | static _entityName = 'A_BuPaAddressUsage'; 25 | /** 26 | * Default url path for the according service. 27 | */ 28 | static _defaultBasePath = '/sap/opu/odata/sap/API_BUSINESS_PARTNER'; 29 | /** 30 | * All key fields of the BuPaAddressUsage entity 31 | */ 32 | static _keys = [ 33 | 'BusinessPartner', 34 | 'ValidityEndDate', 35 | 'AddressUsage', 36 | 'AddressID' 37 | ]; 38 | /** 39 | * Business Partner Number. 40 | * Maximum length: 10. 41 | */ 42 | businessPartner!: DeserializedType; 43 | /** 44 | * Validity End of a Business Partner Address Usage. 45 | */ 46 | validityEndDate!: DeserializedType; 47 | /** 48 | * Address Type. 49 | * Maximum length: 10. 50 | */ 51 | addressUsage!: DeserializedType; 52 | /** 53 | * Address Number. 54 | * Maximum length: 10. 55 | */ 56 | addressId!: DeserializedType; 57 | /** 58 | * Validity Start of a Business Partner Address Usage. 59 | * @nullable 60 | */ 61 | validityStartDate?: DeserializedType | null; 62 | /** 63 | * Indicator: Standard Address Usage. 64 | * @nullable 65 | */ 66 | standardUsage?: DeserializedType | null; 67 | /** 68 | * Authorization Group. 69 | * Maximum length: 4. 70 | * @nullable 71 | */ 72 | authorizationGroup?: DeserializedType | null; 73 | 74 | constructor(readonly _entityApi: BuPaAddressUsageApi) { 75 | super(_entityApi); 76 | } 77 | } 78 | 79 | export interface BuPaAddressUsageType< 80 | T extends DeSerializers = DefaultDeSerializers 81 | > { 82 | businessPartner: DeserializedType; 83 | validityEndDate: DeserializedType; 84 | addressUsage: DeserializedType; 85 | addressId: DeserializedType; 86 | validityStartDate?: DeserializedType | null; 87 | standardUsage?: DeserializedType | null; 88 | authorizationGroup?: DeserializedType | null; 89 | } 90 | -------------------------------------------------------------------------------- /src/api/services/business-partner-service-1/BuPaIndustry.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 SAP SE or an SAP affiliate company. All rights reserved. 3 | * 4 | * This is a generated file powered by the SAP Cloud SDK for JavaScript. 5 | */ 6 | import { 7 | Entity, 8 | DefaultDeSerializers, 9 | DeSerializers, 10 | DeserializedType 11 | } from '@sap-cloud-sdk/odata-v2'; 12 | import type { BuPaIndustryApi } from './BuPaIndustryApi'; 13 | 14 | /** 15 | * This class represents the entity "A_BuPaIndustry" of service "API_BUSINESS_PARTNER". 16 | */ 17 | export class BuPaIndustry 18 | extends Entity 19 | implements BuPaIndustryType 20 | { 21 | /** 22 | * Technical entity name for BuPaIndustry. 23 | */ 24 | static _entityName = 'A_BuPaIndustry'; 25 | /** 26 | * Default url path for the according service. 27 | */ 28 | static _defaultBasePath = '/sap/opu/odata/sap/API_BUSINESS_PARTNER'; 29 | /** 30 | * All key fields of the BuPaIndustry entity 31 | */ 32 | static _keys = ['IndustrySector', 'IndustrySystemType', 'BusinessPartner']; 33 | /** 34 | * Industry. 35 | * Maximum length: 10. 36 | */ 37 | industrySector!: DeserializedType; 38 | /** 39 | * Industry System. 40 | * Maximum length: 4. 41 | */ 42 | industrySystemType!: DeserializedType; 43 | /** 44 | * Business Partner Number. 45 | * Maximum length: 10. 46 | */ 47 | businessPartner!: DeserializedType; 48 | /** 49 | * Industry is Standard for BP in Industry System. 50 | * Maximum length: 1. 51 | * @nullable 52 | */ 53 | isStandardIndustry?: DeserializedType | null; 54 | /** 55 | * Description. 56 | * Maximum length: 100. 57 | * @nullable 58 | */ 59 | industryKeyDescription?: DeserializedType | null; 60 | 61 | constructor(readonly _entityApi: BuPaIndustryApi) { 62 | super(_entityApi); 63 | } 64 | } 65 | 66 | export interface BuPaIndustryType< 67 | T extends DeSerializers = DefaultDeSerializers 68 | > { 69 | industrySector: DeserializedType; 70 | industrySystemType: DeserializedType; 71 | businessPartner: DeserializedType; 72 | isStandardIndustry?: DeserializedType | null; 73 | industryKeyDescription?: DeserializedType | null; 74 | } 75 | -------------------------------------------------------------------------------- /src/api/services/business-partner-service-1/BusinessPartnerRequestBuilder.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 SAP SE or an SAP affiliate company. All rights reserved. 3 | * 4 | * This is a generated file powered by the SAP Cloud SDK for JavaScript. 5 | */ 6 | import { 7 | DefaultDeSerializers, 8 | DeSerializers, 9 | GetAllRequestBuilder, 10 | GetByKeyRequestBuilder, 11 | CreateRequestBuilder, 12 | UpdateRequestBuilder, 13 | DeserializedType, 14 | RequestBuilder 15 | } from '@sap-cloud-sdk/odata-v2'; 16 | import { BusinessPartner } from './BusinessPartner'; 17 | 18 | /** 19 | * Request builder class for operations supported on the {@link BusinessPartner} entity. 20 | */ 21 | export class BusinessPartnerRequestBuilder< 22 | T extends DeSerializers = DefaultDeSerializers 23 | > extends RequestBuilder, T> { 24 | /** 25 | * Returns a request builder for retrieving one `BusinessPartner` entity based on its keys. 26 | * @param businessPartner Key property. See {@link BusinessPartner.businessPartner}. 27 | * @returns A request builder for creating requests to retrieve one `BusinessPartner` entity based on its keys. 28 | */ 29 | getByKey( 30 | businessPartner: DeserializedType 31 | ): GetByKeyRequestBuilder, T> { 32 | return new GetByKeyRequestBuilder, T>(this.entityApi, { 33 | BusinessPartner: businessPartner 34 | }); 35 | } 36 | 37 | /** 38 | * Returns a request builder for querying all `BusinessPartner` entities. 39 | * @returns A request builder for creating requests to retrieve all `BusinessPartner` entities. 40 | */ 41 | getAll(): GetAllRequestBuilder, T> { 42 | return new GetAllRequestBuilder, T>(this.entityApi); 43 | } 44 | 45 | /** 46 | * Returns a request builder for creating a `BusinessPartner` entity. 47 | * @param entity The entity to be created 48 | * @returns A request builder for creating requests that create an entity of type `BusinessPartner`. 49 | */ 50 | create( 51 | entity: BusinessPartner 52 | ): CreateRequestBuilder, T> { 53 | return new CreateRequestBuilder, T>( 54 | this.entityApi, 55 | entity 56 | ); 57 | } 58 | 59 | /** 60 | * Returns a request builder for updating an entity of type `BusinessPartner`. 61 | * @param entity The entity to be updated 62 | * @returns A request builder for creating requests that update an entity of type `BusinessPartner`. 63 | */ 64 | update( 65 | entity: BusinessPartner 66 | ): UpdateRequestBuilder, T> { 67 | return new UpdateRequestBuilder, T>( 68 | this.entityApi, 69 | entity 70 | ); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/api/services/business-partner-service-1/BusinessPartnerRole.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 SAP SE or an SAP affiliate company. All rights reserved. 3 | * 4 | * This is a generated file powered by the SAP Cloud SDK for JavaScript. 5 | */ 6 | import { 7 | Entity, 8 | DefaultDeSerializers, 9 | DeSerializers, 10 | DeserializedType 11 | } from '@sap-cloud-sdk/odata-v2'; 12 | import type { BusinessPartnerRoleApi } from './BusinessPartnerRoleApi'; 13 | 14 | /** 15 | * This class represents the entity "A_BusinessPartnerRole" of service "API_BUSINESS_PARTNER". 16 | */ 17 | export class BusinessPartnerRole 18 | extends Entity 19 | implements BusinessPartnerRoleType 20 | { 21 | /** 22 | * Technical entity name for BusinessPartnerRole. 23 | */ 24 | static _entityName = 'A_BusinessPartnerRole'; 25 | /** 26 | * Default url path for the according service. 27 | */ 28 | static _defaultBasePath = '/sap/opu/odata/sap/API_BUSINESS_PARTNER'; 29 | /** 30 | * All key fields of the BusinessPartnerRole entity 31 | */ 32 | static _keys = ['BusinessPartner', 'BusinessPartnerRole']; 33 | /** 34 | * Business Partner Number. 35 | * Maximum length: 10. 36 | */ 37 | businessPartner!: DeserializedType; 38 | /** 39 | * BP Role. 40 | * Maximum length: 6. 41 | */ 42 | businessPartnerRole!: DeserializedType; 43 | /** 44 | * Validity Start of a BP Role. 45 | * @nullable 46 | */ 47 | validFrom?: DeserializedType | null; 48 | /** 49 | * Validity End of a BP Role. 50 | * @nullable 51 | */ 52 | validTo?: DeserializedType | null; 53 | /** 54 | * Authorization Group. 55 | * Maximum length: 4. 56 | * @nullable 57 | */ 58 | authorizationGroup?: DeserializedType | null; 59 | 60 | constructor(readonly _entityApi: BusinessPartnerRoleApi) { 61 | super(_entityApi); 62 | } 63 | } 64 | 65 | export interface BusinessPartnerRoleType< 66 | T extends DeSerializers = DefaultDeSerializers 67 | > { 68 | businessPartner: DeserializedType; 69 | businessPartnerRole: DeserializedType; 70 | validFrom?: DeserializedType | null; 71 | validTo?: DeserializedType | null; 72 | authorizationGroup?: DeserializedType | null; 73 | } 74 | -------------------------------------------------------------------------------- /src/api/services/business-partner-service-1/BusinessPartnerRoleRequestBuilder.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 SAP SE or an SAP affiliate company. All rights reserved. 3 | * 4 | * This is a generated file powered by the SAP Cloud SDK for JavaScript. 5 | */ 6 | import { 7 | DefaultDeSerializers, 8 | DeSerializers, 9 | GetAllRequestBuilder, 10 | GetByKeyRequestBuilder, 11 | CreateRequestBuilder, 12 | UpdateRequestBuilder, 13 | DeserializedType, 14 | RequestBuilder 15 | } from '@sap-cloud-sdk/odata-v2'; 16 | import { BusinessPartnerRole } from './BusinessPartnerRole'; 17 | 18 | /** 19 | * Request builder class for operations supported on the {@link BusinessPartnerRole} entity. 20 | */ 21 | export class BusinessPartnerRoleRequestBuilder< 22 | T extends DeSerializers = DefaultDeSerializers 23 | > extends RequestBuilder, T> { 24 | /** 25 | * Returns a request builder for retrieving one `BusinessPartnerRole` entity based on its keys. 26 | * @param businessPartner Key property. See {@link BusinessPartnerRole.businessPartner}. 27 | * @param businessPartnerRole Key property. See {@link BusinessPartnerRole.businessPartnerRole}. 28 | * @returns A request builder for creating requests to retrieve one `BusinessPartnerRole` entity based on its keys. 29 | */ 30 | getByKey( 31 | businessPartner: DeserializedType, 32 | businessPartnerRole: DeserializedType 33 | ): GetByKeyRequestBuilder, T> { 34 | return new GetByKeyRequestBuilder, T>( 35 | this.entityApi, 36 | { 37 | BusinessPartner: businessPartner, 38 | BusinessPartnerRole: businessPartnerRole 39 | } 40 | ); 41 | } 42 | 43 | /** 44 | * Returns a request builder for querying all `BusinessPartnerRole` entities. 45 | * @returns A request builder for creating requests to retrieve all `BusinessPartnerRole` entities. 46 | */ 47 | getAll(): GetAllRequestBuilder, T> { 48 | return new GetAllRequestBuilder, T>(this.entityApi); 49 | } 50 | 51 | /** 52 | * Returns a request builder for creating a `BusinessPartnerRole` entity. 53 | * @param entity The entity to be created 54 | * @returns A request builder for creating requests that create an entity of type `BusinessPartnerRole`. 55 | */ 56 | create( 57 | entity: BusinessPartnerRole 58 | ): CreateRequestBuilder, T> { 59 | return new CreateRequestBuilder, T>( 60 | this.entityApi, 61 | entity 62 | ); 63 | } 64 | 65 | /** 66 | * Returns a request builder for updating an entity of type `BusinessPartnerRole`. 67 | * @param entity The entity to be updated 68 | * @returns A request builder for creating requests that update an entity of type `BusinessPartnerRole`. 69 | */ 70 | update( 71 | entity: BusinessPartnerRole 72 | ): UpdateRequestBuilder, T> { 73 | return new UpdateRequestBuilder, T>( 74 | this.entityApi, 75 | entity 76 | ); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/api/services/business-partner-service-1/BusinessPartnerTaxNumber.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 SAP SE or an SAP affiliate company. All rights reserved. 3 | * 4 | * This is a generated file powered by the SAP Cloud SDK for JavaScript. 5 | */ 6 | import { 7 | Entity, 8 | DefaultDeSerializers, 9 | DeSerializers, 10 | DeserializedType 11 | } from '@sap-cloud-sdk/odata-v2'; 12 | import type { BusinessPartnerTaxNumberApi } from './BusinessPartnerTaxNumberApi'; 13 | 14 | /** 15 | * This class represents the entity "A_BusinessPartnerTaxNumber" of service "API_BUSINESS_PARTNER". 16 | */ 17 | export class BusinessPartnerTaxNumber< 18 | T extends DeSerializers = DefaultDeSerializers 19 | > 20 | extends Entity 21 | implements BusinessPartnerTaxNumberType 22 | { 23 | /** 24 | * Technical entity name for BusinessPartnerTaxNumber. 25 | */ 26 | static _entityName = 'A_BusinessPartnerTaxNumber'; 27 | /** 28 | * Default url path for the according service. 29 | */ 30 | static _defaultBasePath = '/sap/opu/odata/sap/API_BUSINESS_PARTNER'; 31 | /** 32 | * All key fields of the BusinessPartnerTaxNumber entity 33 | */ 34 | static _keys = ['BusinessPartner', 'BPTaxType']; 35 | /** 36 | * Business Partner Number. 37 | * Maximum length: 10. 38 | */ 39 | businessPartner!: DeserializedType; 40 | /** 41 | * Tax Number Category. 42 | * Maximum length: 4. 43 | */ 44 | bpTaxType!: DeserializedType; 45 | /** 46 | * Business Partner Tax Number. 47 | * Maximum length: 20. 48 | * @nullable 49 | */ 50 | bpTaxNumber?: DeserializedType | null; 51 | /** 52 | * Business Partner Tax Number. 53 | * Maximum length: 60. 54 | * @nullable 55 | */ 56 | bpTaxLongNumber?: DeserializedType | null; 57 | /** 58 | * Authorization Group. 59 | * Maximum length: 4. 60 | * @nullable 61 | */ 62 | authorizationGroup?: DeserializedType | null; 63 | 64 | constructor(readonly _entityApi: BusinessPartnerTaxNumberApi) { 65 | super(_entityApi); 66 | } 67 | } 68 | 69 | export interface BusinessPartnerTaxNumberType< 70 | T extends DeSerializers = DefaultDeSerializers 71 | > { 72 | businessPartner: DeserializedType; 73 | bpTaxType: DeserializedType; 74 | bpTaxNumber?: DeserializedType | null; 75 | bpTaxLongNumber?: DeserializedType | null; 76 | authorizationGroup?: DeserializedType | null; 77 | } 78 | -------------------------------------------------------------------------------- /src/api/services/business-partner-service-1/CustomerCompanyRequestBuilder.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 SAP SE or an SAP affiliate company. All rights reserved. 3 | * 4 | * This is a generated file powered by the SAP Cloud SDK for JavaScript. 5 | */ 6 | import { 7 | DefaultDeSerializers, 8 | DeSerializers, 9 | GetAllRequestBuilder, 10 | GetByKeyRequestBuilder, 11 | CreateRequestBuilder, 12 | UpdateRequestBuilder, 13 | DeserializedType, 14 | RequestBuilder 15 | } from '@sap-cloud-sdk/odata-v2'; 16 | import { CustomerCompany } from './CustomerCompany'; 17 | 18 | /** 19 | * Request builder class for operations supported on the {@link CustomerCompany} entity. 20 | */ 21 | export class CustomerCompanyRequestBuilder< 22 | T extends DeSerializers = DefaultDeSerializers 23 | > extends RequestBuilder, T> { 24 | /** 25 | * Returns a request builder for retrieving one `CustomerCompany` entity based on its keys. 26 | * @param customer Key property. See {@link CustomerCompany.customer}. 27 | * @param companyCode Key property. See {@link CustomerCompany.companyCode}. 28 | * @returns A request builder for creating requests to retrieve one `CustomerCompany` entity based on its keys. 29 | */ 30 | getByKey( 31 | customer: DeserializedType, 32 | companyCode: DeserializedType 33 | ): GetByKeyRequestBuilder, T> { 34 | return new GetByKeyRequestBuilder, T>(this.entityApi, { 35 | Customer: customer, 36 | CompanyCode: companyCode 37 | }); 38 | } 39 | 40 | /** 41 | * Returns a request builder for querying all `CustomerCompany` entities. 42 | * @returns A request builder for creating requests to retrieve all `CustomerCompany` entities. 43 | */ 44 | getAll(): GetAllRequestBuilder, T> { 45 | return new GetAllRequestBuilder, T>(this.entityApi); 46 | } 47 | 48 | /** 49 | * Returns a request builder for creating a `CustomerCompany` entity. 50 | * @param entity The entity to be created 51 | * @returns A request builder for creating requests that create an entity of type `CustomerCompany`. 52 | */ 53 | create( 54 | entity: CustomerCompany 55 | ): CreateRequestBuilder, T> { 56 | return new CreateRequestBuilder, T>( 57 | this.entityApi, 58 | entity 59 | ); 60 | } 61 | 62 | /** 63 | * Returns a request builder for updating an entity of type `CustomerCompany`. 64 | * @param entity The entity to be updated 65 | * @returns A request builder for creating requests that update an entity of type `CustomerCompany`. 66 | */ 67 | update( 68 | entity: CustomerCompany 69 | ): UpdateRequestBuilder, T> { 70 | return new UpdateRequestBuilder, T>( 71 | this.entityApi, 72 | entity 73 | ); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/api/services/business-partner-service-1/CustomerCompanyText.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 SAP SE or an SAP affiliate company. All rights reserved. 3 | * 4 | * This is a generated file powered by the SAP Cloud SDK for JavaScript. 5 | */ 6 | import { 7 | Entity, 8 | DefaultDeSerializers, 9 | DeSerializers, 10 | DeserializedType 11 | } from '@sap-cloud-sdk/odata-v2'; 12 | import type { CustomerCompanyTextApi } from './CustomerCompanyTextApi'; 13 | 14 | /** 15 | * This class represents the entity "A_CustomerCompanyText" of service "API_BUSINESS_PARTNER". 16 | */ 17 | export class CustomerCompanyText 18 | extends Entity 19 | implements CustomerCompanyTextType 20 | { 21 | /** 22 | * Technical entity name for CustomerCompanyText. 23 | */ 24 | static _entityName = 'A_CustomerCompanyText'; 25 | /** 26 | * Default url path for the according service. 27 | */ 28 | static _defaultBasePath = '/sap/opu/odata/sap/API_BUSINESS_PARTNER'; 29 | /** 30 | * All key fields of the CustomerCompanyText entity 31 | */ 32 | static _keys = ['Customer', 'CompanyCode', 'Language', 'LongTextID']; 33 | /** 34 | * Customer Number. 35 | * Maximum length: 10. 36 | */ 37 | customer!: DeserializedType; 38 | /** 39 | * Company Code. 40 | * Maximum length: 4. 41 | */ 42 | companyCode!: DeserializedType; 43 | /** 44 | * Language key. 45 | * Maximum length: 2. 46 | */ 47 | language!: DeserializedType; 48 | /** 49 | * Text ID. 50 | * Maximum length: 4. 51 | */ 52 | longTextId!: DeserializedType; 53 | /** 54 | * String. 55 | * @nullable 56 | */ 57 | longText?: DeserializedType | null; 58 | 59 | constructor(readonly _entityApi: CustomerCompanyTextApi) { 60 | super(_entityApi); 61 | } 62 | } 63 | 64 | export interface CustomerCompanyTextType< 65 | T extends DeSerializers = DefaultDeSerializers 66 | > { 67 | customer: DeserializedType; 68 | companyCode: DeserializedType; 69 | language: DeserializedType; 70 | longTextId: DeserializedType; 71 | longText?: DeserializedType | null; 72 | } 73 | -------------------------------------------------------------------------------- /src/api/services/business-partner-service-1/CustomerRequestBuilder.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 SAP SE or an SAP affiliate company. All rights reserved. 3 | * 4 | * This is a generated file powered by the SAP Cloud SDK for JavaScript. 5 | */ 6 | import { 7 | DefaultDeSerializers, 8 | DeSerializers, 9 | GetAllRequestBuilder, 10 | GetByKeyRequestBuilder, 11 | UpdateRequestBuilder, 12 | DeserializedType, 13 | RequestBuilder 14 | } from '@sap-cloud-sdk/odata-v2'; 15 | import { Customer } from './Customer'; 16 | 17 | /** 18 | * Request builder class for operations supported on the {@link Customer} entity. 19 | */ 20 | export class CustomerRequestBuilder< 21 | T extends DeSerializers = DefaultDeSerializers 22 | > extends RequestBuilder, T> { 23 | /** 24 | * Returns a request builder for retrieving one `Customer` entity based on its keys. 25 | * @param customer Key property. See {@link Customer.customer}. 26 | * @returns A request builder for creating requests to retrieve one `Customer` entity based on its keys. 27 | */ 28 | getByKey( 29 | customer: DeserializedType 30 | ): GetByKeyRequestBuilder, T> { 31 | return new GetByKeyRequestBuilder, T>(this.entityApi, { 32 | Customer: customer 33 | }); 34 | } 35 | 36 | /** 37 | * Returns a request builder for querying all `Customer` entities. 38 | * @returns A request builder for creating requests to retrieve all `Customer` entities. 39 | */ 40 | getAll(): GetAllRequestBuilder, T> { 41 | return new GetAllRequestBuilder, T>(this.entityApi); 42 | } 43 | 44 | /** 45 | * Returns a request builder for updating an entity of type `Customer`. 46 | * @param entity The entity to be updated 47 | * @returns A request builder for creating requests that update an entity of type `Customer`. 48 | */ 49 | update(entity: Customer): UpdateRequestBuilder, T> { 50 | return new UpdateRequestBuilder, T>(this.entityApi, entity); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/api/services/business-partner-service-1/CustomerSalesAreaRequestBuilder.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 SAP SE or an SAP affiliate company. All rights reserved. 3 | * 4 | * This is a generated file powered by the SAP Cloud SDK for JavaScript. 5 | */ 6 | import { 7 | DefaultDeSerializers, 8 | DeSerializers, 9 | GetAllRequestBuilder, 10 | GetByKeyRequestBuilder, 11 | CreateRequestBuilder, 12 | UpdateRequestBuilder, 13 | DeserializedType, 14 | RequestBuilder 15 | } from '@sap-cloud-sdk/odata-v2'; 16 | import { CustomerSalesArea } from './CustomerSalesArea'; 17 | 18 | /** 19 | * Request builder class for operations supported on the {@link CustomerSalesArea} entity. 20 | */ 21 | export class CustomerSalesAreaRequestBuilder< 22 | T extends DeSerializers = DefaultDeSerializers 23 | > extends RequestBuilder, T> { 24 | /** 25 | * Returns a request builder for retrieving one `CustomerSalesArea` entity based on its keys. 26 | * @param customer Key property. See {@link CustomerSalesArea.customer}. 27 | * @param salesOrganization Key property. See {@link CustomerSalesArea.salesOrganization}. 28 | * @param distributionChannel Key property. See {@link CustomerSalesArea.distributionChannel}. 29 | * @param division Key property. See {@link CustomerSalesArea.division}. 30 | * @returns A request builder for creating requests to retrieve one `CustomerSalesArea` entity based on its keys. 31 | */ 32 | getByKey( 33 | customer: DeserializedType, 34 | salesOrganization: DeserializedType, 35 | distributionChannel: DeserializedType, 36 | division: DeserializedType 37 | ): GetByKeyRequestBuilder, T> { 38 | return new GetByKeyRequestBuilder, T>(this.entityApi, { 39 | Customer: customer, 40 | SalesOrganization: salesOrganization, 41 | DistributionChannel: distributionChannel, 42 | Division: division 43 | }); 44 | } 45 | 46 | /** 47 | * Returns a request builder for querying all `CustomerSalesArea` entities. 48 | * @returns A request builder for creating requests to retrieve all `CustomerSalesArea` entities. 49 | */ 50 | getAll(): GetAllRequestBuilder, T> { 51 | return new GetAllRequestBuilder, T>(this.entityApi); 52 | } 53 | 54 | /** 55 | * Returns a request builder for creating a `CustomerSalesArea` entity. 56 | * @param entity The entity to be created 57 | * @returns A request builder for creating requests that create an entity of type `CustomerSalesArea`. 58 | */ 59 | create( 60 | entity: CustomerSalesArea 61 | ): CreateRequestBuilder, T> { 62 | return new CreateRequestBuilder, T>( 63 | this.entityApi, 64 | entity 65 | ); 66 | } 67 | 68 | /** 69 | * Returns a request builder for updating an entity of type `CustomerSalesArea`. 70 | * @param entity The entity to be updated 71 | * @returns A request builder for creating requests that update an entity of type `CustomerSalesArea`. 72 | */ 73 | update( 74 | entity: CustomerSalesArea 75 | ): UpdateRequestBuilder, T> { 76 | return new UpdateRequestBuilder, T>( 77 | this.entityApi, 78 | entity 79 | ); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/api/services/business-partner-service-1/CustomerSalesAreaTax.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 SAP SE or an SAP affiliate company. All rights reserved. 3 | * 4 | * This is a generated file powered by the SAP Cloud SDK for JavaScript. 5 | */ 6 | import { 7 | Entity, 8 | DefaultDeSerializers, 9 | DeSerializers, 10 | DeserializedType 11 | } from '@sap-cloud-sdk/odata-v2'; 12 | import type { CustomerSalesAreaTaxApi } from './CustomerSalesAreaTaxApi'; 13 | 14 | /** 15 | * This class represents the entity "A_CustomerSalesAreaTax" of service "API_BUSINESS_PARTNER". 16 | */ 17 | export class CustomerSalesAreaTax< 18 | T extends DeSerializers = DefaultDeSerializers 19 | > 20 | extends Entity 21 | implements CustomerSalesAreaTaxType 22 | { 23 | /** 24 | * Technical entity name for CustomerSalesAreaTax. 25 | */ 26 | static _entityName = 'A_CustomerSalesAreaTax'; 27 | /** 28 | * Default url path for the according service. 29 | */ 30 | static _defaultBasePath = '/sap/opu/odata/sap/API_BUSINESS_PARTNER'; 31 | /** 32 | * All key fields of the CustomerSalesAreaTax entity 33 | */ 34 | static _keys = [ 35 | 'Customer', 36 | 'SalesOrganization', 37 | 'DistributionChannel', 38 | 'Division', 39 | 'DepartureCountry', 40 | 'CustomerTaxCategory' 41 | ]; 42 | /** 43 | * Customer Number. 44 | * Maximum length: 10. 45 | */ 46 | customer!: DeserializedType; 47 | /** 48 | * Sales Organization. 49 | * Maximum length: 4. 50 | */ 51 | salesOrganization!: DeserializedType; 52 | /** 53 | * Reference distrib.channel for cust.and material masters. 54 | * Maximum length: 2. 55 | */ 56 | distributionChannel!: DeserializedType; 57 | /** 58 | * Division. 59 | * Maximum length: 2. 60 | */ 61 | division!: DeserializedType; 62 | /** 63 | * Departure Country/Region (from which the goods are sent). 64 | * Maximum length: 3. 65 | */ 66 | departureCountry!: DeserializedType; 67 | /** 68 | * Tax category (sales tax, federal sales tax,...). 69 | * Maximum length: 4. 70 | */ 71 | customerTaxCategory!: DeserializedType; 72 | /** 73 | * Tax classification for customer. 74 | * Maximum length: 1. 75 | * @nullable 76 | */ 77 | customerTaxClassification?: DeserializedType | null; 78 | 79 | constructor(readonly _entityApi: CustomerSalesAreaTaxApi) { 80 | super(_entityApi); 81 | } 82 | } 83 | 84 | export interface CustomerSalesAreaTaxType< 85 | T extends DeSerializers = DefaultDeSerializers 86 | > { 87 | customer: DeserializedType; 88 | salesOrganization: DeserializedType; 89 | distributionChannel: DeserializedType; 90 | division: DeserializedType; 91 | departureCountry: DeserializedType; 92 | customerTaxCategory: DeserializedType; 93 | customerTaxClassification?: DeserializedType | null; 94 | } 95 | -------------------------------------------------------------------------------- /src/api/services/business-partner-service-1/CustomerSalesAreaText.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 SAP SE or an SAP affiliate company. All rights reserved. 3 | * 4 | * This is a generated file powered by the SAP Cloud SDK for JavaScript. 5 | */ 6 | import { 7 | Entity, 8 | DefaultDeSerializers, 9 | DeSerializers, 10 | DeserializedType 11 | } from '@sap-cloud-sdk/odata-v2'; 12 | import type { CustomerSalesAreaTextApi } from './CustomerSalesAreaTextApi'; 13 | 14 | /** 15 | * This class represents the entity "A_CustomerSalesAreaText" of service "API_BUSINESS_PARTNER". 16 | */ 17 | export class CustomerSalesAreaText< 18 | T extends DeSerializers = DefaultDeSerializers 19 | > 20 | extends Entity 21 | implements CustomerSalesAreaTextType 22 | { 23 | /** 24 | * Technical entity name for CustomerSalesAreaText. 25 | */ 26 | static _entityName = 'A_CustomerSalesAreaText'; 27 | /** 28 | * Default url path for the according service. 29 | */ 30 | static _defaultBasePath = '/sap/opu/odata/sap/API_BUSINESS_PARTNER'; 31 | /** 32 | * All key fields of the CustomerSalesAreaText entity 33 | */ 34 | static _keys = [ 35 | 'Customer', 36 | 'SalesOrganization', 37 | 'DistributionChannel', 38 | 'Division', 39 | 'Language', 40 | 'LongTextID' 41 | ]; 42 | /** 43 | * Customer Number. 44 | * Maximum length: 10. 45 | */ 46 | customer!: DeserializedType; 47 | /** 48 | * Sales Organization. 49 | * Maximum length: 4. 50 | */ 51 | salesOrganization!: DeserializedType; 52 | /** 53 | * Distribution Channel. 54 | * Maximum length: 2. 55 | */ 56 | distributionChannel!: DeserializedType; 57 | /** 58 | * Division. 59 | * Maximum length: 2. 60 | */ 61 | division!: DeserializedType; 62 | /** 63 | * Language key. 64 | * Maximum length: 2. 65 | */ 66 | language!: DeserializedType; 67 | /** 68 | * Text ID. 69 | * Maximum length: 4. 70 | */ 71 | longTextId!: DeserializedType; 72 | /** 73 | * String. 74 | * @nullable 75 | */ 76 | longText?: DeserializedType | null; 77 | 78 | constructor(readonly _entityApi: CustomerSalesAreaTextApi) { 79 | super(_entityApi); 80 | } 81 | } 82 | 83 | export interface CustomerSalesAreaTextType< 84 | T extends DeSerializers = DefaultDeSerializers 85 | > { 86 | customer: DeserializedType; 87 | salesOrganization: DeserializedType; 88 | distributionChannel: DeserializedType; 89 | division: DeserializedType; 90 | language: DeserializedType; 91 | longTextId: DeserializedType; 92 | longText?: DeserializedType | null; 93 | } 94 | -------------------------------------------------------------------------------- /src/api/services/business-partner-service-1/CustomerTaxGrouping.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 SAP SE or an SAP affiliate company. All rights reserved. 3 | * 4 | * This is a generated file powered by the SAP Cloud SDK for JavaScript. 5 | */ 6 | import { 7 | Entity, 8 | DefaultDeSerializers, 9 | DeSerializers, 10 | DeserializedType 11 | } from '@sap-cloud-sdk/odata-v2'; 12 | import type { CustomerTaxGroupingApi } from './CustomerTaxGroupingApi'; 13 | 14 | /** 15 | * This class represents the entity "A_CustomerTaxGrouping" of service "API_BUSINESS_PARTNER". 16 | */ 17 | export class CustomerTaxGrouping 18 | extends Entity 19 | implements CustomerTaxGroupingType 20 | { 21 | /** 22 | * Technical entity name for CustomerTaxGrouping. 23 | */ 24 | static _entityName = 'A_CustomerTaxGrouping'; 25 | /** 26 | * Default url path for the according service. 27 | */ 28 | static _defaultBasePath = '/sap/opu/odata/sap/API_BUSINESS_PARTNER'; 29 | /** 30 | * All key fields of the CustomerTaxGrouping entity 31 | */ 32 | static _keys = ['Customer', 'CustomerTaxGroupingCode']; 33 | /** 34 | * Customer Number. 35 | * Maximum length: 10. 36 | */ 37 | customer!: DeserializedType; 38 | /** 39 | * Category Indicator for Tax Codes. 40 | * Maximum length: 3. 41 | */ 42 | customerTaxGroupingCode!: DeserializedType; 43 | /** 44 | * Number of exemption certificate. 45 | * Maximum length: 15. 46 | * @nullable 47 | */ 48 | custTaxGrpExemptionCertificate?: DeserializedType | null; 49 | /** 50 | * Exemption rate. 51 | * @nullable 52 | */ 53 | custTaxGroupExemptionRate?: DeserializedType | null; 54 | /** 55 | * Start date of exemption. 56 | * @nullable 57 | */ 58 | custTaxGroupExemptionStartDate?: DeserializedType | null; 59 | /** 60 | * End date of exemption. 61 | * @nullable 62 | */ 63 | custTaxGroupExemptionEndDate?: DeserializedType | null; 64 | /** 65 | * Subjected from. 66 | * @nullable 67 | */ 68 | custTaxGroupSubjectedStartDate?: DeserializedType | null; 69 | /** 70 | * Subjected until. 71 | * @nullable 72 | */ 73 | custTaxGroupSubjectedEndDate?: DeserializedType | null; 74 | 75 | constructor(readonly _entityApi: CustomerTaxGroupingApi) { 76 | super(_entityApi); 77 | } 78 | } 79 | 80 | export interface CustomerTaxGroupingType< 81 | T extends DeSerializers = DefaultDeSerializers 82 | > { 83 | customer: DeserializedType; 84 | customerTaxGroupingCode: DeserializedType; 85 | custTaxGrpExemptionCertificate?: DeserializedType | null; 86 | custTaxGroupExemptionRate?: DeserializedType | null; 87 | custTaxGroupExemptionStartDate?: DeserializedType | null; 88 | custTaxGroupExemptionEndDate?: DeserializedType | null; 89 | custTaxGroupSubjectedStartDate?: DeserializedType | null; 90 | custTaxGroupSubjectedEndDate?: DeserializedType | null; 91 | } 92 | -------------------------------------------------------------------------------- /src/api/services/business-partner-service-1/CustomerText.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 SAP SE or an SAP affiliate company. All rights reserved. 3 | * 4 | * This is a generated file powered by the SAP Cloud SDK for JavaScript. 5 | */ 6 | import { 7 | Entity, 8 | DefaultDeSerializers, 9 | DeSerializers, 10 | DeserializedType 11 | } from '@sap-cloud-sdk/odata-v2'; 12 | import type { CustomerTextApi } from './CustomerTextApi'; 13 | 14 | /** 15 | * This class represents the entity "A_CustomerText" of service "API_BUSINESS_PARTNER". 16 | */ 17 | export class CustomerText 18 | extends Entity 19 | implements CustomerTextType 20 | { 21 | /** 22 | * Technical entity name for CustomerText. 23 | */ 24 | static _entityName = 'A_CustomerText'; 25 | /** 26 | * Default url path for the according service. 27 | */ 28 | static _defaultBasePath = '/sap/opu/odata/sap/API_BUSINESS_PARTNER'; 29 | /** 30 | * All key fields of the CustomerText entity 31 | */ 32 | static _keys = ['Customer', 'Language', 'LongTextID']; 33 | /** 34 | * Customer Number. 35 | * Maximum length: 10. 36 | */ 37 | customer!: DeserializedType; 38 | /** 39 | * Language key. 40 | * Maximum length: 2. 41 | */ 42 | language!: DeserializedType; 43 | /** 44 | * Text ID. 45 | * Maximum length: 4. 46 | */ 47 | longTextId!: DeserializedType; 48 | /** 49 | * String. 50 | * @nullable 51 | */ 52 | longText?: DeserializedType | null; 53 | 54 | constructor(readonly _entityApi: CustomerTextApi) { 55 | super(_entityApi); 56 | } 57 | } 58 | 59 | export interface CustomerTextType< 60 | T extends DeSerializers = DefaultDeSerializers 61 | > { 62 | customer: DeserializedType; 63 | language: DeserializedType; 64 | longTextId: DeserializedType; 65 | longText?: DeserializedType | null; 66 | } 67 | -------------------------------------------------------------------------------- /src/api/services/business-partner-service-1/SupplierCompanyRequestBuilder.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 SAP SE or an SAP affiliate company. All rights reserved. 3 | * 4 | * This is a generated file powered by the SAP Cloud SDK for JavaScript. 5 | */ 6 | import { 7 | DefaultDeSerializers, 8 | DeSerializers, 9 | GetAllRequestBuilder, 10 | GetByKeyRequestBuilder, 11 | CreateRequestBuilder, 12 | UpdateRequestBuilder, 13 | DeserializedType, 14 | RequestBuilder 15 | } from '@sap-cloud-sdk/odata-v2'; 16 | import { SupplierCompany } from './SupplierCompany'; 17 | 18 | /** 19 | * Request builder class for operations supported on the {@link SupplierCompany} entity. 20 | */ 21 | export class SupplierCompanyRequestBuilder< 22 | T extends DeSerializers = DefaultDeSerializers 23 | > extends RequestBuilder, T> { 24 | /** 25 | * Returns a request builder for retrieving one `SupplierCompany` entity based on its keys. 26 | * @param supplier Key property. See {@link SupplierCompany.supplier}. 27 | * @param companyCode Key property. See {@link SupplierCompany.companyCode}. 28 | * @returns A request builder for creating requests to retrieve one `SupplierCompany` entity based on its keys. 29 | */ 30 | getByKey( 31 | supplier: DeserializedType, 32 | companyCode: DeserializedType 33 | ): GetByKeyRequestBuilder, T> { 34 | return new GetByKeyRequestBuilder, T>(this.entityApi, { 35 | Supplier: supplier, 36 | CompanyCode: companyCode 37 | }); 38 | } 39 | 40 | /** 41 | * Returns a request builder for querying all `SupplierCompany` entities. 42 | * @returns A request builder for creating requests to retrieve all `SupplierCompany` entities. 43 | */ 44 | getAll(): GetAllRequestBuilder, T> { 45 | return new GetAllRequestBuilder, T>(this.entityApi); 46 | } 47 | 48 | /** 49 | * Returns a request builder for creating a `SupplierCompany` entity. 50 | * @param entity The entity to be created 51 | * @returns A request builder for creating requests that create an entity of type `SupplierCompany`. 52 | */ 53 | create( 54 | entity: SupplierCompany 55 | ): CreateRequestBuilder, T> { 56 | return new CreateRequestBuilder, T>( 57 | this.entityApi, 58 | entity 59 | ); 60 | } 61 | 62 | /** 63 | * Returns a request builder for updating an entity of type `SupplierCompany`. 64 | * @param entity The entity to be updated 65 | * @returns A request builder for creating requests that update an entity of type `SupplierCompany`. 66 | */ 67 | update( 68 | entity: SupplierCompany 69 | ): UpdateRequestBuilder, T> { 70 | return new UpdateRequestBuilder, T>( 71 | this.entityApi, 72 | entity 73 | ); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/api/services/business-partner-service-1/SupplierCompanyText.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 SAP SE or an SAP affiliate company. All rights reserved. 3 | * 4 | * This is a generated file powered by the SAP Cloud SDK for JavaScript. 5 | */ 6 | import { 7 | Entity, 8 | DefaultDeSerializers, 9 | DeSerializers, 10 | DeserializedType 11 | } from '@sap-cloud-sdk/odata-v2'; 12 | import type { SupplierCompanyTextApi } from './SupplierCompanyTextApi'; 13 | 14 | /** 15 | * This class represents the entity "A_SupplierCompanyText" of service "API_BUSINESS_PARTNER". 16 | */ 17 | export class SupplierCompanyText 18 | extends Entity 19 | implements SupplierCompanyTextType 20 | { 21 | /** 22 | * Technical entity name for SupplierCompanyText. 23 | */ 24 | static _entityName = 'A_SupplierCompanyText'; 25 | /** 26 | * Default url path for the according service. 27 | */ 28 | static _defaultBasePath = '/sap/opu/odata/sap/API_BUSINESS_PARTNER'; 29 | /** 30 | * All key fields of the SupplierCompanyText entity 31 | */ 32 | static _keys = ['Supplier', 'CompanyCode', 'Language', 'LongTextID']; 33 | /** 34 | * Account Number of Supplier. 35 | * Maximum length: 10. 36 | */ 37 | supplier!: DeserializedType; 38 | /** 39 | * Company Code. 40 | * Maximum length: 4. 41 | */ 42 | companyCode!: DeserializedType; 43 | /** 44 | * Language key. 45 | * Maximum length: 2. 46 | */ 47 | language!: DeserializedType; 48 | /** 49 | * Text ID. 50 | * Maximum length: 4. 51 | */ 52 | longTextId!: DeserializedType; 53 | /** 54 | * String. 55 | * @nullable 56 | */ 57 | longText?: DeserializedType | null; 58 | 59 | constructor(readonly _entityApi: SupplierCompanyTextApi) { 60 | super(_entityApi); 61 | } 62 | } 63 | 64 | export interface SupplierCompanyTextType< 65 | T extends DeSerializers = DefaultDeSerializers 66 | > { 67 | supplier: DeserializedType; 68 | companyCode: DeserializedType; 69 | language: DeserializedType; 70 | longTextId: DeserializedType; 71 | longText?: DeserializedType | null; 72 | } 73 | -------------------------------------------------------------------------------- /src/api/services/business-partner-service-1/SupplierPurchasingOrgRequestBuilder.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 SAP SE or an SAP affiliate company. All rights reserved. 3 | * 4 | * This is a generated file powered by the SAP Cloud SDK for JavaScript. 5 | */ 6 | import { 7 | DefaultDeSerializers, 8 | DeSerializers, 9 | GetAllRequestBuilder, 10 | GetByKeyRequestBuilder, 11 | CreateRequestBuilder, 12 | UpdateRequestBuilder, 13 | DeserializedType, 14 | RequestBuilder 15 | } from '@sap-cloud-sdk/odata-v2'; 16 | import { SupplierPurchasingOrg } from './SupplierPurchasingOrg'; 17 | 18 | /** 19 | * Request builder class for operations supported on the {@link SupplierPurchasingOrg} entity. 20 | */ 21 | export class SupplierPurchasingOrgRequestBuilder< 22 | T extends DeSerializers = DefaultDeSerializers 23 | > extends RequestBuilder, T> { 24 | /** 25 | * Returns a request builder for retrieving one `SupplierPurchasingOrg` entity based on its keys. 26 | * @param supplier Key property. See {@link SupplierPurchasingOrg.supplier}. 27 | * @param purchasingOrganization Key property. See {@link SupplierPurchasingOrg.purchasingOrganization}. 28 | * @returns A request builder for creating requests to retrieve one `SupplierPurchasingOrg` entity based on its keys. 29 | */ 30 | getByKey( 31 | supplier: DeserializedType, 32 | purchasingOrganization: DeserializedType 33 | ): GetByKeyRequestBuilder, T> { 34 | return new GetByKeyRequestBuilder, T>( 35 | this.entityApi, 36 | { 37 | Supplier: supplier, 38 | PurchasingOrganization: purchasingOrganization 39 | } 40 | ); 41 | } 42 | 43 | /** 44 | * Returns a request builder for querying all `SupplierPurchasingOrg` entities. 45 | * @returns A request builder for creating requests to retrieve all `SupplierPurchasingOrg` entities. 46 | */ 47 | getAll(): GetAllRequestBuilder, T> { 48 | return new GetAllRequestBuilder, T>( 49 | this.entityApi 50 | ); 51 | } 52 | 53 | /** 54 | * Returns a request builder for creating a `SupplierPurchasingOrg` entity. 55 | * @param entity The entity to be created 56 | * @returns A request builder for creating requests that create an entity of type `SupplierPurchasingOrg`. 57 | */ 58 | create( 59 | entity: SupplierPurchasingOrg 60 | ): CreateRequestBuilder, T> { 61 | return new CreateRequestBuilder, T>( 62 | this.entityApi, 63 | entity 64 | ); 65 | } 66 | 67 | /** 68 | * Returns a request builder for updating an entity of type `SupplierPurchasingOrg`. 69 | * @param entity The entity to be updated 70 | * @returns A request builder for creating requests that update an entity of type `SupplierPurchasingOrg`. 71 | */ 72 | update( 73 | entity: SupplierPurchasingOrg 74 | ): UpdateRequestBuilder, T> { 75 | return new UpdateRequestBuilder, T>( 76 | this.entityApi, 77 | entity 78 | ); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/api/services/business-partner-service-1/SupplierPurchasingOrgText.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 SAP SE or an SAP affiliate company. All rights reserved. 3 | * 4 | * This is a generated file powered by the SAP Cloud SDK for JavaScript. 5 | */ 6 | import { 7 | Entity, 8 | DefaultDeSerializers, 9 | DeSerializers, 10 | DeserializedType 11 | } from '@sap-cloud-sdk/odata-v2'; 12 | import type { SupplierPurchasingOrgTextApi } from './SupplierPurchasingOrgTextApi'; 13 | 14 | /** 15 | * This class represents the entity "A_SupplierPurchasingOrgText" of service "API_BUSINESS_PARTNER". 16 | */ 17 | export class SupplierPurchasingOrgText< 18 | T extends DeSerializers = DefaultDeSerializers 19 | > 20 | extends Entity 21 | implements SupplierPurchasingOrgTextType 22 | { 23 | /** 24 | * Technical entity name for SupplierPurchasingOrgText. 25 | */ 26 | static _entityName = 'A_SupplierPurchasingOrgText'; 27 | /** 28 | * Default url path for the according service. 29 | */ 30 | static _defaultBasePath = '/sap/opu/odata/sap/API_BUSINESS_PARTNER'; 31 | /** 32 | * All key fields of the SupplierPurchasingOrgText entity 33 | */ 34 | static _keys = [ 35 | 'Supplier', 36 | 'PurchasingOrganization', 37 | 'Language', 38 | 'LongTextID' 39 | ]; 40 | /** 41 | * Supplier. 42 | * Maximum length: 10. 43 | */ 44 | supplier!: DeserializedType; 45 | /** 46 | * Purchasing Organization. 47 | * Maximum length: 4. 48 | */ 49 | purchasingOrganization!: DeserializedType; 50 | /** 51 | * Language key. 52 | * Maximum length: 2. 53 | */ 54 | language!: DeserializedType; 55 | /** 56 | * Text ID. 57 | * Maximum length: 4. 58 | */ 59 | longTextId!: DeserializedType; 60 | /** 61 | * String. 62 | * @nullable 63 | */ 64 | longText?: DeserializedType | null; 65 | 66 | constructor(readonly _entityApi: SupplierPurchasingOrgTextApi) { 67 | super(_entityApi); 68 | } 69 | } 70 | 71 | export interface SupplierPurchasingOrgTextType< 72 | T extends DeSerializers = DefaultDeSerializers 73 | > { 74 | supplier: DeserializedType; 75 | purchasingOrganization: DeserializedType; 76 | language: DeserializedType; 77 | longTextId: DeserializedType; 78 | longText?: DeserializedType | null; 79 | } 80 | -------------------------------------------------------------------------------- /src/api/services/business-partner-service-1/SupplierRequestBuilder.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 SAP SE or an SAP affiliate company. All rights reserved. 3 | * 4 | * This is a generated file powered by the SAP Cloud SDK for JavaScript. 5 | */ 6 | import { 7 | DefaultDeSerializers, 8 | DeSerializers, 9 | GetAllRequestBuilder, 10 | GetByKeyRequestBuilder, 11 | UpdateRequestBuilder, 12 | DeserializedType, 13 | RequestBuilder 14 | } from '@sap-cloud-sdk/odata-v2'; 15 | import { Supplier } from './Supplier'; 16 | 17 | /** 18 | * Request builder class for operations supported on the {@link Supplier} entity. 19 | */ 20 | export class SupplierRequestBuilder< 21 | T extends DeSerializers = DefaultDeSerializers 22 | > extends RequestBuilder, T> { 23 | /** 24 | * Returns a request builder for retrieving one `Supplier` entity based on its keys. 25 | * @param supplier Key property. See {@link Supplier.supplier}. 26 | * @returns A request builder for creating requests to retrieve one `Supplier` entity based on its keys. 27 | */ 28 | getByKey( 29 | supplier: DeserializedType 30 | ): GetByKeyRequestBuilder, T> { 31 | return new GetByKeyRequestBuilder, T>(this.entityApi, { 32 | Supplier: supplier 33 | }); 34 | } 35 | 36 | /** 37 | * Returns a request builder for querying all `Supplier` entities. 38 | * @returns A request builder for creating requests to retrieve all `Supplier` entities. 39 | */ 40 | getAll(): GetAllRequestBuilder, T> { 41 | return new GetAllRequestBuilder, T>(this.entityApi); 42 | } 43 | 44 | /** 45 | * Returns a request builder for updating an entity of type `Supplier`. 46 | * @param entity The entity to be updated 47 | * @returns A request builder for creating requests that update an entity of type `Supplier`. 48 | */ 49 | update(entity: Supplier): UpdateRequestBuilder, T> { 50 | return new UpdateRequestBuilder, T>(this.entityApi, entity); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/api/services/business-partner-service-1/SupplierText.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 SAP SE or an SAP affiliate company. All rights reserved. 3 | * 4 | * This is a generated file powered by the SAP Cloud SDK for JavaScript. 5 | */ 6 | import { 7 | Entity, 8 | DefaultDeSerializers, 9 | DeSerializers, 10 | DeserializedType 11 | } from '@sap-cloud-sdk/odata-v2'; 12 | import type { SupplierTextApi } from './SupplierTextApi'; 13 | 14 | /** 15 | * This class represents the entity "A_SupplierText" of service "API_BUSINESS_PARTNER". 16 | */ 17 | export class SupplierText 18 | extends Entity 19 | implements SupplierTextType 20 | { 21 | /** 22 | * Technical entity name for SupplierText. 23 | */ 24 | static _entityName = 'A_SupplierText'; 25 | /** 26 | * Default url path for the according service. 27 | */ 28 | static _defaultBasePath = '/sap/opu/odata/sap/API_BUSINESS_PARTNER'; 29 | /** 30 | * All key fields of the SupplierText entity 31 | */ 32 | static _keys = ['Supplier', 'Language', 'LongTextID']; 33 | /** 34 | * Account Number of Supplier. 35 | * Maximum length: 10. 36 | */ 37 | supplier!: DeserializedType; 38 | /** 39 | * Language key. 40 | * Maximum length: 2. 41 | */ 42 | language!: DeserializedType; 43 | /** 44 | * Text ID. 45 | * Maximum length: 4. 46 | */ 47 | longTextId!: DeserializedType; 48 | /** 49 | * String. 50 | * @nullable 51 | */ 52 | longText?: DeserializedType | null; 53 | 54 | constructor(readonly _entityApi: SupplierTextApi) { 55 | super(_entityApi); 56 | } 57 | } 58 | 59 | export interface SupplierTextType< 60 | T extends DeSerializers = DefaultDeSerializers 61 | > { 62 | supplier: DeserializedType; 63 | language: DeserializedType; 64 | longTextId: DeserializedType; 65 | longText?: DeserializedType | null; 66 | } 67 | -------------------------------------------------------------------------------- /src/api/src/app.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from "@nestjs/testing"; 2 | import { AppController } from "./app.controller"; 3 | import { AppService } from "./app.service"; 4 | 5 | describe("AppController", () => { 6 | let appController: AppController; 7 | 8 | beforeEach(async () => { 9 | const app: TestingModule = await Test.createTestingModule({ 10 | controllers: [AppController], 11 | providers: [AppService], 12 | }).compile(); 13 | 14 | appController = app.get(AppController); 15 | }); 16 | 17 | let result: object = { 18 | title: "SAP Cloud SDK on Azure", 19 | message: "Hello Azure! Greetings from the", 20 | }; 21 | 22 | describe("root", () => { 23 | it("should return result object", async () => { 24 | expect(await appController.getHello()).toMatchObject(result); 25 | }); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /src/api/src/app.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get, Render } from "@nestjs/common"; 2 | import { AppService } from "./app.service"; 3 | 4 | @Controller() 5 | export class AppController { 6 | constructor(private readonly appService: AppService) {} 7 | 8 | @Get() 9 | @Render('index') 10 | async getHello(): Promise { 11 | return await this.appService.getHello(); 12 | } 13 | 14 | @Get('/health') 15 | async getHealthStatus(): Promise { 16 | return await this.appService.checkServiceAvailability(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/api/src/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from "@nestjs/common"; 2 | import { AppController } from "./app.controller"; 3 | import { AppService } from "./app.service"; 4 | import { BusinessPartnerController } from "./business-partner/business-partner.controller"; 5 | import { BusinessPartnerService } from "./business-partner/business-partner.service"; 6 | import { ConfigModule } from "@nestjs/config"; 7 | 8 | @Module({ 9 | imports: [ConfigModule.forRoot()], 10 | controllers: [AppController, BusinessPartnerController], 11 | providers: [AppService, BusinessPartnerService] 12 | }) 13 | export class AppModule {} 14 | -------------------------------------------------------------------------------- /src/api/src/app.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@nestjs/common"; 2 | import { 3 | executeHttpRequestWithOrigin, 4 | HttpResponse, 5 | } from "@sap-cloud-sdk/http-client"; 6 | import { createLogger } from "@sap-cloud-sdk/util"; 7 | 8 | @Injectable() 9 | export class AppService { 10 | async getHello(): Promise { 11 | return { 12 | title: "SAP Cloud SDK on Azure", 13 | message: "Hello Azure! Greetings from the", 14 | }; 15 | } 16 | 17 | /** 18 | * Check service availability using the SAP Cloud SDK http client using HEAD. Typed client doesn't support HEAD. 19 | * See discussion with SAP here: https://github.com/SAP/cloud-sdk-js/issues/3371 20 | * 21 | * https://sap.github.io/cloud-sdk/api/v2/functions/sap_cloud_sdk_http_client.executeHttpRequest.html 22 | */ 23 | async checkServiceAvailability(): Promise { 24 | const logger = createLogger("health"); 25 | 26 | var response: HttpResponse; 27 | var result: object; 28 | var authHeader = { [process.env.APIKEY_HEADERNAME]: process.env.APIKEY }; 29 | 30 | //if odata username and password are set, add basic auth header 31 | if(process.env.ODATA_USERNAME && process.env.ODATA_USERPWD){ 32 | authHeader = { 33 | ...authHeader, 34 | Authorization: `Basic ${Buffer.from( 35 | `${process.env.ODATA_USERNAME}:${process.env.ODATA_USERPWD}`, 36 | ).toString("base64")}` 37 | }; 38 | } 39 | 40 | try { 41 | response = await executeHttpRequestWithOrigin( 42 | { 43 | url: `${process.env.ODATA_URL}/sap/opu/odata/sap/API_BUSINESS_PARTNER`, 44 | }, 45 | { 46 | method: "head", 47 | headers: { 48 | custom: authHeader, 49 | }, 50 | }, 51 | { 52 | fetchCsrfToken: false, 53 | }, 54 | ); 55 | logger.debug(response); 56 | 57 | result = { code: response.status, statusText: response.statusText }; 58 | } catch (err) { 59 | logger.error(err); 60 | 61 | result = { 62 | status: err.response?.status 63 | ? err.response?.status 64 | : "No Status available", 65 | statusText: err.response?.statusText 66 | ? err.response?.statusText 67 | : "No Status Text available", 68 | message: err.message, 69 | cause: err.cause?.message ? err.cause?.message : "No Cause identified", 70 | rootCause: err.rootCause?.message 71 | ? err.rootCause?.message 72 | : "No Root Cause identified", 73 | responseBody: err.rootCause?.response?.data 74 | ? err.rootCause?.response?.data 75 | : "No Response Body available", 76 | }; 77 | } 78 | 79 | return result; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/api/src/business-partner/business-partner.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from "@nestjs/testing"; 2 | import { BusinessPartnerController } from "./business-partner.controller"; 3 | import { BusinessPartnerService } from "./business-partner.service"; 4 | 5 | describe("BusinessPartnerController", () => { 6 | let controller: BusinessPartnerController; 7 | let service: BusinessPartnerService; 8 | 9 | beforeEach(async () => { 10 | const module: TestingModule = await Test.createTestingModule({ 11 | controllers: [BusinessPartnerController], 12 | providers: [BusinessPartnerService], 13 | }).compile(); 14 | 15 | controller = module.get( 16 | BusinessPartnerController, 17 | ); 18 | }); 19 | 20 | it("should be defined", () => { 21 | expect(controller).toBeDefined(); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /src/api/src/business-partner/business-partner.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from "@nestjs/testing"; 2 | import { BusinessPartnerService } from "./business-partner.service"; 3 | 4 | describe("BusinessPartnerService", () => { 5 | let service: BusinessPartnerService; 6 | 7 | beforeEach(async () => { 8 | const module: TestingModule = await Test.createTestingModule({ 9 | providers: [BusinessPartnerService], 10 | }).compile(); 11 | 12 | service = module.get(BusinessPartnerService); 13 | }); 14 | 15 | it("should be defined", () => { 16 | expect(service).toBeDefined(); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /src/api/src/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from "@nestjs/core"; 2 | import { NestExpressApplication } from "@nestjs/platform-express"; 3 | import { resolve } from "path"; 4 | import { AppModule } from "./app.module"; 5 | 6 | async function bootstrap() { 7 | const app = await NestFactory.create(AppModule); 8 | app.useStaticAssets(resolve("./src/public")); 9 | app.setBaseViewsDir(resolve("./src/views")); 10 | app.setViewEngine("pug"); 11 | 12 | await app.listen(process.env.PORT || 8080, "0.0.0.0"); 13 | } 14 | bootstrap(); 15 | -------------------------------------------------------------------------------- /src/api/src/public/github-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/api/src/public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: white; 3 | font-size: large; 4 | font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; 5 | color: black; 6 | } 7 | 8 | footer { 9 | position: fixed; 10 | left: 0; 11 | bottom: 0; 12 | width: 100%; 13 | background-color: white; 14 | color: white; 15 | text-align: left; 16 | } 17 | 18 | a:link { text-decoration: none; } 19 | 20 | a:hover { text-decoration: underline; } 21 | 22 | p.mynote { 23 | font-size: small; 24 | font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; 25 | color: grey; 26 | } -------------------------------------------------------------------------------- /src/api/src/views/bupa.pug: -------------------------------------------------------------------------------- 1 | html 2 | head 3 | title= title 4 | link(rel='stylesheet' href='/stylesheets/style.css') 5 | link(href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous") 6 | script(src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous") 7 | body(class='background') 8 | button(type="button" class="btn btn-info" onclick="location.href='/'") 🔙 9 | table(class='table table-striped table-bordered table-hover') 10 | thead 11 | tr 12 | th # 13 | th ID 14 | th First Name 15 | th Last Name 16 | th Address ID 17 | tbody 18 | each val, index in bupas 19 | tr 20 | td #{index + 1} 21 | td #{val.businessPartner} 22 | td #{val.firstName} 23 | td #{val.lastName} 24 | if val.toBusinessPartnerAddress[0] 25 | td #{val.toBusinessPartnerAddress[0].addressId} 26 | else 27 | td - -------------------------------------------------------------------------------- /src/api/src/views/index.pug: -------------------------------------------------------------------------------- 1 | html 2 | head 3 | title= title 4 | link(rel='stylesheet' href='/stylesheets/style.css') 5 | body(class='background') 6 | nav 7 | navbar-default 8 | div 9 | h1= message 10 | img(src='/sap-cloud-sdk-logo.svg' alt='SAP Cloud SDK Logo' style='width:150px;') 11 | ul 12 | li 13 | a(href='/') 🛋️Home 14 | li 15 | a(href='/business-partner') 🤝Business-Partners 16 | li 17 | a(href='/health') 💓OData service health-endpoint 18 | li 19 | a(href='http://localhost:3000/sap/opu/odata/sap/API_BUSINESS_PARTNER/A_BusinessPartner?$top=10') 🧪mock-server* (API_BUSINESS_PARTNER) 20 | br 21 | a(href='https://raw.githubusercontent.com/Azure-Samples/app-service-javascript-sap-cloud-sdk-quickstart/main/sample-http-requests/SAP-Cloud-SDK-on-Azure-App-Service-Quickstart.postman_collection.json') 📌Postman collection 22 | br 23 | a(href='https://raw.githubusercontent.com/Azure-Samples/app-service-javascript-sap-cloud-sdk-quickstart/main/sample-http-requests/business-partner-requests.http') 📔VSCode REST Client sample requests 24 | br 25 | p(class='mynote') *mock-server is a separate service provided by SAP as sample running on port 3000 by default. Learn more 26 | a(href='https://sap.github.io/cloud-s4-sdk-book/pages/mock-odata.html') here. 27 | br 28 | p(class='mynote') Be aware that OData service credential or APIKey maintenance on KeyVault after initial web app deployment takes some time to come into effect! 29 | footer(class='footer') 30 | a(href='https://github.com/Azure-Samples/app-service-javascript-sap-cloud-sdk-quickstart') 31 | img(src='/github-logo.svg' alt='GitHub Logo' style='width:30px;padding-left:10px;padding-bottom:5px;') -------------------------------------------------------------------------------- /src/api/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /src/api/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "removeComments": true, 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "allowSyntheticDefaultImports": true, 9 | "target": "ES2021", 10 | "sourceMap": true, 11 | "outDir": "./dist", 12 | "baseUrl": "./", 13 | "incremental": true, 14 | "skipLibCheck": true, 15 | "strictNullChecks": false, 16 | "noImplicitAny": false, 17 | "strictBindCallApply": false, 18 | "forceConsistentCasingInFileNames": false, 19 | "noFallthroughCasesInSwitch": false, 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /templates/.env: -------------------------------------------------------------------------------- 1 | # MOVE ME TO ROOT OR ADJUST CONFIG LOAD AND DONT CHECK ME IN! 2 | # For SAP APIM get key from https://api.sap.com/api/API_BUSINESS_PARTNER/tryout "Show API Key" button | 3 | # For Azure APIM get key from APIM via https://portal.azure.com/ > API Management > API > Subscriptions > Primary Key ... Click Show Keys 4 | APIKEY='YOUR_API_KEY' 5 | # SAP APIM key identifier = APIKey | 6 | # Azure APIM key identifier = Ocp-Apim-Subscription-Key (consider adding trace flag Ocp-Apim-Trace=true to see the log reference in the response header) 7 | APIKEY_HEADERNAME='APIKey' 8 | API_TRACE_HEADERNAME='' 9 | API_TRACE_ACTIVE='' 10 | # enrich error messages with stack trace 11 | DEBUG=false 12 | # SAP Mock Server URL http://localhost:3000/; https://sandbox.api.sap.com/s4hanacloud; 13 | ODATA_URL=https://sandbox.api.sap.com/s4hanacloud 14 | # Maintain only for Basic Authentication. Keep empty for usage with SAP API Business Hub Sandbox! 15 | ODATA_USERNAME= 16 | ODATA_USERPWD= 17 | #Ignore Entra ID token for API key only requests. DON'T USE IN PRODUCTION! 18 | IGNORE_ENTRA_ID_TOKEN=true -------------------------------------------------------------------------------- /templates/azuredeploy.parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "webAppName": { 6 | "value": "GEN-UNIQUE" 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /test/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { INestApplication } from '@nestjs/common'; 3 | import * as request from 'supertest'; 4 | import { AppModule } from './../src/app.module'; 5 | 6 | describe('AppController (e2e)', () => { 7 | let app: INestApplication; 8 | 9 | beforeEach(async () => { 10 | const moduleFixture: TestingModule = await Test.createTestingModule({ 11 | imports: [AppModule], 12 | }).compile(); 13 | 14 | app = moduleFixture.createNestApplication(); 15 | await app.init(); 16 | }); 17 | 18 | it('/ (GET)', () => { 19 | return request(app.getHttpServer()) 20 | .get('/') 21 | .expect(200) 22 | .expect('Hello World!'); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /test/jest-e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "moduleFileExtensions": ["js", "json", "ts"], 3 | "rootDir": ".", 4 | "testEnvironment": "node", 5 | "testRegex": ".e2e-spec.ts$", 6 | "transform": { 7 | "^.+\\.(t|j)s$": "ts-jest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /workspaces/azd-bicep.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "../", 5 | "name": "SAP Cloud SDK on Azure App Service Quickstart - Bicep" 6 | } 7 | ], 8 | "settings": { 9 | "files.exclude": { 10 | "infra-terraform": true, 11 | "infra-btpsa": true, 12 | "workspaces": true, 13 | ".devcontainer/btpsa": true, 14 | ".devcontainer/azd-terraform": true 15 | }, 16 | "debug.internalConsoleOptions": "neverOpen" 17 | } 18 | } -------------------------------------------------------------------------------- /workspaces/azd-terraform.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "../", 5 | "name": "SAP Cloud SDK on Azure App Service Quickstart - Terraform" 6 | } 7 | ], 8 | "settings": { 9 | "files.exclude": { 10 | "infra": true, 11 | "infra-btpsa": true, 12 | "workspaces": true, 13 | ".devcontainer/azd-bicep": true, 14 | ".devcontainer/btpsa": true 15 | }, 16 | "debug.internalConsoleOptions": "neverOpen" 17 | } 18 | } -------------------------------------------------------------------------------- /workspaces/btpsa.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "../", 5 | "name": "SAP Cloud SDK Quickstart - SAP BTP" 6 | } 7 | ], 8 | "settings": { 9 | "files.exclude": { 10 | "infra": true, 11 | "infra-terraform": true, 12 | "workspaces": true, 13 | "azure.yaml": true, 14 | ".azdo": true, 15 | ".github": true, 16 | ".devcontainer/azd-bicep": true, 17 | ".devcontainer/azd-terraform": true 18 | }, 19 | "debug.internalConsoleOptions": "neverOpen" 20 | } 21 | } --------------------------------------------------------------------------------