Skip to content

Services Documentation

This document provides comprehensive documentation for all services in the Yappa Knowledge Hub backend.

Overview

Services contain the core business logic and external API integrations. The backend has two main service categories:

  1. Notion Services - Integration with Notion API
  2. Application Services - Core business logic

Notion Services

Located in src/Service/Notion/

NotionClient

File: src/Service/Notion/NotionClient.php

Description: Low-level HTTP client for Notion API. Handles authentication, request/response formatting, and error handling.

Constructor Parameters:

  • HttpClientInterface $httpClient - Symfony HTTP client
  • string $apiKey - Notion API key
  • string $version - Notion API version (default: '2022-06-28')

Key Methods:

createPage(string $databaseId, array $properties): array

Creates a new page in a Notion database.

php
$properties = [
    'Title' => ['title' => [['text' => ['content' => 'My Page']]]],
    'Status' => ['status' => ['name' => 'In progress']]
];
$page = $notionClient->createPage($databaseId, $properties);

updatePage(string $pageId, array $properties): array

Updates an existing Notion page.

php
$properties = [
    'Status' => ['status' => ['name' => 'Done']]
];
$page = $notionClient->updatePage($pageId, $properties);

getPage(string $pageId): array

Retrieves a single page by ID.

php
$page = $notionClient->getPage($pageId);

queryDatabase(string $databaseId, array $filter, array $sorts): array

Queries a database with filters and sorting.

php
$filter = [
    'property' => 'Status',
    'status' => ['equals' => 'Done']
];
$sorts = [
    ['property' => 'Created', 'direction' => 'descending']
];
$results = $notionClient->queryDatabase($databaseId, $filter, $sorts);

queryDatabaseWithPagination(string $databaseId, array $filter, array $sorts): array

Queries a database and automatically handles pagination to fetch all results.

php
$results = $notionClient->queryDatabaseWithPagination($databaseId, [], []);
// Returns all pages, not just first 100

searchPages(string $query, array $filter): array

Searches across all accessible pages.

php
$results = $notionClient->searchPages('symfony', [
    'property' => 'object',
    'value' => 'page'
]);

createPageWithIcon(string $databaseId, array $properties, ?array $icon): array

Creates a page with an icon.

php
$icon = ['type' => 'emoji', 'emoji' => ''];
$page = $notionClient->createPageWithIcon($databaseId, $properties, $icon);

updatePageWithIcon(string $pageId, array $properties, ?array $icon): array

Updates a page including its icon.


NotionKnowledgeService

File: src/Service/Notion/NotionKnowledgeService.php

Description: High-level service for knowledge item operations in Notion. Handles property mapping and business logic.

Constructor Parameters:

  • NotionClient $client - Notion API client
  • string $knowledgeDatabaseId - Knowledge database ID

Key Methods:

create(array $data): array

Creates a knowledge item in Notion.

php
$data = [
    'title' => 'My Knowledge Item',
    'content' => 'Detailed content...',
    'tags' => ['php', 'symfony'],
    'categoryId' => 'notion-category-id',
    'status' => 'Not started',
    'sourceUrl' => 'https://example.com',
    'slackUserId' => 'U12345',
    'slackMessageTs' => '1234567890.123456',
    'slackChannelId' => 'C12345',
    'aiSummary' => 'AI-generated summary...'
];
$page = $knowledgeService->create($data);

Supported Properties:

  • title (string, required) - Page title
  • content (string) - Rich text content
  • tags (array) - Multi-select tags
  • categoryId (string) - Relation to category
  • status (string) - Status property
  • sourceUrl (string) - URL property
  • slackUserId (string) - Slack user ID
  • slackMessageTs (string) - Slack message timestamp
  • slackChannelId (string) - Slack channel ID
  • aiSummary (string) - AI-generated summary

update(string $pageId, array $data): array

Updates a knowledge item.

php
$data = [
    'title' => 'Updated Title',
    'status' => 'Done',
    'tags' => ['updated', 'tags']
];
$page = $knowledgeService->update($pageId, $data);

get(string $pageId): array

Retrieves a knowledge item.

php
$page = $knowledgeService->get($pageId);

listAll(): array

Lists all knowledge items (with pagination).

php
$pages = $knowledgeService->listAll();
// Returns all pages, not just first 100

