auto create and update hiero topic

This commit is contained in:
einhornimmond 2025-09-03 15:02:36 +02:00
parent b17c381f6c
commit c06f82bf29
6 changed files with 56 additions and 30 deletions

View File

@ -2,10 +2,16 @@ import { GraphQLClient } from 'graphql-request'
import { SignJWT } from 'jose'
import { CONFIG } from '../../config'
import { communitySchema, type Community, homeCommunityGraphqlQuery } from './community.schema'
import {
communitySchema,
type Community,
homeCommunityGraphqlQuery,
setHomeCommunityTopicId
} from './community.schema'
import { getLogger, Logger } from 'log4js'
import { LOG4JS_BASE_CATEGORY } from '../../config/const'
import * as v from 'valibot'
import { HieroId, Uuidv4 } from '../../schemas/typeGuard.schema'
// Source: https://refactoring.guru/design-patterns/singleton/typescript/example
// and ../federation/client/FederationClientFactory.ts
@ -53,9 +59,7 @@ export class BackendClient {
public async getHomeCommunityDraft(): Promise<Community> {
this.logger.info('check home community on backend')
const { data, errors } = await this.client.rawRequest<{ homeCommunity: Community }>(
homeCommunityGraphqlQuery,
{}, // empty variables
await this.getRequestHeader(),
homeCommunityGraphqlQuery, {}, await this.getRequestHeader(),
)
if (errors) {
throw errors[0]
@ -63,8 +67,15 @@ export class BackendClient {
return v.parse(communitySchema, data.homeCommunity)
}
public async setHomeCommunityTopicId(topicId: HieroId) {
public async setHomeCommunityTopicId(uuid: Uuidv4, hieroTopicId: HieroId): Promise<Community> {
this.logger.info('update home community on backend')
const { data, errors } = await this.client.rawRequest<{ updateHomeCommunity: Community }>(
setHomeCommunityTopicId, { uuid, hieroTopicId }, await this.getRequestHeader(),
)
if (errors) {
throw errors[0]
}
return v.parse(communitySchema, data.updateHomeCommunity)
}
private async getRequestHeader(): Promise<{

View File

@ -12,7 +12,7 @@ import { hieroIdSchema, uuidv4Schema } from '../../schemas/typeGuard.schema'
*/
export const communitySchema = v.object({
uuid: uuidv4Schema,
topicId: v.optional(hieroIdSchema, ''),
topicId: v.nullish(hieroIdSchema),
foreign: v.boolean('expect boolean type'),
createdAt: dateSchema,
})
@ -31,3 +31,14 @@ export const homeCommunityGraphqlQuery = gql`
}
}
`
export const setHomeCommunityTopicId = gql`
mutation ($uuid: string, $hieroTopicId: string){
updateHomeCommunity(uuid: $uuid, hieroTopicId: $hieroTopicId) {
uuid
topicId
foreign
creationDate
}
}
`

View File

@ -85,9 +85,9 @@ export class HieroClient {
return balance
}
public async getTopicInfo(topicId: HieroTopicId): Promise<TopicInfoOutput> {
public async getTopicInfo(topicId: HieroId): Promise<TopicInfoOutput> {
const info = await new TopicInfoQuery()
.setTopicId(new TopicId(topicId.getShardNum(), topicId.getRealmNum(), topicId.getTopicNum()))
.setTopicId(TopicId.fromString(topicId))
.execute(this.client)
this.logger.debug(JSON.stringify(info, null, 2))
return parse(topicInfoSchema, {
@ -108,9 +108,10 @@ export class HieroClient {
return parse(hieroIdSchema, createReceipt.topicId?.toString())
}
public async updateTopic(): Promise<void> {
public async updateTopic(topicId: HieroId): Promise<void> {
let transaction = new TopicUpdateTransaction()
transaction.setExpirationTime(new Date(new Date().getTime() + MIN_AUTORENEW_PERIOD * 1000))
transaction.setTopicId(TopicId.fromString(topicId))
transaction = await transaction.freezeWithSigner(this.wallet)
transaction = await transaction.signWithSigner(this.wallet)
const updateResponse = await transaction.executeWithSigner(this.wallet)

View File

@ -1,5 +1,6 @@
import * as v from 'valibot'
import { hieroIdSchema } from '../../schemas/typeGuard.schema'
import { dateSchema } from '../../schemas/typeConverter.schema'
// schema definitions for exporting data from hiero request as json back to caller
/*export const dateStringSchema = v.pipe(
@ -7,28 +8,12 @@ import { hieroIdSchema } from '../../schemas/typeGuard.schema'
v.transform(in: string | Date)
)*/
export const dateStringSchema = v.pipe(
v.union([v.string('expect valid date string'), v.instance(Date, 'expect Date object')]),
v.transform<string | Date, string>((input) => {
let date: Date
if (input instanceof Date) {
date = input
} else {
date = new Date(input)
}
if (isNaN(date.getTime())) {
throw new Error('invalid date')
}
return date.toISOString()
}),
)
export const positiveNumberSchema = v.pipe(v.number(), v.minValue(0))
export const topicInfoSchema = v.object({
topicId: hieroIdSchema,
sequenceNumber: positiveNumberSchema,
expirationTime: dateStringSchema,
expirationTime: dateSchema,
autoRenewPeriod: v.optional(positiveNumberSchema, 0),
autoRenewAccountId: v.optional(hieroIdSchema, '0.0.0'),
})

View File

@ -1 +1,3 @@
export const LOG4JS_BASE_CATEGORY = 'dlt'
// 7 days
export const MIN_TOPIC_EXPIRE_MILLISECONDS_FOR_UPDATE = 1000 * 60 * 60 * 24 * 7

View File

@ -12,6 +12,7 @@ import { KeyPairCacheManager } from './KeyPairCacheManager'
import { keyGenerationSeedSchema } from './schemas/base.schema'
import { isPortOpenRetry } from './utils/network'
import { appRoutes } from './server'
import { MIN_TOPIC_EXPIRE_MILLISECONDS_FOR_UPDATE } from './config/const'
async function main() {
// configure log4js
@ -36,13 +37,28 @@ async function main() {
if (!backend) {
throw new Error('cannot create backend client')
}
const hieroClient = HieroClient.getInstance()
if (!hieroClient) {
throw new Error('cannot create hiero client')
}
// wait for backend server
await isPortOpenRetry(CONFIG.BACKEND_SERVER_URL)
const homeCommunity = await backend.getHomeCommunityDraft()
let homeCommunity = await backend.getHomeCommunityDraft()
// on missing topicId, create one
if (!homeCommunity.topicId) {
const topicId = await HieroClient.getInstance().createTopic()
const topicId = await hieroClient.createTopic()
homeCommunity = await backend.setHomeCommunityTopicId(homeCommunity.uuid, topicId)
} else {
// if topic exist, check if we need to update it
let topicInfo = await hieroClient.getTopicInfo(homeCommunity.topicId)
if (topicInfo.expirationTime.getTime() - new Date().getTime() < MIN_TOPIC_EXPIRE_MILLISECONDS_FOR_UPDATE) {
await hieroClient.updateTopic(homeCommunity.topicId)
topicInfo = await hieroClient.getTopicInfo(homeCommunity.topicId)
logger.info('updated topic info, new expiration time: %s', topicInfo.expirationTime.toLocaleDateString())
}
}
if (!homeCommunity.topicId) {
throw new Error('still no topic id, after creating topic and update community in backend.')
}
KeyPairCacheManager.getInstance().setHomeCommunityTopicId(homeCommunity.topicId)
logger.info('home community topic: %s', homeCommunity.topicId)