docs(feature-control): fix flag key prefix fc. → at. across all docs
Real key pattern is at.<domain>.<name> (e.g. at.ios.only, at.promo). The fc. prefix in the BFF README and API_Mobile_App.md was incorrect. - API_Mobile_App.md: updated request keys, response raw examples, flag table, and Swift usage examples to at. prefix - API_Mobile_App.md: clarified that app uses raw["at.key"] not simplified flags object (FeatureFlagsState.isEnabled reads raw) - feature-control-bff/README.md: updated DEFAULTS_JSON example, request keys, response flags/raw examples, added note about raw usage
This commit is contained in:
@@ -985,11 +985,10 @@ Authorization: Bearer <JWT_Token> ← opcional, mas recomendado
|
|||||||
{
|
{
|
||||||
"environment": "production",
|
"environment": "production",
|
||||||
"keys": [
|
"keys": [
|
||||||
"fc.ios",
|
"at.ios.only",
|
||||||
"fc.android",
|
"at.android.only",
|
||||||
"fc.promo",
|
"at.promo",
|
||||||
"fc.promo-codes",
|
"at.city.aguai"
|
||||||
"fc.city-aguai"
|
|
||||||
],
|
],
|
||||||
"context": {
|
"context": {
|
||||||
"subjectType": "customer",
|
"subjectType": "customer",
|
||||||
@@ -1022,20 +1021,31 @@ Authorization: Bearer <JWT_Token> ← opcional, mas recomendado
|
|||||||
"configVersion": 7,
|
"configVersion": 7,
|
||||||
"evaluatedAt": "2026-06-05T12:00:00.000Z",
|
"evaluatedAt": "2026-06-05T12:00:00.000Z",
|
||||||
"flags": {
|
"flags": {
|
||||||
"ios": true,
|
"atiosonly": true,
|
||||||
"android": true,
|
"atandroidonly": false,
|
||||||
"promo": true,
|
"atpromo": true,
|
||||||
"promoCodes": false,
|
"atcityaguai": true
|
||||||
"cityAguai": true
|
|
||||||
},
|
},
|
||||||
"raw": {
|
"raw": {
|
||||||
"fc.ios": {
|
"at.ios.only": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"variant": "on",
|
"variant": "on",
|
||||||
"payload": null,
|
"payload": null,
|
||||||
"reason": "rollout"
|
"reason": "rollout"
|
||||||
},
|
},
|
||||||
"fc.promo-codes": {
|
"at.promo": {
|
||||||
|
"enabled": true,
|
||||||
|
"variant": "on",
|
||||||
|
"payload": null,
|
||||||
|
"reason": "rollout"
|
||||||
|
},
|
||||||
|
"at.city.aguai": {
|
||||||
|
"enabled": true,
|
||||||
|
"variant": "on",
|
||||||
|
"payload": null,
|
||||||
|
"reason": "segment"
|
||||||
|
},
|
||||||
|
"at.android.only": {
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
"variant": "off",
|
"variant": "off",
|
||||||
"payload": null,
|
"payload": null,
|
||||||
@@ -1062,27 +1072,33 @@ Authorization: Bearer <JWT_Token> ← opcional, mas recomendado
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Como usar `flags` no app
|
#### Como usar `raw` no app (Swift)
|
||||||
|
|
||||||
Os flags são retornados em `flags` com chaves **camelCase** derivadas das chaves originais (prefixo `fc.` removido, hífen/underscore viram camelCase):
|
O app usa o campo **`raw`**, indexado pela chave original completa. O campo `flags` contém uma versão simplificada (pontos removidos), mas o app Swift usa `raw` para manter compatibilidade direta com os nomes de flag.
|
||||||
|
|
||||||
| Chave original | Chave em `flags` |
|
| Chave original | Acesso via `raw` | `flags` simplificado |
|
||||||
|---|---|
|
|---|---|---|
|
||||||
| `fc.ios` | `flags.ios` |
|
| `at.ios.only` | `raw["at.ios.only"]` | `flags["atiosonly"]` |
|
||||||
| `fc.android` | `flags.android` |
|
| `at.android.only` | `raw["at.android.only"]` | `flags["atandroidonly"]` |
|
||||||
| `fc.promo` | `flags.promo` |
|
| `at.promo` | `raw["at.promo"]` | `flags["atpromo"]` |
|
||||||
| `fc.promo-codes` | `flags.promoCodes` |
|
| `at.city.aguai` | `raw["at.city.aguai"]` | `flags["atcityaguai"]` |
|
||||||
| `fc.city-aguai` | `flags.cityAguai` |
|
|
||||||
|
|
||||||
**Regra de leitura:**
|
> **Use sempre `raw`** para acessar flags no app — as chaves simplificadas em `flags` são geradas mecanicamente e menos legíveis.
|
||||||
- `true` → flag habilitado
|
|
||||||
- `false` ou `"off"` → flag desabilitado
|
|
||||||
- Qualquer outro string → variante multivariate (ex: `"variant_a"`, `"variant_b"`)
|
|
||||||
|
|
||||||
**Exemplo Swift:**
|
**Regra de leitura em `raw`:**
|
||||||
|
- `raw["at.promo"].enabled == true` → flag habilitado
|
||||||
|
- `raw["at.promo"].variant == "on"` → variante ativa
|
||||||
|
- `raw["at.promo"].payload` → dados extras opcionais (JSON livre)
|
||||||
|
|
||||||
|
**Exemplo Swift (padrão atual do app):**
|
||||||
```swift
|
```swift
|
||||||
if flags["ios"] as? Bool == true {
|
// FeatureFlagsState.isEnabled() já lê raw automaticamente
|
||||||
// mostrar funcionalidade exclusiva iOS
|
if appState.featureFlags.isEnabled("at.promo") {
|
||||||
|
// mostrar módulo de promoções
|
||||||
|
}
|
||||||
|
|
||||||
|
if appState.featureFlags.isEnabled("at.ios.only") {
|
||||||
|
// funcionalidade exclusiva iOS
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -1112,13 +1128,13 @@ Authorization: Bearer <JWT_Token>
|
|||||||
{
|
{
|
||||||
"events": [
|
"events": [
|
||||||
{
|
{
|
||||||
"featureKey": "fc.promo",
|
"featureKey": "at.promo",
|
||||||
"variant": "on",
|
"variant": "on",
|
||||||
"subjectType": "customer",
|
"subjectType": "customer",
|
||||||
"storeId": "store_1777215327582_akum6"
|
"storeId": "store_1777215327582_akum6"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"featureKey": "fc.ios",
|
"featureKey": "at.ios.only",
|
||||||
"variant": "on",
|
"variant": "on",
|
||||||
"subjectType": "customer"
|
"subjectType": "customer"
|
||||||
}
|
}
|
||||||
@@ -1148,15 +1164,16 @@ Máximo de **100 eventos por request**.
|
|||||||
|
|
||||||
### 3. Flags disponíveis (referência)
|
### 3. Flags disponíveis (referência)
|
||||||
|
|
||||||
|
Padrão de nomenclatura: `at.<domínio>.<descrição>` (pontos como separador, sem hífens).
|
||||||
|
|
||||||
| Flag | Descrição | Default |
|
| Flag | Descrição | Default |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `fc.ios` | Funcionalidades exclusivas iOS habilitadas | `true` |
|
| `at.ios.only` | Funcionalidades exclusivas iOS | `true` |
|
||||||
| `fc.android` | Funcionalidades exclusivas Android habilitadas | `true` |
|
| `at.android.only` | Funcionalidades exclusivas Android | `true` |
|
||||||
| `fc.promo` | Módulo de promoções ativo | `true` |
|
| `at.promo` | Módulo de promoções ativo | `true` |
|
||||||
| `fc.promo-codes` | Cupons de desconto habilitados | `true` |
|
| `at.city.aguai` | Expansão para cidade de Aguaí/SP | `true` |
|
||||||
| `fc.city-aguai` | Expansão para cidade de Aguaí/SP | `true` |
|
|
||||||
|
|
||||||
> Novos flags são criados pelo time Pedi Foods no painel `/feature-control`. Consulte o time antes de codificar um flag inexistente.
|
> Novos flags são criados pelo time Pedi Foods no painel `/feature-control`. Consulte o time antes de codificar um flag inexistente. A lista completa de flags ativos está disponível no painel admin → Feature Control.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -27,11 +27,10 @@ Exemplo de `FEATURE_CONTROL_DEFAULTS_JSON`:
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"fc.promo": { "enabled": true, "variant": "on" },
|
"at.ios.only": { "enabled": true, "variant": "on" },
|
||||||
"fc.promo-codes": { "enabled": true, "variant": "on" },
|
"at.android.only": { "enabled": true, "variant": "on" },
|
||||||
"fc.android": { "enabled": true, "variant": "on" },
|
"at.promo": { "enabled": true, "variant": "on" },
|
||||||
"fc.city-aguai": { "enabled": true, "variant": "on" },
|
"at.city.aguai": { "enabled": true, "variant": "on" }
|
||||||
"fc.ios": { "enabled": true, "variant": "on" }
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -40,7 +39,7 @@ Exemplo de `FEATURE_CONTROL_DEFAULTS_JSON`:
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"environment": "production",
|
"environment": "production",
|
||||||
"keys": ["fc.promo", "fc.promo-codes", "fc.android", "fc.city-aguai", "fc.ios"],
|
"keys": ["at.ios.only", "at.android.only", "at.promo", "at.city.aguai"],
|
||||||
"context": {
|
"context": {
|
||||||
"subjectType": "customer",
|
"subjectType": "customer",
|
||||||
"subjectId": "cust_123",
|
"subjectId": "cust_123",
|
||||||
@@ -48,8 +47,7 @@ Exemplo de `FEATURE_CONTROL_DEFAULTS_JSON`:
|
|||||||
"platform": "ios",
|
"platform": "ios",
|
||||||
"appVersion": "2.3.1",
|
"appVersion": "2.3.1",
|
||||||
"attributes": {
|
"attributes": {
|
||||||
"city": "Belo Horizonte",
|
"city": "Aguaí"
|
||||||
"tier": "gold"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,14 +64,19 @@ Se o app enviar `Authorization: Bearer <JWT>`, o BFF tenta extrair `subjectId` d
|
|||||||
"configVersion": 7,
|
"configVersion": 7,
|
||||||
"evaluatedAt": "2026-04-16T12:00:00.000Z",
|
"evaluatedAt": "2026-04-16T12:00:00.000Z",
|
||||||
"flags": {
|
"flags": {
|
||||||
"promo": true,
|
"atiosonly": true,
|
||||||
"promoCodes": true,
|
"atandroidonly": false,
|
||||||
"android": true,
|
"atpromo": true,
|
||||||
"cityAguai": true,
|
"atcityaguai": true
|
||||||
"ios": true
|
|
||||||
},
|
},
|
||||||
"raw": {
|
"raw": {
|
||||||
"fc.promo": {
|
"at.ios.only": {
|
||||||
|
"enabled": true,
|
||||||
|
"variant": "on",
|
||||||
|
"payload": null,
|
||||||
|
"reason": "rollout"
|
||||||
|
},
|
||||||
|
"at.promo": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"variant": "on",
|
"variant": "on",
|
||||||
"payload": null,
|
"payload": null,
|
||||||
@@ -83,6 +86,9 @@ Se o app enviar `Authorization: Bearer <JWT>`, o BFF tenta extrair `subjectId` d
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> O app Swift usa o campo **`raw`** indexado pela chave original (ex: `raw["at.promo"]`). O campo `flags` é uma versão simplificada gerada mecanicamente (pontos removidos).
|
||||||
|
```
|
||||||
|
|
||||||
Quando o upstream falha (ex.: `429`, `500`, timeout), o BFF devolve `fallback` com defaults estáticos.
|
Quando o upstream falha (ex.: `429`, `500`, timeout), o BFF devolve `fallback` com defaults estáticos.
|
||||||
|
|
||||||
## Execução local
|
## Execução local
|
||||||
|
|||||||
Reference in New Issue
Block a user