Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mzk/server exp updated #7

Merged
merged 7 commits into from
Apr 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions demo/server/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
CERAMIC_URL=""
CERAMIC_PRIVATE_KEY=""

# Interface StreamID of PointsAggregation
AGGREGATION_ID="kjzl6hvfrbw6ca6atwn59x2zltapkaf4dy9v0laqk4ahj5hf337s6rocgia2rxs"
6 changes: 6 additions & 0 deletions demo/server/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"extends": ["3box", "3box/jest", "3box/typescript"],
"parserOptions": {
"project": ["tsconfig.json"]
}
}
9 changes: 9 additions & 0 deletions demo/server/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Dependencies
/node_modules

# env
.env

# Build files
/dist

18 changes: 18 additions & 0 deletions demo/server/composites/sandboxAggregation.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# (for reference only)

interface PointsAggregation @loadModel(id: "kjzl6hvfrbw6cb6393dpd8blke5w8r7pvbl4449mxetuibcav3oab8fnxmys6d6") {
id: ID!
}

type SandboxPointsAggregation implements PointsAggregation
@createModel(
description: "Aggregation of multiple Ceramic Sandbox read points to an account"
accountRelation: SET
accountRelationFields: ["recipient"]
) {
issuer: DID! @documentAccount
recipient: DID! @accountReference
points: Int!
date: DateTime!
context: String! @string(maxLength: 100)
}
74 changes: 74 additions & 0 deletions demo/server/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
{
"name": "@composexp/server",
"version": "0.1.0",
"private": true,
"author": "3Box Labs",
"license": "(Apache-2.0 OR MIT)",
"repository": {
"type": "git",
"url": "https://github.com/ceramicstudio/solutions",
"directory": "demo/server"
},
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"exports": {
".": "./dist/index.js"
},
"files": [
"dist"
],
"engines": {
"node": ">=20"
},
"sideEffects": false,
"scripts": {
"dev": "npx tsx src/index.ts",
"prestart": "pnpm run build",
"build:clean": "del dist",
"build:js": "swc src -d ./dist --config-file ../../.swcrc --strip-leading-paths",
"build:types": "tsc --project tsconfig.build.json --emitDeclarationOnly --skipLibCheck",
"build": "pnpm build:clean && pnpm build:types && pnpm build:js",
"lint": "eslint src --fix",
"prepare": "pnpm build",
"prepublishOnly": "package-check",
"start": "node ."
},
"devDependencies": {
"@types/cors": "^2.8.15",
"@types/express": "^4.17.20",
"key-did-provider-ed25519": "^2.0.1",
"key-did-resolver": "^2.1.3",
"nodemon": "^3.1.0",
"ts-node": "^10.9.1",
"@composexp/points": "workspace:^",
"@composexp/ceramic-utils": "workspace:^",
"@composexp/did-utils": "workspace:^",
"uint8arrays": "^5.0.3"
},
"dependencies": {
"@ceramicnetwork/http-client": "^5.6.0",
"body-parser": "^1.20.2",
"cors": "^2.8.5",
"dotenv": "^16.3.1",
"exec": "^0.2.1",
"express": "^4.18.2",
"tsx": "^4.7.1"
},
"jest": {
"extensionsToTreatAsEsm": [
".ts"
],
"moduleNameMapper": {
"^(\\.{1,2}/.*)\\.js$": "$1"
},
"transform": {
"^.+\\.(t|j)s$": [
"@swc/jest",
{
"root": "../.."
}
]
}
}
}
203 changes: 203 additions & 0 deletions demo/server/src/controllers/multiController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
import { getContext } from '../utils/context.js'
import { Request, Response, NextFunction } from 'express'
import { PointsWriter, PointsReader } from '@composexp/points'

type ContextAggregationContent = {
recipient: string
points: number
date: string
context: string
}

export interface GetContextAggregationRequest extends Request {
body: {
recipient: string
context: string
}
}

export interface UpdateTotalAggregationRequest extends Request {
body: {
recipient: string
amount: number
}
}

export interface UpdateContextAggregationRequest extends Request {
body: {
recipient: string
context: string
amount: number
}
}

export interface GetTotalRequest extends Request {
body: {
recipient: string
}
}

const getContextAggregation = async (
req: GetContextAggregationRequest,
res: Response,
next: NextFunction,
): Promise<void> => {
try {
const { ceramic, aggregationModelID } = await getContext()
const { recipient, context } = req.body

//instantiate a reader
const reader = new PointsReader({
ceramic,
issuer: ceramic.did!.id,
aggregationModelID,
})
const doc = await reader.loadAggregationDocumentFor([recipient, context])
res.locals = {
...res.locals,
contextTotal: doc ? (doc.content ? doc.content.points : 0) : 0,
contextDocument: doc ? doc.content : null,
}
return next()
} catch (error) {
console.error(error)
next(error)
}
}