queryByCategory(string $categoryId, array $additionalFilters = []): array

Queries knowledge items by category.

php
$pages = $knowledgeService->queryByCategory($categoryId);

search(string $query): array

Searches knowledge items.

php
$pages = $knowledgeService->search('symfony deployment');

mapToArray(array $page): array

Maps a Notion page to a PHP array.

php
$page = $notionClient->getPage($pageId);
$knowledge = $knowledgeService->mapToArray($page);
// Returns:
// [
//     'id' => 'notion-page-id',
//     'notionId' => 'notion-page-id',
//     'notionUrl' => 'https://notion.so/...',
//     'title' => 'Page Title',
//     'content' => 'Content...',
//     'tags' => ['tag1', 'tag2'],
//     'targetGroups' => ['group1'],
//     'status' => 'Done',
//     'sourceUrl' => 'https://...',
//     'aiSummary' => 'Summary...',
//     'created' => '2026-02-19T10:30:00.000Z',
//     'lastEdited' => '2026-02-19T11:00:00.000Z',
//     'categoryId' => ['category-id']
// ]

NotionCategoryService

File: src/Service/Notion/NotionCategoryService.php

Description: Manages categories in Notion with icon support and emoji conversion.

Constructor Parameters:

  • NotionClient $client - Notion API client
  • string $categoryDatabaseId - Category database ID

Key Methods:

create(array $data): array

Creates a category in Notion.

php
$data = [
    'name' => 'Development',
    'description' => 'Development resources',
    'icon' => ':rocket:',  // Slack emoji or Unicode
    'defaultTargetGroups' => ['developers', 'devops'],
    'color' => 'blue',
    'isActive' => true,
    'sortOrder' => 1,
    'digestFrequency' => 'weekly'
];
$page = $categoryService->create($data);

Supported Properties:

  • name (string, required) - Category name
  • description (string) - Category description
  • icon (string) - Emoji icon (Slack format or Unicode)
  • defaultTargetGroups (array) - Default target groups
  • color (string) - Category color
  • isActive (boolean) - Active status
  • sortOrder (integer) - Sort order
  • digestFrequency (string) - Digest frequency

update(string $pageId, array $data): array

Updates a category.

php
$data = [
    'name' => 'Updated Name',
    'icon' => '',
    'defaultTargetGroups' => ['developers', 'designers']
];
$page = $categoryService->update($pageId, $data);

get(string $pageId): array

Retrieves a category.

listAll(): array

Lists all categories (with pagination).

php
$categories = $categoryService->listAll();

mapToArray(array $page): array

Maps a Notion category page to a PHP array.

php
$category = $categoryService->mapToArray($page);
// Returns:
// [
//     'id' => 'notion-page-id',
//     'name' => 'Development',
//     'description' => 'Description...',
//     'icon' => '',
//     'defaultTargetGroups' => ['developers'],
//     'color' => 'blue',
//     'isActive' => true,
//     'sortOrder' => 1,
//     'digestFrequency' => 'weekly',
//     'lastDigestAt' => '2026-02-15T10:00:00.000Z',
//     'created' => '2026-01-01T00:00:00.000Z',
//     'updated' => '2026-02-19T10:30:00.000Z'
// ]

Emoji Conversion: The service automatically converts Slack emoji format (:rocket:) to Unicode emoji () for Notion. Supports 60+ common emojis.


NotionSyncService

File: src/Service/Notion/NotionSyncService.php

Description: Bidirectional synchronization between SQLite and Notion. Implements the dual-storage pattern.

Constructor Parameters:

  • NotionClient $notionClient - Notion API client
  • EntityManagerInterface $entityManager - Doctrine entity manager
  • LoggerInterface $logger - Logger
  • string $knowledgeDatabaseId - Knowledge database ID
  • string $categoriesDatabaseId - Categories database ID

Key Methods:

createKnowledge(array $data): array

Creates knowledge in both SQLite and Notion.

php
$data = [
    'title' => 'My Knowledge',
    'content' => 'Content...',
    'tags' => ['tag1', 'tag2'],
    'categoryId' => 'notion-category-id',
    'targetGroups' => ['developers'],
    'status' => 'Not started',
    'url' => 'https://example.com',
    'userId' => 'U12345',
    'sourceMessage' => ['ts' => '123', 'channel' => 'C123']
];

