-
Notifications
You must be signed in to change notification settings - Fork 204
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: ability to add generic records (#702)
feat: extension module creation (#688) Co-authored-by: Berend Sliedrecht <[email protected]> Co-authored-by: Timo Glastra <[email protected]>
- Loading branch information
1 parent
020e6ef
commit e617496
Showing
7 changed files
with
354 additions
and
45 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
77 changes: 77 additions & 0 deletions
77
packages/core/src/modules/generic-records/GenericRecordsModule.ts
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,77 @@ | ||
import type { Logger } from '../../logger' | ||
import type { GenericRecord, GenericRecordTags, SaveGenericRecordOption } from './repository/GenericRecord' | ||
|
||
import { Lifecycle, scoped } from 'tsyringe' | ||
|
||
import { AgentConfig } from '../../agent/AgentConfig' | ||
|
||
import { GenericRecordService } from './service/GenericRecordService' | ||
|
||
export type ContentType = { | ||
content: string | ||
} | ||
|
||
@scoped(Lifecycle.ContainerScoped) | ||
export class GenericRecordsModule { | ||
private genericRecordsService: GenericRecordService | ||
private logger: Logger | ||
public constructor(agentConfig: AgentConfig, genericRecordsService: GenericRecordService) { | ||
this.genericRecordsService = genericRecordsService | ||
this.logger = agentConfig.logger | ||
} | ||
|
||
public async save({ content, tags }: SaveGenericRecordOption) { | ||
try { | ||
const record = await this.genericRecordsService.save({ | ||
content: content, | ||
tags: tags, | ||
}) | ||
return record | ||
} catch (error) { | ||
this.logger.error('Error while saving generic-record', { | ||
error, | ||
content, | ||
errorMessage: error instanceof Error ? error.message : error, | ||
}) | ||
throw error | ||
} | ||
} | ||
|
||
public async delete(record: GenericRecord): Promise<void> { | ||
try { | ||
await this.genericRecordsService.delete(record) | ||
} catch (error) { | ||
this.logger.error('Error while saving generic-record', { | ||
error, | ||
content: record.content, | ||
errorMessage: error instanceof Error ? error.message : error, | ||
}) | ||
throw error | ||
} | ||
} | ||
|
||
public async update(record: GenericRecord): Promise<void> { | ||
try { | ||
await this.genericRecordsService.update(record) | ||
} catch (error) { | ||
this.logger.error('Error while update generic-record', { | ||
error, | ||
content: record.content, | ||
errorMessage: error instanceof Error ? error.message : error, | ||
}) | ||
throw error | ||
} | ||
} | ||
|
||
public async findById(id: string) { | ||
return this.genericRecordsService.findById(id) | ||
} | ||
|
||
public async findAllByQuery(query: Partial<GenericRecordTags>): Promise<GenericRecord[]> { | ||
return this.genericRecordsService.findAllByQuery(query) | ||
} | ||
|
||
public async getAll(): Promise<GenericRecord[]> { | ||
return this.genericRecordsService.getAll() | ||
} | ||
} |
45 changes: 45 additions & 0 deletions
45
packages/core/src/modules/generic-records/repository/GenericRecord.ts
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,45 @@ | ||
import type { RecordTags, TagsBase } from '../../../storage/BaseRecord' | ||
|
||
import { BaseRecord } from '../../../storage/BaseRecord' | ||
import { uuid } from '../../../utils/uuid' | ||
|
||
export type GenericRecordTags = TagsBase | ||
|
||
export type BasicMessageTags = RecordTags<GenericRecord> | ||
|
||
export interface GenericRecordStorageProps { | ||
id?: string | ||
createdAt?: Date | ||
tags?: GenericRecordTags | ||
content: Record<string, unknown> | ||
} | ||
|
||
export interface SaveGenericRecordOption { | ||
content: Record<string, unknown> | ||
id?: string | ||
tags?: GenericRecordTags | ||
} | ||
|
||
export class GenericRecord extends BaseRecord<GenericRecordTags> { | ||
public content!: Record<string, unknown> | ||
|
||
public static readonly type = 'GenericRecord' | ||
public readonly type = GenericRecord.type | ||
|
||
public constructor(props: GenericRecordStorageProps) { | ||
super() | ||
|
||
if (props) { | ||
this.id = props.id ?? uuid() | ||
this.createdAt = props.createdAt ?? new Date() | ||
this.content = props.content | ||
this._tags = props.tags ?? {} | ||
} | ||
} | ||
|
||
public getTags() { | ||
return { | ||
...this._tags, | ||
} | ||
} | ||
} |
14 changes: 14 additions & 0 deletions
14
packages/core/src/modules/generic-records/repository/GenericRecordsRepository.ts
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,14 @@ | ||
import { inject, scoped, Lifecycle } from 'tsyringe' | ||
|
||
import { InjectionSymbols } from '../../../constants' | ||
import { Repository } from '../../../storage/Repository' | ||
import { StorageService } from '../../../storage/StorageService' | ||
|
||
import { GenericRecord } from './GenericRecord' | ||
|
||
@scoped(Lifecycle.ContainerScoped) | ||
export class GenericRecordsRepository extends Repository<GenericRecord> { | ||
public constructor(@inject(InjectionSymbols.StorageService) storageService: StorageService<GenericRecord>) { | ||
super(GenericRecord, storageService) | ||
} | ||
} |
60 changes: 60 additions & 0 deletions
60
packages/core/src/modules/generic-records/service/GenericRecordService.ts
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,60 @@ | ||
import type { GenericRecordTags, SaveGenericRecordOption } from '../repository/GenericRecord' | ||
|
||
import { Lifecycle, scoped } from 'tsyringe' | ||
|
||
import { AriesFrameworkError } from '../../../error' | ||
import { GenericRecord } from '../repository/GenericRecord' | ||
import { GenericRecordsRepository } from '../repository/GenericRecordsRepository' | ||
|
||
@scoped(Lifecycle.ContainerScoped) | ||
export class GenericRecordService { | ||
private genericRecordsRepository: GenericRecordsRepository | ||
|
||
public constructor(genericRecordsRepository: GenericRecordsRepository) { | ||
this.genericRecordsRepository = genericRecordsRepository | ||
} | ||
|
||
public async save({ content, tags }: SaveGenericRecordOption) { | ||
const genericRecord = new GenericRecord({ | ||
content: content, | ||
tags: tags, | ||
}) | ||
|
||
try { | ||
await this.genericRecordsRepository.save(genericRecord) | ||
return genericRecord | ||
} catch (error) { | ||
throw new AriesFrameworkError( | ||
`Unable to store the genericRecord record with id ${genericRecord.id}. Message: ${error}` | ||
) | ||
} | ||
} | ||
|
||
public async delete(record: GenericRecord): Promise<void> { | ||
try { | ||
await this.genericRecordsRepository.delete(record) | ||
} catch (error) { | ||
throw new AriesFrameworkError(`Unable to delete the genericRecord record with id ${record.id}. Message: ${error}`) | ||
} | ||
} | ||
|
||
public async update(record: GenericRecord): Promise<void> { | ||
try { | ||
await this.genericRecordsRepository.update(record) | ||
} catch (error) { | ||
throw new AriesFrameworkError(`Unable to update the genericRecord record with id ${record.id}. Message: ${error}`) | ||
} | ||
} | ||
|
||
public async findAllByQuery(query: Partial<GenericRecordTags>) { | ||
return this.genericRecordsRepository.findByQuery(query) | ||
} | ||
|
||
public async findById(id: string): Promise<GenericRecord | null> { | ||
return this.genericRecordsRepository.findById(id) | ||
} | ||
|
||
public async getAll() { | ||
return this.genericRecordsRepository.getAll() | ||
} | ||
} |
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,110 @@ | ||
import type { GenericRecord } from '../src/modules/generic-records/repository/GenericRecord' | ||
|
||
import { Agent } from '../src/agent/Agent' | ||
|
||
import { getBaseConfig } from './helpers' | ||
|
||
const aliceConfig = getBaseConfig('Agents Alice', { | ||
endpoints: ['rxjs:alice'], | ||
}) | ||
|
||
describe('genericRecords', () => { | ||
let aliceAgent: Agent | ||
|
||
const fooString = { foo: 'Some data saved' } | ||
const fooNumber = { foo: 42 } | ||
|
||
const barString: Record<string, unknown> = fooString | ||
const barNumber: Record<string, unknown> = fooNumber | ||
|
||
afterAll(async () => { | ||
await aliceAgent.shutdown() | ||
await aliceAgent.wallet.delete() | ||
}) | ||
|
||
test('store generic-record record', async () => { | ||
aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) | ||
await aliceAgent.initialize() | ||
|
||
//Save genericRecord message (Minimal) | ||
|
||
const savedRecord1: GenericRecord = await aliceAgent.genericRecords.save({ content: barString }) | ||
|
||
//Save genericRecord message with tag | ||
const tags1 = { myTag: 'foobar1' } | ||
const tags2 = { myTag: 'foobar2' } | ||
|
||
const savedRecord2: GenericRecord = await aliceAgent.genericRecords.save({ content: barNumber, tags: tags1 }) | ||
|
||
expect(savedRecord1).toBeDefined() | ||
expect(savedRecord2).toBeDefined() | ||
|
||
const savedRecord3: GenericRecord = await aliceAgent.genericRecords.save({ content: barString, tags: tags2 }) | ||
expect(savedRecord3).toBeDefined() | ||
}) | ||
|
||
test('get generic-record records', async () => { | ||
//Create genericRecord message | ||
const savedRecords = await aliceAgent.genericRecords.getAll() | ||
expect(savedRecords.length).toBe(3) | ||
}) | ||
|
||
test('get generic-record specific record', async () => { | ||
//Create genericRecord message | ||
const savedRecords1 = await aliceAgent.genericRecords.findAllByQuery({ myTag: 'foobar1' }) | ||
expect(savedRecords1?.length == 1).toBe(true) | ||
expect(savedRecords1[0].content).toEqual({ foo: 42 }) | ||
|
||
const savedRecords2 = await aliceAgent.genericRecords.findAllByQuery({ myTag: 'foobar2' }) | ||
expect(savedRecords2?.length == 1).toBe(true) | ||
expect(savedRecords2[0].content).toEqual({ foo: 'Some data saved' }) | ||
}) | ||
|
||
test('find generic record using id', async () => { | ||
const myId = '100' | ||
const savedRecord1: GenericRecord = await aliceAgent.genericRecords.save({ content: barString, id: myId }) | ||
expect(savedRecord1).toBeDefined() | ||
|
||
const retrievedRecord: GenericRecord | null = await aliceAgent.genericRecords.findById(savedRecord1.id) | ||
|
||
if (retrievedRecord) { | ||
expect(retrievedRecord.content).toEqual({ foo: 'Some data saved' }) | ||
} else { | ||
throw Error('retrieved record not found') | ||
} | ||
}) | ||
|
||
test('delete generic record', async () => { | ||
const myId = '100' | ||
const savedRecord1: GenericRecord = await aliceAgent.genericRecords.save({ content: barString, id: myId }) | ||
expect(savedRecord1).toBeDefined() | ||
|
||
await aliceAgent.genericRecords.delete(savedRecord1) | ||
|
||
const retrievedRecord: GenericRecord | null = await aliceAgent.genericRecords.findById(savedRecord1.id) | ||
expect(retrievedRecord).toBeNull() | ||
}) | ||
|
||
test('update generic record', async () => { | ||
const myId = '100' | ||
const savedRecord1: GenericRecord = await aliceAgent.genericRecords.save({ content: barString, id: myId }) | ||
expect(savedRecord1).toBeDefined() | ||
|
||
let retrievedRecord: GenericRecord | null = await aliceAgent.genericRecords.findById(savedRecord1.id) | ||
expect(retrievedRecord).toBeDefined() | ||
|
||
const amendedFooString = { foo: 'Some even more cool data saved' } | ||
const barString2: Record<string, unknown> = amendedFooString | ||
|
||
savedRecord1.content = barString2 | ||
|
||
await aliceAgent.genericRecords.update(savedRecord1) | ||
|
||
retrievedRecord = await aliceAgent.genericRecords.findById(savedRecord1.id) | ||
if (retrievedRecord) { | ||
expect(retrievedRecord.content).toEqual({ foo: 'Some even more cool data saved' }) | ||
} else { | ||
throw Error('retrieved record not found in update test') | ||
} | ||
}) | ||
}) |
Oops, something went wrong.