Repository: MichalLytek/type-graphql (ts)

Issue:
@ObjectType() with a double underscore in the `name` argument prevents field resolvers from executing
**Describe the Bug**
When the name argument string contains a double underscore, field resolvers for this objectType fail to execute `@ObjectType('product__tag')`

**To Reproduce**
```
@ObjectType('product__tag')
class ProductTagType {
  @Field()
  public id: string;
}

@Resolver(() => ProductTagType)
class ProductTagResolver 
  // this does not execute
  @FieldResolver(() => String)
  public name(): string { 
    return 'hello';
  }
}
```

```
    {
      "message": "Cannot return null for non-nullable field product__tag.name.",
    }
```

**Expected Behavior**
name strings with double underscore should not prevent field resolvers from executing correctly

**Environment (please complete the following information):**

- OS: MacOS
- Node v12.13.1
- Package version 1.1.1
- TypeScript version 4.2.4



Pull request:
fix(resolvers): resolve #1142 name issue and add relevant tests
This PR fixes the issue mentioned [here](https://github.com/MichalLytek/type-graphql/issues/1142) with tests as mentioned [here](https://github.com/MichalLytek/type-graphql/issues/1142#issuecomment-997357073).
The tests successfully fail when the `startsWith` is replaced back with `includes`.

(Edit: The second 'here' is supposed to link to the last comment of the mentioned issue but it's not scrolling.)

Patch:
diff --git a/src/utils/createResolversMap.ts b/src/utils/createResolversMap.ts
index 01c53734..8c5acfdf 100644
--- a/src/utils/createResolversMap.ts
+++ b/src/utils/createResolversMap.ts
@@ -50,7 +50,7 @@ function generateFieldsResolvers(fields: GraphQLFieldMap<any, any>): ResolverObj
 export function createResolversMap(schema: GraphQLSchema): ResolversMap {
   const typeMap = schema.getTypeMap();
   return Object.keys(typeMap)
-    .filter(typeName => !typeName.includes("__"))
+    .filter(typeName => !typeName.startsWith("__"))
     .reduce<ResolversMap>((resolversMap, typeName) => {
       const type = typeMap[typeName];
       if (type instanceof GraphQLObjectType) {


Test patch:
diff --git a/tests/functional/typedefs-resolvers.ts b/tests/functional/typedefs-resolvers.ts
index fa791aee..32bd90fa 100644
--- a/tests/functional/typedefs-resolvers.ts
+++ b/tests/functional/typedefs-resolvers.ts
@@ -26,6 +26,7 @@ import {
   Arg,
   Authorized,
   Field,
+  FieldResolver,
   InputType,
   InterfaceType,
   Mutation,
@@ -107,6 +108,15 @@ describe("typeDefs and resolvers", () => {
         sampleType3StringField!: string;
       }
 
+      @ObjectType("SampleType__4")
+      class SampleType4 {
+        @Field()
+        sampleInterfaceStringField!: string;
+
+        @Field()
+        sampleType4StringField!: string;
+      }
+
       @InputType()
       class SampleInput {
         @Field()
@@ -241,8 +251,26 @@ describe("typeDefs and resolvers", () => {
       }
 
       pubSub = createPubSub();
+
+      @Service()
+      @Resolver(() => SampleType4)
+      class SampleObjectTypeWithDoubleUnderscoreInNameResolver {
+        @FieldResolver(() => String)
+        sampleResolvedField(): string {
+          return "sampleResolvedField";
+        }
+
+        @Query(() => SampleType4)
+        async sampleQueryOnObjectTypeWithDoubleUnderScore(): Promise<SampleType4> {
+          const type4 = new SampleType4();
+          type4.sampleInterfaceStringField = "sampleInterfaceStringField";
+          type4.sampleType4StringField = "sampleType4StringField";
+          return type4;
+        }
+      }
+
       ({ typeDefs, resolvers } = await buildTypeDefsAndResolvers({
-        resolvers: [SampleResolver],
+        resolvers: [SampleResolver, SampleObjectTypeWithDoubleUnderscoreInNameResolver],
         authChecker: () => false,
         pubSub,
         container: Container,
@@ -286,6 +314,10 @@ describe("typeDefs and resolvers", () => {
         const sampleType2 = schemaIntrospection.types.find(
           it => it.name === "SampleType2",
         ) as IntrospectionObjectType;
+        const sampleType4 = schemaIntrospection.types.find(
+          it => it.name === "SampleType__4",
+        ) as IntrospectionObjectType;
+
         const sampleType1StringField = sampleType1.fields.find(
           it => it.name === "sampleType1StringField",
         )!;
@@ -299,6 +331,7 @@ describe("typeDefs and resolvers", () => {
         expect(sampleType1.interfaces).toHaveLength(1);
         expect(sampleType1.interfaces[0].name).toBe("SampleInterface");
         expect(sampleType2StringField.deprecationReason).toBe("sampleType2StringFieldDeprecation");
+        expect(sampleType4.fields).toHaveLength(3);
       });
 
       it("should generate input type", async () => {
@@ -356,7 +389,7 @@ describe("typeDefs and resolvers", () => {
           it => it.name === schemaIntrospection.queryType.name,
         ) as IntrospectionObjectType;
 
-        expect(queryType.fields).toHaveLength(8);
+        expect(queryType.fields).toHaveLength(9);
       });
 
       it("should generate mutations", async () => {
@@ -574,6 +607,26 @@ describe("typeDefs and resolvers", () => {
         expect(enumValue).toBe("OptionTwoString");
       });
 
+      it("should properly execute field resolver for object type with two underscores NOT in the beginning", async () => {
+        const document = gql`
+          query {
+            sampleQueryOnObjectTypeWithDoubleUnderScore {
+              sampleResolvedField
+              sampleInterfaceStringField
+              sampleType4StringField
+            }
+          }
+        `;
+
+        const { data } = await execute({ schema, document });
+
+        expect(data!.sampleQueryOnObjectTypeWithDoubleUnderScore).toEqual({
+          sampleResolvedField: "sampleResolvedField",
+          sampleInterfaceStringField: "sampleInterfaceStringField",
+          sampleType4StringField: "sampleType4StringField",
+        });
+      });
+
       it("should properly run subscriptions", async () => {
         const document = gql`
           subscription {