diff --git a/src/common/schema/opportunities.ts b/src/common/schema/opportunities.ts index 064dcf9a6..eae7ee117 100644 --- a/src/common/schema/opportunities.ts +++ b/src/common/schema/opportunities.ts @@ -43,9 +43,24 @@ export const opportunityContentSchema = z.object({ }).optional(), }); +const opportunityMetaBaseSchema = z.object({ + employmentType: z.coerce.number().min(1).optional(), + teamSize: z.number().int().nonnegative().min(1).max(1_000_000).optional(), + salary: z + .object({ + min: z.number().int().nonnegative().max(100_000_000), + max: z.number().int().nonnegative().max(100_000_000), + period: z.number(), + }) + .partial() + .optional(), + seniorityLevel: z.number().optional(), + roleType: z.union([z.literal(0), z.literal(0.5), z.literal(1)]).optional(), +}); + export const opportunityCreateSchema = z.object({ title: z.string().nonempty().max(240), - tldr: z.string().nonempty().max(480), + tldr: z.string().nonempty().max(480).optional(), keywords: z .array( z.object({ @@ -53,38 +68,27 @@ export const opportunityCreateSchema = z.object({ }), ) .min(1) - .max(100), + .max(100) + .optional(), location: z .array( z.object({ - country: z.string().nonempty().max(240), + country: z.string('No location could be extracted').nonempty().max(240), city: z.string().nonempty().max(240).optional(), subdivision: z.string().nonempty().max(240).optional(), - type: z.coerce.number().min(1), + type: z.coerce.number().min(1).optional(), iso2: z.string().nonempty().max(2).optional(), }), ) .optional(), organizationId: z.string(), - meta: z.object({ - employmentType: z.coerce.number().min(1), - teamSize: z.number().int().nonnegative().min(1).max(1_000_000), - salary: z - .object({ - min: z.number().int().nonnegative().max(100_000_000), - max: z.number().int().nonnegative().max(100_000_000), - period: z.number(), - }) - .partial() - .optional(), - seniorityLevel: z.number(), - roleType: z.union([z.literal(0), z.literal(0.5), z.literal(1)]), - }), - content: opportunityContentSchema.partial(), + meta: opportunityMetaBaseSchema.optional(), + content: opportunityContentSchema.partial().optional(), }); export const opportunityCreateParseSchema = opportunityCreateSchema.extend({ organizationId: opportunityCreateSchema.shape.organizationId.nullish(), + tldr: z.string().max(480).optional().default(''), keywords: z.preprocess((val) => { if (Array.isArray(val)) { return val.map((keyword) => { @@ -96,9 +100,8 @@ export const opportunityCreateParseSchema = opportunityCreateSchema.extend({ return val; }, opportunityCreateSchema.shape.keywords), - meta: opportunityCreateSchema.shape.meta + meta: opportunityMetaBaseSchema .extend({ - teamSize: opportunityCreateSchema.shape.meta.shape.teamSize.optional(), salary: z .object({ min: z.preprocess((val: bigint) => { @@ -120,7 +123,10 @@ export const opportunityCreateParseSchema = opportunityCreateSchema.extend({ .partial() .optional(), }) - .partial(), + .partial() + .optional() + .default({}), + content: opportunityContentSchema.partial().optional().default({}), }); export const opportunityEditSchema = z diff --git a/src/schema/opportunity.ts b/src/schema/opportunity.ts index 35c9323de..9ab5e9a81 100644 --- a/src/schema/opportunity.ts +++ b/src/schema/opportunity.ts @@ -7,13 +7,13 @@ import { traceResolvers } from './trace'; import { AuthContext, BaseContext, type Context } from '../Context'; import graphorm, { LocationVerificationStatus } from '../graphorm'; import { - BrokkrParseRequest, LocationType, OpportunityContent, OpportunityState, ScreeningQuestionsRequest, Opportunity as OpportunityMessage, Location as LocationMessage, + BrokkrParseRequest, } from '@dailydotdev/schema'; import { OpportunityMatch } from '../entity/OpportunityMatch'; import { @@ -117,7 +117,6 @@ import { acceptedOpportunityFileTypes, opportunityMatchBatchSize, } from '../types'; -import { getBrokkrClient } from '../common/brokkr'; import { garmScraperService } from '../common/scraper'; import { Storage } from '@google-cloud/storage'; import { randomUUID } from 'node:crypto'; @@ -130,6 +129,7 @@ import { SubscriptionStatus } from '../common/plus'; import { paddleInstance } from '../common/paddle'; import type { ISubscriptionUpdateItem } from '@paddle/paddle-node-sdk'; import { OpportunityPreviewStatus } from '../common/opportunity/types'; +import { getBrokkrClient } from '../common/brokkr'; export interface GQLOpportunity extends Pick< @@ -2830,6 +2830,8 @@ export const resolvers: IResolvers = traceResolvers< ); }); + ctx.log.info(result, 'brokkrParseOpportunityResponse'); + const parsedOpportunity = await opportunityCreateParseSchema.parseAsync( result.opportunity, ); @@ -2923,12 +2925,14 @@ export const resolvers: IResolvers = traceResolvers< opportunityId: opportunity.id, }); - await entityManager.getRepository(OpportunityKeyword).insert( - parsedOpportunity.keywords.map((keyword) => ({ - opportunityId: opportunity.id, - keyword: keyword.keyword, - })), - ); + if (parsedOpportunity.keywords) { + await entityManager.getRepository(OpportunityKeyword).insert( + parsedOpportunity.keywords.map((keyword) => ({ + opportunityId: opportunity.id, + keyword: keyword.keyword, + })), + ); + } if (ctx.userId) { await entityManager