-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #18 from Phoenix-Commerce/sample-plugin
feat: make sample plugin show off (and also test) some of the other features
- Loading branch information
Showing
11 changed files
with
216 additions
and
43 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import { Kafka, type Producer, type Consumer } from 'kafkajs'; | ||
import { Container, Service } from 'typedi'; | ||
|
||
@Service() | ||
class KafkaEventService { | ||
private kafka: Kafka; | ||
private readonly producer: Producer; | ||
private readonly consumer: Consumer; | ||
private eventHandlers: { [key: string]: ((message: any) => void)[] } = {}; | ||
private topics: string[] = []; | ||
|
||
constructor() { | ||
this.kafka = new Kafka({ | ||
clientId: 'my-app', | ||
brokers: [process.env.KAFKA_BROKER || 'localhost:29092'], // Use environment variable or default to localhost | ||
}); | ||
|
||
this.producer = this.kafka.producer(); | ||
this.consumer = this.kafka.consumer({ groupId: 'sample-group' }); | ||
|
||
this.initialize(); | ||
} | ||
|
||
private async initialize() { | ||
await this.retryConnect(this.producer.connect.bind(this.producer), 5); | ||
await this.retryConnect(this.consumer.connect.bind(this.consumer), 5); | ||
|
||
for (const topic of this.topics) { | ||
await this.consumer.subscribe({ topic, fromBeginning: true }); | ||
} | ||
|
||
await this.consumer.run({ | ||
eachMessage: async ({ topic, partition, message }) => { | ||
if (message.value) { | ||
const handlers = this.eventHandlers[topic] || []; | ||
const parsedMessage = JSON.parse(message.value.toString()); | ||
handlers.forEach(handler => handler(parsedMessage)); | ||
} | ||
}, | ||
}); | ||
} | ||
|
||
private async retryConnect(connectFn: () => Promise<void>, retries: number) { | ||
for (let i = 0; i < retries; i++) { | ||
try { | ||
await connectFn(); | ||
return; | ||
} catch (err) { | ||
if (err instanceof Error) { | ||
console.error(`Connection attempt ${i + 1} failed: ${err.message}`); | ||
} else { | ||
console.error(`Connection attempt ${i + 1} failed: ${err}`); | ||
} | ||
if (i === retries - 1) throw err; | ||
await new Promise(res => setTimeout(res, 2000)); // Wait for 2 seconds before retrying | ||
} | ||
} | ||
} | ||
|
||
registerTopic(topic: string) { | ||
if (!this.topics.includes(topic)) { | ||
this.topics.push(topic); | ||
} | ||
} | ||
|
||
async emitEvent(topic: string, message: any) { | ||
await this.producer.send({ | ||
topic, | ||
messages: [{ value: JSON.stringify(message) }], | ||
}); | ||
} | ||
|
||
subscribeToEvent(topic: string, handler: (message: any) => void) { | ||
if (!this.eventHandlers[topic]) { | ||
this.eventHandlers[topic] = []; | ||
} | ||
this.eventHandlers[topic].push(handler); | ||
} | ||
} | ||
|
||
// Register the service with typedi | ||
Container.set(KafkaEventService, new KafkaEventService()); | ||
|
||
export default KafkaEventService; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,34 @@ | ||
import { Container } from 'typedi'; | ||
import { getModelForClass } from '@typegoose/typegoose'; | ||
import { type GlobalContext } from '../global-context'; | ||
import { Sample } from './models/sample'; | ||
import { SampleResolver } from './resolvers/sample-resolver'; | ||
import type { Plugin } from '../plugin-interface'; | ||
import FunctionRegistry from '../function-registry'; | ||
import { SampleService } from './services/sample-service'; | ||
import KafkaEventService from '../../event/kafka-event-service'; | ||
|
||
const samplePlugin: Plugin = { | ||
name: 'Sample Plugin', | ||
type: 'sample', | ||
export default { | ||
name: 'sample-plugin', | ||
type: 'demonstration', | ||
resolvers: [SampleResolver], | ||
register: (container: any) => { | ||
// Perform any additional registration if necessary | ||
const functionRegistry = FunctionRegistry.getInstance(); | ||
functionRegistry.registerFunction('sample', () => console.log('Sample function called')); | ||
register(container: typeof Container, context: GlobalContext) { | ||
const SampleModel = getModelForClass(Sample); | ||
context.models['Sample'] = { schema: SampleModel.schema, model: SampleModel }; | ||
container.set('SampleModel', SampleModel); | ||
|
||
// Register SampleService and KafkaEventService with typedi | ||
container.set(SampleService, new SampleService(Container.get(KafkaEventService))); | ||
|
||
// Ensure SampleResolver is added to context resolvers | ||
context.resolvers['Sample'] = [SampleResolver]; | ||
|
||
// Register the topic with KafkaEventService | ||
const eventService = Container.get(KafkaEventService); | ||
eventService.registerTopic('sampleCreated'); | ||
|
||
// Set up event handlers using the centralized event service | ||
eventService.subscribeToEvent('sampleCreated', (sample) => { | ||
console.log('Sample created:', sample); | ||
// Additional handling logic here | ||
}); | ||
}, | ||
}; | ||
|
||
export default samplePlugin; |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,20 @@ | ||
// src/services/sample-service.ts | ||
import { Service } from 'typedi'; | ||
import { Sample, SampleModel } from '../entities/sample'; | ||
import { Sample, SampleModel } from '../models/sample'; | ||
import KafkaEventService from '../../../event/kafka-event-service'; | ||
|
||
@Service() | ||
export class SampleService { | ||
constructor(private eventService: KafkaEventService) {} | ||
|
||
async getAllSamples(): Promise<Sample[]> { | ||
return SampleModel.find().exec(); | ||
} | ||
|
||
async createSample(name: string): Promise<Sample> { | ||
const sample = new SampleModel({ name }); | ||
return sample.save(); | ||
const savedSample = await sample.save(); | ||
await this.eventService.emitEvent('sampleCreated', savedSample); // Emit event using the centralized service | ||
return savedSample; | ||
} | ||
} |