Skip to content

Repository files navigation

salesforce-scripts

Toolkit de scripts Python para operações em massa no Salesforce:

  • sf-jwt/ — gera certificado/chave RSA e monta um JWT assinado para o JWT Bearer Flow.
  • bulk-api/ — consulta (query.py) e grava em massa (update.py) qualquer objeto do Salesforce via Bulk API 2.0, autenticado via Client Credentials Flow.
  • split-large-csv/ — divide um CSV grande em N arquivos menores, prontos para alimentar o bulk-api.
  • sf-activate-bot-api/ — localiza um Einstein Bot pelo DeveloperName e ativa a BotVersion mais recente via REST API, autenticado via Client Credentials Flow.
  • sf-waba-mig/ — gera Messaging Components v2 a partir dos XMLs v1, atualizando o templateVersionIdentifier de cada <externalTemplates> com base num JSON de templates do WhatsApp (WABA).
  • sf-csv-update/ — divide um CSV grande e faz insert/update em massa via Bulk API 2.0, detectando objeto e operação direto do próprio CSV (sem configurar SF_OBJECT).
  • sf-bot-intent-flag/ — ativa/desativa a flag "Use first message intent recognition" (BotVersion.initialIntentDetectionEnabled) via Salesforce CLI (retrieve → edita XML → deploy), sem passar pela UI.

Cada pasta tem sua própria venv, requirements.txt e .env (gerado a partir do .env.example de cada uma) — são independentes entre si (exceto bulk-api, que unifica query e update num único ambiente, já que compartilham autenticação e formato de configuração).


⚠️ Segurança — leia antes de tudo

Este repositório é público. Nunca commite: venv/, .env, *.key, *.crt, csv/, logs/, output/. Cada pasta tem seu próprio .gitignore, e agora também existe um .gitignore na raiz cobrindo esses padrões globalmente — isso corrige um problema real: um .env chegou a ser commitado na raiz do repo porque não havia proteção nesse nível (só dentro das subpastas). Nenhuma credencial vazou nesse incidente (o .env em questão só tinha parâmetros de query, sem client secret), mas o princípio vale: a partir de agora, rode os scripts sempre de dentro da pasta do projeto, nunca da raiz.

Antes de qualquer git push, confirme que nada sensível está rastreado:

git ls-files | grep -E "\.key$|\.crt$|\.env$|venv/"

Se não retornar nada, está seguro. Se algo já foi commitado por engano, remova do tracking (git rm --cached arquivo) e, se era credencial de verdade (não é o caso aqui), revogue e gere uma nova.


sf-jwt

Estrutura

sf-jwt/
├── .gitignore
├── gen_cert.py      # gera salesforce.key + connectedAppCertificate.crt
├── gen_jwt.py        # monta o JWT, assina, e troca pelo access token via /services/oauth2/token
├── requirements.txt
└── setup.sh          # cria venv, requirements.txt e .env

# gerados localmente, não versionados:
├── venv/
├── .env
├── salesforce.key
└── connectedAppCertificate.crt

Pré-requisitos

  • Python 3.9+
  • Connected App no Salesforce com "Use digital signatures" (usando o connectedAppCertificate.crt gerado aqui) e "Enable OAuth Settings" habilitados, com o usuário SF_USERNAME Pre-Authorized.

Setup e uso

cd sf-jwt
chmod +x setup.sh
./setup.sh

source venv/bin/activate
python gen_cert.py
# -> faça upload de connectedAppCertificate.crt na Connected App

# edite o .env com SF_CONSUMER_KEY e SF_USERNAME
python gen_jwt.py
# -> imprime access_token e instance_url, prontos para uso

python gen_jwt.py --assertion-only
# -> so monta e imprime o JWT assinado, sem chamar o Salesforce
#    (util quando outro processo/ferramenta vai trocar pelo token)

bulk-api

Unifica os antigos bulkApi_Query e bulkApi_Update numa pasta só — eles compartilhavam o mesmo auth.py (Client Credentials Flow) e o mesmo objeto-alvo na prática, então faz mais sentido um único venv/.env/setup.

Estrutura

bulk-api/
├── .gitignore
├── auth.py                # autenticação (Client Credentials Flow) — compartilhado
├── configure_query.py      # wizard interativo: objeto, campos, WHERE, ORDER BY, LIMIT
├── configure_update.py     # wizard interativo: objeto, operação, external ID field
├── field_mapping.py        # mapeia colunas do CSV -> campos reais da API (via Describe)
├── query.py                 # executa a query (Bulk API 2.0 - jobs/query)
├── update.py                 # executa insert/update/upsert/delete (Bulk API 2.0 - jobs/ingest)
├── run_query.py              # configure_query.py + query.py num comando só
├── run_update.py             # configure_update.py + field_mapping.py (opcional) + update.py
├── requirements.txt
└── setup.sh

