Caderno de estudos — SAP BTP na prática
Mantido durante nossa sessão de aprendizado. Cada aula tem: conceito, comando e lição.
Aula 1 — Subir ferramentas para a nuvem da SAP
Como funciona a "conta" do SAP BTP
| Conceito | O que é | Onde vimos |
| Global account | A conta-mãe (contrato), agrupa tudo | 0494f001trial (trial) |
| Subaccount | "Ambiente/projeto" dentro da global account | trial (ID b3161ad1-...) |
| Entitlement | O direito de usar um serviço (o que você pode) | btp list accounts/entitlement |
| Service offering / plan | Serviço que se instancia e faz bind num app | hana, xsuaa, document-translation |
| Subscription | Ativação de uma aplicação SaaS (ganha URL) | build-code (SAP Build Code) |
| Target | "Onde" os comandos do btp atuam | btp target --subaccount ... |
Anatomia de um comando do btp CLI
btp <ação> <grupo>/<objeto> --parâmetros
Exemplos:
btp list accounts/entitlement # listar direitos
btp list accounts/subscription # listar aplicações assinadas
btp subscribe accounts/subaccount --to-app build-code --plan free
btp target --subaccount <ID> # definir onde os comandos atuam
Regra de ouro: na dúvida, btp help, btp help <ação>, btp help <ação> <objeto>.
Ferramentas instaladas nesta máquina
| Ferramenta | Papel |
btp | Administração da conta (subscrições, serviços, entitlements) |
cf (+ plugin multiapps) | Deploy de aplicações no Cloud Foundry |
| Node.js / npm | Runtime para CAP |
cds (@sap/cds-dk) | Framework CAP: modelo, serviço, build |
mbt | Empacotar tudo num arquivo .mtar |
git | Controle de versão |
O ciclo de desenvolvimento (o caminho do código)
cds watch → develop local (http://localhost:4004)
mbt build → empacota (mta_archives/*.mtar)
cf deploy *.mtar → envia e sobe no Cloud Foundry
cf logs <app> → ver logs de execução
Aula bônus: inspecionar JSON sem adivinhar
btp --format json list accounts/subscription > subs.json
jq 'type' subs.json # object? array?
jq 'keys' subs.json # ["applications"]
jq '.applications[] | select(.appName=="build-code") | {state, subscriptionUrl}' subs.json
Lição que aprendemos na prática: usei .status, mas o campo real era .state.
Resultado: null 15 vezes. Nunca confie num nome de campo sem inspecionar.
Aula 2 — IA no BTP: onde cada coisa mora
| Serviço | O que faz | Disponível no trial? |
| SAP AI Core | Motor para rodar modelos de IA | ❌ (precisa Free Tier / GenAI Hub Trial / conta corporativa) |
| Generative AI Hub | LLMs da SAP (GPT, Gemini, Claude...) via AI Core | ❌ (mesma situação) |
| Joule for developers | Copiloto de programação dentro do SAP Build Code | ✅ (assinamos o build-code free) |
| Joule Skills | Criar skills para a Joule dos usuários finais | ❌ (Joule Studio é pago) |
| Document Information Extraction | IA de extração de dados de documentos | ✅ |
| Document Translation | Tradução de documentos | ✅ |
Nosso exemplo prático: ~/projects/ai-lab
App CAP + UI5 com adaptador de IA que escolhe o provedor automaticamente:
- 1. SAP AI Core (quando existir binding
aicoreouAICORE_SERVICE_KEY) - 2. LLM externo compatível com OpenAI (quando houver
LLM_API_KEY) - 3. mock (fallback — funciona sempre, para desenvolvimento)
Arquivos-chave:
db/schema.cds modelo de dados (Interactions)
srv/ai-service.cds contrato do serviço OData
srv/ai-service.js lógica (handler da action ask)
srv/lib/ai-provider.js adaptador de provedores de IA
app/ui/ interface UI5
mta.yaml receita de deploy (srv + approuter + xsuaa)
Comandos úteis do dia a dia (no projeto)
cd ~/projects/ai-lab
npx cds watch # rodar local
curl 'http://localhost:4004/api/ai/provider()'
mbt build && cf deploy mta_archives/ai-lab_1.0.0.mtar -f
Aula 4 — Autenticação × Autorização (o famoso "Access denied")
| Etapa | Pergunta | Ferramenta |
| Autenticação | "Você é quem diz ser?" | Login (SAP ID) → gera um token JWT |
| Autorização | "Você tem permissão para isso?" | Roles contidas no token |
Regras aprendidas:
- 1. Assinar uma aplicação não dá permissão a ninguém — é preciso atribuir role collections.
- 2. O token JWT fica "congelado" no navegador por horas: depois de mudar roles, faça login de novo (ou use janela anônima).
Comandos usados nesta aula
btp help security # descobrir objetos de segurança
btp list security/role-collection --subaccount <ID> # listar conjuntos de permissões
btp help assign security/role-collection # conferir a sintaxe
btp assign security/role-collection "Build Code - Lobby Admin" \
--to-user [email protected] --subaccount <ID>
Roles do SAP Build Code (encontradas na prática)
| Role collection | Para que serve |
Build Code - Lobby Admin | Acessar o lobby como administrador |
Build Code - Lobby Developer | Acessar o lobby como desenvolvedor |
Aula 5 — Identity Providers (IDP) e Boosters
O caso do "Access Denied" no SAP Build Lobby
Sintoma: assinatura OK + roles atribuídas, mas o lobby responde:
Access Denied — You need permission to access this page. Request access from your administrator.
Causa (encontrada na comunidade SAP): em contas trial, o SAP Build usa um
Custom Identity Provider (IDP) — um "diretório de logins" próprio do serviço.
Se você entra com o IDP padrão (sap.default, ou seja, o SAP ID), a aplicação
não te reconhece, mesmo com as role collections atribuídas.
Conceito: Identity Provider (IDP)
| Termo | O que é |
| IDP | Quem guarda os logins e prova "quem você é" |
Default IDP (sap.default) | O SAP ID (accounts.sap.com) |
| Custom IDP | IDP próprio do tenant/serviço (criado pelo trial para o SAP Build) |
| Trust | A configuração de confiança entre o subaccount e o IDP |
Conceito: Booster
Um assistente guiado do Cockpit que executa a configuração de ponta a ponta:
- valida pré-requisitos e denuncia o que falta
- cria/atualiza assinaturas, roles e trusts
- mostra somente os passos relevantes para o seu cenário
Ação: rodar o booster "Get Started with SAP Build Code"
(Global Account → menu lateral → Boosters).
Lições de investigação (aplicadas nesta aula)
- 1. A mensagem de erro é pista, não obstáculo: ela mandou olhar a "Configuração inicial".
- 2. Documentação oficial (help.sap.com / learning.sap.com) antes de tentar adivinhar.
- 3. Comunidade SAP é uma fonte de verdade validada (solução aceita com selo de especialista).
- 4. Saída truncada engana:
head -30cortou roles e me levou a atribuir as erradas.
Aula 6 — Sessões expiram (e explicam sintomas estranhos)
login → TOKEN de acesso (curto) + TOKEN de renovação (refresh)
→ quando ambos vencem, é preciso logar de novo
Vimos na prática: btp get accounts/global-account falhou com "Your login has expired"
depois de ~4h. Solução: relogar (btp login --sso). Sessões velhas no navegador também
causam erros de autorização ("Access denied", "no global account").
Aula 7 — Identificadores: use a fonte autoritativa
| Objeto | ID correto (CLI) | IDs "errados" que apareceram |
| Global account | 9b6aad2f-4bf4-47fb-869f-5546df9e28fd | 698a0962-... (veio do JSON de assinatura) |
| Subaccount | b3161ad1-354c-405e-a039-c47d410d83b8 | — |
Lição: o mesmo objeto pode aparecer com IDs diferentes em contextos diferentes
(comercial × técnico). Sempre confirme com btp get accounts/global-account.
Como saber se o problema é conta ou sessão
btp get accounts/global-account # a conta existe? qual o ID?
btp list security/user --global-account <subdomain> # quem são os membros?
Se o CLI vê tudo (conta + usuário), mas o navegador não → suspeite da sessão do navegador,
não da conta. Teste rápido: janela anônima.
Aula 8 — Contas trial têm portal de entrada próprio
Sintoma: o cockpit padrão (cockpit.btp.cloud.sap) responde
"Não foi possível encontrar nenhuma conta global associado ao seu usuário" —
mesmo sendo membro da conta, como o CLI comprova.
Causa: contas trial são acessadas por um portal de entrada específico:
https://account.hanatrial.ondemand.com/ → "Go To Your Trial Account"
De lá o cockpit abre no contexto correto (região + IDP do trial).
Entrar pelo cockpit "normal" é o que produz o erro.
Setup correto do SAP Build Code (tutorial oficial)
- 1. Entrar pelo portal do trial (
account.hanatrial.ondemand.com) - 2. (Opcional, conforme tutorial) desassinar o SAP Business Application Studio
- 3. Cockpit → Boosters → "Get Started with SAP Build Code" → Start
- 4. O booster:
- valida autorizações, provedor e região
- atribui as roles necessárias ao usuário no IDP padrão
- cria/verifica as assinaturas
- 5. Se você também usar um IDP customizado (ex.: por causa do SAP Build Apps),
pode ser preciso adicionar também as roles Build Code Administrator / Build Code Developer
Nota oficial: a versão trial do SAP Build Code só está disponível na região US10.
Aula 9 — Cockpits são REGIONAIS (a causa do "nenhuma conta global")
| URL | Resultado real |
https://account.hanatrial.ondemand.com/ (doc antiga) | ❌ 403 — portal desativado |
https://cockpit.btp.cloud.sap/cockpit/?region=trial | ✅ 302 → amer.cockpit.btp.cloud.sap/cockpit/?region=trial |
https://amer.cockpit.btp.cloud.sap/cockpit/?region=trial | ✅ 200 — cockpit correto para trial US10 |
https://cockpit.btp.cloud.sap/ (sem parâmetro) | ❌ não encontra contas trial |
Conclusão: o cockpit é um app por região, e contas trial US10 vivem no cockpit
amer. Sem o ?region=trial, a mensagem é "nenhuma conta global associada ao seu usuário"
— mesmo com a conta existindo e o CLI a enxergando perfeitamente.
Como diagnosticar de verdade (em vez de adivinhar)
curl -sS -o /dev/null -w '%{http_code} -> %{redirect_url}\n' -I "<URL>"
getent hosts <host> # ver DNS / para onde resolve
Esses dois comandos provaram: portal antigo morto (403) e redirecionamento regional (302).
Aula 10 — O portal do TRIAL e os "Feature Sets" do cockpit
Descoberta: existe um portal específico de trial
https://account.hanatrial.ondemand.com/cockpit
→ 302 → https://account.hanatrial.ondemand.com/trial/#/home/trial ← portal do trial
O tutorial do Build Code dizia exatamente isso: *"Access your global account and click
Go To Your Trial Account"*. Era este o endereço.
Lição de diagnóstico: curl sem User-Agent leva 403
curl -I https://account.hanatrial.ondemand.com/ # 403 (parece morto)
curl -A "Mozilla/5.0 ... Chrome/140" https://account.hanatrial... # 302 (vivo!)
Muitos sites bloqueiam curl puro (WAF). **Sempre repita o teste com -A (User-Agent)
e com GET** antes de concluir que algo não existe.
Conceito: Feature Sets do cockpit
Existem duas versões do cockpit SAP BTP:
| Feature Set | Contexto | Observação |
| A | conta antigas/Neo e trials antigos | URL account.*/cockpit |
| B | contas modernas | cockpit.btp.cloud.sap (regional: amer/emea/apac) |
As próprias mensagens internas do cockpit dizem:
*"To see those accounts, you need to go to a version of the SAP BTP cockpit that supports
feature set A"* — ou seja, o cockpit "normal" não lista contas que vivem no outro feature set.
Isso explica o erro recorrente "nenhuma conta global associada ao seu usuário".
Aula 11 — SAP Build Code: assinatura, booster e roles
# Assinar o Build Code (plano free)
btp subscribe accounts/subaccount --subaccount <ID> --to-app build-code --plan free
Ver o que foi criado
btp list security/role-collection --subaccount <ID> | grep -i 'build code'
O "Access denied" no Lobby não era falta de role: era o setup incompleto.
O booster "Get Started with SAP Build Code" (Aceleradores, no cockpit do trial)
executa em sequência:
- 1. Atribuir quotas de serviço
- 2. Assinar aplicativos SaaS
- 3. Criar instâncias de serviço
- 4. Criar role collection (ex.:
Build Code Administrator,Build Code Developer) - 5. Atribuir role collection ao usuário
Lição: dependendo do estado da conta, rodar o booster duas vezes faz diferença —
a primeira rodada pode pular etapas que a segunda executa.
Aula 12 — BAS × Build Code (planos concorrentes)
| Produto | Papel |
| SAP Business Application Studio (BAS) | IDE da SAP (sem Joule) |
| SAP Build Code | BAS + Joule + ferramentas (plano build-code) |
Mensagem da Joule: "upgrade to the build-code plan" → o dev space precisa ser provido
pelo plano do Build Code. O tutorial oficial manda desassinar o BAS antes de rodar o booster.
Estado ao final desta sessão
| Item | Situação |
| Cobrança de assinatura | build-code (free) + sapappstudiotrial (trial) assinados |
| Roles | Build Code Administrator/Developer criadas e atribuídas ✅ |
| Lobby | abre normalmente ✅ |
Projeto ai-joule-lab | criado no lobby ✅ |
Dev space (ws-* no CF) | ❌ não provisionado → editor/Joule ainda não abre |
| Navegador operado por | xdotool (cliques/teclado) + ffmpeg (screenshots) |
Truques de automação usados (úteis para qualquer troubleshooting)
xdotool windowactivate <ID> && xdotool mousemove X Y click 1 # operar a tela
ffmpeg -f x11grab -video_size 1920x1020 -i :0.0+0,0 -frames:v 1 tela.png
curl -A "Mozilla/5.0 ..." https://site # sites bloqueiam curl sem User-Agent
Aula 13 — Dev space "Interrompido" e automação em segundo plano
O problema do "Interrompido"
Ao desinscrever o BAS, o dev space antigo (Full_Stack, criado sob o plano BAS) ficou
"Interrompido" — e o projeto apontava para ele. Solução: ao criar um projeto novo,
escolher Área do desenvolvedor: Novo (criar um dev space limpo no plano build-code).
Resultado: ws-0msl7 criado ✅ e a IDE abriu com o projeto ai-joule-lab2.
Técnica: operar o navegador em SEGUNDO PLANO (sem roubar foco)
- 1. Subir um Chrome separado com CDP (protocolo de depuração):
google-chrome --headless=new --remote-debugging-port=9222 \
--user-data-dir=/tmp/opencode/cdp-profile \
--disable-background-timer-throttling --disable-renderer-backgrounding
- 2. Copiar os cookies do perfil real (
Default/Cookies+Local State) para o novo perfil
→ a sessão já vem logada, sem precisar de senha!
- 3. Controlar via WebSocket + CDP (Node puro, sem dependências):
- Page.navigate, Page.captureScreenshot
- Input.dispatchMouseEvent, Input.dispatchKeyEvent, Input.insertText
- Runtime.evaluate (rodar JS na página)
Bônus: código SSO do Cloud Foundry automatizado
O código temporário do cf login --sso pode ser lido da própria página de passcode:
// navegar para https://login.cf.us10-001.hana.ondemand.com/passcode
// clicar na conta e ler: document.body.innerText
cf login -a https://api.cf.us10-001.hana.ondemand.com --sso-passcode <CODIGO>
Limite encontrado
O workbench do BAS/Build Code roda dentro de iframes que **não aceitam eventos
sintéticos (CDP). Para usar a Joule**, o último passo (abrir o painel dela) deve ser
feito no navegador normal:
Ctrl+Shift+P → Joule: Focus on Joule View
Aula 14 — Joule funcionando! 🎉
Estado final da trilha:
| Item | Status |
| SAP Build Code assinado (free) | ✅ |
Roles (Build Code Administrator/Developer) | ✅ |
Dev space novo (Build Code plan) | ✅ ws-0msl7 |
| Projeto no Storyboard | ✅ ai-joule-lab2 |
| Joule aberta na IDE | ✅ ("Hi! I'm Joule, your digital assistant") |
Comandos que a Joule oferece no trial:
/code-search— busca no repositório/fiori-gen-cap-ui— gera UI Fiori Elements a partir do Storyboard/fiori-gen-spec-app— gera app Fiori Elements/sap-help— busca no Help Portal/ui5-create-app— cria app SAPUI5 do zero
Primeiro prompt sugerido (para colar na Joule)
Create a CAP data model for a purchase order application with entities
PurchaseOrder and PurchaseOrderItem, and expose both in a service
Limite técnico importante (documentado para o futuro)
O workbench do SAP Build Code/BAS (VS Code web dentro de iframes):
- ❌ não aceita cliques/teclas sintéticas via
xdotool(XTEST) - ❌ não aceita eventos
Input.dispatchMouseEvent/dispatchKeyEventvia CDP
(testado com Emulation.setFocusEmulationEnabled e sem override de viewport)
→ Para usar a Joule, o último passo é sempre humano: digitar/colar no painel.
A automação cobre todo o resto (lobby, criação de projeto, dev space, login SSO).
Técnicas que ficaram no repertório
| Técnica | Para que serve | |
| Chrome headless + CDP + cookies copiados | Operar sites logados em segundo plano | |
curl -A "<UA de navegador>" | Testar sites que bloqueiam curl | |
| `btp --format json ... \ | jq` | Automatizar o btp CLI |
| Ler o código SSO da página de passcode | cf login --sso-passcode sem intervenção |
Pendências / próximos passos
- [ ] Usar a Joule no SAP Build Code para criar um projeto de exemplo (em andamento)
- [ ] Persistência real: criar SAP HANA Cloud e rodar
cds add hana - [ ] Habilitar CSRF no approuter antes de uso sério
- [ ] Configurar um provedor de IA real (chave de LLM ou AI Core)
Aula 15 — Copiar texto do OpenCode (TUI) sem travar
Sintoma: ao arrastar o mouse para selecionar um texto no OpenCode, a tela parece
travada e só volta dando Ctrl+C (que fecha o app!).
Causa: o OpenCode (TUI) usa captura de mouse (mouse: true por padrão). Os
eventos de arrastar vão para o app em vez de selecionar no terminal. E Ctrl+C é o
atalho de sair (app.exit).
Soluções:
| Solução | Como |
| Shift + arrastar | Seleção nativa do terminal (fura a captura) + Ctrl+Shift+C para copiar |
| Desligar a captura (aplicado) | ~/.config/opencode/cli.json → {"mouse": false} |
| Cópia automática ao selecionar | "terminal": {"copy": "select"} |
| Copiar mensagem pelo próprio app | <leader>y (padrão: Ctrl+X depois Y) |
| Alternativa infalível | Copiar do arquivo exportado em chats/ (aberto no VS Code) |
Config final aplicada nesta máquina (a que funciona bem):
```json title="~/.config/opencode/cli.json"
{
"$schema": "https://opencode.ai/v2/cli.json",
"mouse": true,
"terminal": { "copy": "select" }
}
> Por que mouse: true (e não false)? O OpenCode roda em tela cheia
> ("alternate screen") — sem a captura de mouse, a rolagem para de funcionar e não
> há scrollback do terminal para usar. Então o melhor equilíbrio é:
> manter o mouse ligado (rolagem/scroll normal) e copiar de outro jeito:
Como copiar Passos
Copiar mensagem (recomendado) Ctrl+X e depois Y (messages.copy) — sem mouse!
Copiar várias mensagens / sessão Ctrl+X e depois X (session.export) → abre num editor
Seleção com mouse Segure Shift enquanto arrasta (fura a captura do app) → copia ao soltar
Alternativa infalível Arquivo exportado em chats/ aberto no VS Code
Rolagem com teclado (sempre funciona): PageUp/PageDown, Ctrl+Alt+U/Ctrl+Alt+D
(meia página), Ctrl+G (início), Ctrl+Alt+G (fim), Ctrl+X + G (linha do tempo).
Aula 16 — 🎉 O fluxo completo: Storyboard → Joule → UI Fiori
Resultado da sessão: a Joule gerou um app SAP Fiori Elements (List Report + Object Page)
a partir do Storyboard! 🏆
O fluxo canônico (decorar!)
- 1) Modelo de dados → db/schema.cds (namespace + entity)
- 2) Serviço → srv/service.cds (service + projeção)
- 3) Storyboard → atualiza sozinho ao salvar os CDS
- 4) Joule → Ctrl+Shift+P → "Joule: Focus on Joule View"
- 5) Comando → /fiori-gen-cap-ui <descrição do que gerar>
Detalhes que fazem diferença
| Detalhe | Explicação |
| O comando precisa de descrição | /fiori-gen-cap-ui sozinho → "Type a prompt to get started". Ex.: /fiori-gen-cap-ui Generate a SAP Fiori Elements list report for PurchaseOrders |
| O Storyboard lê os arquivos | Salvar db/schema.cds + srv/service.cds já atualiza o desenho |
| Terminal da IDE é o canivete | Ctrl+Shift+P → Terminal: Create New Terminal → cria/verifica arquivos |
| Page Map | Depois de gerar, a Joule abre o Page Map (List Report → Object Page) para edição visual |
| Arquivos gerados | app/<appname>/webapp/... (i18n, manifest, journeys de teste) |
Criar arquivos CDS pelo terminal (exemplo usado)
bashcat > ~/projects/ai-joule-lab2/db/schema.cds << 'CDSEOF'
namespace ai.joule.lab;
entity PurchaseOrder {
key ID : UUID;
orderNumber : String(20);
supplier : String(100);
amount : Decimal(15, 2);
status : String(20);
orderDate : Date;
}
CDSEOF
cat > ~/projects/ai-joule-lab2/srv/service.cds << 'CDSEOF'
using { ai.joule.lab as db } from '../db/schema';
service PurchaseService {
entity PurchaseOrders as projection on db.PurchaseOrder;
}
CDSEOF
Automação da IDE (lições práticas)
xdotool funciona na IDE do SAP — desde que:
1. usar as coordenadas da tela (a barra do Chrome ocupa ~104 px no topo);
2. minimizar o OpenCode/VS Code antes (senão o foco vai para eles!);
3. windowactivate --sync + windowraise na janela do Chrome.
- OpenCode:
Ctrl+S suspende a saída (XON/XOFF) e Ctrl+Q retoma — se "congelar", tente isso.
Aula 17 — Rodando o app gerado pela Joule (no dev space)
bash
cd ~/projects/<projeto>
npm install
npm run watch-<appname> # ex.: watch-purchaseorders (script já vem do Build Code)
```
| Detalhe | Explicação |
| Rodar em background | setsid npm run watch-... < /dev/null > /tmp/cds.log 2>&1 & — sem setsid/< /dev/null o processo morre com EBADF: read quando o comando do terminal termina |
| URL do app | O dev space publica a porta sozinho: port4004-workspaces-<dspace>.us10.trial.applicationstudio.cloud.sap/<app>/index.html |
| List Report com busca | A tabela só carrega ao clicar em "Iniciar" (Go) |
| Dados de exemplo | db/data/<namespace>-<Entity>.csv (ex.: ai.joule.lab-PurchaseOrder.csv) — carregados ao reiniciar o cds watch |
| Conferir o servidor | tail -f /tmp/cds.log → mostra Server v10.x launched, requests OData etc. |
Acesso "mais inteligente" ao dev space (o que existe × o que não)
- ❌ API REST da BAS (
/api/v1/devSpaces): exige service instance (build-code/sapappstudio) — não existe no trial - ❌
cf ssh: o dev space não é app CF na nossa org (roda em infra própria da BAS) - ✅ Ponte via GitHub: o terminal do dev space tem internet → versionar o código lá e
trabalhar localmente (CLI/API/Coolify) é o caminho pragmático
Dica de automação (xdotool na IDE)
- Sempre minimizar/afastar o OpenCode antes de automatizar (ele rouba o foco!)
- Trocar de aba do Chrome: clicar na aba certa ou abrir uma nova com a URL desejada
- Interagir com a página do app é igual a qualquer site: cliques por coordenada da tela