Skip to content
Open
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
14 changes: 13 additions & 1 deletion modules/market/market.models.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { optional } from 'zod';
import * as mongo from '../../util/mongo';
import type * as Types from './market.types';
import * as Types from './market.types';

export const Market = mongo.createModel<Types.MarketDocument>('Market', {
value: { type: String, required: true },
Expand Down Expand Up @@ -111,3 +112,14 @@ export const MarketStockSentiment = mongo.createModel<Types.MarketStockSentiment
},
confidence: { type: Number, required: true },
});

export const MarketListing = mongo.createModel<Types.MarketListingDocument>('MarketListing', {
sellerId: { type: mongo.Schema.Types.ObjectId, required: true, ref: 'Profile' },
quantity: { type: Number, required: true },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

default 1 seems fine

currency: { type: String, required: true },
exchange: { type: String, required: true },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MarketExchange is a model, so why is this a string? I forgot tbh, but it doesn't make sense

marketId: { type: mongo.Schema.Types.ObjectId, required: true, ref: 'MarketStock' },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ref MarketStock?

category: { type: String, required: true },
status: { type: String, required: true },
expiryDate: { type: Date, required: false },
});
40 changes: 39 additions & 1 deletion modules/market/market.router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,21 @@ import { z as zod } from 'zod';
import { initTRPC, inferRouterInputs, inferRouterOutputs } from '@trpc/server';
import { customErrorFormatter, hasRole } from '../../util/rpc';
import type { RouterContext } from '../../types';
import { Market, MarketPair, MarketExchange } from './market.schema';
import { Market, MarketPair, MarketExchange, MarketListing } from './market.schema';
import { Query, getQueryInput, getQueryOutput } from '../../schema';

export const z = zod;
export const t = initTRPC.context<RouterContext>().create();
export const router = t.router;
export const procedure = t.procedure;

const MarketListingsInput = z.object({
category: z.string().optional(),
status: z.string().optional(),
exchange: z.string().optional(),
sellerId: z.string().optional(),
});

export const createRouter = () =>
router({
getMarket: procedure
Expand Down Expand Up @@ -66,6 +74,36 @@ export const createRouter = () =>
.use(customErrorFormatter(t))
.input(z.object({ exchangeId: z.string(), data: MarketExchange.partial() }))
.mutation(({ input, ctx }) => (ctx.app.service.Market.updateMarketExchange as any)(input, ctx)),

getMarketListing: procedure
.use(hasRole('guest', t))
.use(customErrorFormatter(t))
.input(getQueryInput(z.object({ listingId: z.string() })))
.query(({ input, ctx }) => (ctx.app.service.Market.getMarketListing as any)(input, ctx)),

createMarketListing: procedure
.use(hasRole('user', t))
.use(customErrorFormatter(t))
.input(getQueryInput(MarketListing))
.mutation(({ input, ctx }) => (ctx.app.service.Market.createMarketListing as any)(input, ctx)),

updateMarketListing: procedure
.use(hasRole('user', t))
.use(customErrorFormatter(t))
.input(getQueryInput(MarketListing.partial()))
.mutation(({ input, ctx }) => (ctx.app.service.Market.updateMarketListing as any)(input, ctx)),

deleteMarketListing: procedure
.use(hasRole('user', t))
.use(customErrorFormatter(t))
.input(getQueryInput(z.object({ listingId: z.string() })))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.input(getQueryInput(MarketListing.partial()))

.mutation(({ input, ctx }) => (ctx.app.service.Market.deleteMarketListing as any)(input, ctx)),

getMarketListings: procedure
.use(hasRole('guest', t))
.use(customErrorFormatter(t))
.input(getQueryInput(MarketListingsInput))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prefer to keep it inline

.query(({ input, ctx }) => (ctx.app.service.Market.getMarketListings as any)(input, ctx)),
});

export type Router = ReturnType<typeof createRouter>;
Expand Down
14 changes: 14 additions & 0 deletions modules/market/market.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,17 @@ export const MarketStockSentiment = Entity.merge(
confidence: z.number().min(0).max(1), // overall confidence level for the analysis
})
);

