devkit-api.ts 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. import {
  2. BaseExtensionMessage,
  3. ExtensionMesssage,
  4. MessageResponse,
  5. NotificationMessage,
  6. WatchQueryFetchPolicy,
  7. } from '@vendure/common/lib/extension-host-types';
  8. import { Observable } from 'rxjs';
  9. import { take } from 'rxjs/operators';
  10. let targetOrigin = 'http://localhost:3000';
  11. /**
  12. * @description
  13. * Set the [window.postMessage API](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage)
  14. * `targetOrigin`. The Vendure ui-devkit uses the postMessage API to
  15. * enable cross-frame and cross-origin communication between the ui extension code and the Admin UI
  16. * app. The `targetOrigin` is a security feature intended to provide control over where messages are sent.
  17. */
  18. export function setTargetOrigin(value: string) {
  19. targetOrigin = value;
  20. }
  21. /**
  22. * @description
  23. * Perform a GraphQL query and returns either an Observable or a Promise of the result.
  24. */
  25. export function graphQlQuery<T, V extends { [key: string]: any }>(
  26. document: string,
  27. variables?: { [key: string]: any },
  28. fetchPolicy?: WatchQueryFetchPolicy,
  29. ): {
  30. then: Promise<T>['then'];
  31. stream: Observable<T>;
  32. } {
  33. const result$ = sendMessage('graphql-query', { document, variables, fetchPolicy });
  34. return {
  35. then: (...args: any[]) =>
  36. result$
  37. .pipe(take(1))
  38. .toPromise()
  39. .then(...args),
  40. stream: result$,
  41. };
  42. }
  43. /**
  44. * @description
  45. * Perform a GraphQL mutation and returns either an Observable or a Promise of the result.
  46. */
  47. export function graphQlMutation<T, V extends { [key: string]: any }>(
  48. document: string,
  49. variables?: { [key: string]: any },
  50. ): {
  51. then: Promise<T>['then'];
  52. stream: Observable<T>;
  53. } {
  54. const result$ = sendMessage('graphql-mutation', { document, variables });
  55. return {
  56. then: (...args: any[]) =>
  57. result$
  58. .pipe(take(1))
  59. .toPromise()
  60. .then(...args),
  61. stream: result$,
  62. };
  63. }
  64. /**
  65. * @description
  66. * Display a toast notification.
  67. */
  68. export function notify(options: NotificationMessage['data']) {
  69. sendMessage('notification', options).toPromise();
  70. }
  71. function sendMessage<T extends ExtensionMesssage>(type: T['type'], data: T['data']): Observable<any> {
  72. const requestId =
  73. type +
  74. '__' +
  75. Math.random()
  76. .toString(36)
  77. .substr(3);
  78. const message: BaseExtensionMessage = {
  79. requestId,
  80. type,
  81. data,
  82. };
  83. return new Observable<any>(subscriber => {
  84. const handleReply = (event: MessageEvent) => {
  85. const response: MessageResponse = event.data;
  86. if (response && response.requestId === requestId) {
  87. if (response.complete) {
  88. subscriber.complete();
  89. tearDown();
  90. return;
  91. }
  92. if (response.error) {
  93. subscriber.error(response.data);
  94. tearDown();
  95. return;
  96. }
  97. subscriber.next(response.data);
  98. }
  99. };
  100. const tearDown = () => {
  101. window.parent.postMessage({ requestId, type: 'cancellation', data: null }, targetOrigin);
  102. };
  103. window.addEventListener('message', handleReply);
  104. window.parent.postMessage(message, targetOrigin);
  105. return tearDown;
  106. });
  107. }