Repository: apollographql/apollo-codegen
Pull request #305 (2017-11-27 07:12:53)

Add typeName for nested property objects
This is the fixed version of #294. The only difference is in commit c7d387f96140ec532519c4719b531a5ffebf00ca.

annalo: Any news on getting this merged?
leethree: @lewisf could you take a look at this?
zapo: We're using this in our fork successfully, we just had to fix compilation with this commit:
https://github.com/adgear/apollo-codegen/commit/11e5d6be12757dfdef59b084f4642d02ebea0e22 (missing a null-check)
leethree: @zapo Thanks for your comment! I rebased this branch to master and fixed the compilation issue. 
lewisf: thanks for the contribution @leethree! apologies for taking so long to take a look at this

Patch:
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();
+    });
   });
 });