Skip to content
This repository was archived by the owner on Mar 27, 2025. It is now read-only.
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
f3f7017
chore: initial commit
sanderPostma Feb 24, 2025
42da943
chore: storeAnonCredentialDefinition
sanderPostma Feb 24, 2025
d66efee
chore: cleanup
sanderPostma Feb 24, 2025
8e0b17e
chore: husky - prettier
sanderPostma Feb 24, 2025
9b84d4c
Merge remote-tracking branch 'origin/develop' into feature/DEV-4_trac…
sanderPostma Feb 24, 2025
9eafb59
chore: iteration of traction-adapter
sanderPostma Feb 25, 2025
6029f7f
chore: CypherUtil.ts
sanderPostma Feb 26, 2025
b3c52df
chore: CypherUtil.ts
sanderPostma Feb 26, 2025
e2c1786
chore: env example
sanderPostma Feb 26, 2025
09162a8
Merge branch 'fixes/generate-model' into feature/DEV-4_traction-adapter
sanderPostma Feb 26, 2025
2700ad8
chore: attached bearer token decryption
sanderPostma Feb 26, 2025
d3a2760
chore: options
sanderPostma Feb 27, 2025
7b1286d
chore: added credential-showcase-adapter-client-api
sanderPostma Feb 27, 2025
98bfe91
Merge remote-tracking branch 'origin/develop' into feature/DEV-4_trac…
sanderPostma Mar 11, 2025
a956c59
chore: saving work on traction adapter
sanderPostma Mar 11, 2025
68864e4
chore: saving work on traction adapter
sanderPostma Mar 11, 2025
0cac000
chore: updated traction adapter implementation
sanderPostma Mar 11, 2025
fb20144
chore: updated traction adapter implementation
sanderPostma Mar 11, 2025
9504ccd
Merge remote-tracking branch 'origin/develop' into feature/DEV-4_trac…
sanderPostma Mar 11, 2025
3f971c6
chore: lockfile
sanderPostma Mar 11, 2025
e7d50f9
chore: traction publishing
sanderPostma Mar 11, 2025
ab45d90
chore: saving drizlle debug logger (commented out)
sanderPostma Mar 13, 2025
e2d53ac
Merge remote-tracking branch 'origin/develop' into feature/DEV-4_trac…
sanderPostma Mar 18, 2025
576a750
chore: updated traction adapter & tests to newer model version
sanderPostma Mar 18, 2025
0a4fb31
chore: save work
sanderPostma Mar 19, 2025
ecaeee3
Merge remote-tracking branch 'origin/develop' into feature/DEV-4_trac…
sanderPostma Mar 21, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ test/*.js

# Ignore Open API generated packages
packages/credential-showcase-openapi/src
packages/credential-showcase-traction-openapi/src
packages/**/dist
packages/**/target
/packages/*/dist
Expand All @@ -61,3 +62,4 @@ packages/**/target
**/.env
**/.env.*
!**/.env.example

3 changes: 3 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
pnpm prettier

# Stage the files that were modified by prettier
git add -u
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ the backend server for the Credential Showcase application. It provides REST API

Defines the OpenAPI specification for the Credential Showcase application. It provides the API contract for the backend services and can be used to generate client code, validate requests, and document the API.

# credential-showcase-traction-openapi

Defines the OpenAPI specification for the Traction backend and contains the generated client code for that.

# credential-showcase-traction-adapter

Picks up Credential Showcase messages from a AMQ v1.0 message broker and translates the actions to Traction API calls

# credential-showcase-ts-sdk

