hydration-test-plugin.ts 8.3 KB

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