From 8998310c4b0acb8eea3e07f39fbaeb888ea3a6be Mon Sep 17 00:00:00 2001 From: William Calderipe Date: Thu, 7 Mar 2024 09:37:51 +0100 Subject: [PATCH] Implement data store service for the file system and HTTP (#156) --- README.md | 10 +- .../core/exception/data-store.exception.ts | 3 + .../factory/data-store-repository.factory.ts | 33 ++++ .../core/repository/data-store.repository.ts | 3 + .../integration/data-store.service.spec.ts | 57 +++++++ .../app/core/service/data-store.service.ts | 51 ++++++ .../file-system-data-store.repository.spec.ts | 45 +++++ .../http-data-store.repository.spec.ts | 46 ++++++ .../__test__/unit/tenant.repository.spec.ts | 6 + .../file-system-data-store.repository.ts | 49 ++++++ .../repository/http-data-store.repository.ts | 26 +++ .../repository/tenant.repository.ts | 35 +++- .../core/service/key-value.service.ts | 4 + .../src/shared/schema/tenant.schema.ts | 2 + .../testing/with-temp-json-file.testing.ts | 24 +++ packages/policy-engine-shared/src/index.ts | 16 +- .../src/lib/schema/data-store.schema.ts | 30 ++-- .../src/lib/schema/entity.schema.ts | 19 ++- .../src/lib/type/entity.type.ts | 155 ++++++------------ 19 files changed, 487 insertions(+), 127 deletions(-) create mode 100644 apps/policy-engine/src/app/core/exception/data-store.exception.ts create mode 100644 apps/policy-engine/src/app/core/factory/data-store-repository.factory.ts create mode 100644 apps/policy-engine/src/app/core/repository/data-store.repository.ts create mode 100644 apps/policy-engine/src/app/core/service/__test__/integration/data-store.service.spec.ts create mode 100644 apps/policy-engine/src/app/core/service/data-store.service.ts create mode 100644 apps/policy-engine/src/app/persistence/repository/__test__/integration/file-system-data-store.repository.spec.ts create mode 100644 apps/policy-engine/src/app/persistence/repository/__test__/integration/http-data-store.repository.spec.ts create mode 100644 apps/policy-engine/src/app/persistence/repository/file-system-data-store.repository.ts create mode 100644 apps/policy-engine/src/app/persistence/repository/http-data-store.repository.ts create mode 100644 apps/policy-engine/src/shared/testing/with-temp-json-file.testing.ts diff --git a/README.md b/README.md index 9e3c2f2b1..3c6e4446f 100644 --- a/README.md +++ b/README.md @@ -64,11 +64,11 @@ below to generate a project of your choice. ```bash # Generate an standard JavaScript library. - npx nx g @nrwl/workspace:lib - # Generate an NestJS library. - npx nx g @nx/nest:library - # Generate an NestJS application. - npx nx g @nx/nest:application +npx nx g @nrwl/workspace:lib +# Generate an NestJS library. +npx nx g @nx/nest:library +# Generate an NestJS application. +npx nx g @nx/nest:application ``` For more information about code generation, please refer to the [NX diff --git a/apps/policy-engine/src/app/core/exception/data-store.exception.ts b/apps/policy-engine/src/app/core/exception/data-store.exception.ts new file mode 100644 index 000000000..0d5dd65f9 --- /dev/null +++ b/apps/policy-engine/src/app/core/exception/data-store.exception.ts @@ -0,0 +1,3 @@ +import { ApplicationException } from '../../../shared/exception/application.exception' + +export class DataStoreException extends ApplicationException {} diff --git a/apps/policy-engine/src/app/core/factory/data-store-repository.factory.ts b/apps/policy-engine/src/app/core/factory/data-store-repository.factory.ts new file mode 100644 index 000000000..7330b7739 --- /dev/null +++ b/apps/policy-engine/src/app/core/factory/data-store-repository.factory.ts @@ -0,0 +1,33 @@ +import { HttpStatus, Injectable } from '@nestjs/common' +import { FileSystemDataStoreRepository } from '../../persistence/repository/file-system-data-store.repository' +import { HttpDataStoreRepository } from '../../persistence/repository/http-data-store.repository' +import { DataStoreException } from '../exception/data-store.exception' +import { DataStoreRepository } from '../repository/data-store.repository' + +@Injectable() +export class DataStoreRepositoryFactory { + constructor( + private fileSystemRepository: FileSystemDataStoreRepository, + private httpRepository: HttpDataStoreRepository + ) {} + + getRepository(url: string): DataStoreRepository { + switch (this.getProtocol(url)) { + case 'file': + return this.fileSystemRepository + case 'http': + case 'https': + return this.httpRepository + default: + throw new DataStoreException({ + message: 'Data store URL protocol not supported', + suggestedHttpStatusCode: HttpStatus.UNPROCESSABLE_ENTITY, + context: { url } + }) + } + } + + private getProtocol(url: string): string { + return url.split(':')[0] + } +} diff --git a/apps/policy-engine/src/app/core/repository/data-store.repository.ts b/apps/policy-engine/src/app/core/repository/data-store.repository.ts new file mode 100644 index 000000000..a34e57910 --- /dev/null +++ b/apps/policy-engine/src/app/core/repository/data-store.repository.ts @@ -0,0 +1,3 @@ +export interface DataStoreRepository { + fetch(url: string): Promise +} diff --git a/apps/policy-engine/src/app/core/service/__test__/integration/data-store.service.spec.ts b/apps/policy-engine/src/app/core/service/__test__/integration/data-store.service.spec.ts new file mode 100644 index 000000000..85e72f87a --- /dev/null +++ b/apps/policy-engine/src/app/core/service/__test__/integration/data-store.service.spec.ts @@ -0,0 +1,57 @@ +import { DataStoreConfiguration, EntityData, EntitySignature, FIXTURE } from '@narval/policy-engine-shared' +import { HttpModule } from '@nestjs/axios' +import { HttpStatus } from '@nestjs/common' +import { Test } from '@nestjs/testing' +import nock from 'nock' +import { FileSystemDataStoreRepository } from '../../../../../app/persistence/repository/file-system-data-store.repository' +import { HttpDataStoreRepository } from '../../../../../app/persistence/repository/http-data-store.repository' +import { withTempJsonFile } from '../../../../../shared/testing/with-temp-json-file.testing' +import { DataStoreRepositoryFactory } from '../../../factory/data-store-repository.factory' +import { DataStoreService } from '../../data-store.service' + +describe(DataStoreService.name, () => { + let service: DataStoreService + + const remoteDataStoreUrl = 'http://9.9.9.9:9000' + + const entityDataStore: EntityData = { + entity: { + data: FIXTURE.ENTITIES + } + } + + const entitySignatureStore: EntitySignature = { + entity: { + signature: 'test-signature' + } + } + + beforeEach(async () => { + const module = await Test.createTestingModule({ + imports: [HttpModule], + providers: [DataStoreService, DataStoreRepositoryFactory, HttpDataStoreRepository, FileSystemDataStoreRepository] + }).compile() + + service = module.get(DataStoreService) + }) + + describe('fetch', () => { + it('fetches data and signature from distinct stores', async () => { + nock(remoteDataStoreUrl).get('/').reply(HttpStatus.OK, entityDataStore) + + await withTempJsonFile(JSON.stringify(entitySignatureStore), async (path) => { + const url = `file://${path}` + const config: DataStoreConfiguration = { + dataUrl: remoteDataStoreUrl, + signatureUrl: url, + keys: [] + } + + const { entity } = await service.fetch(config) + + expect(entity.data).toEqual(entityDataStore.entity.data) + expect(entity.signature).toEqual(entitySignatureStore.entity.signature) + }) + }) + }) +}) diff --git a/apps/policy-engine/src/app/core/service/data-store.service.ts b/apps/policy-engine/src/app/core/service/data-store.service.ts new file mode 100644 index 000000000..dbb55eca3 --- /dev/null +++ b/apps/policy-engine/src/app/core/service/data-store.service.ts @@ -0,0 +1,51 @@ +import { DataStoreConfiguration, entityDataSchema, entitySignatureSchema } from '@narval/policy-engine-shared' +import { HttpStatus, Injectable } from '@nestjs/common' +import { ZodObject, z } from 'zod' +import { DataStoreException } from '../exception/data-store.exception' +import { DataStoreRepositoryFactory } from '../factory/data-store-repository.factory' + +@Injectable() +export class DataStoreService { + constructor(private dataStoreRepositoryFactory: DataStoreRepositoryFactory) {} + + async fetch(config: DataStoreConfiguration) { + const [entityData, entitySignature] = await Promise.all([ + this.fetchByUrl(config.dataUrl, entityDataSchema), + this.fetchByUrl(config.signatureUrl, entitySignatureSchema) + ]) + + return { + entity: { + data: entityData.entity.data, + signature: entitySignature.entity.signature + } + } + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + private async fetchByUrl>( + url: string, + schema: DataSchema + ): Promise> { + const data = await this.dataStoreRepositoryFactory.getRepository(url).fetch(url) + const result = schema.safeParse(data) + + if (result.success) { + return result.data + } + + throw new DataStoreException({ + message: 'Invalid store schema', + suggestedHttpStatusCode: HttpStatus.UNPROCESSABLE_ENTITY, + context: { + ...(schema.description ? { schema: schema.description } : {}), + url, + errors: result.error.errors.map(({ path, message, code }) => ({ + path, + code, + message + })) + } + }) + } +} diff --git a/apps/policy-engine/src/app/persistence/repository/__test__/integration/file-system-data-store.repository.spec.ts b/apps/policy-engine/src/app/persistence/repository/__test__/integration/file-system-data-store.repository.spec.ts new file mode 100644 index 000000000..b54e2b023 --- /dev/null +++ b/apps/policy-engine/src/app/persistence/repository/__test__/integration/file-system-data-store.repository.spec.ts @@ -0,0 +1,45 @@ +import { EntityData, FIXTURE } from '@narval/policy-engine-shared' +import { Test } from '@nestjs/testing' +import { withTempJsonFile } from '../../../../../shared/testing/with-temp-json-file.testing' +import { DataStoreException } from '../../../../core/exception/data-store.exception' +import { FileSystemDataStoreRepository } from '../../file-system-data-store.repository' + +describe(FileSystemDataStoreRepository.name, () => { + let repository: FileSystemDataStoreRepository + + const entityData: EntityData = { + entity: { + data: FIXTURE.ENTITIES + } + } + + beforeEach(async () => { + const module = await Test.createTestingModule({ + providers: [FileSystemDataStoreRepository] + }).compile() + + repository = module.get(FileSystemDataStoreRepository) + }) + + describe('fetch', () => { + it('fetches data from a data source in the local file system', async () => { + await withTempJsonFile(JSON.stringify(entityData), async (path) => { + const data = await repository.fetch(`file://${path}`) + + expect(data).toEqual(entityData) + }) + }) + + it('throws a DataStoreException when file does not exist', async () => { + const notFoundDataStoreUrl = 'file://./this-file-does-not-exist-in-the-file-system.json' + + await expect(() => repository.fetch(notFoundDataStoreUrl)).rejects.toThrow(DataStoreException) + }) + + it('throws a DataStoreException when the json is invalid', async () => { + await withTempJsonFile('[ invalid }', async (path: string) => { + await expect(() => repository.fetch(`file://${path}`)).rejects.toThrow(DataStoreException) + }) + }) + }) +}) diff --git a/apps/policy-engine/src/app/persistence/repository/__test__/integration/http-data-store.repository.spec.ts b/apps/policy-engine/src/app/persistence/repository/__test__/integration/http-data-store.repository.spec.ts new file mode 100644 index 000000000..093076dcc --- /dev/null +++ b/apps/policy-engine/src/app/persistence/repository/__test__/integration/http-data-store.repository.spec.ts @@ -0,0 +1,46 @@ +import { EntityData, FIXTURE } from '@narval/policy-engine-shared' +import { HttpModule } from '@nestjs/axios' +import { HttpStatus } from '@nestjs/common' +import { Test } from '@nestjs/testing' +import nock from 'nock' +import { DataStoreException } from '../../../../../app/core/exception/data-store.exception' +import { HttpDataStoreRepository } from '../../http-data-store.repository' + +describe(HttpDataStoreRepository.name, () => { + let repository: HttpDataStoreRepository + + const dataStoreHost = 'http://some.host:3010' + const dataStoreEndpoint = '/data-store/entities' + const dataStoreUrl = dataStoreHost + dataStoreEndpoint + + const entityData: EntityData = { + entity: { + data: FIXTURE.ENTITIES + } + } + + beforeEach(async () => { + const module = await Test.createTestingModule({ + imports: [HttpModule], + providers: [HttpDataStoreRepository] + }).compile() + + repository = module.get(HttpDataStoreRepository) + }) + + describe('fetch', () => { + it('fetches data from a remote data source via http protocol', async () => { + nock(dataStoreHost).get(dataStoreEndpoint).reply(HttpStatus.OK, entityData) + + const data = await repository.fetch(dataStoreUrl) + + expect(data).toEqual(entityData) + }) + + it('throws a DataStoreException when it fails to fetch', async () => { + nock(dataStoreHost).get(dataStoreEndpoint).reply(HttpStatus.INTERNAL_SERVER_ERROR, {}) + + await expect(() => repository.fetch(dataStoreUrl)).rejects.toThrow(DataStoreException) + }) + }) +}) diff --git a/apps/policy-engine/src/app/persistence/repository/__test__/unit/tenant.repository.spec.ts b/apps/policy-engine/src/app/persistence/repository/__test__/unit/tenant.repository.spec.ts index 138339489..c0e7b2602 100644 --- a/apps/policy-engine/src/app/persistence/repository/__test__/unit/tenant.repository.spec.ts +++ b/apps/policy-engine/src/app/persistence/repository/__test__/unit/tenant.repository.spec.ts @@ -79,5 +79,11 @@ describe(TenantRepository.name, () => { expect(value).not.toEqual(null) expect(tenant).toEqual(actualTenant) }) + + it('indexes the new tenant', async () => { + await repository.create(tenant) + + expect(await repository.getTenantIndex()).toEqual([tenant.clientId]) + }) }) }) diff --git a/apps/policy-engine/src/app/persistence/repository/file-system-data-store.repository.ts b/apps/policy-engine/src/app/persistence/repository/file-system-data-store.repository.ts new file mode 100644 index 000000000..7dcd1e4bc --- /dev/null +++ b/apps/policy-engine/src/app/persistence/repository/file-system-data-store.repository.ts @@ -0,0 +1,49 @@ +import { HttpStatus, Injectable } from '@nestjs/common' +import fs from 'fs/promises' +import { DataStoreException } from '../../core/exception/data-store.exception' +import { DataStoreRepository } from '../../core/repository/data-store.repository' + +@Injectable() +export class FileSystemDataStoreRepository implements DataStoreRepository { + async fetch(url: string): Promise { + const path = this.getPath(url) + + if (await this.exists(path)) { + return this.read(path) as Data + } + + throw new DataStoreException({ + message: 'Data store file does not exist in the instance host', + suggestedHttpStatusCode: HttpStatus.NOT_FOUND, + context: { url } + }) + } + + private async read(path: string) { + try { + const content = await fs.readFile(path, 'utf-8') + + return JSON.parse(content) + } catch (error) { + throw new DataStoreException({ + message: 'Unable to parse data store file into JSON', + suggestedHttpStatusCode: HttpStatus.UNPROCESSABLE_ENTITY, + origin: error + }) + } + } + + private async exists(path: string): Promise { + try { + await fs.stat(path) + + return true + } catch (error) { + return false + } + } + + private getPath(url: string): string { + return url.replace('file://', '') + } +} diff --git a/apps/policy-engine/src/app/persistence/repository/http-data-store.repository.ts b/apps/policy-engine/src/app/persistence/repository/http-data-store.repository.ts new file mode 100644 index 000000000..2b5d162b3 --- /dev/null +++ b/apps/policy-engine/src/app/persistence/repository/http-data-store.repository.ts @@ -0,0 +1,26 @@ +import { HttpService } from '@nestjs/axios' +import { HttpStatus, Injectable } from '@nestjs/common' +import { catchError, lastValueFrom, map } from 'rxjs' +import { DataStoreException } from '../../core/exception/data-store.exception' +import { DataStoreRepository } from '../../core/repository/data-store.repository' + +@Injectable() +export class HttpDataStoreRepository implements DataStoreRepository { + constructor(private httpService: HttpService) {} + + fetch(url: string): Promise { + return lastValueFrom( + this.httpService.get(url).pipe( + map((response) => response.data), + catchError((error) => { + throw new DataStoreException({ + message: 'Unable to fetch remote data source via HTTP', + suggestedHttpStatusCode: HttpStatus.UNPROCESSABLE_ENTITY, + context: { url }, + origin: error + }) + }) + ) + ) + } +} diff --git a/apps/policy-engine/src/app/persistence/repository/tenant.repository.ts b/apps/policy-engine/src/app/persistence/repository/tenant.repository.ts index 83418e3be..7063e45f5 100644 --- a/apps/policy-engine/src/app/persistence/repository/tenant.repository.ts +++ b/apps/policy-engine/src/app/persistence/repository/tenant.repository.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common' import { KeyValueService } from '../../../shared/module/key-value/core/service/key-value.service' -import { tenantSchema } from '../../../shared/schema/tenant.schema' +import { tenantIndexSchema, tenantSchema } from '../../../shared/schema/tenant.schema' import { Tenant } from '../../../shared/type/domain.type' @Injectable() @@ -19,19 +19,50 @@ export class TenantRepository { async create(tenant: Tenant): Promise { await this.keyValueService.set(this.getKey(tenant.clientId), this.encode(tenant)) + await this.index(tenant) return tenant } + private async index(tenant: Tenant): Promise { + const currentIndex = await this.getTenantIndex() + + await this.keyValueService.set(this.getIndexKey(), this.encodeIndex([...currentIndex, tenant.clientId])) + + return true + } + + async getTenantIndex(): Promise { + const index = await this.keyValueService.get(this.getIndexKey()) + + if (index) { + return this.decodeIndex(index) + } + + return [] + } + getKey(clientId: string): string { return `tenant:${clientId}` } + getIndexKey(): string { + return `tenants` + } + private encode(tenant: Tenant): string { - return JSON.stringify(tenant) + return KeyValueService.encode(tenantSchema.parse(tenant)) } private decode(value: string): Tenant { return tenantSchema.parse(JSON.parse(value)) } + + private encodeIndex(value: string[]): string { + return KeyValueService.encode(tenantIndexSchema.parse(value)) + } + + private decodeIndex(value: string): string[] { + return tenantIndexSchema.parse(JSON.parse(value)) + } } diff --git a/apps/policy-engine/src/shared/module/key-value/core/service/key-value.service.ts b/apps/policy-engine/src/shared/module/key-value/core/service/key-value.service.ts index 54a11a02c..eca17dc9d 100644 --- a/apps/policy-engine/src/shared/module/key-value/core/service/key-value.service.ts +++ b/apps/policy-engine/src/shared/module/key-value/core/service/key-value.service.ts @@ -43,4 +43,8 @@ export class KeyValueService { async delete(key: string): Promise { return this.keyValueRepository.delete(key) } + + static encode(value: unknown): string { + return JSON.stringify(value) + } } diff --git a/apps/policy-engine/src/shared/schema/tenant.schema.ts b/apps/policy-engine/src/shared/schema/tenant.schema.ts index 64848aca8..f34c0f87c 100644 --- a/apps/policy-engine/src/shared/schema/tenant.schema.ts +++ b/apps/policy-engine/src/shared/schema/tenant.schema.ts @@ -11,3 +11,5 @@ export const tenantSchema = z.object({ createdAt: z.coerce.date(), updatedAt: z.coerce.date() }) + +export const tenantIndexSchema = z.array(z.string()) diff --git a/apps/policy-engine/src/shared/testing/with-temp-json-file.testing.ts b/apps/policy-engine/src/shared/testing/with-temp-json-file.testing.ts new file mode 100644 index 000000000..4192db232 --- /dev/null +++ b/apps/policy-engine/src/shared/testing/with-temp-json-file.testing.ts @@ -0,0 +1,24 @@ +import { unlink, writeFile } from 'fs/promises' +import { v4 as uuid } from 'uuid' + +/** + * Executes a callback function with a temporary JSON file. + * + * The file is created with the provided data and deleted after the callback is + * executed. + * + * @param data - The data to be written to the temporary JSON file. + * @param thunk - The callback function to be executed with the path of the + * temporary JSON file. + */ +export const withTempJsonFile = async (data: string, thunk: (path: string) => void | Promise) => { + const path = `./test-temp-data-store-${uuid()}.json` + + await writeFile(path, data, 'utf-8') + + try { + await thunk(path) + } finally { + await unlink(path) + } +} diff --git a/packages/policy-engine-shared/src/index.ts b/packages/policy-engine-shared/src/index.ts index c3fc3db79..6e9c30971 100644 --- a/packages/policy-engine-shared/src/index.ts +++ b/packages/policy-engine-shared/src/index.ts @@ -3,18 +3,26 @@ export * from './lib/decorators/is-asset-id.decorator' export * from './lib/decorators/is-hex-string.decorator' export * from './lib/decorators/is-not-empty-array-enum.decorator' export * from './lib/decorators/is-not-empty-array-string.decorator' -export * as FIXTURE from './lib/dev.fixture' + export * from './lib/dto' -export * from './lib/schema/address.schema' -export * from './lib/schema/hex.schema' + export * from './lib/type/action.type' export * from './lib/type/data-store.type' export * from './lib/type/domain.type' export * from './lib/type/entity.type' + +export * as EntityUtil from './lib/util/entity.util' + export * from './lib/util/caip.util' export * from './lib/util/encoding.util' -export * as EntityUtil from './lib/util/entity.util' export * from './lib/util/enum.util' export * from './lib/util/evm.util' export * from './lib/util/json.util' export * from './lib/util/typeguards' + +export * from './lib/schema/address.schema' +export * from './lib/schema/data-store.schema' +export * from './lib/schema/entity.schema' +export * from './lib/schema/hex.schema' + +export * as FIXTURE from './lib/dev.fixture' diff --git a/packages/policy-engine-shared/src/lib/schema/data-store.schema.ts b/packages/policy-engine-shared/src/lib/schema/data-store.schema.ts index 6399c3615..8a8a1f9ac 100644 --- a/packages/policy-engine-shared/src/lib/schema/data-store.schema.ts +++ b/packages/policy-engine-shared/src/lib/schema/data-store.schema.ts @@ -22,20 +22,26 @@ export const dataStoreConfigurationSchema = z.object({ keys: z.array(jsonWebKeySchema) }) -export const entityDataSchema = z.object({ - entity: z.object({ - data: entitiesSchema +export const entityDataSchema = z + .object({ + entity: z.object({ + data: entitiesSchema + }) }) -}) + .describe('Entity data') -export const entitySignatureSchema = z.object({ - entity: z.object({ - signature: z.string() +export const entitySignatureSchema = z + .object({ + entity: z.object({ + signature: z.string() + }) }) -}) + .describe('Entity data signature') -export const entityJsonWebKeySetSchema = z.object({ - entity: z.object({ - keys: z.array(jsonWebKeySchema) +export const entityJsonWebKeySetSchema = z + .object({ + entity: z.object({ + keys: z.array(jsonWebKeySchema) + }) }) -}) + .describe('Entity JWKS') diff --git a/packages/policy-engine-shared/src/lib/schema/entity.schema.ts b/packages/policy-engine-shared/src/lib/schema/entity.schema.ts index af83f0293..5234f935a 100644 --- a/packages/policy-engine-shared/src/lib/schema/entity.schema.ts +++ b/packages/policy-engine-shared/src/lib/schema/entity.schema.ts @@ -2,11 +2,24 @@ import { Alg } from '@narval/signature' import { z } from 'zod' import { addressSchema } from './address.schema' -export const userRoleSchema = z.enum(['ROOT', 'ADMIN', 'MEMBER', 'MANAGER']) +export const userRoleSchema = z.nativeEnum({ + ROOT: 'root', + ADMIN: 'admin', + MEMBER: 'member', + MANAGER: 'manager' +} as const) -export const accountTypeSchema = z.enum(['EOA', '4337']) +export const accountTypeSchema = z.nativeEnum({ + EOA: 'eoa', + AA: '4337' +} as const) -export const accountClassificationSchema = z.enum(['EXTERNAL', 'COUNTERPARTY', 'INTERNAL', 'WALLET']) +export const accountClassificationSchema = z.nativeEnum({ + EXTERNAL: 'external', + COUNTERPARTY: 'counterparty', + INTERNAL: 'internal', + WALLET: 'wallet' +} as const) export const credentialEntitySchema = z.object({ id: z.string(), diff --git a/packages/policy-engine-shared/src/lib/type/entity.type.ts b/packages/policy-engine-shared/src/lib/type/entity.type.ts index eaf4956eb..8f7717fe4 100644 --- a/packages/policy-engine-shared/src/lib/type/entity.type.ts +++ b/packages/policy-engine-shared/src/lib/type/entity.type.ts @@ -1,101 +1,54 @@ -import { Alg } from '@narval/signature' -import { Address } from './domain.type' - -export const UserRole = { - ROOT: 'root', - ADMIN: 'admin', - MEMBER: 'member', - MANAGER: 'manager' -} as const - -export type UserRole = (typeof UserRole)[keyof typeof UserRole] - -export const AccountType = { - EOA: 'eoa', - AA: '4337' -} as const - -export type AccountType = (typeof AccountType)[keyof typeof AccountType] - -export const AccountClassification = { - EXTERNAL: 'external', - COUNTERPARTY: 'counterparty', - INTERNAL: 'internal', - WALLET: 'wallet' -} as const - -export type AccountClassification = (typeof AccountClassification)[keyof typeof AccountClassification] - -export type CredentialEntity = { - id: string - pubKey: string - alg: Alg - userId: string -} - -export type OrganizationEntity = { - id: string -} - -export type UserEntity = { - id: string - role: UserRole -} - -export type UserGroupEntity = { - id: string -} - -export type UserWalletEntity = { - userId: string - walletId: string -} - -export type UserGroupMemberEntity = { - userId: string - groupId: string -} - -export type WalletEntity = { - id: string - address: Address - accountType: AccountType - chainId?: number -} - -export type WalletGroupEntity = { - id: string -} - -export type WalletGroupMemberEntity = { - walletId: string - groupId: string -} - -export type AddressBookAccountEntity = { - id: string - address: Address - chainId: number - classification: AccountClassification -} - -export type TokenEntity = { - id: string - address: Address - symbol: string - chainId: number - decimals: number -} - -export type Entities = { - addressBook: AddressBookAccountEntity[] - credentials: CredentialEntity[] - tokens: TokenEntity[] - userGroupMembers: UserGroupMemberEntity[] - userGroups: UserGroupEntity[] - userWallets: UserWalletEntity[] - users: UserEntity[] - walletGroupMembers: WalletGroupMemberEntity[] - walletGroups: WalletGroupEntity[] - wallets: WalletEntity[] -} +import { z } from 'zod' +import { + accountClassificationSchema, + accountTypeSchema, + addressBookAccountEntitySchema, + credentialEntitySchema, + entitiesSchema, + organizationEntitySchema, + tokenEntitySchema, + userEntitySchema, + userGroupEntitySchema, + userGroupMemberEntitySchema, + userRoleSchema, + userWalletEntitySchema, + walletEntitySchema, + walletGroupEntitySchema, + walletGroupMemberEntitySchema +} from '../schema/entity.schema' + +export const UserRole = userRoleSchema.enum + +export type UserRole = z.infer + +export const AccountType = accountTypeSchema.enum + +export type AccountType = z.infer + +export const AccountClassification = accountClassificationSchema.enum + +export type AccountClassification = z.infer + +export type CredentialEntity = z.infer + +export type OrganizationEntity = z.infer + +export type UserEntity = z.infer + +export type UserGroupEntity = z.infer + +export type UserWalletEntity = z.infer + +export type UserGroupMemberEntity = z.infer + +export type WalletEntity = z.infer + +export type WalletGroupEntity = z.infer + +export type WalletGroupMemberEntity = z.infer + +export type AddressBookAccountEntity = z.infer + +export type TokenEntity = z.infer + +export type Entities = z.infer