export const MarketListing = Entity.merge(
z.object({
price: z.number(),
currency: z.string(),
quantity: z.number(),
exchange: z.string(),
sellerId: ObjectId,
marketId: ObjectId,
category: z.enum(['Stock', 'ChainToken']),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need a new category to support game items, right?

status: z.enum(['Active', 'Closed', 'Withdrawn', 'Expired']),
expiryDate: z.date().optional(),
})
);
78 changes: 78 additions & 0 deletions modules/market/market.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@ import type {
Market,
MarketPair,
MarketExchange,
MarketListing,
RouterContext,
Router,
RouterInput,
RouterOutput,
} from './market.types';
import { getFilter } from '../../util/api';
import { ARXError } from '../../util/rpc';

export class Service {
async getMarket(input: RouterInput['getMarket'], ctx: RouterContext): Promise<RouterOutput['getMarket']> {
Expand Down Expand Up @@ -119,4 +122,79 @@ export class Service {

return updatedMarketExchange as MarketExchange;
}

async getMarketListing(
input: RouterInput['getMarketListing'],
ctx: RouterContext
): Promise<RouterOutput['getMarketListing']> {
if (!input) throw new ARXError('NO_INPUT');
console.log('Market.Service.getMarketListing', input);

const listing = await ctx.app.model.MarketListing.findOne(getFilter(input)).exec();
if (!listing) throw new Error('MarketListing not found');

return listing as MarketListing;
}

async createMarketListing(
input: RouterInput['createMarketListing'],
ctx: RouterContext
): Promise<RouterOutput['createMarketListing']> {
if (!input) throw new ARXError('NO_INPUT');
console.log('Market.Service.createMarketListing', input);

const marketListing = await ctx.app.model.MarketListing.create(input);
return marketListing as MarketListing;
}

async updateMarketListing(
input: RouterInput['updateMarketListing'],
ctx: RouterContext
): Promise<RouterOutput['updateMarketListing']> {
if (!input) throw new ARXError('NO_INPUT');
console.log('Market.Service.updateMarketListing', input);

const updatedListing = await ctx.app.model.MarketListing.findByIdAndUpdate(input.where.id.equals, input.data, {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use the helper

new: true,
})
.lean()
.exec();
if (!updatedListing) throw new Error('MarketListing update failed');

return updatedListing as MarketListing;
}

async deleteMarketListing(
input: RouterInput['deleteMarketListing'],
ctx: RouterContext
): Promise<RouterOutput['deleteMarketListing']> {
if (!input) throw new ARXError('NO_INPUT');
console.log('Market.Service.deleteMarketListing', input);

const deletedListing = await ctx.app.model.MarketListing.findByIdAndDelete(input.where.id.quals).lean().exec();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

if (!deletedListing) throw new Error('MarketListing deletion failed');

return deletedListing as MarketListing;
}

async getMarketListings(
input: {
category?: string;
status?: string;
exchange?: string;
sellerId?: string;
},
ctx: RouterContext
): Promise<RouterOutput['getMarketListings']> {
console.log('Market.Service.getMarketListings', input);

const query: Record<string, any> = {};
if (input.category) query.category = input.category;
if (input.status) query.status = input.status;
if (input.exchange) query.exchange = input.exchange;
if (input.sellerId) query.sellerId = input.sellerId;

const listings = await ctx.app.model.MarketListing.find(query).lean().exec();
return listings as MarketListing[];
}
}
3 changes: 3 additions & 0 deletions modules/market/market.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export type MarketToken = z.infer<typeof schema.MarketToken>;
export type MarketCompany = z.infer<typeof schema.MarketCompany>;
export type MarketETF = z.infer<typeof schema.MarketETF>;
export type MarketStockSentiment = z.infer<typeof schema.MarketStockSentiment>;
export type MarketListing = z.infer<typeof schema.MarketListing>;

export type MarketDocument = Market & Document;
export type MarketPairDocument = MarketPair & Document;
Expand All @@ -36,6 +37,7 @@ export type MarketTokenDocument = MarketToken & Document;
export type MarketCompanyDocument = MarketCompany & Document;
export type MarketETFDocument = MarketETF & Document;
export type MarketStockSentimentDocument = MarketStockSentiment & Document;
export type MarketListingDocument = MarketListing & Document;

export type Mappings = {
Market: Model<MarketDocument>;
Expand All @@ -50,6 +52,7 @@ export type Mappings = {
MarketCompany: Model<MarketCompanyDocument>;
MarketETF: Model<MarketETFDocument>;
MarketStockSentiment: Model<MarketStockSentimentDocument>;
MarketListing: Model<MarketListingDocument>;
};

export type RouterInput = inferRouterInputs<Router>;
Expand Down