$result = $syncService->createKnowledge($data);
// Returns:
// [
//     'sqlite_id' => 1,
//     'notion_id' => 'notion-page-id',
//     'notion_url' => 'https://notion.so/...'
// ]

Process:

  1. Creates entity in SQLite
  2. Creates page in Notion
  3. Links Notion ID to SQLite entity
  4. Returns both IDs

searchKnowledge(string $query, array $options = []): array

Searches in both SQLite and Notion.

php
$results = $syncService->searchKnowledge('symfony');
// Returns merged results from both sources

getKnowledge(string $id): ?array

Gets knowledge from SQLite or Notion.

php
$knowledge = $syncService->getKnowledge($id);
// Tries SQLite first, falls back to Notion

syncToNotion(int $limit = 100): array

Syncs existing SQLite knowledge to Notion.

php
$result = $syncService->syncToNotion(50);
// Returns:
// [
//     'synced' => 45,
//     'failed' => 5,
//     'total' => 50
// ]

Use Case: Bulk sync of existing SQLite data to Notion.


NotionPropertyMapper

File: src/Service/Notion/NotionPropertyMapper.php

Description: Static utility class for mapping between Notion property formats and PHP values.

Static Methods:

Property Creators (PHP Notion)

php
// Title property
NotionPropertyMapper::title('My Title');
// Returns: ['title' => [['text' => ['content' => 'My Title']]]]

// Rich text property
NotionPropertyMapper::richText('Content...');
// Returns: ['rich_text' => [['text' => ['content' => 'Content...']]]]

// Multi-select property
NotionPropertyMapper::multiSelect(['tag1', 'tag2']);
// Returns: ['multi_select' => [['name' => 'tag1'], ['name' => 'tag2']]]

// Select property
NotionPropertyMapper::select('option');
// Returns: ['select' => ['name' => 'option']]

// Status property
NotionPropertyMapper::status('Done');
// Returns: ['status' => ['name' => 'Done']]

// Checkbox property
NotionPropertyMapper::checkbox(true);
// Returns: ['checkbox' => true]

// Number property
NotionPropertyMapper::number(42);
// Returns: ['number' => 42]

// URL property
NotionPropertyMapper::url('https://example.com');
// Returns: ['url' => 'https://example.com']

// Date property
NotionPropertyMapper::date('2026-02-19');
// Returns: ['date' => ['start' => '2026-02-19']]

// Relation property
NotionPropertyMapper::relation(['page-id-1', 'page-id-2']);
// Returns: ['relation' => [['id' => 'page-id-1'], ['id' => 'page-id-2']]]

Property Extractors (Notion PHP)

php
// Extract title
$title = NotionPropertyMapper::extractTitle($property);

// Extract rich text
$text = NotionPropertyMapper::extractRichText($property);

// Extract multi-select
$tags = NotionPropertyMapper::extractMultiSelect($property);
// Returns: ['tag1', 'tag2']

// Extract select
$option = NotionPropertyMapper::extractSelect($property);

// Extract status
$status = NotionPropertyMapper::extractStatus($property);

// Extract checkbox
$checked = NotionPropertyMapper::extractCheckbox($property);

// Extract number
$number = NotionPropertyMapper::extractNumber($property);

// Extract URL
$url = NotionPropertyMapper::extractUrl($property);

// Extract date
$date = NotionPropertyMapper::extractDate($property);

// Extract relation
$ids = NotionPropertyMapper::extractRelation($property);
// Returns: ['page-id-1', 'page-id-2']

NotionDatabaseService

File: src/Service/Notion/NotionDatabaseService.php

Description: Database-level operations in Notion.

(Implementation details would be added based on actual code)


NotionDigestService

File: src/Service/Notion/NotionDigestService.php

Description: Digest operations in Notion.

(Implementation details would be added based on actual code)


NotionException

File: src/Service/Notion/NotionException.php

Description: Custom exception for Notion API errors.

php
throw new NotionException('Failed to create page: ' . $error);

Application Services

Located in src/Service/

OpenAiService

File: src/Service/OpenAiService.php

Description: OpenAI API integration for generating AI completions with retry logic and mock mode.