Contains the generated TypeScript SDK client code for interacting with the Credential Showcase API. It provides a set of functions and utilities to connect with the backend services.
Expand Down
1 change: 1 addition & 0 deletions apps/credential-showcase-api-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"dependencies": {
"cors": "^2.8.5",
"credential-showcase-openapi": "workspace:*",
"credential-showcase-adapter-client-api": "workspace:*",
"dotenv-flow": "^4.1.0",
"drizzle-orm": "^0.39.3",
"express": "^4.21.2",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,29 @@
import { BadRequestError, Body, Delete, Get, HttpCode, JsonController, OnUndefined, Param, Post, Put } from 'routing-controllers'
import { BadRequestError, Body, Delete, Get, HeaderParam, HttpCode, JsonController, OnUndefined, Param, Post, Put } from 'routing-controllers'
import { Service } from 'typedi'
import {
ShowcaseResponse,
ShowcaseResponseFromJSONTyped,
instanceOfShowcaseRequest,
IssuanceScenario,
Issuer,
Showcase,
ShowcaseRequest,
ShowcaseRequestToJSONTyped,
ShowcaseResponse,
ShowcaseResponseFromJSONTyped,
ShowcasesResponse,
ShowcasesResponseFromJSONTyped,
instanceOfShowcaseRequest,
ShowcaseStatus,
} from 'credential-showcase-openapi'
import ShowcaseService from '../services/ShowcaseService'
import { showcaseDTOFrom } from '../utils/mappers'
import { AdapterClientApi } from 'credential-showcase-adapter-client-api'

@JsonController('/showcases')
@Service()
class ShowcaseController {
constructor(private showcaseService: ShowcaseService) {}
constructor(
private showcaseService: ShowcaseService,
private adapterClientApi: AdapterClientApi,
) {}

@Get('/')
public async getAll(): Promise<ShowcasesResponse> {
Expand Down Expand Up @@ -47,12 +55,15 @@ class ShowcaseController {

@HttpCode(201)
@Post('/')
public async post(@Body() showcaseRequest: ShowcaseRequest): Promise<ShowcaseResponse> {
public async post(@HeaderParam('authorization') authHeader: string, @Body() showcaseRequest: ShowcaseRequest): Promise<ShowcaseResponse> {
try {
if (!instanceOfShowcaseRequest(showcaseRequest)) {
return Promise.reject(new BadRequestError())
}
const result = await this.showcaseService.createShowcase(ShowcaseRequestToJSONTyped(showcaseRequest))
if (showcaseRequest.status === ShowcaseStatus.Active) {
void (await this.publishFromShowcase(showcaseDTOFrom(result), authHeader))
}
return ShowcaseResponseFromJSONTyped({ showcase: showcaseDTOFrom(result) }, false)
} catch (e) {
if (e.httpCode !== 404) {
Expand All @@ -70,6 +81,9 @@ class ShowcaseController {
return Promise.reject(new BadRequestError())
}
const result = await this.showcaseService.updateShowcase(id, ShowcaseRequestToJSONTyped(showcaseRequest))
if (showcaseRequest.status === ShowcaseStatus.Active) {
void (await this.publishFromShowcase(showcaseDTOFrom(result), authHeader))
}
return ShowcaseResponseFromJSONTyped({ showcase: showcaseDTOFrom(result) }, false)
} catch (e) {
if (e.httpCode !== 404) {
Expand All @@ -79,6 +93,61 @@ class ShowcaseController {
}
}

private async publishFromShowcase(showcase: Showcase, authHeader: string) {
console.log(`Publishing showcase ${showcase.name} to Traction`)

// Get issuers from scenarios
const issuers: Array<Issuer> = showcase.scenarios
?.filter((scenario) => 'issuer' in scenario && scenario.issuer)
.map((scenario) => (scenario as IssuanceScenario).issuer)

// Process each issuer
const processedIssuerIds = new Set<string>()

for (const issuer of issuers) {
// Skip if we've already processed this issuer
if (processedIssuerIds.has(issuer.id)) {
continue
}
processedIssuerIds.add(issuer.id)

const newIssuer: Issuer = {
id: issuer.id,
name: issuer.name,
description: issuer.description,
type: issuer.type,
organization: issuer.organization,
identifierType: issuer.identifierType,
identifier: issuer.identifier,
logo: issuer.logo,
credentialDefinitions: [],
credentialSchemas: [],
createdAt: issuer.createdAt,
updatedAt: issuer.updatedAt,
}

// Find matching credential definitions from showcase
const matchingCredDefs = showcase.credentialDefinitions.filter((credDef) =>
issuer.credentialDefinitions.some((issuerCredDef) => issuerCredDef.id === credDef.id),
)

newIssuer.credentialDefinitions = matchingCredDefs

// Get schema IDs from matching credential definitions
const schemaIds = matchingCredDefs.map((credDef) => credDef.credentialSchema.id)

// Look up schemas from ALL issuers (not just the current one)
newIssuer.credentialSchemas = issuers
.flatMap((i) => i.credentialSchemas || [])
.filter((schema) => schemaIds.includes(schema.id))
// Remove duplicates
.filter((schema, index, self) => index === self.findIndex((s) => s.id === schema.id))

// Publish the issuer
void (await this.adapterClientApi.publishIssuer(newIssuer, authHeader)) // TODO create reduced type
}
}

@OnUndefined(204)
@Delete('/:slug')
public async delete(@Param('slug') slug: string): Promise<void> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ describe('IssuanceScenarioController Integration Tests', () => {
name: 'Test Definition',
version: '1.0',
identifierType: IdentifierType.DID,
identifier: 'did:test:123',
identifier: 'did:sov:YUeUZauFLeBNofY3NhaZCA',
icon: asset.id,
type: CredentialType.ANONCRED,
credentialSchema: credentialSchema.id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ describe('IssuerController Integration Tests', () => {
name: 'Test Definition',
version: '1.0',
identifierType: IdentifierType.DID,
identifier: 'did:test:123',
identifier: 'did:sov:YUeUZauFLeBNofY3NhaZCA',
icon: asset.id,
type: CredentialType.ANONCRED,
credentialSchema: credentialSchema.id,
Expand Down Expand Up @@ -224,7 +224,7 @@ describe('IssuerController Integration Tests', () => {
name: 'Test Definition 1',
version: '1.0',
identifierType: IdentifierType.DID,
identifier: 'did:test:123',
identifier: 'did:sov:YUeUZauFLeBNofY3NhaZCA',
icon: asset.id,
type: CredentialType.ANONCRED,
credentialSchema: credentialSchema.id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ describe('PresentationScenarioController Integration Tests', () => {
name: 'Test Definition',
version: '1.0',
identifierType: IdentifierType.DID,
identifier: 'did:test:123',
identifier: 'did:sov:YUeUZauFLeBNofY3NhaZCA',
icon: asset.id,
type: CredentialType.ANONCRED,
credentialSchema: credentialSchema.id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ describe('RelyingPartyController Integration Tests', () => {
name: 'Test Definition',
version: '1.0',
identifierType: IdentifierType.DID,
identifier: 'did:test:123',
identifier: 'did:sov:YUeUZauFLeBNofY3NhaZCA',
icon: asset.id,
type: CredentialType.ANONCRED,
credentialSchema: credentialSchema.id,
Expand Down Expand Up @@ -204,7 +204,7 @@ describe('RelyingPartyController Integration Tests', () => {
name: 'Test Definition 1',
version: '1.0',
identifierType: IdentifierType.DID,
identifier: 'did:test:123',
identifier: 'did:sov:YUeUZauFLeBNofY3NhaZCA',
icon: asset.id,
type: CredentialType.ANONCRED,
credentialSchema: credentialSchema.id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import * as schema from '../../database/schema'
import { NodePgDatabase } from 'drizzle-orm/node-postgres'
import { migrate } from 'drizzle-orm/node-postgres/migrator'
import DatabaseService from '../../services/DatabaseService'
import { AdapterClientApi } from 'credential-showcase-adapter-client-api'

describe('ShowcaseController Integration Tests', () => {
let client: PGlite
Expand All @@ -34,6 +35,13 @@ describe('ShowcaseController Integration Tests', () => {
getConnection: jest.fn().mockResolvedValue(database),
}
Container.set(DatabaseService, mockDatabaseService)

const mockAdapterClientApi = {
publishIssuer: jest.fn().mockResolvedValue(undefined),
close: jest.fn().mockResolvedValue(undefined),
}
Container.set(AdapterClientApi, mockAdapterClientApi)

useContainer(Container)
Container.get(AssetRepository)
Container.get(CredentialSchemaRepository)
Expand Down Expand Up @@ -89,7 +97,7 @@ describe('ShowcaseController Integration Tests', () => {
name: 'Test Definition',
version: '1.0',
identifierType: IdentifierType.DID,
identifier: 'did:test:123',
identifier: 'did:sov:YUeUZauFLeBNofY3NhaZCA',
icon: asset.id,
type: CredentialType.ANONCRED,
credentialSchema: credentialSchema.id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,15 @@ export class DatabaseService {
public async getConnection(): Promise<NodePgDatabase<typeof schema>> {
if (!this.db) {
const pool = new Pool({ connectionString: this.getDbUrl() })
this.db = drizzle(pool, { schema })
this.db = drizzle(pool, {
schema,
/*logger: {
logQuery: (query, params) => {
console.log('Query:', query)
console.log('Params:', params)
},
},*/
})

const migrationsFolder = path.resolve(__dirname, '../database/migrations')
await migrate(this.db, { migrationsFolder })
Expand Down
2 changes: 2 additions & 0 deletions apps/credential-showcase-api-server/src/types/schema/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ export type IssuanceScenario = Omit<typeof scenarios.$inferSelect, 'relyingParty
issuer?: Issuer | null
bannerImage?: Asset | null
}

export type NewIssuanceScenario = Omit<typeof scenarios.$inferInsert, 'relyingParty' | 'scenarioType' | 'slug'> & {
personas: string[]
issuer: string
Expand Down Expand Up @@ -203,6 +204,7 @@ export type Showcase = Omit<typeof showcases.$inferSelect, 'bannerImage'> & {
personas: Persona[]
bannerImage?: Asset | null
}

export type NewShowcase = Omit<typeof showcases.$inferInsert, 'slug'> & {
scenarios: string[]
credentialDefinitions: string[]
Expand Down
3 changes: 3 additions & 0 deletions apps/credential-showcase-api-server/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
"references": [
{
"path": "../../packages/credential-showcase-openapi"
},
{
"path": "../../packages/credential-showcase-adapter-client-api"
}
],
"exclude": ["**/__tests__/**/*", "**/dist/**/*", "**/drizzle.config.ts"]
Expand Down
36 changes: 36 additions & 0 deletions packages/credential-showcase-adapter-client-api/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"name": "credential-showcase-adapter-client-api",
"version": "0.1.0",
"source": "src/index.ts",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"start": "ts-node src/index.ts",
"build": "tsc",
"build:clean": "tsc --build --clean && tsc --build",
"generate-key": "ts-node src/util/CypherUtil.ts generate-key"
},
"dependencies": {
"credential-showcase-openapi": "workspace:*",
"bs58": "^6.0.0",
"rhea": "^3.0.3",
"rhea-promise": "^3.0.3",
"typedi": "^0.10.0"
},
"devDependencies": {
"@types/node": "^22.13.1"
},
"files": [
"dist/**/*",
"README.md",
"LICENSE"
],
"private": false,
"publishConfig": {
"access": "public"
},
"repository": "git@github.com:Sphereon-Opensource/credential-showcase-api.git",
"author": "4Sure",
"license": "Apache-2.0",
"keywords": []
}
Loading