const getTotalAggregation = async (
req: GetTotalRequest,
res: Response,
next: NextFunction,
): Promise<void> => {
try {
const { ceramic } = await getContext()
const { recipient } = req.body

//instantiate a reader
const reader = new PointsReader({
ceramic,
issuer: ceramic.did!.id,
})
const doc = await reader.loadAggregationDocumentFor([recipient])
res.locals = {
...res.locals,
total: doc ? (doc.content ? doc.content.points : 0) : 0,
document: doc ? doc.content : null,
}
return next()
} catch (error) {
console.error(error)
next(error)
}
}

const updateContextAggregation = async (
req: UpdateContextAggregationRequest,
res: Response,
next: NextFunction,
): Promise<void> => {
try {
const { ceramic, aggregationModelID } = await getContext()
const { amount, recipient, context } = req.body

//instantiate a writer and reader
const contextWriter = new PointsWriter<ContextAggregationContent>({
ceramic,
aggregationModelID,
})

const contextReader = new PointsReader({
ceramic,
issuer: ceramic.did!.id,
aggregationModelID,
})

// load the document for the recipient and context
const doc = await contextReader.loadAggregationDocumentFor([recipient, context])
if (!doc) {
await contextWriter.setPointsAggregationFor([recipient, context], amount, {
recipient,
points: amount,
date: new Date().toISOString(),
context,
})
res.locals = {
...res.locals,
contextTotal: amount,
}
} else {
const updatedDoc = await contextWriter.updatePointsAggregationFor(
[recipient, context],
(content) => {
return {
points: content ? content.points + amount : amount,
date: new Date().toISOString(),
recipient,
context,
}
},
)
res.locals = {
...res.locals,
contextTotal: updatedDoc.content ? updatedDoc.content.points : 0,
}
}
return next()
} catch (error) {
console.error(error)
next(error)
}
}

const updateTotalAggregation = async (
req: UpdateTotalAggregationRequest,
res: Response,
next: NextFunction,
): Promise<void> => {
try {
const { ceramic } = await getContext()
const { amount, recipient } = req.body

//instantiate a writer and reader
const totalWriter = new PointsWriter({ ceramic })
const totalReader = new PointsReader({ ceramic, issuer: ceramic.did!.id })

// load the document for the recipient and context
const doc = await totalReader.loadAggregationDocumentFor([recipient])
if (!doc) {
await totalWriter.setPointsAggregationFor([recipient], amount, {
recipient,
points: amount,
date: new Date().toISOString(),
})
res.locals = {
...res.locals,
total: amount,
}
} else {
const updatedDoc = await totalWriter.updatePointsAggregationFor([recipient], (content) => {
return {
points: content ? content.points + amount : amount,
date: new Date().toISOString(),
recipient,
}
})
res.locals = {
...res.locals,
total: updatedDoc.content ? updatedDoc.content.points : 0,
}
}
return next()
} catch (error) {
console.error(error)
next(error)
}
}

export const multiplePointsController = {
updateContextAggregation,
updateTotalAggregation,
getContextAggregation,
getTotalAggregation,
}
70 changes: 70 additions & 0 deletions demo/server/src/controllers/singleController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { getContext } from '../utils/context.js'
import { Request, Response, NextFunction } from 'express'
import { SinglePointReader, SinglePointWriter } from '@composexp/points'

export interface CreateSinglePointRequest extends Request {
body: {
recipient: string
}
}

const createSinglePoint = async (
req: CreateSinglePointRequest,
res: Response,
next: NextFunction,
) => {
try {
const { ceramic } = await getContext()
const writer = new SinglePointWriter({ ceramic })
await writer.addPointTo(req.body.recipient)
res.locals.ceramic = ceramic
return next()
} catch (error) {
console.error(error)
return error
}
}

const removeSinglePoint = async (
req: CreateSinglePointRequest,
_res: Response,
next: NextFunction,
) => {
try {
const { ceramic } = await getContext()
const reader = new SinglePointReader({
ceramic,
issuer: ceramic.did!.id,
})
const documents = await reader.queryPointDocumentsFor(req.body.recipient)
const id = documents.documents[documents.documents.length - 1].id
const writer = new SinglePointWriter({ ceramic })
await writer.removePoint(id.toString())
return next()
} catch (error) {
console.error(error)
return error
}
}

const getSinglePoints = async (
req: CreateSinglePointRequest,
res: Response,
next: NextFunction,
) => {
try {
const { ceramic } = await getContext()
const reader = new SinglePointReader({
ceramic,
issuer: ceramic.did!.id,
})
const totalPoints = await reader.countPointsFor(req.body.recipient)
res.locals.totalPoints = totalPoints
return next()
} catch (error) {
console.error(error)
return error
}
}

export const singlePointController = { createSinglePoint, getSinglePoints, removeSinglePoint }
Loading
Loading