# gerados localmente, não versionados:
├── venv/
├── .env
├── csv/                     # CSVs de entrada pro update (ex: vindos do split-large-csv)
├── output/                  # CSV de saída da query
├── header_mapping.json      # gerado por field_mapping.py
└── logs/                    # log de execução + resultados de sucesso/erro por job

Pré-requisitos

  • Python 3.9+
  • Connected App no Salesforce com "Enable Client Credentials Flow" habilitado e "Run As" apontando pro usuário de integração, com permissão no objeto/operação que você for usar.

Setup

cd bulk-api
chmod +x setup.sh
./setup.sh

Preencha pelo menos SF_CLIENT_ID, SF_CLIENT_SECRET e SF_LOGIN_URL (My Domain, ex: https://suaorg.my.salesforce.com) no .env. O resto pode ser preenchido na mão ou de forma guiada pelos comandos abaixo.

Uso — consultar (query)

source venv/bin/activate
python run_query.py

Pergunta objeto, campos, WHERE/ORDER BY/LIMIT (cada um opcional, com pergunta sim/não), roda a query e salva o CSV em SF_OUTPUT_PATH.

Uso — atualizar em massa (insert/update/upsert/delete)

source venv/bin/activate
python run_update.py

Pergunta objeto, operação e CSV_DIR; pergunta se as colunas do CSV batem com a API (se não, chama field_mapping.py automaticamente); processa todos os CSVs de CSV_DIR em ordem numérica, um job por vez, parando a sequência se algum tiver falha.

Cada peça também roda isolada, se preferir: python configure_query.py, python query.py, python configure_update.py, python field_mapping.py, python update.py.


split-large-csv

Estrutura

split-large-csv/
├── .gitignore
├── requirements.txt
├── setup.sh
└── split_csv.py

Setup e uso

cd split-large-csv
chmod +x setup.sh
./setup.sh

source venv/bin/activate
# edite o .env: INPUT_FILE, OUTPUT_DIR, OUTPUT_PREFIX, MAX_ROWS
python split_csv.py

Fluxo típico: aponte OUTPUT_DIR direto pra bulk-api/csv/, depois rode python run_update.py no bulk-api.


sf-activate-bot-api

Localiza um Einstein Bot pelo DeveloperName (API Name) e ativa a BotVersion mais recente via REST API — equivalente a um curl -X POST .../connect/bot-versions/{id}/activation -d '{"status": "Active"}'.

Estrutura

sf-activate-bot-api/
├── auth.py           # autenticação (Client Credentials Flow)
├── config.py          # wizard interativo: grava SF_BOT_API_NAME no .env
├── activate_bot.py    # busca o Bot + BotVersion mais recente e ativa
├── requirements.txt
├── requirements-dev.txt
├── setup.sh
└── tests/

# gerados localmente, não versionados:
├── .venv/
└── .env

Pré-requisitos

  • Python 3.9+
  • Connected App no Salesforce com "Enable Client Credentials Flow" habilitado e "Run As" apontando pro usuário de integração, com permissão para gerenciar bots (Manage Bots ou equivalente) — sem isso o PATCH retorna 403.

Setup e uso

cd sf-activate-bot-api
chmod +x setup.sh
./setup.sh

source .venv/bin/activate
# edite o .env com SF_LOGIN_URL, SF_CLIENT_ID, SF_CLIENT_SECRET
python config.py            # informa o DeveloperName do bot -> grava SF_BOT_API_NAME
python activate_bot.py

Se o bot já estiver com Status = Active, o script não faz nada. SF_API_VERSION é opcional no .env (padrão v61.0).

Testes

pip install -r requirements-dev.txt
pytest tests/ -v

sf-waba-mig

Gera os Messaging Components v2 a partir dos XMLs v1: para cada bloco <externalTemplates> de um .conversationMessageDefinition-meta.xml, busca no JSON de templates (exportado do WhatsApp Business Account) o item cujo name bate com o <templateName> do XML — desempatando por <language> quando há mais de um candidato com o mesmo nome — e substitui apenas o conteúdo de <templateVersionIdentifier>, mantendo todo o resto do arquivo (formatação, outras tags) exatamente igual.

Não acessa a API do Salesforce nem usa credenciais — é um processamento local de arquivos. O .env aqui é só conveniência: guarda INPUT_DIR/JSON_PATH/OUTPUT_DIR pra você não precisar passar os três argumentos toda vez. Argumentos de linha de comando, quando informados, sempre têm prioridade sobre o .env.

Estrutura

sf-waba-mig/
├── .env.example
├── .gitignore
├── mig_waba.py           # lê os XMLs v1 + JSON de templates, gera os XMLs v2
├── requirements.txt       # python-dotenv
├── requirements-dev.txt   # + pytest, para rodar os testes
├── setup.sh               # cria venv, instala requirements.txt e .env
└── tests/
    ├── conftest.py
    └── test_mig_waba.py

# gerados localmente, não versionados:
├── venv/
├── .env
└── (pasta de output apontada em --output/OUTPUT_DIR)

Pré-requisitos

  • Python 3.9+
  • Um JSON com a lista de templates do WABA no formato:
    [
        {
            "name": "validacao_de_instalacao_instalador__v2",
            "language": "pt_BR",
            "status": "APPROVED",
            "id": "860968043551396"
        }
    ]

Setup e uso

cd sf-waba-mig
chmod +x setup.sh
./setup.sh

source venv/bin/activate

# opção 1: argumentos de linha de comando
python mig_waba.py \
    --input ./messaging_components_v1 \
    --json ./templates.json \
    --output ./messaging_components_v2

# opção 2: preencha INPUT_DIR/JSON_PATH/OUTPUT_DIR no .env e rode sem argumentos
python mig_waba.py

Testes

pip install -r requirements-dev.txt
pytest tests/ -v

sf-csv-update

Combina split + update num único subprojeto: divide um CSV grande em partes menores e roda cada parte como um job de insert/update via Bulk API 2.0, autenticado via Client Credentials Flow. Diferente do bulk-api, aqui não se configura SF_OBJECT/SF_OPERATION no .env — objeto e operação são detectados a partir do próprio CSV, arquivo por arquivo.

Formato do CSV de entrada

A 1ª coluna não é um campo do Salesforce — é o marcador do objeto alvo, com o mesmo valor repetido em toda linha do arquivo (ex: Account, Custom_Object__c). Ela é removida antes do upload. As demais colunas são os campos reais (nomes batendo com o Field API Name):

  • Se uma delas se chamar exatamente Id → operação = update (casa pelo Id existente).
  • Se não houver Id → operação = insert (cria tudo como registro novo — rodar o mesmo arquivo 2x duplica dados).
Account,Id,Name,BillingCity
Account,001XXXXXXXXXXXXAAA,Acme Corp,São Paulo
Account,001XXXXXXXXXXXXBBB,Globex Corp,Rio de Janeiro
Account,Name,Phone
Account,Acme Corp,11999990000
Account,Globex Corp,11988887777

Cada arquivo só pode ter um objeto — se a 1ª coluna variar de valor dentro do mesmo CSV, o processamento desse arquivo falha antes de subir qualquer coisa.

Erro comum: um CSV "largo" com vários objetos misturados

Não dá pra colocar Account/Contact/Opportunity/Product2 num único CSV "largo" (um header genérico, cada bloco de linhas usando um subconjunto de colunas diferente). Isso falha rápido com BulkApiError: linha N tem objeto 'X', diferente de 'Y' ... Cada arquivo só pode ter um objeto — e mesmo que não falhasse, os valores cairiam nas colunas erradas, porque o header é fixo e cada objeto "usa" as colunas em posições diferentes.

O jeito certo é separar em um CSV por objeto, cada um só com as colunas que fazem sentido pra ele:

Account,Id,Name,Industry,AnnualRevenue
Account,001A000001,Acme Corp,Manufacturing,5000000
Contact,FirstName,LastName,Email,AccountId
Contact,Ana,Souza,ana.souza@example.com,001A000001
Opportunity,Id,Name,AccountId,StageName,Amount
Opportunity,006A000001,Acme Expansion,001A000001,Proposal,250000
Product2,Name,ProductCode,IsActive,Family
Product2,SuperWidget,SW-100,true,Widgets

(Contact e Product2 ficam sem Id → viram insert; Account e Opportunity têm Id → viram update.) Pode colocar os 4 arquivos dentro de INPUT_DIR e rodar python update.py direto — ele processa todos os .csv da pasta em sequência, cada um com seu objeto/operação detectados independentemente.

Estrutura

sf-csv-update/
├── .env.example
├── .gitignore
├── auth.py           # autenticação (Client Credentials Flow)
├── split_csv.py       # divide INPUT_FILE em partes (INPUT_DIR/INPUT_PREFIX_N.csv)
├── update.py           # detecta objeto/operação por arquivo e roda o job Bulk API 2.0
├── run.py               # split_csv.py + update.py num comando só
├── requirements.txt
├── requirements-dev.txt
├── setup.sh
└── tests/

# gerados localmente, não versionados:
├── venv/
├── .env
├── input/              # CSVs divididos (gerados por split_csv.py)
└── logs/                # log de execução + resultados de sucesso/erro por job

Pré-requisitos

  • Python 3.9+
  • Connected App no Salesforce com "Enable Client Credentials Flow" habilitado e "Run As" apontando pro usuário de integração, com permissão de insert/update nos objetos referenciados pelos CSVs.

Setup e uso

cd sf-csv-update
chmod +x setup.sh
./setup.sh

source venv/bin/activate
# edite o .env: SF_CLIENT_ID, SF_CLIENT_SECRET, SF_LOGIN_URL, INPUT_FILE
python run.py

Cada peça também roda isolada, se preferir: python split_csv.py, python update.py.

Testes

pip install -r requirements-dev.txt
pytest tests/ -v

sf-bot-intent-flag

Ativa (ou desativa, com --disable) a flag "Use first message intent recognition with this bot" (BotVersion.initialIntentDetectionEnabled) sem passar pela UI: faz sf project retrieve da BotVersion, edita o XML de metadata localmente, e faz sf project deploy de volta.

Esse campo só existe na Metadata API — não tem equivalente no SObject BotVersion nem na Tooling API (conferido na documentação oficial), então o retrieve/deploy em si continua via Salesforce CLI (sf). A autenticação, porém, segue o mesmo padrão dos outros subprojetos: auth.py pega um access_token via Client Credentials Flow e repassa pro sf CLI (sf org login access-token), então você não precisa mais rodar sf org login manualmente antes — só configurar SF_CLIENT_ID/SF_CLIENT_SECRET/SF_LOGIN_URL no .env, como nos demais.

Estrutura

sf-bot-intent-flag/
├── .env.example
├── auth.py                             # autenticação (Client Credentials Flow)
├── config.py                            # wizard: grava SF_BOT_API_NAME/SF_BOT_VERSION/SF_TARGET_ORG/SF_PROJECT_DIR no .env
├── enable_first_message_intent.py        # autentica -> repassa token pro sf CLI -> retrieve -> edita XML -> deploy
├── pytest.ini                             # pythonpath=. (import do módulo pelos testes)
├── requirements.txt
├── requirements-dev.txt
├── setup.sh
└── tests/

# gerados localmente, não versionados:
├── venv/
└── .env

Pré-requisitos

  • Python 3.9+
  • Salesforce CLI (sf) instalado (npm install --global @salesforce/cli) — não precisa mais rodar sf org login manualmente, o script autentica sozinho.
  • Connected App no Salesforce com "Enable Client Credentials Flow" habilitado e "Run As" apontando pro usuário de integração, com permissão de retrieve/deploy de BotVersion na org.

Setup e uso

cd sf-bot-intent-flag
chmod +x setup.sh
./setup.sh

source venv/bin/activate
# edite o .env: SF_CLIENT_ID, SF_CLIENT_SECRET, SF_LOGIN_URL
python config.py    # opcional: grava SF_BOT_API_NAME/SF_BOT_VERSION/SF_TARGET_ORG/SF_PROJECT_DIR no .env

python enable_first_message_intent.py --bot MeuBot --version v1 --org meu-alias
python enable_first_message_intent.py --bot MeuBot --version v1 --org meu-alias --disable
python enable_first_message_intent.py --bot MeuBot --version v1   # usa SF_TARGET_ORG do .env

--org/SF_TARGET_ORG não é mais uma org que precisa estar pré-autenticada — é só o alias local que o script cria/atualiza no sf CLI a cada execução, usando o access_token obtido via Client Credentials Flow (nunca passado por argumento de linha de comando, só por variável de ambiente SF_ACCESS_TOKEN isolada pro subprocesso).

Prioridade de config: flags de linha de comando > .env > erro (--bot/--org obrigatórios de um jeito ou de outro). Se a flag já estiver no valor desejado, o script não faz deploy (idempotente).

Testes

pip install -r requirements-dev.txt
pytest tests/ -v

Fluxo ponta a ponta (exemplo: update de 23M registros)

# 1. Dividir o CSV de origem em partes de 1M linhas
cd split-large-csv && ./setup.sh
source venv/bin/activate
# .env: OUTPUT_DIR=../bulk-api/csv
python split_csv.py
deactivate

# 2. Configurar e rodar o update em massa
cd ../bulk-api && ./setup.sh
source venv/bin/activate
python run_update.py

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages