issue-1664.ts 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. import { OnApplicationBootstrap } from '@nestjs/common';
  2. import { DEFAULT_CHANNEL_CODE } from '@vendure/common/lib/shared-constants';
  3. import {
  4. Asset,
  5. Channel,
  6. CustomOrderFields,
  7. CustomProductFields,
  8. Order,
  9. OrderService,
  10. PluginCommonModule,
  11. Product,
  12. RequestContext,
  13. TransactionalConnection,
  14. User,
  15. VendurePlugin,
  16. } from '@vendure/core';
  17. import gql from 'graphql-tag';
  18. import { ProfileAsset } from './profile-asset.entity';
  19. import { Profile } from './profile.entity';
  20. declare module '@vendure/core' {
  21. interface CustomOrderFields {
  22. productOwner: User;
  23. }
  24. interface CustomProductFields {
  25. owner: User;
  26. }
  27. }
  28. const schema = gql`
  29. type Profile implements Node {
  30. id: ID!
  31. createdAt: DateTime!
  32. updatedAt: DateTime!
  33. name: String!
  34. user: User!
  35. }
  36. `;
  37. /**
  38. * Test plugin for https://github.com/vendure-ecommerce/vendure/issues/1664
  39. *
  40. * Test query:
  41. * ```graphql
  42. * query {
  43. * product(id: 1) {
  44. * name
  45. * customFields {
  46. * owner {
  47. * id
  48. * identifier
  49. * customFields {
  50. * profile {
  51. * id
  52. * name
  53. * }
  54. * }
  55. * }
  56. * }
  57. * }
  58. * }
  59. * ```
  60. */
  61. @VendurePlugin({
  62. imports: [PluginCommonModule],
  63. entities: () => [Profile, ProfileAsset],
  64. shopApiExtensions: { schema, resolvers: [] },
  65. adminApiExtensions: { schema, resolvers: [] },
  66. configuration: config => {
  67. // Order
  68. config.customFields.Order.push({
  69. name: 'productOwner', // because orders are always c2c (and should be stored redundant in case product get's deleted)
  70. nullable: true,
  71. type: 'relation',
  72. entity: User,
  73. public: false,
  74. eager: true,
  75. readonly: true,
  76. });
  77. config.customFields.Product.push({
  78. name: 'owner',
  79. nullable: true,
  80. type: 'relation',
  81. entity: User,
  82. public: false,
  83. eager: true, // needs to be eager to enable indexing of user->profile attributes like name, etc.
  84. readonly: true,
  85. });
  86. // User
  87. config.customFields.User.push({
  88. name: 'profile',
  89. type: 'relation',
  90. entity: Profile,
  91. nullable: true,
  92. public: false,
  93. internal: false,
  94. readonly: true,
  95. eager: true, // needs to be eager to enable indexing of profile attributes like name, etc.
  96. });
  97. return config;
  98. },
  99. })
  100. export class Test1664Plugin implements OnApplicationBootstrap {
  101. constructor(private connection: TransactionalConnection, private orderService: OrderService) {}
  102. async onApplicationBootstrap() {
  103. await this.createDummyProfiles();
  104. await this.createDummyOrder();
  105. }
  106. async createDummyProfiles() {
  107. const profilesCount = await this.connection.rawConnection.getRepository(Profile).count();
  108. if (0 < profilesCount) {
  109. return;
  110. }
  111. // Create a Profile and assign it to all the products
  112. const users = await this.connection.rawConnection.getRepository(User).find();
  113. // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
  114. const user = users[1]!;
  115. const profile = await this.connection.rawConnection.getRepository(Profile).save(
  116. new Profile({
  117. name: 'Test Profile',
  118. user,
  119. }),
  120. );
  121. (user.customFields as any).profile = profile;
  122. await this.connection.rawConnection.getRepository(User).save(user);
  123. const asset = await this.connection.rawConnection.getRepository(Asset).findOne({ where: { id: 1 } });
  124. if (asset) {
  125. const profileAsset = this.connection.rawConnection.getRepository(ProfileAsset).save({
  126. asset,
  127. profile,
  128. });
  129. }
  130. const products = await this.connection.rawConnection.getRepository(Product).find();
  131. for (const product of products) {
  132. (product.customFields as any).owner = user;
  133. await this.connection.rawConnection.getRepository(Product).save(product);
  134. }
  135. }
  136. async createDummyOrder() {
  137. const orderCount = await this.connection.rawConnection.getRepository(Order).count();
  138. if (0 < orderCount) {
  139. return;
  140. }
  141. const defaultChannel = await this.connection.getRepository(Channel).findOne({
  142. relations: ['defaultShippingZone', 'defaultTaxZone'],
  143. where: {
  144. code: DEFAULT_CHANNEL_CODE,
  145. },
  146. });
  147. if (!defaultChannel) {
  148. throw new Error(`Channel with code ${DEFAULT_CHANNEL_CODE} could not be found.`);
  149. }
  150. const ctx = new RequestContext({
  151. apiType: 'shop',
  152. authorizedAsOwnerOnly: false,
  153. channel: defaultChannel,
  154. isAuthorized: true,
  155. languageCode: defaultChannel.defaultLanguageCode,
  156. });
  157. // Create order
  158. const users = await this.connection.rawConnection.getRepository(User).find();
  159. // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
  160. const customer = users[1]!;
  161. const created = await this.orderService.create(ctx, customer.id);
  162. // Add products
  163. const products = await this.connection.rawConnection
  164. .getRepository(Product)
  165. .find({ relations: ['variants'] });
  166. const product = products[0];
  167. await this.orderService.addItemToOrder(ctx, created.id, product.variants[0].id, 1);
  168. // Add the product owner to order
  169. const productOwner = product.customFields.owner;
  170. await this.orderService.updateCustomFields(ctx, created.id, { productOwner });
  171. }
  172. }