simple-graphql-client.ts 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. import { DocumentNode } from 'graphql';
  2. import { GraphQLClient } from 'graphql-request';
  3. import gql from 'graphql-tag';
  4. import { print } from 'graphql/language/printer';
  5. import { Curl } from 'node-libcurl';
  6. import { CreateAssets } from 'shared/generated-types';
  7. import { SUPER_ADMIN_USER_IDENTIFIER, SUPER_ADMIN_USER_PASSWORD } from 'shared/shared-constants';
  8. import { CREATE_ASSETS } from '../../admin-ui/src/app/data/definitions/product-definitions';
  9. import { getConfig } from '../src/config/vendure-config';
  10. // tslint:disable:no-console
  11. /**
  12. * A minimalistic GraphQL client for populating and querying test data.
  13. */
  14. export class SimpleGraphQLClient {
  15. private client: GraphQLClient;
  16. private authToken: string;
  17. private channelToken: string;
  18. constructor(private apiUrl: string = '') {
  19. this.client = new GraphQLClient(apiUrl);
  20. }
  21. setAuthToken(token: string) {
  22. this.authToken = token;
  23. this.setHeaders();
  24. }
  25. getAuthToken(): string {
  26. return this.authToken;
  27. }
  28. setChannelToken(token: string) {
  29. this.channelToken = token;
  30. this.setHeaders();
  31. }
  32. /**
  33. * Performs both query and mutation operations.
  34. */
  35. async query<T = any, V = Record<string, any>>(query: DocumentNode, variables?: V): Promise<T> {
  36. const queryString = print(query);
  37. const result = await this.client.rawRequest<T>(queryString, variables);
  38. const authToken = result.headers.get(getConfig().authOptions.authTokenHeaderKey);
  39. if (authToken != null) {
  40. this.setAuthToken(authToken);
  41. }
  42. return result.data as T;
  43. }
  44. async queryStatus<T = any, V = Record<string, any>>(query: DocumentNode, variables?: V): Promise<number> {
  45. const queryString = print(query);
  46. const result = await this.client.rawRequest<T>(queryString, variables);
  47. return result.status;
  48. }
  49. /**
  50. * Uses curl to post a multipart/form-data request to create new assets. Due to differences between the Node and browser
  51. * environments, we cannot just use an existing library like apollo-upload-client.
  52. *
  53. * Upload spec: https://github.com/jaydenseric/graphql-multipart-request-spec
  54. * Discussion of issue: https://github.com/jaydenseric/apollo-upload-client/issues/32
  55. */
  56. uploadAssets(filePaths: string[]): Promise<CreateAssets.Mutation> {
  57. return new Promise((resolve, reject) => {
  58. const curl = new Curl();
  59. const postData: any[] = [
  60. {
  61. name: 'operations',
  62. contents: JSON.stringify({
  63. operationName: 'CreateAssets',
  64. variables: {
  65. input: filePaths.map(() => ({ file: null })),
  66. },
  67. query: print(CREATE_ASSETS),
  68. }),
  69. },
  70. {
  71. name: 'map',
  72. contents:
  73. '{' +
  74. filePaths.map((filePath, i) => `"${i}":["variables.input.${i}.file"]`).join(',') +
  75. '}',
  76. },
  77. ...filePaths.map((filePath, i) => ({
  78. name: i.toString(),
  79. file: filePath,
  80. })),
  81. ];
  82. curl.setOpt(Curl.option.URL, this.apiUrl);
  83. curl.setOpt(Curl.option.VERBOSE, false);
  84. curl.setOpt(Curl.option.TIMEOUT_MS, 30000);
  85. curl.setOpt(Curl.option.HTTPPOST, postData);
  86. curl.setOpt(Curl.option.HTTPHEADER, [
  87. `Authorization: Bearer ${this.authToken}`,
  88. `${getConfig().channelTokenKey}: ${this.channelToken}`,
  89. ]);
  90. curl.perform();
  91. curl.on('end', (statusCode, body) => {
  92. curl.close();
  93. resolve(JSON.parse(body).data);
  94. });
  95. curl.on('error', err => {
  96. curl.close();
  97. console.log(err);
  98. reject(err);
  99. });
  100. });
  101. }
  102. async asUserWithCredentials(username: string, password: string) {
  103. // first log out as the current user
  104. if (this.authToken) {
  105. await this.query(
  106. gql`
  107. mutation {
  108. logout
  109. }
  110. `,
  111. );
  112. }
  113. const result = await this.query(
  114. gql`
  115. mutation($username: String!, $password: String!) {
  116. login(username: $username, password: $password) {
  117. user {
  118. id
  119. identifier
  120. channelTokens
  121. }
  122. }
  123. }
  124. `,
  125. {
  126. username,
  127. password,
  128. },
  129. );
  130. }
  131. async asSuperAdmin() {
  132. await this.asUserWithCredentials(SUPER_ADMIN_USER_IDENTIFIER, SUPER_ADMIN_USER_PASSWORD);
  133. }
  134. async asAnonymousUser() {
  135. await this.query(
  136. gql`
  137. mutation {
  138. logout
  139. }
  140. `,
  141. );
  142. }
  143. private setHeaders() {
  144. const headers: any = {
  145. [getConfig().channelTokenKey]: this.channelToken,
  146. };
  147. if (this.authToken) {
  148. headers.Authorization = `Bearer ${this.authToken}`;
  149. }
  150. this.client.setHeaders(headers);
  151. }
  152. }