Repository: apollographql/apollo-codegen
Pull request #348 (2018-01-03 19:14:22)

*-Modern variables
This PR adds the generation of the variables object type for each operation. In the case of flow it generates a flow annotation, and in the case of Typescript, an interface. It follows the same nullability rules as when creating input objects (they can be set to undefined or not even set).

lewisf: Ah -- happy that it wasn't terrible hard to do. Looks good, thanks @leoasis !

Patch:
diff --git a/src/javascript/flow/codeGeneration.ts b/src/javascript/flow/codeGeneration.ts
index 7af01be5..586d4212 100644
--- a/src/javascript/flow/codeGeneration.ts
+++ b/src/javascript/flow/codeGeneration.ts
@@ -154,6 +154,7 @@ export class FlowAPIGenerator extends FlowGenerator {
     const {
       operationType,
       operationName,
+      variables,
       selectionSet
     } = operation;
 
@@ -178,6 +179,19 @@ export class FlowAPIGenerator extends FlowGenerator {
 
     this.printer.enqueue(exportedTypeAlias);
     this.scopeStackPop();
+
+    // Generate the variables interface if the operation has any variables
+    if (variables.length > 0) {
+      const interfaceName = operationName + 'Variables';
+      this.scopeStackPush(interfaceName);
+      this.printer.enqueue(this.exportDeclaration(
+        this.typeAliasObject(interfaceName, variables.map((variable) => ({
+          name: variable.name,
+          annotation: this.typeAnnotationFromGraphQLType(variable.type)
+        })), { keyInheritsNullability: true })
+      ));
+      this.scopeStackPop();
+    }
   }
 
   public typeAliasesForFragment(fragment: Fragment) {
diff --git a/src/javascript/typescript/codeGeneration.ts b/src/javascript/typescript/codeGeneration.ts
index 79c1dec3..6423a087 100644
--- a/src/javascript/typescript/codeGeneration.ts
+++ b/src/javascript/typescript/codeGeneration.ts
@@ -144,6 +144,7 @@ export class TypescriptAPIGenerator extends TypescriptGenerator {
     const {
       operationType,
       operationName,
+      variables,
       selectionSet
     } = operation;
 
@@ -168,6 +169,19 @@ export class TypescriptAPIGenerator extends TypescriptGenerator {
 
     this.printer.enqueue(exportedTypeAlias);
     this.scopeStackPop();
+
+    // Generate the variables interface if the operation has any variables
+    if (variables.length > 0) {
+      const interfaceName = operationName + 'Variables';
+      this.scopeStackPush(interfaceName);
+      this.printer.enqueue(this.exportDeclaration(
+        this.interface(interfaceName, variables.map((variable) => ({
+          name: variable.name,
+          type: this.typeFromGraphQLType(variable.type)
+        })), { keyInheritsNullability: true })
+      ));
+      this.scopeStackPop();
+    }
   }
 
   public interfacesForFragment(fragment: Fragment) {


Test patch:
diff --git a/src/javascript/flow/__tests__/__snapshots__/codeGeneration.ts.snap b/src/javascript/flow/__tests__/__snapshots__/codeGeneration.ts.snap
index 894753a2..b5487da0 100644
--- a/src/javascript/flow/__tests__/__snapshots__/codeGeneration.ts.snap
+++ b/src/javascript/flow/__tests__/__snapshots__/codeGeneration.ts.snap
@@ -37,6 +37,10 @@ export type HeroName = {
   hero: ?HeroName_hero
 };
 
+export type HeroNameVariables = {
+  episode?: ?Episode
+};
+
 //==============================================================
 // START Enums and Input Objects
 // All enums and input objects are included in every output file
@@ -309,6 +313,10 @@ export type HeroInlineFragment = {
   hero: ?HeroInlineFragment_hero
 };
 
+export type HeroInlineFragmentVariables = {
+  episode?: ?Episode
+};
+
 //==============================================================
 // START Enums and Input Objects
 // All enums and input objects are included in every output file
@@ -360,6 +368,10 @@ export type HeroName = {
   hero: ?HeroName_hero
 };
 
+export type HeroNameVariables = {
+  episode?: ?Episode
+};
+
 //==============================================================
 // START Enums and Input Objects
 // All enums and input objects are included in every output file
@@ -417,6 +429,10 @@ export type HeroName = {
   hero: ?HeroName_hero
 };
 
+export type HeroNameVariables = {
+  episode?: ?Episode
+};
+
 //==============================================================
 // START Enums and Input Objects
 // All enums and input objects are included in every output file
@@ -457,6 +473,10 @@ export type HeroName = {
   hero: ?HeroName_hero
 };
 
+export type HeroNameVariables = {
+  episode?: ?Episode
+};
+
 //==============================================================
 // START Enums and Input Objects
 // All enums and input objects are included in every output file
@@ -510,6 +530,10 @@ export type SomeOther = {
   hero: ?SomeOther_hero
 };
 
+export type SomeOtherVariables = {
+  episode?: ?Episode
+};
+
 //==============================================================
 // START Enums and Input Objects
 // All enums and input objects are included in every output file
@@ -563,6 +587,11 @@ export type ReviewMovie = {
   createReview: ?ReviewMovie_createReview
 };
 
+export type ReviewMovieVariables = {
+  episode?: ?Episode,
+  review?: ?ReviewInput,
+};
+
 //==============================================================
 // START Enums and Input Objects
 // All enums and input objects are included in every output file
@@ -663,6 +692,10 @@ export type HeroFragment = {
   hero: ?HeroFragment_hero
 };
 
+export type HeroFragmentVariables = {
+  episode?: ?Episode
+};
+
 //==============================================================
 // START Enums and Input Objects
 // All enums and input objects are included in every output file
@@ -762,6 +795,10 @@ export type HeroName = {
   hero: ?HeroName_hero
 };
 
+export type HeroNameVariables = {
+  episode?: ?Episode
+};
+
 //==============================================================
 // START Enums and Input Objects
 // All enums and input objects are included in every output file
@@ -801,6 +838,11 @@ export type ReviewMovie = {
   createReview: ?ReviewMovie_createReview
 };
 
+export type ReviewMovieVariables = {
+  episode?: ?Episode,
+  review?: ?ReviewInput,
+};
+
 //==============================================================
 // START Enums and Input Objects
 // All enums and input objects are included in every output file
diff --git a/src/javascript/typescript/__tests__/__snapshots__/codeGeneration.ts.snap b/src/javascript/typescript/__tests__/__snapshots__/codeGeneration.ts.snap
index 62e7e9a4..52acf84d 100644
--- a/src/javascript/typescript/__tests__/__snapshots__/codeGeneration.ts.snap
+++ b/src/javascript/typescript/__tests__/__snapshots__/codeGeneration.ts.snap
@@ -45,6 +45,10 @@ export interface HeroName {
   hero: HeroName_hero | null;
 }
 
+export interface HeroNameVariables {
+  episode?: Episode | null;
+}
+
 //==============================================================
 // START Enums and Input Objects
 // All enums and input objects are included in every output file
@@ -341,6 +345,10 @@ export interface HeroInlineFragment {
   hero: HeroInlineFragment_hero | null;
 }
 
+export interface HeroInlineFragmentVariables {
+  episode?: Episode | null;
+}
+
 //==============================================================
 // START Enums and Input Objects
 // All enums and input objects are included in every output file
@@ -400,6 +408,10 @@ export interface HeroName {
   hero: HeroName_hero | null;
 }
 
+export interface HeroNameVariables {
+  episode?: Episode | null;
+}
+
 //==============================================================
 // START Enums and Input Objects
 // All enums and input objects are included in every output file
@@ -465,6 +477,10 @@ export interface HeroName {
   hero: HeroName_hero | null;
 }
 
+export interface HeroNameVariables {
+  episode?: Episode | null;
+}
+
 //==============================================================
 // START Enums and Input Objects
 // All enums and input objects are included in every output file
@@ -509,6 +525,10 @@ export interface HeroName {
   hero: HeroName_hero | null;
 }
 
+export interface HeroNameVariables {
+  episode?: Episode | null;
+}
+
 //==============================================================
 // START Enums and Input Objects
 // All enums and input objects are included in every output file
@@ -566,6 +586,10 @@ export interface SomeOther {
   hero: SomeOther_hero | null;
 }
 
+export interface SomeOtherVariables {
+  episode?: Episode | null;
+}
+
 //==============================================================
 // START Enums and Input Objects
 // All enums and input objects are included in every output file
@@ -623,6 +647,11 @@ export interface ReviewMovie {
   createReview: ReviewMovie_createReview | null;
 }
 
+export interface ReviewMovieVariables {
+  episode?: Episode | null;
+  review?: ReviewInput | null;
+}
+
 //==============================================================
 // START Enums and Input Objects
 // All enums and input objects are included in every output file
@@ -731,6 +760,10 @@ export interface HeroFragment {
   hero: HeroFragment_hero | null;
 }
 
+export interface HeroFragmentVariables {
+  episode?: Episode | null;
+}
+
 //==============================================================
 // START Enums and Input Objects
 // All enums and input objects are included in every output file
@@ -838,6 +871,10 @@ export interface HeroName {
   hero: HeroName_hero | null;
 }
 
+export interface HeroNameVariables {
+  episode?: Episode | null;
+}
+
 //==============================================================
 // START Enums and Input Objects
 // All enums and input objects are included in every output file
@@ -881,6 +918,11 @@ export interface ReviewMovie {
   createReview: ReviewMovie_createReview | null;
 }
 
+export interface ReviewMovieVariables {
+  episode?: Episode | null;
+  review?: ReviewInput | null;
+}
+
 //==============================================================
 // START Enums and Input Objects
 // All enums and input objects are included in every output file