feat(medusa): Convert ShippingProfileService to TypeScript (#1963)

This commit is contained in:
Philip Korsholm
2022-08-08 16:51:55 +02:00
committed by GitHub
parent 40ae53567a
commit 152934f8b0
8 changed files with 519 additions and 512 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"@medusajs/medusa": patch
---
Convert ShippingProfileService to TypeScript
@@ -31,7 +31,7 @@ export default (app) => {
return app
}
export const defaultAdminShippingProfilesFields = [
export const defaultAdminShippingProfilesFields: (keyof ShippingProfile)[] = [
"id",
"name",
"type",
@@ -43,10 +43,8 @@ export const defaultAdminShippingProfilesFields = [
export type AdminDeleteShippingProfileRes = DeleteResponse
export const defaultAdminShippingProfilesRelations = [
"products",
"shipping_options",
]
export const defaultAdminShippingProfilesRelations: (keyof ShippingProfile)[] =
["products", "shipping_options"]
export type AdminShippingProfilesRes = {
shipping_profile: ShippingProfile
@@ -29,21 +29,21 @@ describe("ShippingProfileService", () => {
describe("update", () => {
const profRepo = MockRepository({
findOne: q => {
findOne: (q) => {
return Promise.resolve({ id: q.where.id })
},
})
const productService = {
update: jest.fn(),
withTransaction: function() {
withTransaction: function () {
return this
},
}
const shippingOptionService = {
update: jest.fn(),
withTransaction: function() {
withTransaction: function () {
return this
},
}
@@ -98,7 +98,7 @@ describe("ShippingProfileService", () => {
describe("delete", () => {
const profRepo = MockRepository({
findOne: q => {
findOne: (q) => {
return Promise.resolve({ id: q.where.id })
},
})
@@ -126,7 +126,7 @@ describe("ShippingProfileService", () => {
const productService = {
update: jest.fn(),
withTransaction: function() {
withTransaction: function () {
return this
},
}
@@ -156,7 +156,7 @@ describe("ShippingProfileService", () => {
describe("fetchCartOptions", () => {
const profRepo = MockRepository({
find: q => {
find: (q) => {
switch (q.where.id) {
default:
return Promise.resolve([
@@ -188,8 +188,8 @@ describe("ShippingProfileService", () => {
},
])
}),
validateCartOption: jest.fn().mockImplementation(s => s),
withTransaction: function() {
validateCartOption: jest.fn().mockImplementation((s) => s),
withTransaction: function () {
return this
},
}
@@ -301,7 +301,7 @@ describe("ShippingProfileService", () => {
const shippingOptionService = {
update: jest.fn(),
withTransaction: function() {
withTransaction: function () {
return this
},
}
@@ -1,489 +0,0 @@
import _ from "lodash"
import { MedusaError } from "medusa-core-utils"
import { BaseService } from "medusa-interfaces"
import { Any } from "typeorm"
/**
* Provides layer to manipulate profiles.
* @class
* @implements {BaseService}
*/
class ShippingProfileService extends BaseService {
constructor({
manager,
shippingProfileRepository,
productService,
productRepository,
shippingOptionService,
customShippingOptionService,
}) {
super()
/** @private @const {EntityManager} */
this.manager_ = manager
/** @private @const {ShippingProfileRepository} */
this.shippingProfileRepository_ = shippingProfileRepository
/** @private @const {ProductService} */
this.productService_ = productService
/** @private @const {ProductReppsitory} */
this.productRepository_ = productRepository
/** @private @const {ShippingOptionService} */
this.shippingOptionService_ = shippingOptionService
/** @private @const {CustomShippingOptionService} */
this.customShippingOptionService_ = customShippingOptionService
}
withTransaction(transactionManager) {
if (!transactionManager) {
return this
}
const cloned = new ShippingProfileService({
manager: transactionManager,
shippingProfileRepository: this.shippingProfileRepository_,
productService: this.productService_,
shippingOptionService: this.shippingOptionService_,
customShippingOptionService: this.customShippingOptionService_,
})
cloned.transactionManager_ = transactionManager
return cloned
}
/**
* @param {Object} selector - the query object for find
* @param {Object} config - the config object for find
* @return {Promise} the result of the find operation
*/
async list(selector = {}, config = { relations: [], skip: 0, take: 10 }) {
const shippingProfileRepo = this.manager_.getCustomRepository(
this.shippingProfileRepository_
)
const query = this.buildQuery_(selector, config)
return shippingProfileRepo.find(query)
}
async fetchOptionsByProductIds(productIds, filter) {
const products = await this.productService_.list(
{
id: Any(productIds),
},
{
relations: [
"profile",
"profile.shipping_options",
"profile.shipping_options.requirements",
],
}
)
const profiles = products.map((p) => p.profile)
const optionIds = profiles.reduce(
(acc, next) => acc.concat(next.shipping_options),
[]
)
const options = await Promise.all(
optionIds.map(async (option) => {
let canSend = true
if (filter.region_id) {
if (filter.region_id !== option.region_id) {
canSend = false
}
}
if (option.deleted_at !== null) {
canSend = false
}
return canSend ? option : null
})
)
return options.filter((o) => !!o)
}
/**
* Gets a profile by id.
* Throws in case of DB Error and if profile was not found.
* @param {string} profileId - the id of the profile to get.
* @param {Object} options - options opf the query.
* @return {Promise<Product>} the profile document.
*/
async retrieve(profileId, options = {}) {
const profileRepository = this.manager_.getCustomRepository(
this.shippingProfileRepository_
)
const validatedId = this.validateId_(profileId)
const query = {
where: { id: validatedId },
}
if (options.select) {
query.select = options.select
}
if (options.relations) {
query.relations = options.relations
}
const profile = await profileRepository.findOne(query)
if (!profile) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`Profile with id: ${profileId} was not found`
)
}
return profile
}
async retrieveDefault() {
const profileRepository = this.manager_.getCustomRepository(
this.shippingProfileRepository_
)
const profile = await profileRepository.findOne({
where: { type: "default" },
})
return profile
}
/**
* Creates a default shipping profile, if this does not already exist.
* @return {Promise<ShippingProfile>} the shipping profile
*/
async createDefault() {
return this.atomicPhase_(async (manager) => {
let profile = await this.retrieveDefault()
if (!profile) {
const profileRepository = manager.getCustomRepository(
this.shippingProfileRepository_
)
const p = await profileRepository.create({
type: "default",
name: "Default Shipping Profile",
})
profile = await profileRepository.save(p)
}
return profile
})
}
/**
* Retrieves the default gift card profile
* @return {Object} the shipping profile for gift cards
*/
async retrieveGiftCardDefault() {
const profileRepository = this.manager_.getCustomRepository(
this.shippingProfileRepository_
)
const giftCardProfile = await profileRepository.findOne({
where: { type: "gift_card" },
})
return giftCardProfile
}
/**
* Creates a default shipping profile, for gift cards if unless it already
* exists.
* @return {Promise<ShippingProfile>} the shipping profile
*/
async createGiftCardDefault() {
return this.atomicPhase_(async (manager) => {
let profile = await this.retrieveGiftCardDefault()
if (!profile) {
const profileRepository = manager.getCustomRepository(
this.shippingProfileRepository_
)
const p = await profileRepository.create({
type: "gift_card",
name: "Gift Card Profile",
})
profile = await profileRepository.save(p)
}
return profile
})
}
/**
* Creates a new shipping profile.
* @param {ShippingProfile} profile - the shipping profile to create from
* @return {Promise} the result of the create operation
*/
async create(profile) {
return this.atomicPhase_(async (manager) => {
const profileRepository = manager.getCustomRepository(
this.shippingProfileRepository_
)
if (profile.products || profile.shipping_options) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Please add products and shipping_options after creating Shipping Profiles"
)
}
const created = profileRepository.create(profile)
const result = await profileRepository.save(created)
return result
})
}
/**
* Updates a profile. Metadata updates and product updates should use
* dedicated methods, e.g. `setMetadata`, `addProduct`, etc. The function
* will throw errors if metadata or product updates are attempted.
* @param {string} profileId - the id of the profile. Must be a string that
* can be casted to an ObjectId
* @param {object} update - an object with the update values.
* @return {Promise} resolves to the update result.
*/
async update(profileId, update) {
return this.atomicPhase_(async (manager) => {
const profileRepository = manager.getCustomRepository(
this.shippingProfileRepository_
)
const profile = await this.retrieve(profileId, {
relations: [
"products",
"products.profile",
"shipping_options",
"shipping_options.profile",
],
})
const { metadata, products, shipping_options, ...rest } = update
if (metadata) {
profile.metadata = this.setMetadata_(profile, metadata)
}
if (products) {
for (const pId of products) {
await this.productService_.withTransaction(manager).update(pId, {
profile_id: profile.id,
})
}
}
if (shipping_options) {
for (const oId of shipping_options) {
await this.shippingOptionService_
.withTransaction(manager)
.update(oId, {
profile_id: profile.id,
})
}
}
for (const [key, value] of Object.entries(rest)) {
profile[key] = value
}
const result = await profileRepository.save(profile)
return result
})
}
/**
* Deletes a profile with a given profile id.
* @param {string} profileId - the id of the profile to delete. Must be
* castable as an ObjectId
* @return {Promise} the result of the delete operation.
*/
async delete(profileId) {
return this.atomicPhase_(async (manager) => {
const profileRepo = manager.getCustomRepository(
this.shippingProfileRepository_
)
// Should not fail, if profile does not exist, since delete is idempotent
const profile = await profileRepo.findOne({ where: { id: profileId } })
if (!profile) {
return Promise.resolve()
}
await profileRepo.softRemove(profile)
return Promise.resolve()
})
}
/**
* Adds a product to a profile. The method is idempotent, so multiple calls
* with the same product variant will have the same result.
* @param {string} profileId - the profile to add the product to.
* @param {string} productId - the product to add.
* @return {Promise} the result of update
*/
async addProduct(profileId, productId) {
return this.atomicPhase_(async (manager) => {
await this.productService_
.withTransaction(manager)
.update(productId, { profile_id: profileId })
const updated = await this.retrieve(profileId)
return updated
})
}
/**
* Adds a shipping option to the profile. The shipping option can be used to
* fulfill the products in the products field.
* @param {string} profileId - the profile to apply the shipping option to
* @param {string} optionId - the option to add to the profile
* @return {Promise} the result of the model update operation
*/
async addShippingOption(profileId, optionId) {
return this.atomicPhase_(async (manager) => {
await this.shippingOptionService_
.withTransaction(manager)
.update(optionId, { profile_id: profileId })
const updated = await this.retrieve(profileId)
return updated
})
}
/**
* Decorates a profile.
* @param {Profile} profile - the profile to decorate.
* @param {string[]} fields - the fields to include.
* @param {string[]} expandFields - fields to expand.
* @return {Profile} return the decorated profile.
*/
async decorate(profile, fields, expandFields = []) {
const requiredFields = ["_id", "metadata"]
const decorated = _.pick(profile, fields.concat(requiredFields))
if (expandFields.includes("products") && profile.products) {
decorated.products = await Promise.all(
profile.products.map((pId) => this.productService_.retrieve(pId))
)
}
if (expandFields.includes("shipping_options") && profile.shipping_options) {
decorated.shipping_options = await Promise.all(
profile.shipping_options.map((oId) =>
this.shippingOptionService_.retrieve(oId)
)
)
}
const final = await this.runDecorators_(decorated)
return final
}
/**
* Returns a list of all the productIds in the cart.
* @param {Cart} cart - the cart to extract products from
* @return {[string]} a list of product ids
*/
getProfilesInCart_(cart) {
return cart.items.reduce((acc, next) => {
// We may have line items that are not associated with a product
if (next.variant && next.variant.product) {
if (!acc.includes(next.variant.product.profile_id)) {
acc.push(next.variant.product.profile_id)
}
}
return acc
}, [])
}
/**
* Finds all the shipping profiles that cover the products in a cart, and
* validates all options that are available for the cart.
* @param {Cart} cart - the cart object to find shipping options for
* @return {Promise<[ShippingOption]>} a list of the available shipping options
*/
async fetchCartOptions(cart) {
const profileIds = this.getProfilesInCart_(cart)
const selector = {
profile_id: profileIds,
admin_only: false,
}
const customShippingOptions = await this.customShippingOptionService_
.withTransaction(this.manager_)
.list(
{
cart_id: cart.id,
},
{ select: ["id", "shipping_option_id", "price"] }
)
const hasCustomShippingOptions = customShippingOptions?.length
// if there are custom shipping options associated with the cart, use those
if (hasCustomShippingOptions) {
selector.id = customShippingOptions.map((cso) => cso.shipping_option_id)
}
const rawOpts = await this.shippingOptionService_
.withTransaction(this.manager_)
.list(selector, {
relations: ["requirements", "profile"],
})
// if there are custom shipping options associated with the cart, return cart shipping options with custom price
if (hasCustomShippingOptions) {
return rawOpts.map((so) => {
const customOption = customShippingOptions.find(
(cso) => cso.shipping_option_id === so.id
)
return {
...so,
amount: customOption?.price,
}
})
}
const options = await Promise.all(
rawOpts.map(async (so) => {
try {
const option = await this.shippingOptionService_
.withTransaction(this.manager_)
.validateCartOption(so, cart)
if (option) {
return option
}
return null
} catch (err) {
// if validateCartOption fails it means the option is not valid
return null
}
})
)
return options.filter(Boolean)
}
}
export default ShippingProfileService
@@ -0,0 +1,481 @@
import { MedusaError } from "medusa-core-utils"
import { EntityManager } from "typeorm"
import { TransactionBaseService } from "../interfaces"
import {
Cart,
ShippingOption,
ShippingProfile,
ShippingProfileType,
} from "../models"
import { ProductRepository } from "../repositories/product"
import { ShippingProfileRepository } from "../repositories/shipping-profile"
import { FindConfig, Selector } from "../types/common"
import {
CreateShippingProfile,
UpdateShippingProfile,
} from "../types/shipping-profile"
import { buildQuery, setMetadata } from "../utils"
import CustomShippingOptionService from "./custom-shipping-option"
import ProductService from "./product"
import ShippingOptionService from "./shipping-option"
type InjectedDependencies = {
manager: EntityManager
productService: ProductService
shippingOptionService: ShippingOptionService
customShippingOptionService: CustomShippingOptionService
shippingProfileRepository: typeof ShippingProfileRepository
productRepository: typeof ProductRepository
}
/**
* Provides layer to manipulate profiles.
* @constructor
* @implements {BaseService}
*/
class ShippingProfileService extends TransactionBaseService<ShippingProfileService> {
protected readonly productService_: ProductService
protected readonly shippingOptionService_: ShippingOptionService
protected readonly customShippingOptionService_: CustomShippingOptionService
protected readonly shippingProfileRepository_: typeof ShippingProfileRepository
protected readonly productRepository_: typeof ProductRepository
protected manager_: EntityManager
protected transactionManager_: EntityManager | undefined
constructor({
manager,
shippingProfileRepository,
productService,
productRepository,
shippingOptionService,
customShippingOptionService,
}: InjectedDependencies) {
super({
manager,
shippingProfileRepository,
productService,
productRepository,
shippingOptionService,
customShippingOptionService,
})
this.manager_ = manager
this.shippingProfileRepository_ = shippingProfileRepository
this.productService_ = productService
this.productRepository_ = productRepository
this.shippingOptionService_ = shippingOptionService
this.customShippingOptionService_ = customShippingOptionService
}
/**
* @param selector - the query object for find
* @param config - the config object for find
* @return the result of the find operation
*/
async list(
selector: Selector<ShippingProfile> = {},
config: FindConfig<ShippingProfile> = { relations: [], skip: 0, take: 10 }
): Promise<ShippingProfile[]> {
const shippingProfileRepo = this.manager_.getCustomRepository(
this.shippingProfileRepository_
)
const query = buildQuery(selector, config)
return shippingProfileRepo.find(query)
}
async fetchOptionsByProductIds(
productIds: string[],
filter: Selector<ShippingOption>
): Promise<ShippingOption[]> {
const products = await this.productService_.list(
{
id: productIds,
},
{
relations: [
"profile",
"profile.shipping_options",
"profile.shipping_options.requirements",
],
}
)
const profiles = products.map((p) => p.profile)
const shippingOptions = profiles.reduce(
(acc: ShippingOption[], next: ShippingProfile) =>
acc.concat(next.shipping_options),
[]
)
const options = await Promise.all(
shippingOptions.map(async (option) => {
let canSend = true
if (filter.region_id) {
if (filter.region_id !== option.region_id) {
canSend = false
}
}
if (option.deleted_at !== null) {
canSend = false
}
return canSend ? option : null
})
)
return options.filter(Boolean) as ShippingOption[]
}
/**
* Gets a profile by id.
* Throws in case of DB Error and if profile was not found.
* @param profileId - the id of the profile to get.
* @param options - options opf the query.
* @return {Promise<Product>} the profile document.
*/
async retrieve(
profileId: string,
options: FindConfig<ShippingProfile> = {}
): Promise<ShippingProfile> {
const profileRepository = this.manager_.getCustomRepository(
this.shippingProfileRepository_
)
const query = buildQuery({ id: profileId }, options)
const profile = await profileRepository.findOne(query)
if (!profile) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`Profile with id: ${profileId} was not found`
)
}
return profile
}
async retrieveDefault(): Promise<ShippingProfile | undefined> {
const profileRepository = this.manager_.getCustomRepository(
this.shippingProfileRepository_
)
const profile = await profileRepository.findOne({
where: { type: "default" },
})
return profile
}
/**
* Creates a default shipping profile, if this does not already exist.
* @return {Promise<ShippingProfile>} the shipping profile
*/
async createDefault(): Promise<ShippingProfile> {
return await this.atomicPhase_(async (manager) => {
let profile = await this.retrieveDefault()
if (!profile) {
const profileRepository = manager.getCustomRepository(
this.shippingProfileRepository_
)
const toCreate = {
type: ShippingProfileType.DEFAULT,
name: "Default Shipping Profile",
}
const created = await profileRepository.create(toCreate)
profile = await profileRepository.save(created)
}
return profile
})
}
/**
* Retrieves the default gift card profile
* @return the shipping profile for gift cards
*/
async retrieveGiftCardDefault(): Promise<ShippingProfile | undefined> {
const profileRepository = this.manager_.getCustomRepository(
this.shippingProfileRepository_
)
const giftCardProfile = await profileRepository.findOne({
where: { type: "gift_card" },
})
return giftCardProfile
}
/**
* Creates a default shipping profile, for gift cards if unless it already
* exists.
* @return the shipping profile
*/
async createGiftCardDefault(): Promise<ShippingProfile> {
return await this.atomicPhase_(async (manager) => {
let profile = await this.retrieveGiftCardDefault()
if (!profile) {
const profileRepository = manager.getCustomRepository(
this.shippingProfileRepository_
)
const created = await profileRepository.create({
type: ShippingProfileType.GIFT_CARD,
name: "Gift Card Profile",
})
profile = await profileRepository.save(created)
}
return profile
})
}
/**
* Creates a new shipping profile.
* @param profile - the shipping profile to create from
* @return the result of the create operation
*/
async create(profile: CreateShippingProfile): Promise<ShippingProfile> {
return await this.atomicPhase_(async (manager) => {
const profileRepository = manager.getCustomRepository(
this.shippingProfileRepository_
)
if (profile["products"] || profile["shipping_options"]) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Please add products and shipping_options after creating Shipping Profiles"
)
}
const created = profileRepository.create(profile)
const result = await profileRepository.save(created)
return result
})
}
/**
* Updates a profile. Metadata updates and product updates should use
* dedicated methods, e.g. `setMetadata`, `addProduct`, etc. The function
* will throw errors if metadata or product updates are attempted.
* @param profileId - the id of the profile. Must be a string that
* can be casted to an ObjectId
* @param update - an object with the update values.
* @return resolves to the update result.
*/
async update(
profileId: string,
update: UpdateShippingProfile
): Promise<ShippingProfile> {
return await this.atomicPhase_(async (manager) => {
const profileRepository = manager.getCustomRepository(
this.shippingProfileRepository_
)
const profile = await this.retrieve(profileId, {
relations: [
"products",
"products.profile",
"shipping_options",
"shipping_options.profile",
],
})
const { metadata, products, shipping_options, ...rest } = update
if (metadata) {
profile.metadata = setMetadata(profile, metadata)
}
if (products) {
for (const pId of products) {
await this.productService_.withTransaction(manager).update(pId, {
profile_id: profile.id,
})
}
}
if (shipping_options) {
for (const oId of shipping_options) {
await this.shippingOptionService_
.withTransaction(manager)
.update(oId, {
profile_id: profile.id,
})
}
}
for (const [key, value] of Object.entries(rest)) {
profile[key] = value
}
return await profileRepository.save(profile)
})
}
/**
* Deletes a profile with a given profile id.
* @param profileId - the id of the profile to delete. Must be
* castable as an ObjectId
* @return the result of the delete operation.
*/
async delete(profileId: string): Promise<void> {
return await this.atomicPhase_(async (manager) => {
const profileRepo = manager.getCustomRepository(
this.shippingProfileRepository_
)
// Should not fail, if profile does not exist, since delete is idempotent
const profile = await profileRepo.findOne({ where: { id: profileId } })
if (!profile) {
return Promise.resolve()
}
await profileRepo.softRemove(profile)
return Promise.resolve()
})
}
/**
* Adds a product to a profile. The method is idempotent, so multiple calls
* with the same product variant will have the same result.
* @param profileId - the profile to add the product to.
* @param productId - the product to add.
* @return the result of update
*/
async addProduct(
profileId: string,
productId: string
): Promise<ShippingProfile> {
return await this.atomicPhase_(async (manager) => {
await this.productService_
.withTransaction(manager)
.update(productId, { profile_id: profileId })
return await this.retrieve(profileId)
})
}
/**
* Adds a shipping option to the profile. The shipping option can be used to
* fulfill the products in the products field.
* @param profileId - the profile to apply the shipping option to
* @param optionId - the option to add to the profile
* @return the result of the model update operation
*/
async addShippingOption(
profileId: string,
optionId: string
): Promise<ShippingProfile> {
return await this.atomicPhase_(async (manager) => {
await this.shippingOptionService_
.withTransaction(manager)
.update(optionId, { profile_id: profileId })
const updated = await this.retrieve(profileId)
return updated
})
}
/**
* Finds all the shipping profiles that cover the products in a cart, and
* validates all options that are available for the cart.
* @param cart - the cart object to find shipping options for
* @return a list of the available shipping options
*/
async fetchCartOptions(cart): Promise<ShippingOption[]> {
return await this.atomicPhase_(async (manager) => {
const profileIds = this.getProfilesInCart(cart)
const selector: Selector<ShippingOption> = {
profile_id: profileIds,
admin_only: false,
}
const customShippingOptions = await this.customShippingOptionService_
.withTransaction(manager)
.list(
{
cart_id: cart.id,
},
{ select: ["id", "shipping_option_id", "price"] }
)
const hasCustomShippingOptions = customShippingOptions?.length
// if there are custom shipping options associated with the cart, use those
if (hasCustomShippingOptions) {
selector.id = customShippingOptions.map((cso) => cso.shipping_option_id)
}
const rawOpts = await this.shippingOptionService_
.withTransaction(manager)
.list(selector, {
relations: ["requirements", "profile"],
})
// if there are custom shipping options associated with the cart, return cart shipping options with custom price
if (hasCustomShippingOptions) {
return rawOpts.map((so) => {
const customOption = customShippingOptions.find(
(cso) => cso.shipping_option_id === so.id
)
return {
...so,
amount: customOption?.price,
}
}) as ShippingOption[]
}
const options = await Promise.all(
rawOpts.map(async (so) => {
try {
const option = await this.shippingOptionService_
.withTransaction(manager)
.validateCartOption(so, cart)
if (option) {
return option
}
return null
} catch (err) {
// if validateCartOption fails it means the option is not valid
return null
}
})
)
return options.filter(Boolean) as ShippingOption[]
})
}
/**
* Returns a list of all the productIds in the cart.
* @param cart - the cart to extract products from
* @return a list of product ids
*/
protected getProfilesInCart(cart: Cart): string[] {
return cart.items.reduce((acc, next) => {
// We may have line items that are not associated with a product
if (next.variant && next.variant.product) {
if (!acc.includes(next.variant.product.profile_id)) {
acc.push(next.variant.product.profile_id)
}
}
return acc
}, [] as string[])
}
}
export default ShippingProfileService
+7 -9
View File
@@ -20,11 +20,10 @@ import { ClassConstructor } from "./global"
/**
* Utility type used to remove some optional attributes (coming from K) from a type T
*/
export type WithRequiredProperty<T, K extends keyof T> = T &
{
// -? removes 'optional' from a property
[Property in K]-?: T[Property]
}
export type WithRequiredProperty<T, K extends keyof T> = T & {
// -? removes 'optional' from a property
[Property in K]-?: T[Property]
}
export type PartialPick<T, K extends keyof T> = {
[P in K]?: T[P]
@@ -80,10 +79,9 @@ export interface FindConfig<Entity> {
export interface CustomFindOptions<TModel, InKeys extends keyof TModel> {
select?: FindManyOptions<TModel>["select"]
where?: FindManyOptions<TModel>["where"] &
{
[P in InKeys]?: TModel[P][]
}
where?: FindManyOptions<TModel>["where"] & {
[P in InKeys]?: TModel[P][]
}
order?: OrderByCondition
skip?: number
take?: number
@@ -69,5 +69,6 @@ export type UpdateShippingOptionInput = {
requirements?: ShippingOptionRequirement[]
region_id?: string
provider_id?: string
profile_id?: string
data?: string
}
@@ -0,0 +1,13 @@
import { Product, ShippingOption, ShippingProfileType } from "../models"
export type CreateShippingProfile = {
name: string
}
export type UpdateShippingProfile = {
name?: string
metadata?: Record<string, unknown>
type?: ShippingProfileType
products?: string[]
shipping_options?: string[]
}