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

[WIP][PAUSED] LENS-192 : add quote-related endpoints to API #397

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
8 changes: 8 additions & 0 deletions backend/src/client.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
// For more information about this file see https://dove.feathersjs.com/guides/cli/client.html
import { feathers } from '@feathersjs/feathers'
import authenticationClient from '@feathersjs/authentication-client'
import { orgOrdersClient } from './services/org-orders/org-orders.shared.js'

import { quotesClient } from './services/quotes/quotes.shared.js'

import { userEngagementsClient } from './services/user-engagements/user-engagements.shared.js'

import { notificationsClient } from './services/notifications/notifications.shared.js'
Expand Down Expand Up @@ -100,5 +104,9 @@ export const createClient = (connection, authenticationOptions = {}) => {

client.configure(userEngagementsClient)

client.configure(quotesClient)

client.configure(orgOrdersClient)

return client
}
7 changes: 7 additions & 0 deletions backend/src/services/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ import { notifications } from './notifications/notifications.js'

import { userEngagements } from './user-engagements/user-engagements.js'

import { orgOrders } from './org-orders/org-orders.js'

import { quotes } from './quotes/quotes.js'

export const services = (app) => {
app.configure(orgSecondaryReferences)
Expand Down Expand Up @@ -84,5 +87,9 @@ export const services = (app) => {

app.configure(userEngagements)

app.configure(orgOrders)

app.configure(quotes)

// All services will be registered here
}
60 changes: 60 additions & 0 deletions backend/src/services/org-orders/commands/getNewQuote.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import {BadRequest} from "@feathersjs/errors";
import {
buildFileVersionProductionQuote,
buildSharedModelProductionQuote,
QuoteTargetMap
} from "../org-orders.subdocs.schema.js";
import {CurrencyType, currencyTypeMap} from "../../../currencies.js";
import {ObjectIdSchema, Type} from "@feathersjs/typebox";
import {ThirdPartyVendorType} from "../../quotes/quotes.subdocs.schema.js";
import {ObjectId} from "mongodb";

export const getNewQuote = async (context) => {
const { data } = context;
const user = context.params.user;
const quotesService = context.app.service('quotes');

let fileId = data.fileId;
let versionId = data.versionId;
let sharedModelId = data.sharedModelId;
let quantity = data.quantity || 1;
let priceCurrency = data.priceCurrency || currencyTypeMap.USD;
let targetSource = data.source;

let quoteType = sharedModelId ? QuoteTargetMap.sharedModel : QuoteTargetMap.fileVersion;
let updatedProductionQuotes = context.beforePatchCopy.productionQuotes;

let newQuote = await quotesService.create({
originatingUserId: user._id,
orderId: new ObjectId(),
fileId: fileId,
fileVersionId: versionId,
source: targetSource,
priceCurrency: priceCurrency,
quantity: quantity,
otherParams: {},
promotion: {},
})

let newQuoteSummary;
switch (quoteType) {
case QuoteTargetMap.fileVersion:
newQuoteSummary = buildFileVersionProductionQuote(newQuote, user);
break;
case QuoteTargetMap.sharedModel:
newQuoteSummary = buildSharedModelProductionQuote(newQuote, user);
break;
default:
throw new BadRequest(`Unknown quote type: ${quoteType}`);
}
updatedProductionQuotes.push(newQuoteSummary);

context.data.productionQuotes = updatedProductionQuotes;
delete context.data.shouldGetNewQuote;
delete context.data.fileId;
delete context.data.versionId;
delete context.data.sharedModelId;
delete context.data.quantity;
delete context.data.priceCurrency;
delete context.data.source;
}
11 changes: 11 additions & 0 deletions backend/src/services/org-orders/org-orders.class.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { MongoDBService } from '@feathersjs/mongodb'

// By default calls the standard MongoDB adapter service methods but can be customized with your own functionality.
export class OrgOrdersService extends MongoDBService {}

export const getOptions = (app) => {
return {
paginate: app.get('paginate'),
Model: app.get('mongodbClient').then((db) => db.collection('org-orders'))
}
}
32 changes: 32 additions & 0 deletions backend/src/services/org-orders/org-orders.distrib.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import {ObjectId} from "mongodb";

export const copyOrInsertOrgOrdersBeforePatch = async (context) => {
// basically, support "upsert"
const orgOrderService = context.app.service('org-orders');
const orgOrdersDb = await orgOrderService.options.Model;
const orgId = new ObjectId(context.id);

let ooCopy = await orgOrdersDb.findOne({ _id: orgId });
if (!ooCopy) {
const orgService = context.app.service('organizations');
const refOrg = await orgService.get(orgId);
if (!refOrg) {
console.log(`Somebody tried to get a org-order for an organization that does not exist (${orgId})`);
} else {
ooCopy = await orgOrderService.create({
_id: orgId,
});
}
}
if (ooCopy) {
switch (context.method) {
case 'patch':
context.beforePatchCopy = ooCopy;
break;
case 'get':
context.result = ooCopy; // we are done.
break;
}
}
return context;
}
118 changes: 118 additions & 0 deletions backend/src/services/org-orders/org-orders.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// For more information about this file see https://dove.feathersjs.com/guides/cli/service.html
import { authenticate } from '@feathersjs/authentication'

import { hooks as schemaHooks } from '@feathersjs/schema'
import {
orgOrdersDataValidator,
orgOrdersPatchValidator,
orgOrdersQueryValidator,
orgOrdersResolver,
orgOrdersExternalResolver,
orgOrdersDataResolver,
orgOrdersPatchResolver,
orgOrdersQueryResolver, orgOrdersSchema, orgOrdersDataSchema, orgOrdersPatchSchema, orgOrdersQuerySchema
} from './org-orders.schema.js'
import { OrgOrdersService, getOptions } from './org-orders.class.js'
import { orgOrdersPath, orgOrdersMethods } from './org-orders.shared.js'
import {copyOrInsertOrgOrdersBeforePatch} from "./org-orders.distrib.js";
import {disallow, iff, preventChanges} from "feathers-hooks-common";
import {getNewQuote} from "./commands/getNewQuote.js";
import swagger from "feathers-swagger";
import {
workspaceDataSchema,
workspacePatchSchema,
workspaceQuerySchema,
workspaceSchema
} from "../workspaces/workspaces.schema.js";

export * from './org-orders.class.js'
export * from './org-orders.schema.js'

// A configure function that registers the service and its hooks via `app.configure`
export const orgOrders = (app) => {
// Register our service on the Feathers application
app.use(orgOrdersPath, new OrgOrdersService(getOptions(app)), {
// A list of all methods this service exposes externally
methods: orgOrdersMethods,
// You can add additional custom events to be sent to clients here
events: [],
docs: swagger.createSwaggerServiceOptions({
schemas: { orgOrdersSchema, orgOrdersDataSchema, orgOrdersPatchSchema , orgOrdersQuerySchema, },
docs: {
description: 'Organization orders for quotes and production from 3rd parties',
idType: 'string',
securities: ['all'],
operations: {
get: {
'parameters': [
{
'description': 'Organization OID',
'in': 'path',
'name': '_id',
'schema': {
'type': 'string'
},
'required': true,
},
]
},
patch: {
'parameters': [
{
'description': 'Organization OID',
'in': 'path',
'name': '_id',
'schema': {
'type': 'string'
},
'required': true,
},
]
},
}
}
})
})
// Initialize hooks
app.service(orgOrdersPath).hooks({
around: {
all: [
authenticate('jwt'),
schemaHooks.resolveExternal(orgOrdersExternalResolver),
schemaHooks.resolveResult(orgOrdersResolver)
]
},
before: {
all: [
schemaHooks.validateQuery(orgOrdersQueryValidator),
schemaHooks.resolveQuery(orgOrdersQueryResolver)
],
find: [],
get: [
copyOrInsertOrgOrdersBeforePatch,
],
create: [
disallow('external'),
schemaHooks.validateData(orgOrdersDataValidator),
schemaHooks.resolveData(orgOrdersDataResolver)
],
patch: [
copyOrInsertOrgOrdersBeforePatch,
preventChanges(false, 'productionQuotes'),
iff(
context => context.data.shouldGetNewQuote,
getNewQuote
),
schemaHooks.validateData(orgOrdersPatchValidator),
schemaHooks.resolveData(orgOrdersPatchResolver)
],
remove: []
},
after: {
all: []
},
error: {
all: []
}
})
}
58 changes: 58 additions & 0 deletions backend/src/services/org-orders/org-orders.schema.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// // For more information about this file see https://dove.feathersjs.com/guides/cli/service.schemas.html
import { resolve } from '@feathersjs/schema'
import { Type, getValidator, querySyntax } from '@feathersjs/typebox'
import { ObjectIdSchema } from '@feathersjs/typebox'
import { dataValidator, queryValidator } from '../../validators.js'
import {fileVersionProductionQuoteSchema, sharedModelProductionQuoteSchema} from "./org-orders.subdocs.schema.js";

// Main data model schema
export const orgOrdersSchema = Type.Object(
{
_id: ObjectIdSchema(), // this is the Organization's _id
productionQuotes: Type.Array(Type.Union([
fileVersionProductionQuoteSchema,
sharedModelProductionQuoteSchema,
])),
},
{ $id: 'OrgOrders', additionalProperties: false }
)
export const orgOrdersValidator = getValidator(orgOrdersSchema, dataValidator)
export const orgOrdersResolver = resolve({})

export const orgOrdersExternalResolver = resolve({})

// Schema for creating new entries
export const orgOrdersDataSchema = Type.Pick(
orgOrdersSchema,
['_id'], // this endpoint REQUIRES that the organization's _id be supplied as the _id for CREATE
{$id: 'OrgOrdersData'}
)
export const orgOrdersDataValidator = getValidator(orgOrdersDataSchema, dataValidator)
export const orgOrdersDataResolver = resolve({
productionQuotes: async (value, _message, _context) => {
if (value) {
return value;
}
return [];
}
})

// Schema for updating existing entries
export const orgOrdersPatchSchema = Type.Partial(orgOrdersSchema, {
$id: 'OrgOrdersPatch'
})
export const orgOrdersPatchValidator = getValidator(orgOrdersPatchSchema, dataValidator)
export const orgOrdersPatchResolver = resolve({})

// Schema for allowed query properties
export const orgOrdersQueryProperties = Type.Pick(orgOrdersSchema, ['_id', 'text'])
export const orgOrdersQuerySchema = Type.Intersect(
[
querySyntax(orgOrdersQueryProperties),
// Add additional query properties here
Type.Object({}, { additionalProperties: false })
],
{ additionalProperties: false }
)
export const orgOrdersQueryValidator = getValidator(orgOrdersQuerySchema, queryValidator)
export const orgOrdersQueryResolver = resolve({})
11 changes: 11 additions & 0 deletions backend/src/services/org-orders/org-orders.shared.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export const orgOrdersPath = 'org-orders'

export const orgOrdersMethods = ['get', 'create', 'patch']

export const orgOrdersClient = (client) => {
const connection = client.get('connection')

client.use(orgOrdersPath, connection.service(orgOrdersPath), {
methods: orgOrdersMethods
})
}
60 changes: 60 additions & 0 deletions backend/src/services/org-orders/org-orders.subdocs.schema.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import {ObjectIdSchema, Type} from "@feathersjs/typebox";
import {userSummarySchema} from "../users/users.subdocs.schema.js";
import {buildQuotesSummary, quotesSummarySchema} from "../quotes/quotes.distrib.js";
import {buildUserSummary} from "../users/users.distrib.js";
import {calculateRemainingAge} from "../quotes/quotes.helpers.js";

export const QuoteTargetMap = {
fileVersion: 'file-version',
sharedModel: 'shared-model',
}

export const fileVersionProductionQuoteSchema = Type.Object(
{
quoteTarget: Type.Literal(QuoteTargetMap.fileVersion),
requestedBy: userSummarySchema,
requestedAt: Type.Number(),
daysGoodFor: Type.Number(),
quote: quotesSummarySchema,
notes: Type.String(),
},
)

export function buildFileVersionProductionQuote(quote, user) {
const userSummary = buildUserSummary(user);
const quoteSummary = buildQuotesSummary(quote);
const result = {
quoteTarget: QuoteTargetMap.fileVersion,
requestedBy: userSummary,
requestedAt: quote.requestedAt,
daysGoodFor: calculateRemainingAge(quote),
quote: quoteSummary,
notes: '',
}
return result;
}

export const sharedModelProductionQuoteSchema = Type.Object(
{
quoteTarget: Type.Literal(QuoteTargetMap.sharedModel),
sharedModelId: ObjectIdSchema(),
requestedBy: userSummarySchema,
requestedAt: Type.Number(),
daysGoodFor: Type.Number(),
quote: quotesSummarySchema,
notes: Type.String(),
},
)

export function buildSharedModelProductionQuote(quote, user) {
const userSummary = buildUserSummary(user);
const quoteSummary = buildQuotesSummary(quote);
return {
quoteTarget: QuoteTargetMap.sharedModel,
requestedBy: userSummary,
requestedAt: quote.requestedAt,
daysGoodFor: calculateRemainingAge(quote),
quote: quoteSummary,
notes: '',
}
}
Loading