Constructor Parameters:

  • HttpClientInterface $httpClient - HTTP client
  • LoggerInterface $logger - Logger
  • string $apiKey - OpenAI API key
  • string $model - Model name (default: 'gpt-4')
  • bool $mockMode - Enable mock mode (default: false)

Key Methods:

generateCompletion(string $prompt, array $options = []): string

Generates an AI completion.

php
$prompt = "Summarize this article for developers: ...";
$options = [
    'temperature' => 0.7,
    'max_tokens' => 500
];
$summary = $openAiService->generateCompletion($prompt, $options);

Options:

  • temperature (float, default: 0.7) - Creativity level (0-1)
  • max_tokens (int, default: 500) - Maximum response length

Features:

  • Automatic Retries: Retries up to 3 times with exponential backoff
  • Timeout: 30-second timeout per request
  • Error Handling: Logs warnings and throws exception after max retries
  • Mock Mode: Returns predefined responses for testing

Mock Mode: When mockMode is enabled, returns realistic mock summaries based on keywords in the prompt:

  • Detects target groups (technical, marketing, management, etc.)
  • Returns appropriate mock content
  • No API calls made
  • Perfect for development/testing
php
// Enable mock mode
$service = new OpenAiService($httpClient, $logger, '', 'gpt-4', true);
$summary = $service->generateCompletion($prompt);
// Returns mock summary, no API call

isMockMode(): bool

Checks if mock mode is enabled.

php
if ($openAiService->isMockMode()) {
    echo "Using mock responses";
}

getModel(): string

Gets the configured model.

php
$model = $openAiService->getModel(); // 'gpt-4'

SummaryService

File: src/Service/SummaryService.php

Description: Manages summary generation and storage.

(Implementation details would be added based on actual code)


TargetGroupService

File: src/Service/TargetGroupService.php

Description: Manages target groups for knowledge items.

(Implementation details would be added based on actual code)


SlackHomeRefreshService

File: src/Service/SlackHomeRefreshService.php

Description: Refreshes Slack home tab when knowledge items change.

(Implementation details would be added based on actual code)


Service Configuration

Services are configured in config/services.yaml:

yaml
services:
    # Notion services
    App\Service\Notion\NotionClient:
        arguments:
            $apiKey: '%env(NOTION_API_KEY)%'
            $version: '2022-06-28'

    App\Service\Notion\NotionKnowledgeService:
        arguments:
            $knowledgeDatabaseId: '%env(NOTION_KNOWLEDGE_DATABASE_ID)%'

    App\Service\Notion\NotionCategoryService:
        arguments:
            $categoryDatabaseId: '%env(NOTION_CATEGORIES_DATABASE_ID)%'

    App\Service\Notion\NotionSyncService:
        arguments:
            $knowledgeDatabaseId: '%env(NOTION_KNOWLEDGE_DATABASE_ID)%'
            $categoriesDatabaseId: '%env(NOTION_CATEGORIES_DATABASE_ID)%'

    # OpenAI service
    App\Service\OpenAiService:
        arguments:
            $apiKey: '%env(OPENAI_API_KEY)%'
            $model: '%env(OPENAI_MODEL)%'
            $mockMode: '%env(bool:OPENAI_MOCK_MODE)%'

Environment Variables

Required environment variables for services:

bash
# Notion API
NOTION_API_KEY=secret_xxx
NOTION_KNOWLEDGE_DATABASE_ID=xxx
NOTION_CATEGORIES_DATABASE_ID=xxx

# OpenAI API
OPENAI_API_KEY=sk-xxx
OPENAI_MODEL=gpt-4
OPENAI_MOCK_MODE=false

Error Handling

All services implement proper error handling:

  1. Logging: Errors are logged with context
  2. Exceptions: Throw meaningful exceptions
  3. Retries: Automatic retries for transient failures (OpenAI)
  4. Fallbacks: Graceful degradation (sync service)

Testing Services

Services can be tested using PHPUnit:

php
use App\Service\OpenAiService;
use PHPUnit\Framework\TestCase;

class OpenAiServiceTest extends TestCase
{
    public function testMockMode(): void
    {
        $service = new OpenAiService(
            $this->createMock(HttpClientInterface::class),
            $this->createMock(LoggerInterface::class),
            '',
            'gpt-4',
            true // mock mode
        );

        $result = $service->generateCompletion('Test prompt');
        $this->assertNotEmpty($result);
    }
}