# Extensions
Q&A provides possibility to extend functionalities with extensions.
## Markdown extensions
It's possible to add custom `remark` and `rehype` plugins to process markdown content.
To do that, you need to provide `markdown` configuration with `remarkPlugins` and/or `rehypePlugins` options in
the `QetaPage` component and `PostsTableCard` component properties.
```tsx
import rehypeMermaid from 'rehype-mermaid';
}
rehypePlugins={[rehypeMermaid]}
/>;
```
In the new frontend system, this is made possible via `QetaMarkdownRehypePluginBlueprint` and `QetaMarkdownRemarkPluginBlueprint` extension points.
Example of adding `rehype-mermaid` plugin:
```tsx
import rehypeMermaid from 'rehype-mermaid';
const module = createFrontendModule({
pluginId: 'qeta',
extensions: [
QetaMarkdownRehypePluginBlueprint.make({
params: {
plugin: rehypeMermaid,
},
}),
],
});
```
- Available rehype plugins: https://github.com/rehypejs/rehype/blob/HEAD/doc/plugins.md#list-of-plugins
- Available remark plugins: https://github.com/remarkjs/remark/blob/main/doc/plugins.md#list-of-plugins
## Custom Badge Evaluators
You can create custom badge evaluators to award badges based on your own criteria. Badge evaluators implement the `BadgeEvaluator` interface from `@drodil/backstage-plugin-qeta-node`.
### Creating a Custom Badge Evaluator
```typescript
import { BadgeEvaluator } from '@drodil/backstage-plugin-qeta-node';
export class CustomBadgeEvaluator implements BadgeEvaluator {
// Unique key for the badge
public readonly key = 'custom-badge';
// Display name
public readonly name = 'Custom Badge';
// Description shown to users
public readonly description = 'Awarded for custom achievement';
// Icon name (see BadgeChip.tsx for available icons)
public readonly icon = 'star';
// Badge level: 'bronze' | 'silver' | 'gold' | 'diamond'
public readonly level = 'gold' as const;
// Badge type: 'one-time' (awarded once) or 'repetitive' (can be awarded multiple times)
public readonly type = 'one-time' as const;
// Reputation points awarded when badge is earned
public readonly reputation = 50;
// Option 1: Evaluate individual entities
async evaluate(entity: QetaIdEntity): Promise {
// Return true if the entity qualifies for this badge
return false;
}
// Option 2: Evaluate based on user statistics
async evaluateUser(user: UserResponse): Promise {
// Example: Award badge when user has 100+ followers
return user.totalFollowers >= 100;
}
// Option 3: Evaluate a collection of entities
async evaluateCollection(entities: QetaIdEntity[]): Promise {
// Example: Award badge when user has 10+ correct answers
return entities.filter(e => 'correct' in e && e.correct).length >= 10;
}
}
```
### Registering Custom Badge Evaluators
Register your custom evaluators using the `qetaBadgeEvaluatorExtensionPoint`:
```typescript
import { createBackendModule } from '@backstage/backend-plugin-api';
import { qetaBadgeEvaluatorExtensionPoint } from '@drodil/backstage-plugin-qeta-node';
import { CustomBadgeEvaluator } from './CustomBadgeEvaluator';
export const qetaCustomBadgesModule = createBackendModule({
pluginId: 'qeta',
moduleId: 'custom-badges',
register(reg) {
reg.registerInit({
deps: {
badges: qetaBadgeEvaluatorExtensionPoint,
},
async init({ badges }) {
badges.addEvaluator(new CustomBadgeEvaluator());
},
});
},
});
```
Then add the module to your backend in `packages/backend/src/index.ts`:
```typescript
import { qetaCustomBadgesModule } from './modules/qetaCustomBadges';
backend.add(qetaCustomBadgesModule);
```
### Badge Levels and Icons
Badge levels determine the visual styling:
- `bronze` - Bronze/copper gradient
- `silver` - Silver/gray gradient
- `gold` - Gold/yellow gradient
- `diamond` - Diamond/cyan gradient
See `BadgeChip.tsx` in `@drodil/backstage-plugin-qeta-react` for available icon names.
## AI Handler Extension
For details and the OpenAI reference implementation, see [AI Integration Documentation](ai.md).
## Tag Database Extension
You can provide custom tag descriptions by implementing the `TagDatabase` interface. This allows you to define custom tags with descriptions that are integrated into the plugin.
### Creating a Custom Tag Database
```typescript
import { TagDatabase } from '@drodil/backstage-plugin-qeta-node';
export class CustomTagDatabase implements TagDatabase {
/**
* Get custom tag descriptions.
* The format is { 'tag name': 'tag description' }.
*/
async getTags(): Promise> {
return {
kubernetes: 'Questions about Kubernetes orchestration',
'ci-cd': 'Continuous Integration and Continuous Deployment topics',
security: 'Security-related questions and best practices',
};
}
}
```
### Registering the Tag Database
Register your tag database using the `qetaTagDatabaseExtensionPoint`:
```typescript
import { createBackendModule } from '@backstage/backend-plugin-api';
import { qetaTagDatabaseExtensionPoint } from '@drodil/backstage-plugin-qeta-node';
import { CustomTagDatabase } from './CustomTagDatabase';
export const qetaCustomTagsModule = createBackendModule({
pluginId: 'qeta',
moduleId: 'custom-tags',
register(reg) {
reg.registerInit({
deps: {
tags: qetaTagDatabaseExtensionPoint,
},
async init({ tags }) {
tags.setTagDatabase(new CustomTagDatabase());
},
});
},
});
```
Then add the module to your backend in `packages/backend/src/index.ts`:
```typescript
import { qetaCustomTagsModule } from './modules/qetaCustomTags';
backend.add(qetaCustomTagsModule);
```
## Notification Receivers Extension
You can customize who receives notifications by implementing the `NotificationReceiversHandler` interface. This allows you to control notification routing based on your organization's requirements.
For more details, see [Notifications Documentation](notifications.md).