issue-1664.ts 5.7 KB

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