hydration-test-plugin.ts 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. /* eslint-disable @typescript-eslint/no-non-null-assertion */
  2. import { Args, Query, Resolver } from '@nestjs/graphql';
  3. import {
  4. Asset,
  5. ChannelService,
  6. Ctx,
  7. DeepPartial,
  8. EntityHydrator,
  9. ID,
  10. LanguageCode,
  11. OrderService,
  12. PluginCommonModule,
  13. Product,
  14. ProductService,
  15. ProductVariantService,
  16. RequestContext,
  17. TransactionalConnection,
  18. VendureEntity,
  19. VendurePlugin,
  20. } from '@vendure/core';
  21. import gql from 'graphql-tag';
  22. import { Entity, ManyToOne, OneToMany } from 'typeorm';
  23. @Resolver()
  24. export class TestAdminPluginResolver {
  25. constructor(
  26. private connection: TransactionalConnection,
  27. private orderService: OrderService,
  28. private channelService: ChannelService,
  29. private productVariantService: ProductVariantService,
  30. private productService: ProductService,
  31. private entityHydrator: EntityHydrator,
  32. ) {}
  33. @Query()
  34. async hydrateProduct(@Ctx() ctx: RequestContext, @Args() args: { id: ID }) {
  35. const product = await this.connection.getRepository(ctx, Product).findOne({
  36. where: { id: args.id },
  37. relations: ['facetValues'],
  38. });
  39. // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
  40. await this.entityHydrator.hydrate(ctx, product!, {
  41. relations: [
  42. 'variants.options',
  43. 'variants.product',
  44. 'assets.product',
  45. 'facetValues.facet',
  46. 'featuredAsset',
  47. 'variants.stockMovements',
  48. ],
  49. applyProductVariantPrices: true,
  50. });
  51. return product;
  52. }
  53. // Test case for https://github.com/vendurehq/vendure/issues/1153
  54. @Query()
  55. async hydrateProductAsset(@Ctx() ctx: RequestContext, @Args() args: { id: ID }) {
  56. const product = await this.connection.getRepository(ctx, Product).findOne({ where: { id: args.id } });
  57. // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
  58. await this.entityHydrator.hydrate(ctx, product!, {
  59. relations: ['assets'],
  60. });
  61. return product;
  62. }
  63. // Test case for https://github.com/vendurehq/vendure/issues/1161
  64. @Query()
  65. async hydrateProductVariant(@Ctx() ctx: RequestContext, @Args() args: { id: ID }) {
  66. const [variant] = await this.productVariantService.findByIds(ctx, [args.id]);
  67. await this.entityHydrator.hydrate(ctx, variant, {
  68. relations: ['product.facetValues.facet'],
  69. });
  70. return variant;
  71. }
  72. // Test case for https://github.com/vendurehq/vendure/issues/1324
  73. @Query()
  74. async hydrateProductWithNoFacets(@Ctx() ctx: RequestContext) {
  75. const product = await this.productService.create(ctx, {
  76. enabled: true,
  77. translations: [
  78. {
  79. languageCode: LanguageCode.en,
  80. name: 'test',
  81. slug: 'test',
  82. description: 'test',
  83. },
  84. ],
  85. });
  86. await this.entityHydrator.hydrate(ctx, product, {
  87. relations: ['facetValues', 'facetValues.facet'],
  88. });
  89. return product;
  90. }
  91. // Test case for https://github.com/vendurehq/vendure/issues/1172
  92. @Query()
  93. async hydrateOrder(@Ctx() ctx: RequestContext, @Args() args: { id: ID }) {
  94. const order = await this.orderService.findOne(ctx, args.id);
  95. await this.entityHydrator.hydrate(ctx, order!, {
  96. relations: ['payments'],
  97. });
  98. return order;
  99. }
  100. // Test case for https://github.com/vendurehq/vendure/issues/1229
  101. @Query()
  102. async hydrateOrderReturnQuantities(@Ctx() ctx: RequestContext, @Args() args: { id: ID }) {
  103. const order = await this.orderService.findOne(ctx, args.id);
  104. await this.entityHydrator.hydrate(ctx, order!, {
  105. relations: [
  106. 'lines',
  107. 'lines.productVariant',
  108. 'lines.productVariant.product',
  109. 'lines.productVariant.product.assets',
  110. ],
  111. });
  112. return order?.lines.map(line => line.quantity);
  113. }
  114. // Test case for https://github.com/vendurehq/vendure/issues/1284
  115. @Query()
  116. async hydrateChannel(@Ctx() ctx: RequestContext, @Args() args: { id: ID }) {
  117. const channel = await this.channelService.findOne(ctx, args.id);
  118. await this.entityHydrator.hydrate(ctx, channel!, {
  119. relations: ['customFields.thumb'],
  120. });
  121. return channel;
  122. }
  123. @Query()
  124. async hydrateChannelWithNestedRelation(@Ctx() ctx: RequestContext, @Args() args: { id: ID }) {
  125. const channel = await this.channelService.findOne(ctx, args.id);
  126. await this.entityHydrator.hydrate(ctx, channel!, {
  127. relations: [
  128. 'customFields.thumb',
  129. 'customFields.additionalConfig',
  130. 'customFields.additionalConfig.backgroundImage',
  131. ],
  132. });
  133. return channel;
  134. }
  135. @Query()
  136. async hydrateChannelWithVeryLongPropertyName(@Ctx() ctx: RequestContext, @Args() args: { id: ID }) {
  137. const channel = await this.channelService.findOne(ctx, args.id);
  138. await this.entityHydrator.hydrate(ctx, channel!, {
  139. relations: ['customFields.additionalConfig.treeEntity'],
  140. });
  141. // Make sure we start on a tree entity to make use of tree-relations-qb-joiner.ts
  142. await Promise.all(
  143. ((channel!.customFields as any).additionalConfig.treeEntity as TreeEntity[]).map(treeEntity =>
  144. this.entityHydrator.hydrate(ctx, treeEntity, {
  145. relations: [
  146. 'childrenPropertyWithAVeryLongNameThatExceedsPostgresLimitsEasilyByItself',
  147. 'childrenPropertyWithAVeryLongNameThatExceedsPostgresLimitsEasilyByItself',
  148. 'childrenPropertyWithAVeryLongNameThatExceedsPostgresLimitsEasilyByItself.image1',
  149. 'childrenPropertyWithAVeryLongNameThatExceedsPostgresLimitsEasilyByItself.image2',
  150. ],
  151. }),
  152. ),
  153. );
  154. return channel;
  155. }
  156. }
  157. @Entity()
  158. export class AdditionalConfig extends VendureEntity {
  159. constructor(input?: DeepPartial<AdditionalConfig>) {
  160. super(input);
  161. }
  162. @ManyToOne(() => Asset, { onDelete: 'SET NULL', nullable: true })
  163. backgroundImage: Asset;
  164. @OneToMany(() => TreeEntity, entity => entity.additionalConfig)
  165. treeEntity: TreeEntity[];
  166. }
  167. @Entity()
  168. export class TreeEntity extends VendureEntity {
  169. constructor(input?: DeepPartial<TreeEntity>) {
  170. super(input);
  171. }
  172. @ManyToOne(() => AdditionalConfig, e => e.treeEntity, { nullable: true })
  173. additionalConfig: AdditionalConfig;
  174. @OneToMany(() => TreeEntity, entity => entity.parent)
  175. childrenPropertyWithAVeryLongNameThatExceedsPostgresLimitsEasilyByItself: TreeEntity[];
  176. @ManyToOne(
  177. () => TreeEntity,
  178. entity => entity.childrenPropertyWithAVeryLongNameThatExceedsPostgresLimitsEasilyByItself,
  179. {
  180. nullable: true,
  181. },
  182. )
  183. parent: TreeEntity;
  184. @ManyToOne(() => Asset)
  185. image1: Asset;
  186. @ManyToOne(() => Asset)
  187. image2: Asset;
  188. }
  189. @VendurePlugin({
  190. imports: [PluginCommonModule],
  191. entities: [AdditionalConfig, TreeEntity],
  192. adminApiExtensions: {
  193. resolvers: [TestAdminPluginResolver],
  194. schema: gql`
  195. extend type Query {
  196. hydrateProduct(id: ID!): JSON
  197. hydrateProductWithNoFacets: JSON
  198. hydrateProductAsset(id: ID!): JSON
  199. hydrateProductVariant(id: ID!): JSON
  200. hydrateOrder(id: ID!): JSON
  201. hydrateOrderReturnQuantities(id: ID!): JSON
  202. hydrateChannel(id: ID!): JSON
  203. hydrateChannelWithNestedRelation(id: ID!): JSON
  204. hydrateChannelWithVeryLongPropertyName(id: ID!): JSON
  205. }
  206. `,
  207. },
  208. configuration: config => {
  209. config.customFields.Channel.push({ name: 'thumb', type: 'relation', entity: Asset, nullable: true });
  210. config.customFields.Channel.push({
  211. name: 'additionalConfig',
  212. type: 'relation',
  213. entity: AdditionalConfig,
  214. graphQLType: 'JSON',
  215. nullable: true,
  216. });
  217. return config;
  218. },
  219. })
  220. export class HydrationTestPlugin {}