Skip to content

Add LinkedIn Ads handler to MindsDB#48

Merged
magroski merged 2 commits into
mainfrom
patrick/linkedin-ads-mindsdb-20260327
Mar 30, 2026
Merged

Add LinkedIn Ads handler to MindsDB#48
magroski merged 2 commits into
mainfrom
patrick/linkedin-ads-mindsdb-20260327

Conversation

@patrickadeelino
Copy link
Copy Markdown
Collaborator

@patrickadeelino patrickadeelino commented Mar 27, 2026

Resumo

Adiciona um novo handler linkedin_ads ao MindsDB, expondo dados do LinkedIn Ads através de tabelas SQL para integração com o Data Agent da Talentify.

O handler foi refatorado em uma arquitetura modular com separação clara de responsabilidades, resultando em código mais manutenível, testável e organizado.


🏗️ Arquitetura e Organização

O handler foi estruturado em 4 módulos especializados com responsabilidades bem definidas:

📦 Módulos Criados

1. linkedin_ads_auth.py (250 linhas)

Responsabilidade: Gerenciamento do ciclo de vida de tokens OAuth

  • Token lifecycle completo:

    • Refresh automático de tokens expirados
    • Armazenamento criptografado via handler_storage
    • Validação de expiração com buffer configurável (5 min)
  • Thread-safety:

    • Lock para operações de refresh concorrentes
    • Double-check pattern para evitar refreshes desnecessários
  • Fallback inteligente:

    • Carrega tokens do storage primeiro
    • Usa tokens do connection_data como fallback
    • Exchange automático de refresh token quando access token está ausente

2. linkedin_ads_client.py (262 linhas)

Responsabilidade: Comunicação HTTP com a API do LinkedIn Ads

  • Cliente HTTP autenticado:

    • Sessão reutilizável com headers pré-configurados
    • Retry automático com refresh de token em 401
    • Suporte a headers customizados por request
  • Paginação robusta:

    • Fetch de coleções com pageToken
    • Limites configuráveis (default: 100, max: 1000)
    • Suporte a 3 modos de query: custom params, search filters, default search
  • Batch operations:

    • Endpoint fetch_by_ids para queries otimizadas
    • Extração automática de results/elements

3. linkedin_ads_utils.py (793 linhas)

Responsabilidade: Processamento e normalização de dados

  • Search & Filter:

    • extract_search_filters: Extrai filtros SQL para API filters
    • build_search_expression: Constrói RestLi search expressions
    • build_creative_params: Parâmetros para creative criteria endpoint
    • build_campaign_analytics_params: Parâmetros complexos de analytics
  • URN Handling:

    • extract_urn_id: Extrai IDs numéricos de URNs
    • normalize_search_id_filters: Adiciona prefixos URN automaticamente
    • format_creative_filter_value: Formata valores com URN correto
  • Data Normalization:

    • normalize_campaign: 30 campos estruturados + budgets + schedules
    • normalize_campaign_group: Grupos com budgets e serving status
    • normalize_creative: Criativos com content references
    • normalize_campaign_analytics: Métricas com time granularity
  • Helpers:

    • extract_money: Extrai amount + currencyCode
    • coerce_date: Converte strings/datetime para date
    • build_date_range_param: Constrói RestLi date ranges
    • sort_rows_locally: Aplica sort client-side
    • filter_rows_locally: Aplica filtros client-side

4. linkedin_ads_handler.py (286 linhas, -70% do original)

Responsabilidade: Orquestração de alto nível

  • Interface pública simplificada:

    • fetch_campaigns: Campanhas com search filters
    • fetch_campaign_groups: Grupos com batch optimization
    • fetch_creatives: Criativos com criteria filters
    • fetch_campaign_analytics: Analytics com date ranges
  • Delegação limpa:

    • Auth → LinkedInAdsAuthManager
    • HTTP → LinkedInAdsClient
    • Processing → linkedin_ads_utils
  • Configuração centralizada:

    • Endpoints, campos, filtros e constantes
    • Mapeamentos de filtros por tabela
    • Time granularities suportados

📊 Tabelas Expostas

O handler expõe 4 tabelas SQL-addressable:

  1. campaigns - Campanhas publicitárias

    • Metadados: name, status, type, test
    • Relacionamentos: account_id, campaign_group_id
    • Orçamentos: daily_budget, total_budget, unit_cost (com moeda)
    • Configuração: locale, objective_type, format
    • Agendamento: run_schedule_start/end
    • Auditoria: created_at, last_modified_at
  2. campaign_groups - Grupos de campanhas

    • Metadados: name, status, test
    • Orçamentos: daily_budget, total_budget (com moeda)
    • Status: serving_statuses (multi-value)
    • Agendamento: run_schedule_start/end
  3. creatives - Criativos das campanhas

    • IDs: creative_id, campaign_id, account_id
    • Status: intended_status, is_test, is_serving
    • Conteúdo: content_reference (URN para share)
    • Motivos: serving_hold_reasons
    • Auditoria: created_at, last_modified_at
  4. campaign_analytics - Métricas de performance

    • Dimensões: campaign_id, date_start/end, time_granularity
    • Métricas: impressions, clicks, landing_page_clicks
    • Engagement: likes, shares
    • Conversões: external_website_conversions
    • Custo: cost_in_local_currency

🔗 Integração com Data Agent

O handler permite que o Data Agent da Talentify faça queries SQL como:

-- Campanhas ativas nos últimos 30 dias
SELECT * FROM linkedin_ads.campaigns 
WHERE status = 'ACTIVE' 
AND account_id = '516413367';

-- Analytics agregado por campanha
SELECT 
    campaign_id,
    SUM(impressions) as total_impressions,
    SUM(clicks) as total_clicks,
    SUM(cost_in_local_currency) as total_cost
FROM linkedin_ads.campaign_analytics
WHERE date_start >= '2024-01-01'
GROUP BY campaign_id;

-- Join de campanhas com analytics
SELECT 
    c.name,
    c.status,
    a.impressions,
    a.clicks
FROM linkedin_ads.campaigns c
JOIN linkedin_ads.campaign_analytics a ON c.id = a.campaign_id
WHERE c.status = 'ACTIVE';

patrickadeelino and others added 2 commits March 27, 2026 16:31
…dules

Refactor the LinkedIn Ads handler from a monolithic 965-line file into four
well-organized modules with clear separation of concerns:

**New modules:**
- linkedin_ads_auth.py (250 lines): Token lifecycle management
  - OAuth token refresh with thread-safe operations
  - Token storage and expiry validation
  - Automatic token refresh with configurable skew buffer

- linkedin_ads_client.py (262 lines): API communication layer
  - HTTP client with authenticated requests
  - Paginated collection fetching
  - Automatic token refresh on 401 responses

- linkedin_ads_utils.py (793 lines): Data processing utilities
  - URN handling and normalization
  - Search expression building
  - Data normalization for all entity types
  - Date coercion and money extraction helpers

**Refactored handler (286 lines, -70%):**
- High-level orchestration only
- Clean delegation to specialized modules
- Simplified public API

**Bug fixes:**
- Fix sort handling in campaign_analytics (now applies sort locally and
  marks sort_column.applied = True)

**Benefits:**
- Improved maintainability with single-responsibility modules
- Enhanced testability (components can be tested in isolation)
- Better code organization and readability
- Reduced cognitive load in main handler

Total: 1,591 lines across 4 well-structured files vs 965 lines monolith

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@patrickadeelino patrickadeelino marked this pull request as ready for review March 30, 2026 15:03
@magroski magroski merged commit 4c8f3e4 into main Mar 30, 2026
1 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants