Repository: apollographql/apollo-codegen
Pull request #363 (2018-01-18 16:49:47)

Support semicolons in header values
Header values are currently corrupted if they contain semicolons (introspect-schema and download-schema commands). As of https://tools.ietf.org/html/rfc7230 semicolons in header values are allowed.

meteor-bot: @planser: Thank you for submitting a pull request! Before we can merge it, you'll need to sign the Meteor Contributor Agreement here: https://contribute.meteor.com/

Patch:
diff --git a/src/cli.js b/src/cli.js
index 48d44290..99807c2f 100755
--- a/src/cli.js
+++ b/src/cli.js
@@ -39,7 +39,9 @@ yargs
         coerce: (arg) => {
           let additionalHeaders = {};
           for (const header of arg) {
-            const [name, value] = header.split(/\s*:\s*/);
+            const separator = header.indexOf(":");
+            const name = header.substring(0, separator).trim();
+            const value = header.substring(separator + 1).trim();
             if (!(name && value)) {
               throw new ToolError('Headers should be specified as "Name: Value"');
             }
diff --git a/src/flow/codeGeneration.js b/src/flow/codeGeneration.js
index c5f0d8b2..cac87dcf 100644
--- a/src/flow/codeGeneration.js
+++ b/src/flow/codeGeneration.js
@@ -374,7 +374,19 @@ export function propertyDeclarations(generator, properties, isInput) {
         || property.fragmentSpreads && property.fragmentSpreads.length > 0
       ) {
         propertyDeclaration(generator, property, () => {
-          const properties = propertiesFromFields(generator.context, property.fields);
+          const fields = property.fields.map(field => {
+            if (field.fieldName === '__typename') {
+              const objectTypeName = getObjectTypeName(property.fieldType || property.type);
+              return {
+                ...field,
+                typeName: objectTypeName,
+                type: { name: objectTypeName }
+              }
+            } else {
+              return field;
+            }
+          });
+          const properties = propertiesFromFields(generator.context, fields);
           propertyDeclarations(generator, properties, isInput);
         });
       } else {
diff --git a/src/typescript/codeGeneration.ts b/src/typescript/codeGeneration.ts
index bc8f10c0..afe02aef 100644
--- a/src/typescript/codeGeneration.ts
+++ b/src/typescript/codeGeneration.ts
@@ -395,7 +395,19 @@ export function propertyDeclarations(generator: CodeGenerator, properties: Prope
         || property.fragmentSpreads && property.fragmentSpreads.length > 0
       ) {
         propertyDeclaration(generator, property, () => {
-          const properties = propertiesFromFields(generator.context, property.fields!);
+          const fields = property.fields!.map(field => {
+            if (field.fieldName === '__typename') {
+              const objectTypeName = getObjectTypeName(property.fieldType || property.type!);
+              return {
+                ...field,
+                typeName: objectTypeName,
+                type: { name: objectTypeName }
+              }
+            } else {
+              return field;
+            }
+          });
+          const properties = propertiesFromFields(generator.context, fields);
           propertyDeclarations(generator, properties, isInput);
         });
       } else {


Test patch:
diff --git a/test/flow/__snapshots__/codeGeneration.js.snap b/test/flow/__snapshots__/codeGeneration.js.snap
index d3e28fb8..056cb715 100644
--- a/test/flow/__snapshots__/codeGeneration.js.snap
+++ b/test/flow/__snapshots__/codeGeneration.js.snap
@@ -520,6 +520,65 @@ export type CustomScalarQuery = {|
 |};"
 `;
 
+exports[`Flow code generation #generateSource() should have __typename value in nested property 1`] = `
+"/* @flow */
+/* eslint-disable */
+//  This file was automatically generated and should not be edited.
+
+export type HeroNameQuery = {|
+  hero: ?( {
+      __typename: \\"Human\\",
+      // The friends of the human exposed as a connection with edges
+      friendsConnection: {|
+        __typename: \\"FriendsConnection\\",
+        // The edges for each of the character's friends.
+        edges: ? Array<? {|
+          __typename: \\"FriendsEdge\\",
+          // The character represented by this friendship edge
+          node: ?( {
+              __typename: \\"Human\\",
+              // The name of the character
+              name: string,
+            } | {
+              __typename: \\"Droid\\",
+              // The name of the character
+              name: string,
+            }
+          ),
+        |} >,
+      |},
+      // A list of starships this person has piloted, or an empty list if none
+      starships: ? Array<? {|
+        __typename: \\"Starship\\",
+        // The name of the starship
+        name: string,
+      |} >,
+    } | {
+      __typename: \\"Droid\\",
+      // The friends of the character exposed as a connection with edges
+      friendsConnection: {|
+        __typename: \\"FriendsConnection\\",
+        // The edges for each of the character's friends.
+        edges: ? Array<? {|
+          __typename: \\"FriendsEdge\\",
+          // The character represented by this friendship edge
+          node: ?( {
+              __typename: \\"Human\\",
+              // The name of the character
+              name: string,
+            } | {
+              __typename: \\"Droid\\",
+              // The name of the character
+              name: string,
+            }
+          ),
+        |} >,
+      |},
+    }
+  ),
+|};"
+`;
+
 exports[`Flow code generation #generateSource() should have __typename value matching fragment type on generic type 1`] = `
 "/* @flow */
 /* eslint-disable */
diff --git a/test/flow/codeGeneration.js b/test/flow/codeGeneration.js
index 245d1b9c..e4c5d65a 100644
--- a/test/flow/codeGeneration.js
+++ b/test/flow/codeGeneration.js
@@ -370,5 +370,31 @@ describe('Flow code generation', function() {
       const source = generateSource(context);
       expect(source).toMatchSnapshot();
     });
+    
+    test('should have __typename value in nested property', () => {
+      const { compileFromSource } = setup(starWarsSchema);
+      const context = compileFromSource(`
+        query HeroName {
+          hero {
+            friendsConnection {
+              edges {
+                node {
+                  name
+                  
+                }
+              }
+            }
+            ... on Human {
+              starships {
+                name
+              }
+            }
+          }
+        }
+      `);
+
+      const source = generateSource(context);
+      expect(source).toMatchSnapshot();
+    });
   });
 });
diff --git a/test/typescript/__snapshots__/codeGeneration.js.snap b/test/typescript/__snapshots__/codeGeneration.js.snap
index 8ae8e8a7..935f3814 100644
--- a/test/typescript/__snapshots__/codeGeneration.js.snap
+++ b/test/typescript/__snapshots__/codeGeneration.js.snap
@@ -414,6 +414,65 @@ export interface CustomScalarQuery {
 "
 `;
 
+exports[`TypeScript code generation #generateSource() should have __typename value in nested property 1`] = `
+"/* tslint:disable */
+//  This file was automatically generated and should not be edited.
+
+export interface HeroNameQuery {
+  hero: ( {
+      __typename: \\"Human\\",
+      // The friends of the human exposed as a connection with edges
+      friendsConnection:  {
+        __typename: \\"FriendsConnection\\",
+        // The edges for each of the character's friends.
+        edges:  Array< {
+          __typename: \\"FriendsEdge\\",
+          // The character represented by this friendship edge
+          node: ( {
+              __typename: \\"Human\\",
+              // The name of the character
+              name: string,
+            } | {
+              __typename: \\"Droid\\",
+              // The name of the character
+              name: string,
+            }
+          ) | null,
+        } | null > | null,
+      },
+      // A list of starships this person has piloted, or an empty list if none
+      starships:  Array< {
+        __typename: \\"Starship\\",
+        // The name of the starship
+        name: string,
+      } | null > | null,
+    } | {
+      __typename: \\"Droid\\",
+      // The friends of the character exposed as a connection with edges
+      friendsConnection:  {
+        __typename: \\"FriendsConnection\\",
+        // The edges for each of the character's friends.
+        edges:  Array< {
+          __typename: \\"FriendsEdge\\",
+          // The character represented by this friendship edge
+          node: ( {
+              __typename: \\"Human\\",
+              // The name of the character
+              name: string,
+            } | {
+              __typename: \\"Droid\\",
+              // The name of the character
+              name: string,
+            }
+          ) | null,
+        } | null > | null,
+      },
+    }
+  ) | null,
+};
+"
+`;
+
 exports[`TypeScript code generation #generateSource() should have __typename value matching fragment type on generic type 1`] = `
 "/* tslint:disable */
 //  This file was automatically generated and should not be edited.
diff --git a/test/typescript/codeGeneration.js b/test/typescript/codeGeneration.js
index 47359650..3c5320b9 100644
--- a/test/typescript/codeGeneration.js
+++ b/test/typescript/codeGeneration.js
@@ -313,5 +313,31 @@ describe('TypeScript code generation', function() {
       const source = generateSource(context);
       expect(source).toMatchSnapshot();
     });
+    
+    test('should have __typename value in nested property', () => {
+      const { compileFromSource } = setup(starWarsSchema);
+      const context = compileFromSource(`
+        query HeroName {
+          hero {
+            friendsConnection {
+              edges {
+                node {
+                  name
+                  
+                }
+              }
+            }
+            ... on Human {
+              starships {
+                name
+              }
+            }
+          }
+        }
+      `);
+
+      const source = generateSource(context);
+      expect(source).toMatchSnapshot();
+    });
   });
 });