feat(medusa, link-modules): sales channel <> pubkey link (#5820)
This commit is contained in:
7
.changeset/rotten-lobsters-move.md
Normal file
7
.changeset/rotten-lobsters-move.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@medusajs/link-modules": patch
|
||||
"@medusajs/medusa": patch
|
||||
|
||||
---
|
||||
|
||||
feat: PubKey <> SC joiner config
|
||||
@@ -338,14 +338,14 @@ describe("Publishable API keys", () => {
|
||||
expect(response.status).toBe(200)
|
||||
|
||||
expect(mappings).toEqual([
|
||||
{
|
||||
expect.objectContaining({
|
||||
sales_channel_id: salesChannel1.id,
|
||||
publishable_key_id: pubKeyId,
|
||||
},
|
||||
{
|
||||
}),
|
||||
expect.objectContaining({
|
||||
sales_channel_id: salesChannel2.id,
|
||||
publishable_key_id: pubKeyId,
|
||||
},
|
||||
}),
|
||||
])
|
||||
|
||||
expect(response.data.publishable_api_key).toMatchObject({
|
||||
@@ -386,10 +386,10 @@ describe("Publishable API keys", () => {
|
||||
|
||||
await dbConnection.manager.query(
|
||||
`INSERT INTO publishable_api_key_sales_channel
|
||||
(publishable_key_id, sales_channel_id)
|
||||
VALUES ('${pubKeyId}', '${salesChannel1.id}'),
|
||||
('${pubKeyId}', '${salesChannel2.id}'),
|
||||
('${pubKeyId}', '${salesChannel3.id}');`
|
||||
(id, publishable_key_id, sales_channel_id)
|
||||
VALUES ('pksc-1','${pubKeyId}', '${salesChannel1.id}'),
|
||||
('pksc-2','${pubKeyId}', '${salesChannel2.id}'),
|
||||
('pksc-3','${pubKeyId}', '${salesChannel3.id}');`
|
||||
)
|
||||
})
|
||||
|
||||
@@ -417,16 +417,16 @@ describe("Publishable API keys", () => {
|
||||
const mappings = await dbConnection.manager.query(
|
||||
`SELECT *
|
||||
FROM publishable_api_key_sales_channel
|
||||
WHERE publishable_key_id = '${pubKeyId}'`
|
||||
WHERE publishable_key_id = '${pubKeyId}';`
|
||||
)
|
||||
|
||||
expect(response.status).toBe(200)
|
||||
|
||||
expect(mappings).toEqual([
|
||||
{
|
||||
expect.objectContaining({
|
||||
sales_channel_id: salesChannel3.id,
|
||||
publishable_key_id: pubKeyId,
|
||||
},
|
||||
}),
|
||||
])
|
||||
|
||||
expect(response.data.publishable_api_key).toMatchObject({
|
||||
@@ -467,9 +467,9 @@ describe("Publishable API keys", () => {
|
||||
|
||||
await dbConnection.manager.query(
|
||||
`INSERT INTO publishable_api_key_sales_channel
|
||||
(publishable_key_id, sales_channel_id)
|
||||
VALUES ('${pubKeyId}', '${salesChannel1.id}'),
|
||||
('${pubKeyId}', '${salesChannel2.id}');`
|
||||
(id, publishable_key_id, sales_channel_id)
|
||||
VALUES ('pksc-1', '${pubKeyId}', '${salesChannel1.id}'),
|
||||
('pksc-2', '${pubKeyId}', '${salesChannel2.id}');`
|
||||
)
|
||||
})
|
||||
|
||||
|
||||
@@ -5,3 +5,4 @@ export * from "./product-shipping-profile"
|
||||
export * from "./product-sales-channel"
|
||||
export * from "./cart-sales-channel"
|
||||
export * from "./order-sales-channel"
|
||||
export * from "./publishable-api-key-sales-channel"
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
import { ModuleJoinerConfig } from "@medusajs/types"
|
||||
import { LINKS } from "../links"
|
||||
|
||||
export const PublishableApiKeySalesChannel: ModuleJoinerConfig = {
|
||||
serviceName: LINKS.PublishableApiKeySalesChannel,
|
||||
isLink: true,
|
||||
databaseConfig: {
|
||||
tableName: "publishable_api_key_sales_channel",
|
||||
idPrefix: "pksc",
|
||||
},
|
||||
alias: [
|
||||
{
|
||||
name: "publishable_api_key_sales_channel",
|
||||
},
|
||||
{
|
||||
name: "publishable_api_key_sales_channels",
|
||||
},
|
||||
],
|
||||
primaryKeys: ["id", "publishable_key_id", "sales_channel_id"],
|
||||
relationships: [
|
||||
{
|
||||
serviceName: "publishableApiKeyService",
|
||||
isInternalService: true,
|
||||
primaryKey: "id",
|
||||
foreignKey: "publishable_key_id",
|
||||
alias: "publishable_key",
|
||||
},
|
||||
{
|
||||
serviceName: "salesChannelService",
|
||||
isInternalService: true,
|
||||
primaryKey: "id",
|
||||
foreignKey: "sales_channel_id",
|
||||
alias: "sales_channel",
|
||||
},
|
||||
],
|
||||
extends: [
|
||||
{
|
||||
serviceName: "publishableApiKeyService",
|
||||
fieldAlias: {
|
||||
sales_channels: "sales_channels_link.sales_channel",
|
||||
},
|
||||
relationship: {
|
||||
serviceName: LINKS.PublishableApiKeySalesChannel,
|
||||
isInternalService: true,
|
||||
primaryKey: "publishable_key_id",
|
||||
foreignKey: "id",
|
||||
alias: "sales_channels_link",
|
||||
isList: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
serviceName: "salesChannelService",
|
||||
relationship: {
|
||||
serviceName: LINKS.PublishableApiKeySalesChannel,
|
||||
isInternalService: true,
|
||||
primaryKey: "sales_channel_id",
|
||||
foreignKey: "id",
|
||||
alias: "publishable_keys_link",
|
||||
isList: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -40,4 +40,10 @@ export const LINKS = {
|
||||
"salesChannelService",
|
||||
"sales_channel_id"
|
||||
),
|
||||
PublishableApiKeySalesChannel: composeLinkName(
|
||||
"publishableApiKeyService",
|
||||
"publishable_key_id",
|
||||
"salesChannelService",
|
||||
"sales_channel_id"
|
||||
),
|
||||
}
|
||||
|
||||
@@ -3,3 +3,4 @@ export * as customer from "./customer-service"
|
||||
export * as region from "./region-service"
|
||||
export * as salesChannel from "./sales-channel-service"
|
||||
export * as shippingProfile from "./shipping-profile-service"
|
||||
export * as publishableApiKey from "./publishable-api-key-service"
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { ModuleJoinerConfig } from "@medusajs/types"
|
||||
|
||||
export default {
|
||||
serviceName: "publishableApiKeyService",
|
||||
primaryKeys: ["id"],
|
||||
linkableKeys: { publishable_key_id: "PublishableApiKey" },
|
||||
schema: `
|
||||
scalar Date
|
||||
scalar JSON
|
||||
|
||||
type PublishableApiKey {
|
||||
id: ID!
|
||||
sales_channel_id: String!
|
||||
publishable_key_id: String!
|
||||
created_at: Date!
|
||||
updated_at: Date!
|
||||
deleted_at: Date
|
||||
}
|
||||
`,
|
||||
alias: [
|
||||
{
|
||||
name: ["publishable_api_key", "publishable_api_keys"],
|
||||
args: { entity: "PublishableApiKey" },
|
||||
},
|
||||
],
|
||||
} as ModuleJoinerConfig
|
||||
@@ -0,0 +1,29 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm"
|
||||
|
||||
export class PublishableKeySalesChannelLink1701894188811
|
||||
implements MigrationInterface
|
||||
{
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE "publishable_api_key_sales_channel" ADD COLUMN IF NOT EXISTS "id" text;
|
||||
UPDATE "publishable_api_key_sales_channel" SET "id" = 'pksc_' || substr(md5(random()::text), 0, 27) WHERE id is NULL;
|
||||
ALTER TABLE "publishable_api_key_sales_channel" ALTER COLUMN "id" SET NOT NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS "IDX_id_publishable_api_key_sales_channel" ON "publishable_api_key_sales_channel" ("id");
|
||||
|
||||
ALTER TABLE "publishable_api_key_sales_channel" ADD COLUMN IF NOT EXISTS "created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now();
|
||||
ALTER TABLE "publishable_api_key_sales_channel" ADD COLUMN IF NOT EXISTS "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now();
|
||||
ALTER TABLE "publishable_api_key_sales_channel" ADD COLUMN IF NOT EXISTS "deleted_at" TIMESTAMP WITH TIME ZONE;
|
||||
`)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`
|
||||
DROP INDEX IF EXISTS "IDX_id_publishable_api_key_sales_channel";
|
||||
ALTER TABLE "publishable_api_key_sales_channel" DROP COLUMN IF EXISTS "id";
|
||||
ALTER TABLE "publishable_api_key_sales_channel" DROP COLUMN IF EXISTS "created_at";
|
||||
ALTER TABLE "publishable_api_key_sales_channel" DROP COLUMN IF EXISTS "updated_at";
|
||||
ALTER TABLE "publishable_api_key_sales_channel" DROP COLUMN IF EXISTS "deleted_at";
|
||||
`)
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,25 @@
|
||||
import { Entity, PrimaryColumn } from "typeorm"
|
||||
import { BeforeInsert, Column, Entity, PrimaryColumn } from "typeorm"
|
||||
import { BaseEntity } from "../interfaces"
|
||||
import { generateEntityId } from "../utils"
|
||||
|
||||
@Entity()
|
||||
export class PublishableApiKeySalesChannel {
|
||||
@PrimaryColumn()
|
||||
@Entity("publishable_api_key_sales_channel")
|
||||
export class PublishableApiKeySalesChannel extends BaseEntity {
|
||||
@Column({ type: "text" })
|
||||
id: string
|
||||
|
||||
@PrimaryColumn({ type: "text" })
|
||||
sales_channel_id: string
|
||||
|
||||
@PrimaryColumn()
|
||||
@PrimaryColumn({ type: "text" })
|
||||
publishable_key_id: string
|
||||
|
||||
/**
|
||||
* @apiIgnore
|
||||
*/
|
||||
@BeforeInsert()
|
||||
private beforeInsert(): void {
|
||||
this.id = generateEntityId(this.id, "pksc")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,6 +11,7 @@ import { SalesChannelLocation } from "./sales-channel-location"
|
||||
import { Product } from "./product"
|
||||
import { Cart } from "./cart"
|
||||
import { Order } from "./order"
|
||||
import { PublishableApiKey } from "./publishable-api-key"
|
||||
|
||||
@FeatureFlagEntity("sales_channels")
|
||||
export class SalesChannel extends SoftDeletableEntity {
|
||||
@@ -74,6 +75,20 @@ export class SalesChannel extends SoftDeletableEntity {
|
||||
)
|
||||
orders: Order[]
|
||||
|
||||
@ManyToMany(() => PublishableApiKey)
|
||||
@JoinTable({
|
||||
name: "publishable_api_key_sales_channel",
|
||||
inverseJoinColumn: {
|
||||
name: "publishable_key_id",
|
||||
referencedColumnName: "id",
|
||||
},
|
||||
joinColumn: {
|
||||
name: "sales_channel_id",
|
||||
referencedColumnName: "id",
|
||||
},
|
||||
})
|
||||
publishableKeys: PublishableApiKey[]
|
||||
|
||||
@OneToMany(
|
||||
() => SalesChannelLocation,
|
||||
(scLocation) => scLocation.sales_channel,
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Brackets, In } from "typeorm"
|
||||
import { PublishableApiKeySalesChannel, SalesChannel } from "../models"
|
||||
import { dataSource } from "../loaders/database"
|
||||
import SalesChannelRepository from "./sales-channel"
|
||||
import { generateEntityId } from "../utils"
|
||||
|
||||
const publishableApiKeySalesChannelAlias = "PublishableKeySalesChannel"
|
||||
|
||||
@@ -64,15 +65,16 @@ export const PublishableApiKeySalesChannelRepository = dataSource
|
||||
publishableApiKeyId: string,
|
||||
salesChannelIds: string[]
|
||||
): Promise<void> {
|
||||
const valuesToInsert = salesChannelIds.map((id) => ({
|
||||
id: generateEntityId(undefined, "pksc"),
|
||||
sales_channel_id: id,
|
||||
publishable_key_id: publishableApiKeyId,
|
||||
}))
|
||||
|
||||
await this.createQueryBuilder()
|
||||
.insert()
|
||||
.into(PublishableApiKeySalesChannel)
|
||||
.values(
|
||||
salesChannelIds.map((id) => ({
|
||||
sales_channel_id: id,
|
||||
publishable_key_id: publishableApiKeyId,
|
||||
}))
|
||||
)
|
||||
.values(valuesToInsert)
|
||||
.orIgnore()
|
||||
.execute()
|
||||
},
|
||||
|
||||
@@ -41,19 +41,13 @@ describe("PublishableApiKeyService", () => {
|
||||
await publishableApiKeyService.retrieve(
|
||||
IdMap.getId("order-edit-with-changes")
|
||||
)
|
||||
expect(
|
||||
publishableApiKeyRepository.findOne
|
||||
).toHaveBeenCalledTimes(1)
|
||||
expect(
|
||||
publishableApiKeyRepository.findOne
|
||||
).toHaveBeenCalledWith(
|
||||
{
|
||||
relationLoadStrategy: "query",
|
||||
where: {
|
||||
id: IdMap.getId("order-edit-with-changes")
|
||||
}
|
||||
}
|
||||
)
|
||||
expect(publishableApiKeyRepository.findOne).toHaveBeenCalledTimes(1)
|
||||
expect(publishableApiKeyRepository.findOne).toHaveBeenCalledWith({
|
||||
relationLoadStrategy: "query",
|
||||
where: {
|
||||
id: IdMap.getId("order-edit-with-changes"),
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it("should create a publishable api key and call the repository with the right arguments as well as the event bus service", async () => {
|
||||
|
||||
Reference in New Issue
Block a user