graphql#GraphQLFloat TypeScript Examples

The following examples show how to use graphql#GraphQLFloat. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: utils.ts    From proto2graphql with MIT License 7 votes vote down vote up
ScalarTypeMap = {
  double: GraphQLFloat,
  float: GraphQLFloat,
  int32: GraphQLInt,
  int64: GraphQLInt,
  uint32: GraphQLInt,
  uint64: GraphQLInt,
  sint32: GraphQLInt,
  sint64: GraphQLInt,
  fixed32: GraphQLInt,
  fixed64: GraphQLInt,
  sfixed32: GraphQLInt,
  sfixed64: GraphQLInt,
  bool: GraphQLBoolean,
  string: GraphQLString,
  bytes: GraphQLString
}
Example #2
Source File: grades.ts    From peterportal-public-api with MIT License 6 votes vote down vote up
gradeDistributionCollectionAggregateType: GraphQLObjectType = new GraphQLObjectType({
  name: "GradeDistributionCollectionAggregate",

  fields: () => ({
    sum_grade_a_count: { type: GraphQLFloat }, 
    sum_grade_b_count: { type: GraphQLFloat }, 
    sum_grade_c_count: { type: GraphQLFloat }, 
    sum_grade_d_count: { type: GraphQLFloat }, 
    sum_grade_f_count: { type: GraphQLFloat }, 
    sum_grade_p_count: { type: GraphQLFloat }, 
    sum_grade_np_count: { type: GraphQLFloat }, 
    sum_grade_w_count: { type: GraphQLFloat }, 
    average_gpa: { type: GraphQLFloat },
    count: {type: GraphQLFloat}
  })
})
Example #3
Source File: grades.ts    From peterportal-public-api with MIT License 6 votes vote down vote up
gradeDistributionType: GraphQLObjectType = new GraphQLObjectType({
  name: "GradeDistribution",

  fields: () => ({
    grade_a_count: { type: GraphQLFloat }, 
    grade_b_count: { type: GraphQLFloat }, 
    grade_c_count: { type: GraphQLFloat }, 
    grade_d_count: { type: GraphQLFloat }, 
    grade_f_count: { type: GraphQLFloat }, 
    grade_p_count: { type: GraphQLFloat }, 
    grade_np_count: { type: GraphQLFloat }, 
    grade_w_count: { type: GraphQLFloat }, 
    average_gpa: { type: GraphQLFloat },
    course_offering: { type: courseOfferingType }
  })
})
Example #4
Source File: GraphqlTypeInterpreter.test.ts    From aloxide with Apache License 2.0 6 votes vote down vote up
describe('test GraphqlTypeInterpreter', () => {
  describe('interpret()', () => {
    const intepreter = new GraphqlTypeInterpreter();

    it('should throw error when Field Type is not support', () => {
      expect(() => {
        // @ts-ignore
        intepreter.interpret('not supported type');
      }).toThrowError('unknow type');
    });

    it('should return Graphql Int when the input is uint', () => {
      expect(intepreter.interpret(FieldTypeEnum.uint16_t)).toBe(GraphQLInt);
      expect(intepreter.interpret(FieldTypeEnum.uint32_t)).toBe(GraphQLInt);
      expect(intepreter.interpret(FieldTypeEnum.uint64_t)).toBe(GraphQLInt);
    });

    it('should return Graphql Float when the input is number or double', () => {
      expect(intepreter.interpret(FieldTypeEnum.number)).toBe(GraphQLFloat);
      expect(intepreter.interpret(FieldTypeEnum.double)).toBe(GraphQLFloat);
    });

    it('should return Graphql Boolean when the input is boolean', () => {
      expect(intepreter.interpret(FieldTypeEnum.bool)).toBe(GraphQLBoolean);
    });

    it('should return Graphql String when the input is string', () => {
      expect(intepreter.interpret(FieldTypeEnum.account)).toBe(GraphQLString);
      expect(intepreter.interpret(FieldTypeEnum.string)).toBe(GraphQLString);
    });
  });
});
Example #5
Source File: GraphqlTypeInterpreter.ts    From aloxide with Apache License 2.0 6 votes vote down vote up
interpret(input: FieldTypeEnum): GraphQLScalarType {
    let type: GraphQLScalarType;

    switch (input) {
      case FieldTypeEnum.uint16_t:
      case FieldTypeEnum.uint32_t:
      case FieldTypeEnum.uint64_t:
        type = GraphQLInt;
        break;
      case FieldTypeEnum.number:
      case FieldTypeEnum.double:
        type = GraphQLFloat;
        break;
      case FieldTypeEnum.bool:
        type = GraphQLBoolean;
        break;
      case FieldTypeEnum.account:
      case FieldTypeEnum.string:
        type = GraphQLString;
        break;
      default:
        throw new Error(`unknow type ${type}`);
    }

    return type;
  }
Example #6
Source File: helpers.ts    From amplify-codegen with Apache License 2.0 5 votes vote down vote up
builtInScalarMap = {
  [GraphQLString.name]: t.stringTypeAnnotation(),
  [GraphQLInt.name]: t.numberTypeAnnotation(),
  [GraphQLFloat.name]: t.numberTypeAnnotation(),
  [GraphQLBoolean.name]: t.booleanTypeAnnotation(),
  [GraphQLID.name]: t.stringTypeAnnotation(),
}
Example #7
Source File: schema.ts    From Deep-Lynx with MIT License 5 votes vote down vote up
// each key in the metatype should be included on the input object as a field to be filtered on
    inputFieldsForMetatype(metatype: Metatype): {[key: string]: any} {
        const fields: {[key: string]: any} = {};

        metatype.keys?.forEach((metatypeKey) => {
            const propertyName = stringToValidPropertyName(metatypeKey.property_name);

            switch (metatypeKey.data_type) {
                // because we have no specification on our internal number type, we
                // must set this as a float for now
                case 'number': {
                    fields[propertyName] = {
                        type: GraphQLFloat,
                    };
                    break;
                }

                case 'boolean': {
                    fields[propertyName] = {
                        type: GraphQLBoolean,
                    };
                    break;
                }

                case 'string' || 'date' || 'file': {
                    fields[propertyName] = {
                        type: GraphQLString,
                    };
                    break;
                }

                case 'list': {
                    fields[propertyName] = {
                        type: new GraphQLList(GraphQLJSON),
                    };
                    break;
                }

                case 'enumeration': {
                    const enumMap: {[key: string]: GraphQLEnumValueConfig} = {};

                    if (metatypeKey.options) {
                        metatypeKey.options.forEach((option) => {
                            enumMap[option] = {
                                value: option,
                            };
                        });
                    }

                    // we have to include a UUID here so that we can insure a uniquely named type
                    fields[propertyName] = {
                        type: new GraphQLEnumType({
                            name: stringToValidPropertyName(`${metatype.name}_${metatypeKey.name}_Enum_Type_B`),
                            values: enumMap,
                        }),
                    };
                    break;
                }

                default: {
                    fields[propertyName] = {
                        type: GraphQLString,
                    };
                }
            }
        });

        return fields;
    }
Example #8
Source File: schema.ts    From Deep-Lynx with MIT License 5 votes vote down vote up
// each key in the relationship should be included on the input object as a field to be filtered on
    inputFieldsForRelationship(relationship: MetatypeRelationship): {[key: string]: any} {
        const fields: {[key: string]: any} = {};

        relationship.keys?.forEach((relationshipKey) => {
            const propertyName = stringToValidPropertyName(relationshipKey.property_name);

            switch (relationshipKey.data_type) {
                // because we have no specification on our internal number type, we
                // must set this as a float for now
                case 'number': {
                    fields[propertyName] = {
                        type: GraphQLFloat,
                    };
                    break;
                }

                case 'boolean': {
                    fields[propertyName] = {
                        type: GraphQLBoolean,
                    };
                    break;
                }

                case 'string' || 'date' || 'file': {
                    fields[propertyName] = {
                        type: GraphQLString,
                    };
                    break;
                }

                case 'list': {
                    fields[propertyName] = {
                        type: new GraphQLList(GraphQLJSON),
                    };
                    break;
                }

                case 'enumeration': {
                    const enumMap: {[key: string]: GraphQLEnumValueConfig} = {};

                    if (relationshipKey.options) {
                        relationshipKey.options.forEach((option) => {
                            enumMap[option] = {
                                value: option,
                            };
                        });
                    }

                    fields[propertyName] = {
                        type: new GraphQLEnumType({
                            name: stringToValidPropertyName(`${relationship.name}_${relationshipKey.name}_Enum_TypeB`),
                            values: enumMap,
                        }),
                    };
                    break;
                }

                default: {
                    fields[propertyName] = {
                        type: GraphQLString,
                    };
                }
            }
        });
        return fields;
    }
Example #9
Source File: schedule.ts    From peterportal-public-api with MIT License 5 votes vote down vote up
courseOfferingType: GraphQLObjectType = new GraphQLObjectType({
  name: "CourseOffering",

  fields: () => ({
    year: { type: GraphQLString },
    quarter: { type: GraphQLString },
    instructors: { 
      type: new GraphQLList(instructorType),
      resolve: (offering) => {
        return offering.instructors.map((name: string) => {
          
          //Fetch all possible ucinetids from the instructor.
          let ucinetids : string[] = getUCINetIDFromName(name);
          
          //If only one ucinetid exists and it's in the instructor cache, 
          //then we can return the instructor for it.
          if (ucinetids && ucinetids.length == 1) { 
            const instructor = getInstructor(ucinetids[0]);
            if (instructor) { return instructor; }
          }
          //If there is more than one and the course exists, 
          //use the course to figure it out.
          else if (ucinetids && ucinetids.length > 1 && offering.course) {

              //Filter our instructors by those with related departments.
              let course_dept = offering.course.department;
              let instructors = ucinetids.map(id => getInstructor(id)).filter( temp => temp.related_departments.includes(course_dept));
              
              //If only one is left and it's in the instructor cache, we can return it.
              if (instructors.length == 1) {
                return instructors[0];
              } else {
                //Filter instructors by those that taught the course before.
                instructors = instructors.filter( inst => {
                  return inst.course_history.map((course) => course.replace(/ /g, "")).includes(offering.course.id);
                });
              
                //If only one is left and it's in the instructor cache, we can return it.
                if (instructors.length == 1) { 
                  return instructors[0];
                }
              }
          }
          
          //If we haven't found any instructors, then just return the shortened name.
          return {shortened_name: name};
        })
      }
    }, 
    final_exam: { type: GraphQLString },
    max_capacity: { type: GraphQLFloat },
    meetings: { type: new GraphQLList(meetingType) },
    num_section_enrolled: { type: GraphQLFloat },
    num_total_enrolled: { type: GraphQLFloat },
    num_new_only_reserved: { type: GraphQLFloat },
    num_on_waitlist: { 
      type: GraphQLFloat, 
      resolve: (offering) => {
          return offering.num_on_waitlist === 'n/a' ? null : offering.num_on_waitlist;
        } 
    },
    num_requested: { type: GraphQLFloat },
    restrictions: { type: GraphQLString },
    section: { type: sectionInfoType },  
    status: { type: GraphQLString },
    units: { type: GraphQLString },
    course: { 
      type: courseType,
      resolve: (offering) => {
        // Get the course from the cache.
        const course = getCourse(offering.course.id);
        // If it's not in our cache, return whatever information was provided.
        // Usually, it will at least have id, department, and number
        return course ? course : offering.course;
      }
    }
  })
})
Example #10
Source File: generateSchema.ts    From davinci with MIT License 5 votes vote down vote up
scalarDict = {
	number: GraphQLFloat,
	string: GraphQLString,
	boolean: GraphQLBoolean,
	object: GraphQLJSON,
	date: GraphQLDateTime
}
Example #11
Source File: typeNameFromGraphQLType.ts    From amplify-codegen with Apache License 2.0 5 votes vote down vote up
describe('Swift code generation: Types', () => {
  let helpers: Helpers;

  beforeEach(() => {
    helpers = new Helpers({});
  });

  describe('#typeNameFromGraphQLType()', () => {
    it('should return String? for GraphQLString', () => {
      expect(helpers.typeNameFromGraphQLType(GraphQLString)).toBe('String?');
    });

    it('should return String for GraphQLNonNull(GraphQLString)', () => {
      expect(helpers.typeNameFromGraphQLType(new GraphQLNonNull(GraphQLString))).toBe('String');
    });

    it('should return [String?]? for GraphQLList(GraphQLString)', () => {
      expect(helpers.typeNameFromGraphQLType(new GraphQLList(GraphQLString))).toBe('[String?]?');
    });

    it('should return [String?] for GraphQLNonNull(GraphQLList(GraphQLString))', () => {
      expect(helpers.typeNameFromGraphQLType(new GraphQLNonNull(new GraphQLList(GraphQLString)))).toBe('[String?]');
    });

    it('should return [String]? for GraphQLList(GraphQLNonNull(GraphQLString))', () => {
      expect(helpers.typeNameFromGraphQLType(new GraphQLList(new GraphQLNonNull(GraphQLString)))).toBe('[String]?');
    });

    it('should return [String] for GraphQLNonNull(GraphQLList(GraphQLNonNull(GraphQLString)))', () => {
      expect(helpers.typeNameFromGraphQLType(new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(GraphQLString))))).toBe('[String]');
    });

    it('should return [[String?]?]? for GraphQLList(GraphQLList(GraphQLString))', () => {
      expect(helpers.typeNameFromGraphQLType(new GraphQLList(new GraphQLList(GraphQLString)))).toBe('[[String?]?]?');
    });

    it('should return [[String?]]? for GraphQLList(GraphQLNonNull(GraphQLList(GraphQLString)))', () => {
      expect(helpers.typeNameFromGraphQLType(new GraphQLList(new GraphQLNonNull(new GraphQLList(GraphQLString))))).toBe('[[String?]]?');
    });

    it('should return Int? for GraphQLInt', () => {
      expect(helpers.typeNameFromGraphQLType(GraphQLInt)).toBe('Int?');
    });

    it('should return Double? for GraphQLFloat', () => {
      expect(helpers.typeNameFromGraphQLType(GraphQLFloat)).toBe('Double?');
    });

    it('should return Bool? for GraphQLBoolean', () => {
      expect(helpers.typeNameFromGraphQLType(GraphQLBoolean)).toBe('Bool?');
    });

    it('should return GraphQLID? for GraphQLID', () => {
      expect(helpers.typeNameFromGraphQLType(GraphQLID)).toBe('GraphQLID?');
    });

    it('should return String? for a custom scalar type', () => {
      expect(helpers.typeNameFromGraphQLType(new GraphQLScalarType({ name: 'CustomScalarType', serialize: String }))).toBe('String?');
    });

    it('should return a passed through custom scalar type with the passthroughCustomScalars option', () => {
      helpers.options.passthroughCustomScalars = true;
      helpers.options.customScalarsPrefix = '';

      expect(helpers.typeNameFromGraphQLType(new GraphQLScalarType({ name: 'CustomScalarType', serialize: String }))).toBe(
        'CustomScalarType?'
      );
    });

    it('should return a passed through custom scalar type with a prefix with the customScalarsPrefix option', () => {
      helpers.options.passthroughCustomScalars = true;
      helpers.options.customScalarsPrefix = 'My';

      expect(helpers.typeNameFromGraphQLType(new GraphQLScalarType({ name: 'CustomScalarType', serialize: String }))).toBe(
        'MyCustomScalarType?'
      );
    });
  });
});
Example #12
Source File: types.ts    From amplify-codegen with Apache License 2.0 5 votes vote down vote up
builtInScalarMap = {
  [GraphQLString.name]: 'string',
  [GraphQLInt.name]: 'number',
  [GraphQLFloat.name]: 'number',
  [GraphQLBoolean.name]: 'boolean',
  [GraphQLID.name]: 'string',
}
Example #13
Source File: helpers.ts    From amplify-codegen with Apache License 2.0 5 votes vote down vote up
builtInScalarMap = {
  [GraphQLString.name]: 'String',
  [GraphQLInt.name]: 'Int',
  [GraphQLFloat.name]: 'Double',
  [GraphQLBoolean.name]: 'Bool',
  [GraphQLID.name]: 'GraphQLID',
}
Example #14
Source File: custom-type-resolver.ts    From graphql-mesh with MIT License 5 votes vote down vote up
double = GraphQLFloat;
Example #15
Source File: custom-type-resolver.ts    From graphql-mesh with MIT License 5 votes vote down vote up
float = GraphQLFloat;
Example #16
Source File: custom-type-resolver.ts    From graphql-mesh with MIT License 5 votes vote down vote up
decimal = GraphQLFloat;
Example #17
Source File: schema_builder.ts    From graphql-mesh with MIT License 5 votes vote down vote up
/**
 * Returns the GraphQL scalar type matching the given JSON schema type
 */
function getScalarType<TSource, TContext, TArgs>({
  def,
  data,
}: CreateOrReuseSimpleTypeParams<TSource, TContext, TArgs>): GraphQLScalarType {
  switch (def.targetGraphQLType) {
    case 'id':
      def.graphQLType = GraphQLID;
      break;
    case 'string':
      def.graphQLType = GraphQLString;
      break;
    case 'integer':
      def.graphQLType = GraphQLInt;
      break;
    case 'int64':
      def.graphQLType = GraphQLBigInt;
      break;
    case 'number':
      def.graphQLType = GraphQLFloat;
      break;
    case 'float':
      def.graphQLType = GraphQLFloat;
      break;
    case 'boolean':
      def.graphQLType = GraphQLBoolean;
      break;
    case 'json':
      def.graphQLType = GraphQLJSON;
      break;
    case 'dateTime':
      def.graphQLType = GraphQLDateTime;
      break;
    default:
      throw new Error(`Cannot process schema type '${def.targetGraphQLType}'.`);
  }

  return def.graphQLType as GraphQLScalarType;
}
Example #18
Source File: scalars.ts    From squid with GNU General Public License v3.0 5 votes vote down vote up
Float = GraphQLFloat
Example #19
Source File: course.ts    From peterportal-public-api with MIT License 4 votes vote down vote up
courseType: GraphQLObjectType = new GraphQLObjectType({
    name: 'Course',
  
    // fields must match schema from database
    // In this case, we're using a .json cache
    fields: () => ({
      id: { type: GraphQLString },
      department: { type: GraphQLString },
      number: { type: GraphQLString },
      school: { type: GraphQLString },
      title: { type: GraphQLString },
      course_level: { type: GraphQLString },
      department_alias: { type: new GraphQLList(GraphQLString) },
      units: { type: new GraphQLList(GraphQLFloat) },
      description: { type: GraphQLString },
      department_name: { type: GraphQLString },
      instructor_history: { 
        type: new GraphQLList(instructorType),
        resolve: (course) => {
          return course.professor_history.map((instructor_netid: string) => getInstructor(instructor_netid));
        } 
      },
      prerequisite_tree: { type: GraphQLString },
      prerequisite_list: { 
        type: new GraphQLList(courseType),
        resolve: (course) => {
          return course.prerequisite_list.map((prereq_id: string) => getCourse(prereq_id.replace(/ /g, "")));
        }
      },
      prerequisite_text: { type: GraphQLString },
      prerequisite_for: { 
        type: new GraphQLList(courseType),
        resolve: (course) => {
          return course.prerequisite_for.map((prereq_id: string) => getCourse(prereq_id.replace(/ /g, "")));
        }
      },
      repeatability: { type: GraphQLString },
      concurrent: { type: GraphQLString },
      same_as: { type: GraphQLString },
      restriction: { type: GraphQLString },
      overlap: { type: GraphQLString },
      corequisite: { type: GraphQLString },
      ge_list: { type: new GraphQLList(GraphQLString) },
      ge_text: { type: GraphQLString },
      terms: { type: new GraphQLList(GraphQLString) },
      // can't add "same as" or "grading option" due to whitespace :((
  
      offerings: {
        type: new GraphQLList(courseOfferingType),
        args: {
          year: { type: GraphQLFloat},
          quarter: { type: GraphQLString},
          ge: { type: GraphQLString},
          division: { type: GraphQLString},
          section_codes: { type: GraphQLString },
          instructor: { type: GraphQLString },
          section_type: { type: GraphQLString },
          units: { type: GraphQLString },
          days: { type: GraphQLString },
          start_time: { type: GraphQLString },
          end_time: { type: GraphQLString },
          max_capacity: { type: GraphQLString },
          full_courses: { type: GraphQLString },
          cancelled_courses: { type: GraphQLString },
          building: { type: GraphQLString },
          room: { type: GraphQLString }
        },
        resolve: async (course, args, _, info) => {
          if ('offerings' in course) {
            return course.offerings;
          }
  
          // Only fetch course schedule if it's a root course query.
          // This is because we don't want to spam webreg with successive calls from
          // queries like allCourses/allProfessors.
          const prev_path_type = info.path.prev.typename;
  
          if (prev_path_type == 'Query'){
            const query = scheduleArgsToQuery({
              department: course.department,
              course_number: course.number, 
              ...args
            })
            const results = (await getCourseSchedules(query));
            return results;
          }
  
          // TODO: only return one error for a query, instead of one per item in the list
          throw new Error(`Accessing a course's offerings from a nested list is not allowed. Please use the schedules query`)
        }
      }
    })
  })
Example #20
Source File: schema.spec.ts    From Deep-Lynx with MIT License 4 votes vote down vote up
describe('The GraphQL Schema Generator', async () => {
    let containerID: string = process.env.TEST_CONTAINER_ID || '';
    let typeMappingID: string = '';
    let typeMapping: TypeMapping | undefined;
    let dataSourceID: string = '';
    let resultMetatypeRelationships: MetatypeRelationship[] = [];
    let data: DataStaging | undefined;
    let user: User;

    let maintenancePair: MetatypeRelationshipPair | undefined;
    let carDriverPair: MetatypeRelationshipPair | undefined;
    let driverLicensePair: MetatypeRelationshipPair | undefined;

    const car_metatype_keys: MetatypeKey[] = [
        new MetatypeKey({
            name: 'id',
            property_name: 'id',
            description: 'id of car',
            data_type: 'string',
            required: true,
        }),
        new MetatypeKey({
            name: 'name',
            property_name: 'name',
            description: 'name of car',
            data_type: 'string',
            required: true,
        }),
        new MetatypeKey({
            name: 'trim',
            property_name: 'trim',
            description: 'trim package',
            data_type: 'enumeration',
            options: ['le', 'lx'],
            required: true,
        }),
    ];

    const driver_metatype_keys: MetatypeKey[] = [
        new MetatypeKey({
            name: 'id',
            property_name: 'id',
            description: 'id of driver',
            data_type: 'string',
            required: true,
        }),
        new MetatypeKey({
            name: 'name',
            property_name: 'name',
            description: 'name of driver',
            data_type: 'string',
            required: true,
        }),
        new MetatypeKey({
            name: 'age',
            property_name: 'age',
            description: 'age of driver',
            data_type: 'string',
            required: true,
        }),
    ];

    const license_metatype_keys: MetatypeKey[] = [
        new MetatypeKey({
            name: 'id',
            property_name: 'id',
            description: 'license id',
            data_type: 'string',
            required: true,
        }),
        new MetatypeKey({
            name: 'type',
            property_name: 'type',
            description: 'type of license',
            data_type: 'enumeration',
            options: ['learners_permit', 'drivers_license'],
            required: true,
        }),
    ];

    const component_metatype_keys: MetatypeKey[] = [
        new MetatypeKey({
            name: 'id',
            property_name: 'id',
            description: 'id of car',
            data_type: 'number',
            required: true,
        }),
        new MetatypeKey({
            name: 'name',
            property_name: 'name',
            description: 'name of car',
            data_type: 'string',
            required: true,
        }),
    ];

    const manufacturer_metatype_keys: MetatypeKey[] = [
        new MetatypeKey({
            name: 'id',
            property_name: 'id',
            description: 'id of car',
            data_type: 'string',
            required: true,
        }),
        new MetatypeKey({
            name: 'name',
            property_name: 'name',
            description: 'name of car',
            data_type: 'string',
            required: true,
        }),
        new MetatypeKey({
            name: 'location',
            property_name: 'location',
            description: 'location of manufacturer',
            data_type: 'string',
            required: true,
        }),
    ];

    const tire_pressure_metatype_keys: MetatypeKey[] = [
        new MetatypeKey({
            name: 'id',
            property_name: 'id',
            description: 'id of car',
            data_type: 'string',
            required: true,
        }),
        new MetatypeKey({
            name: 'measurement',
            property_name: 'measurement',
            description: 'measurement',
            data_type: 'number',
            required: true,
        }),
        new MetatypeKey({
            name: 'measurement unit',
            property_name: 'measurement_unit',
            description: 'unit of measurement',
            data_type: 'string',
            required: true,
        }),
        new MetatypeKey({
            name: 'measurement name',
            property_name: 'measurement_name',
            description: 'name of measurement',
            data_type: 'string',
            required: true,
        }),
    ];

    const car_maintenance_metatype_keys: MetatypeKey[] = [
        new MetatypeKey({
            name: 'id',
            property_name: 'id',
            description: 'id of car',
            data_type: 'string',
            required: true,
        }),
        new MetatypeKey({
            name: 'name',
            property_name: 'name',
            description: 'name',
            data_type: 'string',
            required: true,
        }),
        new MetatypeKey({
            name: 'start date',
            property_name: 'start_date',
            description: 'start date',
            data_type: 'date',
            required: true,
        }),
        new MetatypeKey({
            name: 'average visits per year',
            property_name: 'average_visits',
            description: 'average visits per year',
            data_type: 'number',
            required: true,
        }),
    ];

    const maintenance_entry_metatype_keys: MetatypeKey[] = [
        new MetatypeKey({
            name: 'id',
            property_name: 'id',
            description: 'id',
            data_type: 'number',
            required: true,
        }),
        new MetatypeKey({
            name: 'check engine light flag',
            property_name: 'check_engine_light_flag',
            description: 'check engine light flag',
            data_type: 'boolean',
            required: true,
        }),
        new MetatypeKey({
            name: 'type',
            property_name: 'type',
            description: 'type',
            data_type: 'string',
            required: true,
        }),
    ];

    const partKeys: MetatypeKey[] = [
        new MetatypeKey({
            name: 'id',
            property_name: 'id',
            description: 'id of car',
            data_type: 'string',
            required: true,
        }),
        new MetatypeKey({
            name: 'name',
            property_name: 'name',
            description: 'name',
            data_type: 'string',
            required: true,
        }),
        new MetatypeKey({
            name: 'price',
            property_name: 'price',
            description: 'price',
            data_type: 'number',
            required: true,
        }),
        new MetatypeKey({
            name: 'quantity',
            property_name: 'quantity',
            description: 'quantity',
            data_type: 'number',
            required: true,
        }),
    ];

    const test_metatypes: Metatype[] = [
        new Metatype({
            name: 'Car',
            description: 'A Vehicle',
            keys: car_metatype_keys,
        }),
        new Metatype({
            name: 'Driver',
            description: 'Person driving Car',
            keys: driver_metatype_keys,
        }),
        new Metatype({
            name: 'License',
            description: 'License of Driver',
            keys: license_metatype_keys,
        }),
        new Metatype({
            name: 'Manufacturer',
            description: 'Creator of Car',
            keys: manufacturer_metatype_keys,
        }),
        new Metatype({
            name: 'Tire Pressure',
            description: 'Pressure of tire',
            keys: tire_pressure_metatype_keys,
        }),
        new Metatype({
            name: 'Maintenance',
            description: 'Maintenance records',
            keys: car_maintenance_metatype_keys,
        }),
        new Metatype({
            name: 'Maintenance Entry',
            description: 'Maintenance entries',
            keys: maintenance_entry_metatype_keys,
        }),
        new Metatype({
            name: 'Part',
            description: 'Physical part of car',
            keys: partKeys,
        }),
        new Metatype({
            name: 'Component',
            description: 'Base component of part',
            keys: component_metatype_keys,
        }),
    ];

    before(async function () {
        if (process.env.CORE_DB_CONNECTION_STRING === '') {
            Logger.debug('skipping export tests, no storage layer');
            this.skip();
        }

        await PostgresAdapter.Instance.init();
        const mapper = ContainerMapper.Instance;

        const container = await mapper.Create(
            'test suite',
            new Container({
                name: faker.name.findName(),
                description: faker.random.alphaNumeric(),
            }),
        );

        expect(container.isError).false;
        expect(container.value.id).not.null;
        containerID = container.value.id!;

        test_metatypes.forEach((metatype) => (metatype.container_id = containerID));

        const userResult = await UserMapper.Instance.Create(
            'test suite',
            new User({
                identity_provider_id: faker.random.uuid(),
                identity_provider: 'username_password',
                admin: false,
                display_name: faker.name.findName(),
                email: faker.internet.email(),
                roles: ['superuser'],
            }),
        );

        expect(userResult.isError).false;
        expect(userResult.value).not.empty;
        user = userResult.value;

        const relationshipMapper = MetatypeRelationshipMapper.Instance;

        const metatypeRepo = new MetatypeRepository();
        const created = await metatypeRepo.bulkSave(user, test_metatypes);
        expect(created.isError).false;

        const test_metatype_relationships: MetatypeRelationship[] = [
            new MetatypeRelationship({
                container_id: containerID,
                name: 'parent',
                description: 'item has parent',
            }),
            new MetatypeRelationship({
                container_id: containerID,
                name: 'uses',
                description: 'actor uses item',
            }),
            new MetatypeRelationship({
                container_id: containerID,
                name: 'has',
                description: 'item has item',
            }),
        ];

        // create the relationships
        const metatypeRelationships = await relationshipMapper.BulkCreate('test suite', test_metatype_relationships);
        expect(metatypeRelationships.isError).false;
        expect(metatypeRelationships.value).not.empty;
        resultMetatypeRelationships = metatypeRelationships.value;

        const test_metatype_relationship_pairs: MetatypeRelationshipPair[] = [
            new MetatypeRelationshipPair({
                name: 'owns',
                description: 'owns another entity',
                origin_metatype: test_metatypes.find((m) => m.name === 'Maintenance')!.id!,
                destination_metatype: test_metatypes.find((m) => m.name === 'Maintenance Entry')!.id!,
                relationship: resultMetatypeRelationships.find((m) => m.name === 'parent')!.id!,
                relationship_type: 'one:one',
                container_id: containerID,
            }),
            new MetatypeRelationshipPair({
                name: 'drives',
                description: 'actor drives item',
                origin_metatype: test_metatypes.find((m) => m.name === 'Driver')!.id!,
                destination_metatype: test_metatypes.find((m) => m.name === 'Car')!.id!,
                relationship: resultMetatypeRelationships.find((m) => m.name === 'uses')!.id!,
                relationship_type: 'one:many',
                container_id: containerID,
            }),
            new MetatypeRelationshipPair({
                name: 'holds',
                description: 'actor holds item',
                origin_metatype: test_metatypes.find((m) => m.name === 'Driver')!.id!,
                destination_metatype: test_metatypes.find((m) => m.name === 'License')!.id!,
                relationship: resultMetatypeRelationships.find((m) => m.name === 'uses')!.id!,
                relationship_type: 'one:one',
                container_id: containerID,
            }),
            new MetatypeRelationshipPair({
                name: 'has',
                description: 'item has item',
                origin_metatype: test_metatypes.find((m) => m.name === 'Car')!.id!,
                destination_metatype: test_metatypes.find((m) => m.name === 'Maintenance')!.id!,
                relationship: resultMetatypeRelationships.find((m) => m.name === 'has')!.id!,
                relationship_type: 'one:one',
                container_id: containerID,
            }),
        ];

        const pairs = await MetatypeRelationshipPairMapper.Instance.BulkCreate('test suite', test_metatype_relationship_pairs);
        expect(pairs.isError).false;
        expect(pairs.value).not.empty;
        maintenancePair = pairs.value[0];
        carDriverPair = pairs.value[1];
        driverLicensePair = pairs.value[2];

        const exp = await DataSourceMapper.Instance.Create(
            'test suite',
            new DataSourceRecord({
                container_id: containerID,
                name: 'Test Data Source',
                active: true,
                adapter_type: 'standard',
                data_format: 'json',
            }),
        );
        expect(exp.isError).false;
        expect(exp.value).not.empty;
        dataSourceID = exp.value.id!;

        const mapping = new TypeMapping({
            container_id: containerID,
            data_source_id: exp.value.id!,
            sample_payload: test_payload[0],
        });
        const saved = await new TypeMappingRepository().save(mapping, user);
        expect(saved.isError).false;
        typeMappingID = mapping.id!;
        typeMapping = mapping;

        // now import the data
        const newImport = await ImportMapper.Instance.CreateImport(
            'test suite',
            new Import({
                data_source_id: dataSourceID,
                reference: 'testing suite upload',
            }),
        );
        expect(newImport.isError).false;

        const inserted = await DataStagingMapper.Instance.Create(
            new DataStaging({
                data_source_id: dataSourceID,
                import_id: newImport.value.id!,
                data: test_payload[0],
                shape_hash: typeMapping.shape_hash,
            }),
        );
        expect(inserted.isError).false;
        expect(inserted.value.id).not.undefined;

        const stagingRepo = new DataStagingRepository();
        const insertedData = await stagingRepo.where().importID('eq', newImport.value.id).list({limit: 1});
        expect(insertedData.isError).false;
        expect(insertedData.value).not.empty;
        data = insertedData.value[0];

        const dataSource = new DataSourceFactory().fromDataSourceRecord(exp.value);

        // create the transformations and process
        const carMaintenanceKeys = test_metatypes.find((m) => m.name === 'Maintenance')!.keys;
        const carDriverKeys = test_metatypes.find((m) => m.name === 'Driver')!.keys;
        const driverLicenseKeys = test_metatypes.find((m) => m.name === 'License')!.keys;
        const entryKeys = test_metatypes.find((m) => m.name === 'Maintenance Entry')!.keys;
        // first generate all transformations for the type mapping, and set active
        const nodeTransformations: TypeTransformation[] = [
            new TypeTransformation({
                type: 'node',
                container_id: containerID,
                data_source_id: dataSource!.DataSourceRecord!.id!,
                type_mapping_id: typeMappingID,
                keys: [
                    new KeyMapping({
                        key: 'car_maintenance.id',
                        metatype_key_id: carMaintenanceKeys!.find((key) => key.name === 'id')?.id,
                    }),
                    new KeyMapping({
                        key: 'car_maintenance.name',
                        metatype_key_id: carMaintenanceKeys!.find((key) => key.name === 'name')?.id,
                    }),
                    new KeyMapping({
                        key: 'car_maintenance.start_date',
                        metatype_key_id: carMaintenanceKeys!.find((key) => key.name === 'start date')?.id,
                    }),
                    new KeyMapping({
                        key: 'car_maintenance.average_visits_per_year',
                        metatype_key_id: carMaintenanceKeys!.find((key) => key.name === 'average visits per year')?.id,
                    }),
                    new KeyMapping({
                        key: 'car_maintenance.visit_dates',
                        metatype_key_id: carMaintenanceKeys!.find((key) => key.name === 'visit dates')?.id,
                    }),
                ],
                metatype_id: test_metatypes.find((m) => m.name === 'Maintenance')!.id,
                unique_identifier_key: 'car_maintenance.id',
            }),
            new TypeTransformation({
                type: 'node',
                container_id: containerID,
                data_source_id: dataSource!.DataSourceRecord!.id!,
                type_mapping_id: typeMappingID,
                keys: [
                    new KeyMapping({
                        key: 'driver.id',
                        metatype_key_id: carDriverKeys!.find((key) => key.name === 'id')?.id,
                    }),
                    new KeyMapping({
                        key: 'driver.name',
                        metatype_key_id: carDriverKeys!.find((key) => key.name === 'name')?.id,
                    }),
                    new KeyMapping({
                        key: 'driver.age',
                        metatype_key_id: carDriverKeys!.find((key) => key.name === 'age')?.id,
                    }),
                ],
                metatype_id: test_metatypes.find((m) => m.name === 'Driver')!.id,
                unique_identifier_key: 'driver.id',
            }),
            new TypeTransformation({
                type: 'node',
                container_id: containerID,
                data_source_id: dataSource!.DataSourceRecord!.id!,
                type_mapping_id: typeMappingID,
                keys: [
                    new KeyMapping({
                        key: 'license.id',
                        metatype_key_id: driverLicenseKeys!.find((key) => key.name === 'id')?.id,
                    }),
                    new KeyMapping({
                        key: 'license.type',
                        metatype_key_id: driverLicenseKeys!.find((key) => key.name === 'type')?.id,
                    }),
                ],
                metatype_id: test_metatypes.find((m) => m.name === 'License')!.id,
                unique_identifier_key: 'license.id',
            }),
            new TypeTransformation({
                type: 'node',
                container_id: containerID,
                data_source_id: dataSource!.DataSourceRecord!.id!,
                type_mapping_id: typeMappingID,
                keys: [
                    new KeyMapping({
                        key: 'car_maintenance.maintenance_entries.[].id',
                        metatype_key_id: entryKeys!.find((key) => key.name === 'id')?.id,
                    }),
                    new KeyMapping({
                        key: 'car_maintenance.maintenance_entries.[].type',
                        metatype_key_id: entryKeys!.find((key) => key.name === 'type')?.id,
                    }),
                    new KeyMapping({
                        key: 'car_maintenance.maintenance_entries.[].check_engine_light_flag',
                        metatype_key_id: entryKeys!.find((key) => key.name === 'check engine light flag')?.id,
                    }),
                ],
                metatype_id: test_metatypes.find((m) => m.name === 'Maintenance Entry')?.id,
                unique_identifier_key: 'car_maintenance.maintenance_entries.[].id',
                root_array: 'car_maintenance.maintenance_entries',
            }),
        ];

        const nodeResult = await TypeTransformationMapper.Instance.BulkCreate('test suite', nodeTransformations);
        expect(nodeResult.isError).false;

        const edgeTransformations: TypeTransformation[] = [
            new TypeTransformation({
                type: 'edge',
                container_id: containerID,
                data_source_id: dataSource!.DataSourceRecord!.id!,
                type_mapping_id: typeMappingID,
                metatype_relationship_pair_id: maintenancePair!.id,
                origin_id_key: 'car_maintenance.id',
                origin_data_source_id: dataSource!.DataSourceRecord!.id!,
                origin_metatype_id: test_metatypes.find((m) => m.name === 'Maintenance')!.id,
                destination_id_key: 'car_maintenance.maintenance_entries.[].id',
                destination_data_source_id: dataSource!.DataSourceRecord!.id!,
                destination_metatype_id: test_metatypes.find((m) => m.name === 'Maintenance Entry')!.id,
                root_array: 'car_maintenance.maintenance_entries',
                keys: [],
            }),
            new TypeTransformation({
                type: 'edge',
                container_id: containerID,
                data_source_id: dataSource!.DataSourceRecord!.id!,
                type_mapping_id: typeMappingID,
                metatype_relationship_pair_id: carDriverPair!.id,
                origin_id_key: 'driver.id',
                origin_data_source_id: dataSource!.DataSourceRecord!.id!,
                origin_metatype_id: test_metatypes.find((m) => m.name === 'Driver')!.id,
                destination_id_key: 'car.id',
                destination_data_source_id: dataSource!.DataSourceRecord!.id!,
                destination_metatype_id: test_metatypes.find((m) => m.name === 'Car')!.id,
                keys: [],
            }),
            new TypeTransformation({
                type: 'edge',
                container_id: containerID,
                data_source_id: dataSource!.DataSourceRecord!.id!,
                type_mapping_id: typeMappingID,
                metatype_relationship_pair_id: driverLicensePair!.id,
                origin_id_key: 'driver.id',
                origin_data_source_id: dataSource!.DataSourceRecord!.id!,
                origin_metatype_id: test_metatypes.find((m) => m.name === 'Driver')!.id,
                destination_id_key: 'license.id',
                destination_data_source_id: dataSource!.DataSourceRecord!.id!,
                destination_metatype_id: test_metatypes.find((m) => m.name === 'License')!.id,
                keys: [],
            }),
        ];

        const edgeResult = await TypeTransformationMapper.Instance.BulkCreate('test suite', edgeTransformations);
        expect(edgeResult.isError).false;

        const active = await TypeMappingMapper.Instance.SetActive(typeMappingID);
        expect(active.isError).false;

        const dataStagingRepo = new DataStagingRepository();
        const records = await dataStagingRepo.where().dataSourceID('eq', dataSource!.DataSourceRecord!.id).list();
        expect(records.isError).false;
        expect(records.value.length).gt(0);

        for (const record of records.value) {
            const result = await ProcessData(record);
            expect(result.isError, result.error?.error).false;
        }

        return Promise.resolve();
    });

    after(async () => {
        await UserMapper.Instance.Delete(user.id!);
        await ContainerMapper.Instance.Delete(containerID);
        return PostgresAdapter.Instance.close();
    });

    it('can generate a valid schema', async () => {
        const schemaGenerator = new GraphQLSchemaGenerator();

        const containerSchema = await schemaGenerator.ForContainer(containerID, {});
        expect(containerSchema.isError).false;
        const typeMap = containerSchema.value.getTypeMap();

        // verify the types exist
        expect(typeMap['Garbage']).undefined;
        expect(typeMap['Car']).not.undefined;
        expect(typeMap['Driver']).not.undefined;
        expect(typeMap['License']).not.undefined;
        expect(typeMap['Manufacturer']).not.undefined;
        expect(typeMap['Tire_Pressure']).not.undefined;
        expect(typeMap['Maintenance']).not.undefined;
        expect(typeMap['Maintenance_Entry']).not.undefined;
        expect(typeMap['Part']).not.undefined;
        expect(typeMap['Component']).not.undefined;

        // check the fields now, make sure they're the right type
        expect((typeMap['Car'] as GraphQLObjectType).getFields()['id'].type).eq(GraphQLString);
        expect((typeMap['Car'] as GraphQLObjectType).getFields()['name'].type).eq(GraphQLString);
        expect((typeMap['Car'] as GraphQLObjectType).getFields()['trim'].type.toString()).eq('Car_trim_Enum_TypeA');
        // check the enum values
        const carVals = ((typeMap['Car'] as GraphQLObjectType).getFields()['trim'].type as GraphQLEnumType).getValues();
        expect(carVals.length).eq(2);
        expect(carVals[0].name).oneOf(['le', 'lx']);
        expect(carVals[1].name).oneOf(['le', 'lx']);

        expect((typeMap['Driver'] as GraphQLObjectType).getFields()['id'].type).eq(GraphQLString);
        expect((typeMap['Driver'] as GraphQLObjectType).getFields()['name'].type).eq(GraphQLString);
        expect((typeMap['Driver'] as GraphQLObjectType).getFields()['age'].type).eq(GraphQLString);

        expect((typeMap['License'] as GraphQLObjectType).getFields()['id'].type).eq(GraphQLString);
        expect((typeMap['License'] as GraphQLObjectType).getFields()['type'].type.toString()).eq('License_type_Enum_TypeA');
        // check the enum values
        const values = ((typeMap['License'] as GraphQLObjectType).getFields()['type'].type as GraphQLEnumType).getValues();
        expect(values.length).eq(2);
        expect(values[0].name).oneOf(['learners_permit', 'drivers_license']);
        expect(values[1].name).oneOf(['learners_permit', 'drivers_license']);

        expect((typeMap['Manufacturer'] as GraphQLObjectType).getFields()['id'].type).eq(GraphQLString);
        expect((typeMap['Manufacturer'] as GraphQLObjectType).getFields()['name'].type).eq(GraphQLString);
        expect((typeMap['Manufacturer'] as GraphQLObjectType).getFields()['location'].type).eq(GraphQLString);

        expect((typeMap['Tire_Pressure'] as GraphQLObjectType).getFields()['id'].type).eq(GraphQLString);
        expect((typeMap['Tire_Pressure'] as GraphQLObjectType).getFields()['measurement'].type).eq(GraphQLFloat);
        expect((typeMap['Tire_Pressure'] as GraphQLObjectType).getFields()['measurement_unit'].type).eq(GraphQLString);
        expect((typeMap['Tire_Pressure'] as GraphQLObjectType).getFields()['measurement_name'].type).eq(GraphQLString);

        expect((typeMap['Maintenance'] as GraphQLObjectType).getFields()['id'].type).eq(GraphQLString);
        expect((typeMap['Maintenance'] as GraphQLObjectType).getFields()['name'].type).eq(GraphQLString);
        expect((typeMap['Maintenance'] as GraphQLObjectType).getFields()['start_date'].type).eq(GraphQLString);
        expect((typeMap['Maintenance'] as GraphQLObjectType).getFields()['average_visits'].type).eq(GraphQLFloat);

        expect((typeMap['Maintenance_Entry'] as GraphQLObjectType).getFields()['id'].type).eq(GraphQLFloat);
        expect((typeMap['Maintenance_Entry'] as GraphQLObjectType).getFields()['check_engine_light_flag'].type).eq(GraphQLBoolean);
        expect((typeMap['Maintenance_Entry'] as GraphQLObjectType).getFields()['type'].type).eq(GraphQLString);

        expect((typeMap['Part'] as GraphQLObjectType).getFields()['id'].type).eq(GraphQLString);
        expect((typeMap['Part'] as GraphQLObjectType).getFields()['name'].type).eq(GraphQLString);
        expect((typeMap['Part'] as GraphQLObjectType).getFields()['price'].type).eq(GraphQLFloat);
        expect((typeMap['Part'] as GraphQLObjectType).getFields()['quantity'].type).eq(GraphQLFloat);
    });

    // the processed data should generate 1 Maintenance record and 2 Maintenance Entry records by this point
    it('can return nodes based on metadata', async () => {
        const schemaGenerator = new GraphQLSchemaGenerator();

        const containerSchema = await schemaGenerator.ForContainer(containerID, {});
        expect(containerSchema.isError).false;
    });
});
Example #21
Source File: buildMutationInputType.ts    From payload with MIT License 4 votes vote down vote up
function buildMutationInputType(name: string, fields: Field[], parentName: string, forceNullable = false): GraphQLInputObjectType {
  const fieldToSchemaMap = {
    number: (field: NumberField) => {
      const type = field.name === 'id' ? GraphQLInt : GraphQLFloat;
      return { type: withNullableType(field, type, forceNullable) };
    },
    text: (field: TextField) => ({ type: withNullableType(field, GraphQLString, forceNullable) }),
    email: (field: EmailField) => ({ type: withNullableType(field, GraphQLString, forceNullable) }),
    textarea: (field: TextareaField) => ({ type: withNullableType(field, GraphQLString, forceNullable) }),
    richText: (field: RichTextField) => ({ type: withNullableType(field, GraphQLJSON, forceNullable) }),
    code: (field: CodeField) => ({ type: withNullableType(field, GraphQLString, forceNullable) }),
    date: (field: DateField) => ({ type: withNullableType(field, GraphQLString, forceNullable) }),
    upload: (field: UploadField) => ({ type: withNullableType(field, GraphQLString, forceNullable) }),
    radio: (field: RadioField) => ({ type: withNullableType(field, GraphQLString, forceNullable) }),
    point: (field: PointField) => ({ type: withNullableType(field, GraphQLList(GraphQLFloat), forceNullable) }),
    checkbox: () => ({ type: GraphQLBoolean }),
    select: (field: SelectField) => {
      const formattedName = `${combineParentName(parentName, field.name)}_MutationInput`;
      let type: GraphQLType = new GraphQLEnumType({
        name: formattedName,
        values: field.options.reduce((values, option) => {
          if (typeof option === 'object' && option.value) {
            return {
              ...values,
              [formatName(option.value)]: {
                value: option.value,
              },
            };
          }

          if (typeof option === 'string') {
            return {
              ...values,
              [option]: {
                value: option,
              },
            };
          }

          return values;
        }, {}),
      });

      type = field.hasMany ? new GraphQLList(type) : type;
      type = withNullableType(field, type, forceNullable);

      return { type };
    },
    relationship: (field: RelationshipField) => {
      const { relationTo } = field;
      type PayloadGraphQLRelationshipType = GraphQLScalarType | GraphQLList<GraphQLScalarType> | GraphQLInputObjectType;
      let type: PayloadGraphQLRelationshipType;

      if (Array.isArray(relationTo)) {
        const fullName = `${combineParentName(parentName, field.label === false ? toWords(field.name, true) : field.label)}RelationshipInput`;
        type = new GraphQLInputObjectType({
          name: fullName,
          fields: {
            relationTo: {
              type: new GraphQLEnumType({
                name: `${fullName}RelationTo`,
                values: relationTo.reduce((values, option) => ({
                  ...values,
                  [formatName(option)]: {
                    value: option,
                  },
                }), {}),
              }),
            },
            value: { type: GraphQLJSON },
          },
        });
      } else {
        type = getCollectionIDType(payload.collections[relationTo].config);
      }

      return { type: field.hasMany ? new GraphQLList(type) : type };
    },
    array: (field: ArrayField) => {
      const fullName = combineParentName(parentName, field.label === false ? toWords(field.name, true) : field.label);
      let type: GraphQLType | GraphQLList<GraphQLType> = buildMutationInputType(fullName, field.fields, fullName);
      type = new GraphQLList(withNullableType(field, type, forceNullable));
      return { type };
    },
    group: (field: GroupField) => {
      const requiresAtLeastOneField = field.fields.some((subField) => (!fieldIsPresentationalOnly(subField) && subField.required && !subField.localized));
      const fullName = combineParentName(parentName, field.label === false ? toWords(field.name, true) : field.label);
      let type: GraphQLType = buildMutationInputType(fullName, field.fields, fullName);
      if (requiresAtLeastOneField) type = new GraphQLNonNull(type);
      return { type };
    },
    blocks: () => ({ type: GraphQLJSON }),
    row: (field: RowField) => field.fields.reduce((acc, rowField: RowField) => {
      const getFieldSchema = fieldToSchemaMap[rowField.type];

      if (getFieldSchema) {
        const fieldSchema = getFieldSchema(rowField);

        return [
          ...acc,
          fieldSchema,
        ];
      }

      return acc;
    }, []),
  };

  const fieldTypes = fields.reduce((schema, field: Field) => {
    if (!fieldIsPresentationalOnly(field) && !field.hidden) {
      const getFieldSchema: (field: Field) => { type: GraphQLType } = fieldToSchemaMap[field.type];

      if (getFieldSchema) {
        const fieldSchema = getFieldSchema(field);

        if (fieldHasSubFields(field) && Array.isArray(fieldSchema)) {
          return fieldSchema.reduce((acc, subField, i) => {
            const currentSubField = field.fields[i];
            if (fieldAffectsData(currentSubField)) {
              return {
                ...acc,
                [currentSubField.name]: subField,
              };
            }

            return {
              ...acc,
              ...fieldSchema,
            };
          }, schema);
        }

        if (fieldAffectsData(field)) {
          return {
            ...schema,
            [field.name]: fieldSchema,
          };
        }

        return {
          ...schema,
          ...fieldSchema,
        };
      }
    }

    return schema;
  }, {});

  const fieldName = formatName(name);

  return new GraphQLInputObjectType({
    name: `mutation${fieldName}Input`,
    fields: {
      ...fieldTypes,
    },
  });
}
Example #22
Source File: buildObjectType.ts    From payload with MIT License 4 votes vote down vote up
function buildObjectType(name: string, fields: Field[], parentName: string, baseFields: BaseFields = {}): GraphQLType {
  const recursiveBuildObjectType = buildObjectType.bind(this);

  const fieldToSchemaMap = {
    number: (field: NumberField) => ({ type: withNullableType(field, GraphQLFloat) }),
    text: (field: TextField) => ({ type: withNullableType(field, GraphQLString) }),
    email: (field: EmailField) => ({ type: withNullableType(field, EmailAddressResolver) }),
    textarea: (field: TextareaField) => ({ type: withNullableType(field, GraphQLString) }),
    code: (field: CodeField) => ({ type: withNullableType(field, GraphQLString) }),
    date: (field: DateField) => ({ type: withNullableType(field, DateTimeResolver) }),
    point: (field: PointField) => ({ type: withNullableType(field, new GraphQLList(GraphQLFloat)) }),
    richText: (field: RichTextField) => ({
      type: withNullableType(field, GraphQLJSON),
      async resolve(parent, args, context) {
        if (args.depth > 0) {
          await createRichTextRelationshipPromise({
            req: context.req,
            siblingDoc: parent,
            depth: args.depth,
            field,
            showHiddenFields: false,
          });
        }

        return parent[field.name];
      },
      args: {
        depth: {
          type: GraphQLInt,
        },
      },
    }),
    upload: (field: UploadField) => {
      const { relationTo, label } = field;

      const uploadName = combineParentName(parentName, label === false ? toWords(field.name, true) : label);

      // If the relationshipType is undefined at this point,
      // it can be assumed that this blockType can have a relationship
      // to itself. Therefore, we set the relationshipType equal to the blockType
      // that is currently being created.

      const type = this.collections[relationTo].graphQL.type || newlyCreatedBlockType;

      const uploadArgs = {} as LocaleInputType;

      if (this.config.localization) {
        uploadArgs.locale = {
          type: this.types.localeInputType,
        };

        uploadArgs.fallbackLocale = {
          type: this.types.fallbackLocaleInputType,
        };
      }

      const relatedCollectionSlug = field.relationTo;
      const relatedCollection = this.collections[relatedCollectionSlug];

      const { find } = this.operations.collections;

      const upload = {
        args: uploadArgs,
        type,
        extensions: { complexity: 20 },
        async resolve(parent, args, context) {
          const value = parent[field.name];
          const locale = args.locale || context.req.locale;
          const fallbackLocale = args.fallbackLocale || context.req.fallbackLocale;

          let id = value;

          if (id) {
            id = id.toString();

            const relatedDocumentQuery = {
              collection: relatedCollection,
              where: {
                ...(args.where || {}),
                _id: {
                  equals: id,
                },
              },
              res: context.res,
              req: {
                ...context.req,
                locale,
                fallbackLocale,
              },
              depth: 0,
              pagination: false,
            };

            const relatedDocument = await find(relatedDocumentQuery);

            if (relatedDocument.docs[0]) return relatedDocument.docs[0];

            return null;
          }

          return null;
        },
      };

      const whereFields = this.collections[relationTo].config.fields;

      upload.args.where = {
        type: this.buildWhereInputType(
          uploadName,
          whereFields,
          uploadName,
        ),
      };

      return upload;
    },
    radio: (field: RadioField) => ({
      type: withNullableType(
        field,
        new GraphQLEnumType({
          name: combineParentName(parentName, field.name),
          values: formatOptions(field),
        }),
      ),
    }),
    checkbox: (field: CheckboxField) => ({ type: withNullableType(field, GraphQLBoolean) }),
    select: (field: SelectField) => {
      const fullName = combineParentName(parentName, field.name);

      let type: GraphQLType = new GraphQLEnumType({
        name: fullName,
        values: formatOptions(field),
      });

      type = field.hasMany ? new GraphQLList(type) : type;
      type = withNullableType(field, type);

      return { type };
    },
    relationship: (field: RelationshipField) => {
      const { relationTo, label } = field;
      const isRelatedToManyCollections = Array.isArray(relationTo);
      const hasManyValues = field.hasMany;
      const relationshipName = combineParentName(parentName, label === false ? toWords(field.name, true) : label);

      let type;
      let relationToType = null;

      if (Array.isArray(relationTo)) {
        relationToType = new GraphQLEnumType({
          name: `${relationshipName}_RelationTo`,
          values: relationTo.reduce((relations, relation) => ({
            ...relations,
            [formatName(relation)]: {
              value: relation,
            },
          }), {}),
        });

        const types = relationTo.map((relation) => this.collections[relation].graphQL.type);

        type = new GraphQLObjectType({
          name: `${relationshipName}_Relationship`,
          fields: {
            relationTo: {
              type: relationToType,
            },
            value: {
              type: new GraphQLUnionType({
                name: relationshipName,
                types,
                async resolveType(data, { req: { payload } }) {
                  return payload.collections[data.collection].graphQL.type.name;
                },
              }),
            },
          },
        });
      } else {
        ({ type } = this.collections[relationTo as string].graphQL);
      }

      // If the relationshipType is undefined at this point,
      // it can be assumed that this blockType can have a relationship
      // to itself. Therefore, we set the relationshipType equal to the blockType
      // that is currently being created.

      type = type || newlyCreatedBlockType;

      const relationshipArgs: {
        locale?: unknown
        fallbackLocale?: unknown
        where?: unknown
        page?: unknown
        limit?: unknown
      } = {};

      if (this.config.localization) {
        relationshipArgs.locale = {
          type: this.types.localeInputType,
        };

        relationshipArgs.fallbackLocale = {
          type: this.types.fallbackLocaleInputType,
        };
      }

      const {
        collections,
        operations: {
          collections: {
            find,
          },
        },
      } = this;

      const relationship = {
        args: relationshipArgs,
        type: hasManyValues ? new GraphQLList(type) : type,
        extensions: { complexity: 10 },
        async resolve(parent, args, context) {
          const value = parent[field.name];
          const locale = args.locale || context.req.locale;
          const fallbackLocale = args.fallbackLocale || context.req.fallbackLocale;
          let relatedCollectionSlug = field.relationTo;

          if (hasManyValues) {
            const results = [];
            const resultPromises = [];

            const createPopulationPromise = async (relatedDoc, i) => {
              let id = relatedDoc;
              let collectionSlug = field.relationTo;

              if (isRelatedToManyCollections) {
                collectionSlug = relatedDoc.relationTo;
                id = relatedDoc.value;
              }

              const result = await find({
                collection: collections[collectionSlug as string],
                where: {
                  ...(args.where || {}),
                  _id: {
                    equals: id,
                  },
                },
                res: context.res,
                req: {
                  ...context.req,
                  locale,
                  fallbackLocale,
                },
                depth: 0,
                pagination: false,
              });

              if (result.docs.length === 1) {
                if (isRelatedToManyCollections) {
                  results[i] = {
                    relationTo: collectionSlug,
                    value: {
                      ...result.docs[0],
                      collection: collectionSlug,
                    },
                  };
                } else {
                  [results[i]] = result.docs;
                }
              }
            };

            if (value) {
              value.forEach((relatedDoc, i) => {
                resultPromises.push(createPopulationPromise(relatedDoc, i));
              });
            }

            await Promise.all(resultPromises);
            return results;
          }

          let id = value;
          if (isRelatedToManyCollections && value) {
            id = value.value;
            relatedCollectionSlug = value.relationTo;
          }

          if (id) {
            id = id.toString();

            const relatedDocumentQuery = {
              collection: collections[relatedCollectionSlug as string],
              where: {
                ...(args.where || {}),
                id: {
                  equals: id,
                },
              },
              ...context,
              depth: 0,
            };

            if (args.page) relatedDocumentQuery.paginate.page = args.page;
            if (args.limit) relatedDocumentQuery.paginate.limit = args.limit;

            const relatedDocument = await find(relatedDocumentQuery);

            if (relatedDocument.docs[0]) {
              if (isRelatedToManyCollections) {
                return {
                  relationTo: relatedCollectionSlug,
                  value: {
                    ...relatedDocument.docs[0],
                    collection: relatedCollectionSlug,
                  },
                };
              }

              return relatedDocument.docs[0];
            }

            return null;
          }

          return null;
        },
      };

      if (hasManyValues) {
        relationship.args.page = { type: GraphQLInt };
        relationship.args.limit = { type: GraphQLInt };
      }

      if (Array.isArray(relationTo)) {
        const relatedCollectionFields = relationTo.reduce((allFields, relation) => [
          ...allFields,
          ...collections[relation].config.fields,
        ], []);

        relationship.args.where = {
          type: this.buildWhereInputType(
            relationshipName,
            relatedCollectionFields,
            relationshipName,
          ),
        };
      } else {
        const whereFields = this.collections[relationTo].config.fields;

        relationship.args.where = {
          type: this.buildWhereInputType(
            relationshipName,
            whereFields,
            relationshipName,
          ),
        };
      }

      return relationship;
    },
    array: (field: ArrayField) => {
      const fullName = combineParentName(parentName, field.label === false ? toWords(field.name, true) : field.label);
      let type = recursiveBuildObjectType(fullName, field.fields, fullName);
      type = new GraphQLList(withNullableType(field, type));

      return { type };
    },
    group: (field: GroupField) => {
      const fullName = combineParentName(parentName, field.label === false ? toWords(field.name, true) : field.label);
      const type = recursiveBuildObjectType(fullName, field.fields, fullName);

      return { type };
    },
    blocks: (field: BlockField) => {
      const blockTypes = field.blocks.map((block) => {
        this.buildBlockType(block);
        return this.types.blockTypes[block.slug];
      });

      const fullName = combineParentName(parentName, field.label === false ? toWords(field.name, true) : field.label);

      const type = new GraphQLList(new GraphQLUnionType({
        name: fullName,
        types: blockTypes,
        resolveType: (data) => this.types.blockTypes[data.blockType].name,
      }));

      return { type };
    },
    row: (field) => field.fields.reduce((subFieldSchema, subField) => {
      const buildSchemaType = fieldToSchemaMap[subField.type];

      if (!fieldIsPresentationalOnly(subField) && buildSchemaType) {
        return {
          ...subFieldSchema,
          [formatName(subField.name)]: buildSchemaType(subField),
        };
      }

      return subFieldSchema;
    }, {}),
  };

  const objectSchema = {
    name,
    fields: () => fields.reduce((schema, field) => {
      if (!fieldIsPresentationalOnly(field) && !field.hidden) {
        const fieldSchema = fieldToSchemaMap[field.type];
        if (fieldSchema) {
          if (fieldAffectsData(field)) {
            return {
              ...schema,
              [formatName(field.name)]: fieldSchema(field),
            };
          }

          return {
            ...schema,
            ...fieldSchema(field),
          };
        }
      }

      return schema;
    }, baseFields),
  };

  const newlyCreatedBlockType = new GraphQLObjectType(objectSchema);

  return newlyCreatedBlockType;
}
Example #23
Source File: node_schema.spec.ts    From Deep-Lynx with MIT License 4 votes vote down vote up
describe('A Node Schema Generator', async () => {
    let containerID: string = process.env.TEST_CONTAINER_ID || '';
    let transformationID: string = '';
    let nodeID: string = '';

    // this covers testing the hypertable creation and deletion as well
    before(async function () {
        if (process.env.CORE_DB_CONNECTION_STRING === '') {
            Logger.debug('skipping export tests, no storage layer');
            this.skip();
        }

        await PostgresAdapter.Instance.init();
        const mapper = ContainerStorage.Instance;
        const mMapper = MetatypeMapper.Instance;
        const keyStorage = MetatypeKeyMapper.Instance;
        const mappingStorage = TypeMappingMapper.Instance;

        const container = await mapper.Create(
            'test suite',
            new Container({
                name: faker.name.findName(),
                description: faker.random.alphaNumeric(),
            }),
        );

        expect(container.isError).false;
        expect(container.value.id).not.null;
        containerID = container.value.id!;

        const metatype = await mMapper.Create(
            'test suite',
            new Metatype({
                container_id: containerID,
                name: faker.name.findName(),
                description: faker.random.alphaNumeric(),
            }),
        );

        expect(metatype.isError).false;
        expect(metatype.value).not.empty;

        const testKeys = [...test_keys];
        testKeys.forEach((key) => (key.metatype_id = metatype.value.id!));

        const keys = await keyStorage.BulkCreate('test suite', testKeys);
        expect(keys.isError).false;

        const exp = await DataSourceMapper.Instance.Create(
            'test suite',
            new DataSourceRecord({
                container_id: containerID,
                name: 'Test Data Source',
                active: false,
                adapter_type: 'standard',
                data_format: 'json',
            }),
        );

        expect(exp.isError).false;
        expect(exp.value).not.empty;

        const mapping = await mappingStorage.CreateOrUpdate(
            'test suite',
            new TypeMapping({
                container_id: containerID,
                data_source_id: exp.value.id!,
                sample_payload: test_raw_payload,
            }),
        );

        const transformation = await TypeTransformationMapper.Instance.Create(
            'test suite',
            new TypeTransformation({
                name: 'Test',
                type_mapping_id: mapping.value.id!,
                type: 'timeseries',
                keys: [
                    new KeyMapping({
                        key: 'RADIUS',
                        column_name: 'radius',
                        value_type: 'float',
                    }),
                    new KeyMapping({
                        key: 'COLOR',
                        column_name: 'color',
                        value_type: 'string',
                    }),
                    new KeyMapping({
                        key: 'OPEN',
                        column_name: 'open',
                        value_type: 'boolean',
                    }),
                    new KeyMapping({
                        key: 'AT',
                        column_name: 'at',
                        value_type: 'date',
                        is_primary_timestamp: true,
                    }),
                ],
            }),
        );

        expect(transformation.isError).false;
        transformationID = transformation.value.id!;

        let created = await TypeTransformationMapper.Instance.CreateHypertable(transformation.value);
        expect(created.isError, created.error?.error).false;

        const mixed = new Node({
            container_id: containerID,
            metatype: metatype.value.id!,
            properties: {},
        });

        const node = await NodeMapper.Instance.CreateOrUpdateByCompositeID('test suite', mixed);
        expect(node.isError, metatype.error?.error).false;

        nodeID = node.value.id!;

        const nodeTransformation = await NodeMapper.Instance.AddTransformation(nodeID, transformationID);
        expect(nodeTransformation.isError).false;

        const entries = [
            new TimeseriesEntry({
                transformation_id: transformationID,
                data: [
                    new TimeseriesData({
                        column_name: 'radius',
                        value_type: 'float',
                        value: 1.2,
                    }),
                    new TimeseriesData({
                        column_name: 'color',
                        value_type: 'string',
                        value: 'green',
                    }),
                    new TimeseriesData({
                        column_name: 'open',
                        value_type: 'boolean',
                        value: false,
                    }),
                    new TimeseriesData({
                        column_name: 'at',
                        value_type: 'timestamp',
                        value: new Date(),
                    }),
                ],
            }),
            new TimeseriesEntry({
                transformation_id: transformationID,
                data: [
                    new TimeseriesData({
                        column_name: 'radius',
                        value_type: 'float',
                        value: 0.2,
                    }),
                    new TimeseriesData({
                        column_name: 'color',
                        value_type: 'string',
                        value: 'blue',
                    }),
                    new TimeseriesData({
                        column_name: 'open',
                        value_type: 'boolean',
                        value: false,
                    }),
                    new TimeseriesData({
                        column_name: 'at',
                        value_type: 'timestamp',
                        value: new Date(),
                    }),
                ],
            }),
        ];

        const repo = new TimeseriesEntryRepository();

        const saved = await repo.bulkSave(entries);

        expect(saved.isError, saved.error?.error).false;

        return Promise.resolve();
    });

    after(async () => {
        await TypeTransformationMapper.Instance.DeleteHypertable(transformationID);
        await ContainerMapper.Instance.Delete(containerID);
        // for some reason this suite of tests likes to not let go of the db, so this way we don't wait for it
        void PostgresAdapter.Instance.close();

        return Promise.resolve();
    });

    it('can generate the proper schema', async () => {
        const schemaGenerator = new GraphQLSchemaGenerator();

        const schema = await schemaGenerator.ForNode(nodeID, {});
        expect(schema.isError).false;

        const typeMap = schema.value.getTypeMap();
        expect(typeMap['Test']).not.undefined;

        expect((typeMap['Test'] as GraphQLObjectType).getFields()['radius'].type).eq(GraphQLFloat);
        expect((typeMap['Test'] as GraphQLObjectType).getFields()['open'].type).eq(GraphQLBoolean);
        expect((typeMap['Test'] as GraphQLObjectType).getFields()['color'].type).eq(GraphQLString);
        expect((typeMap['Test'] as GraphQLObjectType).getFields()['at'].type).eq(GraphQLString);

        return Promise.resolve();
    });

    it('can query data correctly', async () => {
        const schemaGenerator = new GraphQLSchemaGenerator();

        const schema = await schemaGenerator.ForNode(nodeID, {});
        expect(schema.isError).false;

        // double-check the schema
        const typeMap = schema.value.getTypeMap();
        expect(typeMap['Test']).not.undefined;

        expect((typeMap['Test'] as GraphQLObjectType).getFields()['radius'].type).eq(GraphQLFloat);
        expect((typeMap['Test'] as GraphQLObjectType).getFields()['open'].type).eq(GraphQLBoolean);
        expect((typeMap['Test'] as GraphQLObjectType).getFields()['color'].type).eq(GraphQLString);
        expect((typeMap['Test'] as GraphQLObjectType).getFields()['at'].type).eq(GraphQLString);

        //simple query
        try {
            let results = await graphql({
                schema: schema.value,
                source: simpleQuery,
            });

            if (results.errors) expect.fail(results.errors.join(','));
            expect(results.data?.Test.length).eq(2);
        } catch (e: any) {
            expect.fail(e);
        }

        //limit query
        try {
            let results = await graphql({
                schema: schema.value,
                source: limitQuery,
            });

            if (results.errors) expect.fail(results.errors.join(','));
            expect(results.data?.Test.length).eq(1);
        } catch (e: any) {
            expect.fail(e);
        }

        //color query
        try {
            let results = await graphql({
                schema: schema.value,
                source: colorQuery,
            });

            if (results.errors) expect.fail(results.errors.join(','));
            expect(results.data?.Test.length).eq(1);
        } catch (e: any) {
            expect.fail(e);
        }

        //color in query
        try {
            let results = await graphql({
                schema: schema.value,
                source: colorInQuery,
            });

            if (results.errors) expect.fail(results.errors.join(','));
            expect(results.data?.Test.length).eq(2);
        } catch (e: any) {
            expect.fail(e);
        }

        //time query
        try {
            let results = await graphql({
                schema: schema.value,
                source: timeQuery,
            });

            if (results.errors) expect.fail(results.errors.join(','));
            expect(results.data?.Test.length).eq(2);
        } catch (e: any) {
            expect.fail(e);
        }

        //reverse time query
        try {
            let results = await graphql({
                schema: schema.value,
                source: reverseTimeQuery,
            });

            if (results.errors) expect.fail(results.errors.join(','));
            expect(results.data?.Test.length).eq(0);
        } catch (e: any) {
            expect.fail(e);
        }

        return Promise.resolve();
    }).timeout(300000);

    it('can save data to file correctly', async () => {
        const schemaGenerator = new GraphQLSchemaGenerator();

        const schema = await schemaGenerator.ForNode(nodeID, {returnFile: true});
        expect(schema.isError).false;

        //simple query
        try {
            let results = await graphql({
                schema: schema.value,
                source: simpleFileQuery,
            });

            if (results.errors) expect.fail(results.errors.join(','));
            expect(results.data?.Test.file_size).gt(0);
        } catch (e: any) {
            expect.fail(e);
        }

        return Promise.resolve();
    });
});
Example #24
Source File: schema.ts    From Deep-Lynx with MIT License 4 votes vote down vote up
inputFieldsForTransformation(transformation: TypeTransformation): {[key: string]: any} {
        const fields: {[key: string]: any} = {};

        transformation.keys?.forEach((key) => {
            if (!key.column_name || !key.value_type) return;

            const propertyName = stringToValidPropertyName(key.column_name);

            switch (key.value_type) {
                // because we have no specification on our internal number type, we
                // must set this as a float for now
                case 'number': {
                    fields[propertyName] = {
                        type: new GraphQLInputObjectType({
                            name: stringToValidPropertyName(`z_${transformation.id}` + key.column_name),
                            fields: {
                                operator: {type: GraphQLString},
                                value: {type: new GraphQLList(GraphQLInt)},
                            },
                        }),
                    };
                    break;
                }

                case 'float': {
                    fields[propertyName] = {
                        type: new GraphQLInputObjectType({
                            name: stringToValidPropertyName(`z_${transformation.id}` + key.column_name),
                            fields: {
                                operator: {type: GraphQLString},
                                value: {type: new GraphQLList(GraphQLFloat)},
                            },
                        }),
                    };
                    break;
                }

                case 'boolean': {
                    fields[propertyName] = {
                        type: new GraphQLInputObjectType({
                            name: stringToValidPropertyName(`z_${transformation.id}` + key.column_name),
                            fields: {
                                operator: {type: GraphQLString},
                                value: {type: new GraphQLList(GraphQLBoolean)},
                            },
                        }),
                    };
                    break;
                }

                case 'string' || 'date' || 'file': {
                    fields[propertyName] = {
                        type: new GraphQLInputObjectType({
                            name: stringToValidPropertyName(`z_${transformation.id}` + key.column_name),
                            fields: {
                                operator: {type: GraphQLString},
                                value: {type: new GraphQLList(GraphQLString)},
                            },
                        }),
                    };
                    break;
                }

                case 'list': {
                    fields[propertyName] = {
                        type: new GraphQLInputObjectType({
                            name: stringToValidPropertyName(`z_${transformation.id}` + key.column_name),
                            fields: {
                                operator: {type: GraphQLString},
                                value: {type: new GraphQLList(GraphQLJSON)},
                            },
                        }),
                    };
                    break;
                }

                default: {
                    fields[propertyName] = {
                        type: new GraphQLInputObjectType({
                            name: stringToValidPropertyName(`z_${transformation.id}` + key.column_name),
                            fields: {
                                operator: {type: GraphQLString},
                                value: {type: new GraphQLList(GraphQLString)},
                            },
                        }),
                    };
                }
            }
        });

        return fields;
    }
Example #25
Source File: fieldToWhereInputSchemaMap.ts    From payload with MIT License 4 votes vote down vote up
fieldToSchemaMap: (parentName: string) => any = (parentName: string) => ({
  number: (field: NumberField) => {
    const type = GraphQLFloat;
    return {
      type: withOperators(
        field,
        type,
        parentName,
        [...operators.equality, ...operators.comparison],
      ),
    };
  },
  text: (field: TextField) => {
    const type = GraphQLString;
    return {
      type: withOperators(
        field,
        type,
        parentName,
        [...operators.equality, 'like', 'contains'],
      ),
    };
  },
  email: (field: EmailField) => {
    const type = EmailAddressResolver;
    return {
      type: withOperators(
        field,
        type,
        parentName,
        [...operators.equality, 'like', 'contains'],
      ),
    };
  },
  textarea: (field: TextareaField) => {
    const type = GraphQLString;
    return {
      type: withOperators(
        field,
        type,
        parentName,
        [...operators.equality, 'like', 'contains'],
      ),
    };
  },
  richText: (field: RichTextField) => {
    const type = GraphQLJSON;
    return {
      type: withOperators(
        field,
        type,
        parentName,
        [...operators.equality, 'like', 'contains'],
      ),
    };
  },
  code: (field: CodeField) => {
    const type = GraphQLString;
    return {
      type: withOperators(
        field,
        type,
        parentName,
        [...operators.equality, 'like', 'contains'],
      ),
    };
  },
  radio: (field: RadioField) => ({
    type: withOperators(
      field,
      new GraphQLEnumType({
        name: `${combineParentName(parentName, field.name)}_Input`,
        values: field.options.reduce((values, option) => {
          if (optionIsObject(option)) {
            return {
              ...values,
              [formatName(option.value)]: {
                value: option.value,
              },
            };
          }

          return {
            ...values,
            [formatName(option)]: {
              value: option,
            },
          };
        }, {}),
      }),
      parentName,
      [...operators.equality, 'like', 'contains'],
    ),
  }),
  date: (field: DateField) => {
    const type = DateTimeResolver;
    return {
      type: withOperators(
        field,
        type,
        parentName,
        [...operators.equality, ...operators.comparison, 'like'],
      ),
    };
  },
  point: (field: PointField) => {
    const type = GraphQLList(GraphQLFloat);
    return {
      type: withOperators(
        field,
        type,
        parentName,
        [...operators.equality, ...operators.comparison, ...operators.geo],
      ),
    };
  },
  relationship: (field: RelationshipField) => {
    let type = withOperators(
      field,
      GraphQLString,
      parentName,
      [...operators.equality, ...operators.contains],
    );

    if (Array.isArray(field.relationTo)) {
      type = new GraphQLInputObjectType({
        name: `${combineParentName(parentName, field.name)}_Relation`,
        fields: {
          relationTo: {
            type: new GraphQLEnumType({
              name: `${combineParentName(parentName, field.name)}_Relation_RelationTo`,
              values: field.relationTo.reduce((values, relation) => ({
                ...values,
                [formatName(relation)]: {
                  value: relation,
                },
              }), {}),
            }),
          },
          value: { type: GraphQLString },
        },
      });
    }

    return { type };
  },
  upload: (field: UploadField) => ({
    type: withOperators(
      field,
      GraphQLString,
      parentName,
      [...operators.equality],
    ),
  }),
  checkbox: (field: CheckboxField) => ({
    type: withOperators(
      field,
      GraphQLBoolean,
      parentName,
      [...operators.equality],
    ),
  }),
  select: (field: SelectField) => ({
    type: withOperators(
      field,
      new GraphQLEnumType({
        name: `${combineParentName(parentName, field.name)}_Input`,
        values: field.options.reduce((values, option) => {
          if (typeof option === 'object' && option.value) {
            return {
              ...values,
              [formatName(option.value)]: {
                value: option.value,
              },
            };
          }

          if (typeof option === 'string') {
            return {
              ...values,
              [option]: {
                value: option,
              },
            };
          }

          return values;
        }, {}),
      }),
      parentName,
      [...operators.equality, ...operators.contains],
    ),
  }),
  array: (field: ArrayField) => recursivelyBuildNestedPaths(parentName, field),
  group: (field: GroupField) => recursivelyBuildNestedPaths(parentName, field),
  row: (field: RowField) => field.fields.reduce((rowSchema, rowField) => {
    const getFieldSchema = fieldToSchemaMap(parentName)[rowField.type];

    if (getFieldSchema) {
      const rowFieldSchema = getFieldSchema(rowField);

      if (fieldHasSubFields(rowField)) {
        return [
          ...rowSchema,
          ...rowFieldSchema,
        ];
      }

      if (fieldAffectsData(rowField)) {
        return [
          ...rowSchema,
          {
            key: rowField.name,
            type: rowFieldSchema,
          },
        ];
      }
    }


    return rowSchema;
  }, []),
})
Example #26
Source File: schema.ts    From Deep-Lynx with MIT License 4 votes vote down vote up
// generate requires a containerID because the schema it generates is based on a user's ontology and ontologies are
    // separated by containers
    async ForContainer(containerID: string, options: ResolverOptions): Promise<Result<GraphQLSchema>> {
        // fetch the currently published ontology if the versionID wasn't provided
        if (!options.ontologyVersionID) {
            const ontResults = await this.#ontologyRepo
                .where()
                .containerID('eq', containerID)
                .and()
                .status('eq', 'published')
                .list({sortBy: 'id', sortDesc: true});
            if (ontResults.isError || ontResults.value.length === 0) {
                Logger.error('unable to fetch current ontology, or no currently published ontology');
            } else {
                options.ontologyVersionID = ontResults.value[0].id;
            }
        }

        // fetch all metatypes for the container, with their keys - the single most expensive call of this function
        const metatypeResults = await this.#metatypeRepo
            .where()
            .containerID('eq', containerID)
            .and()
            .ontologyVersion('eq', options.ontologyVersionID)
            .list(true);
        if (metatypeResults.isError) {
            return Promise.resolve(Result.Pass(metatypeResults));
        }

        // fetch all metatype relationship pairs - used for _relationship queries.
        const metatypePairResults = await this.#metatypePairRepo
            .where()
            .containerID('eq', containerID)
            .and()
            .ontologyVersion('eq', options.ontologyVersionID)
            .list();
        if (metatypePairResults.isError) {
            return Promise.resolve(Result.Pass(metatypePairResults));
        }

        // fetch all relationship types. Used for relationship wrapper queries.
        const relationshipResults = await this.#relationshipRepo
            .where()
            .containerID('eq', containerID)
            .and()
            .ontologyVersion('eq', options.ontologyVersionID)
            .list(true);
        if (relationshipResults.isError) {
            return Promise.resolve(Result.Pass(relationshipResults));
        }

        // used for querying edges based on node (see input._relationship resolver)
        const metatypePairObjects: {[key: string]: any} = {};
        metatypePairResults.value.forEach((pair) => {
            const origin = stringToValidPropertyName(pair.origin_metatype_name!);
            const rel = stringToValidPropertyName(pair.relationship_name!);
            const dest = stringToValidPropertyName(pair.destination_metatype_name!);
            // populate list for forward searching
            if (!(origin in metatypePairObjects)) {
                metatypePairObjects[origin] = {};
            }
            if (!(rel in metatypePairObjects[origin])) {
                metatypePairObjects[origin][rel] = {};
            }
            if (!(dest in metatypePairObjects[origin][rel])) {
                metatypePairObjects[origin][rel][dest] = {type: GraphQLString};
            }
        });

        const metatypeGraphQLObjects: {[key: string]: any} = {};

        // we must declare the metadata input object beforehand so we can include it in the final schema entry for each
        // metatype
        const recordInputType = new GraphQLInputObjectType({
            name: 'record_input',
            fields: {
                id: {type: GraphQLString},
                data_source_id: {type: GraphQLString},
                original_id: {type: GraphQLJSON}, // since the original ID might be a number, treat it as valid JSON
                import_id: {type: GraphQLString},
                limit: {type: GraphQLInt, defaultValue: 10000},
                page: {type: GraphQLInt, defaultValue: 1},
            },
        });

        const recordInfo = new GraphQLObjectType({
            name: 'recordInfo',
            fields: {
                id: {type: GraphQLString},
                data_source_id: {type: GraphQLString},
                original_id: {type: GraphQLJSON}, // since the original ID might be a number, treat it as valid JSON
                import_id: {type: GraphQLString},
                metatype_id: {type: GraphQLString},
                metatype_name: {type: GraphQLString},
                created_at: {type: GraphQLString},
                created_by: {type: GraphQLString},
                modified_at: {type: GraphQLString},
                modified_by: {type: GraphQLString},
                metadata: {type: GraphQLJSON},
                count: {type: GraphQLInt},
                page: {type: GraphQLInt},
            },
        });

        // needed when a user decides they want the results as a file vs. a raw return
        const fileInfo = new GraphQLObjectType({
            name: 'fileInfo',
            fields: {
                id: {type: GraphQLString},
                file_name: {type: GraphQLString},
                file_size: {type: GraphQLFloat},
                md5hash: {type: GraphQLString},
                metadata: {type: GraphQLJSON},
                url: {type: GraphQLString},
            },
        });

        metatypeResults.value.forEach((metatype) => {
            if (!metatype.keys || metatype.keys.length === 0) return;

            // the following 4 input/object types are used for querying or introspection on _relationship
            const destinationInputType = new GraphQLInputObjectType({
                name: `${stringToValidPropertyName(metatype.name)}_destination_input`,
                // needed because the return type accepts an object, but throws a fit about it
                // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                // @ts-ignore
                fields: () => {
                    const fields: {[key: string]: {[key: string]: any}} = {};
                    if (metatypePairObjects[stringToValidPropertyName(metatype.name)]) {
                        Object.keys(metatypePairObjects[stringToValidPropertyName(metatype.name)]).forEach((pair) => {
                            Object.keys(metatypePairObjects[stringToValidPropertyName(metatype.name)][pair]).forEach((dest) => {
                                fields[dest] = {type: GraphQLBoolean};
                            });
                        });
                    }
                    return fields;
                },
            });

            const relationshipInputType = new GraphQLInputObjectType({
                name: `${stringToValidPropertyName(metatype.name)}_relationship_input`,
                // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                // @ts-ignore
                fields: () => {
                    const fields: {[key: string]: {[key: string]: GraphQLNamedType | GraphQLList<any>}} = {};
                    if (metatypePairObjects[metatype.name]) {
                        Object.keys(metatypePairObjects[metatype.name]).forEach((rel) => {
                            fields[rel] = {type: new GraphQLList(destinationInputType)};
                        });
                    } else {
                        // if no relationships exists, set relationship to _none: true
                        fields._none = {type: GraphQLBoolean};
                    }
                    return fields;
                },
            });

            const destinationInfo = new GraphQLObjectType({
                name: `${stringToValidPropertyName(metatype.name)}_destinationInfo`,
                // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                // @ts-ignore
                fields: () => {
                    const fields: {[key: string]: any} = {};
                    if (metatypePairObjects[metatype.name]) {
                        Object.keys(metatypePairObjects[metatype.name]).forEach((pair) => {
                            Object.keys(metatypePairObjects[metatype.name][pair]).forEach((dest) => {
                                fields[dest] = {type: GraphQLString};
                            });
                        });
                    }
                    return fields;
                },
            });

            const relationshipInfo = new GraphQLObjectType({
                name: `${stringToValidPropertyName(metatype.name)}_relationshipInfo`,
                // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                // @ts-ignore
                fields: () => {
                    const fields: {[key: string]: any} = {};
                    if (metatypePairObjects[metatype.name]) {
                        Object.keys(metatypePairObjects[metatype.name]).forEach((pair) => {
                            fields[pair] = {type: destinationInfo};
                        });
                    } else {
                        // if no relationships exists, set relationship to _none: true
                        fields._none = {type: GraphQLBoolean};
                    }
                    return fields;
                },
            });

            metatypeGraphQLObjects[stringToValidPropertyName(metatype.name)] = {
                args: {
                    ...this.inputFieldsForMetatype(metatype),
                    _record: {type: recordInputType},
                    _relationship: {type: relationshipInputType},
                },
                description: metatype.description,
                type: options.returnFile
                    ? fileInfo
                    : new GraphQLList(
                        new GraphQLObjectType({
                            name: stringToValidPropertyName(metatype.name),
                            // needed because the return type accepts an object, but throws a fit about it
                            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                            // @ts-ignore
                            fields: () => {
                                const output: {[key: string]: {[key: string]: GraphQLNamedType | GraphQLList<any>}} = {};
                                output._record = {type: recordInfo};
                                output._relationship = {type: relationshipInfo};
                                output._file = {type: fileInfo};

                                metatype.keys?.forEach((metatypeKey) => {
                                    // keys must match the regex format of /^[_a-zA-Z][_a-zA-Z0-9]*$/ in order to be considered
                                    // valid graphql property names. While we force the user to meet these requirements at key
                                    // creation, we can't guarantee that legacy data will conform to these standards
                                    const propertyName = stringToValidPropertyName(metatypeKey.property_name);

                                    switch (metatypeKey.data_type) {
                                        // because we have no specification on our internal number type, we
                                        // must set this as a float for now
                                        case 'number': {
                                            output[propertyName] = {
                                                type: GraphQLFloat,
                                            };
                                            break;
                                        }

                                        case 'boolean': {
                                            output[propertyName] = {
                                                type: GraphQLBoolean,
                                            };
                                            break;
                                        }

                                        case 'string' || 'date' || 'file': {
                                            output[propertyName] = {
                                                type: GraphQLString,
                                            };
                                            break;
                                        }

                                        case 'list': {
                                            output[propertyName] = {
                                                type: new GraphQLList(GraphQLJSON),
                                            };
                                            break;
                                        }

                                        case 'enumeration': {
                                            const enumMap: {[key: string]: GraphQLEnumValueConfig} = {};

                                            if (metatypeKey.options) {
                                                metatypeKey.options.forEach((option) => {
                                                    enumMap[option] = {
                                                        value: option,
                                                    };
                                                });
                                            }

                                            output[propertyName] = {
                                                type: new GraphQLEnumType({
                                                    name: stringToValidPropertyName(`${metatype.name}_${metatypeKey.name}_Enum_TypeA`),
                                                    values: enumMap,
                                                }),
                                            };
                                            break;
                                        }

                                        default: {
                                            output[propertyName] = {
                                                type: GraphQLString,
                                            };
                                        }
                                    }
                                });

                                return output;
                            },
                        }),
                    ),
                resolve: this.resolverForMetatype(containerID, metatype, options),
            };
        });

        const relationshipGraphQLObjects: {[key: string]: any} = {};

        // metadata objects for edges (metatype relationships)
        const edgeRecordInputType = new GraphQLInputObjectType({
            name: 'edge_record_input',
            fields: {
                id: {type: GraphQLString},
                pair_id: {type: GraphQLString},
                data_source_id: {type: GraphQLString},
                import_id: {type: GraphQLString},
                origin_id: {type: GraphQLString},
                destination_id: {type: GraphQLString},
                limit: {type: GraphQLInt, defaultValue: 10000},
                page: {type: GraphQLInt, defaultValue: 1},
            },
        });

        const edgeRecordInfo = new GraphQLObjectType({
            name: 'edge_recordInfo',
            fields: {
                id: {type: GraphQLString},
                pair_id: {type: GraphQLString},
                data_source_id: {type: GraphQLString},
                import_id: {type: GraphQLString},
                origin_id: {type: GraphQLString},
                origin_metatype_id: {type: GraphQLString},
                destination_id: {type: GraphQLString},
                destination_metatype_id: {type: GraphQLString},
                relationship_name: {type: GraphQLString},
                created_at: {type: GraphQLString},
                created_by: {type: GraphQLString},
                modified_at: {type: GraphQLString},
                modified_by: {type: GraphQLString},
                metadata: {type: GraphQLJSON},
                count: {type: GraphQLInt},
                page: {type: GraphQLInt},
            },
        });

        relationshipResults.value.forEach((relationship) => {
            relationshipGraphQLObjects[stringToValidPropertyName(relationship.name)] = {
                args: {
                    ...this.inputFieldsForRelationship(relationship),
                    _record: {type: edgeRecordInputType},
                },
                description: relationship.description,
                type: (options.returnFile) ? fileInfo : new GraphQLList(
                    new GraphQLObjectType({
                        name: stringToValidPropertyName(relationship.name),
                        // needed because the return type accepts an object, but throws a fit about it
                        // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                        // @ts-ignore
                        fields: () => {
                            const output: {[key: string]: {[key: string]: GraphQLNamedType | GraphQLList<any>}} = {};
                            output._record = {type: edgeRecordInfo};

                            relationship.keys?.forEach((relationshipKey) => {
                                const propertyName = stringToValidPropertyName(relationshipKey.property_name);

                                switch (relationshipKey.data_type) {
                                    // because we have no specification on our internal number type, we
                                    // must set this as a float for now
                                    case 'number': {
                                        output[propertyName] = {
                                            type: GraphQLFloat,
                                        };
                                        break;
                                    }

                                    case 'boolean': {
                                        output[propertyName] = {
                                            type: GraphQLBoolean,
                                        };
                                        break;
                                    }

                                    case 'string' || 'date' || 'file': {
                                        output[propertyName] = {
                                            type: GraphQLString,
                                        };
                                        break;
                                    }

                                    case 'list': {
                                        output[propertyName] = {
                                            type: new GraphQLList(GraphQLJSON),
                                        };
                                        break;
                                    }

                                    case 'enumeration': {
                                        const enumMap: {[key: string]: GraphQLEnumValueConfig} = {};

                                        if (relationshipKey.options) {
                                            relationshipKey.options.forEach((option) => {
                                                enumMap[option] = {
                                                    value: option,
                                                };
                                            });
                                        }

                                        output[propertyName] = {
                                            type: new GraphQLEnumType({
                                                name: stringToValidPropertyName(`${relationship.name}_${relationshipKey.name}_Enum_TypeA`),
                                                values: enumMap,
                                            }),
                                        };
                                        break;
                                    }

                                    default: {
                                        output[propertyName] = {
                                            type: GraphQLString,
                                        };
                                    }
                                }
                            });
                            return output;
                        },
                    }),
                ),
                resolve: this.resolverForRelationships(containerID, relationship, options),
            };
        });

        const metatypeObjects = new GraphQLObjectType({
            name: 'metatypes',
            fields: metatypeGraphQLObjects,
        });

        const relationshipObjects = new GraphQLObjectType({
            name: 'relationships',
            fields: relationshipGraphQLObjects,
        });

        // nodeType and edgeType for flexibility in filtering graph return
        const nodeInputType = new GraphQLInputObjectType({
            name: 'node_input',
            fields: {
                name: {type: GraphQLString},
                id: {type: GraphQLString},
                origin_name: {type: GraphQLString},
                origin_id: {type: GraphQLString},
                destination_name: {type: GraphQLString},
                destination_id: {type: GraphQLString},
            },
        });

        const edgeInputType = new GraphQLInputObjectType({
            name: 'edge_input',
            fields: {
                name: {type: GraphQLString},
                id: {type: GraphQLString},
            },
        });

        // the fields on which a user can filter the graph return
        const graphInput: {[key: string]: any} = {
            root_node: {type: new GraphQLNonNull(GraphQLString)}, // root node must be specified
            node_type: {type: nodeInputType},
            edge_type: {type: edgeInputType},
            depth: {type: new GraphQLNonNull(GraphQLString)}, // depth must be specified
        };

        const graphType = new GraphQLList(
            new GraphQLObjectType({
                name: 'graph_type',
                fields: {
                    // For more advanced querying these may become an Object type of their own
                    // to retrieve only specific properties from origin, edge and destination.
                    // For now json will do.
                    origin_properties: {type: GraphQLJSON},
                    edge_properties: {type: GraphQLJSON},
                    destination_properties: {type: GraphQLJSON},
                    // origin data
                    origin_id: {type: GraphQLString},
                    origin_metatype_name: {type: GraphQLString},
                    origin_metatype_id: {type: GraphQLString},
                    origin_data_source: {type: GraphQLString},
                    origin_metadata: {type: GraphQLJSON},
                    origin_created_by: {type: GraphQLString},
                    origin_created_at: {type: GraphQLString},
                    origin_modified_by: {type: GraphQLString},
                    origin_modified_at: {type: GraphQLString},
                    // edge data
                    edge_id: {type: GraphQLString},
                    relationship_name: {type: GraphQLString},
                    relationship_pair_id: {type: GraphQLString},
                    relationship_id: {type: GraphQLString},
                    edge_data_source: {type: GraphQLString},
                    edge_metadata: {type: GraphQLJSON},
                    edge_created_by: {type: GraphQLString},
                    edge_created_at: {type: GraphQLString},
                    edge_modified_by: {type: GraphQLString},
                    edge_modified_at: {type: GraphQLString},
                    // destination data
                    destination_id: {type: GraphQLString},
                    destination_metatype_name: {type: GraphQLString},
                    destination_metatype_id: {type: GraphQLString},
                    destination_data_source: {type: GraphQLString},
                    destination_metadata: {type: GraphQLJSON},
                    destination_created_by: {type: GraphQLString},
                    destination_created_at: {type: GraphQLString},
                    destination_modified_by: {type: GraphQLString},
                    destination_modified_at: {type: GraphQLString},
                    // graph metadata
                    depth: {type: GraphQLInt},
                    path: {type: GraphQLList(GraphQLString)},
                },
            }),
        );

        const fields: {[key: string]: any} = {};

        if (Object.keys(metatypeGraphQLObjects).length > 0) {
            fields.metatypes = {
                type: metatypeObjects,
                resolve: () => {
                    return metatypeGraphQLObjects;
                },
            };
        }

        if (Object.keys(relationshipGraphQLObjects).length > 0) {
            fields.relationships = {
                type: relationshipObjects,
                resolve: () => {
                    return relationshipGraphQLObjects;
                },
            };
        }

        fields.graph = {
            args: {...graphInput},
            type: (options.returnFile) ? fileInfo : graphType,
            resolve: this.resolverForGraph(containerID, options) as any,
        };

        return Promise.resolve(
            Result.Success(
                new GraphQLSchema({
                    query: new GraphQLObjectType({
                        name: 'Query',
                        fields,
                    }),
                }),
            ),
        );
    }
Example #27
Source File: schema.ts    From Deep-Lynx with MIT License 4 votes vote down vote up
async ForNode(nodeID: string, options: ResolverOptions): Promise<Result<GraphQLSchema>> {
        // first we declare the needed,nested graphql types for record info
        const recordInputType = new GraphQLInputObjectType({
            name: 'record_input',
            fields: {
                limit: {type: GraphQLInt, defaultValue: Config.limit_default},
                page: {type: GraphQLInt},
                sortBy: {type: GraphQLString},
                sortDesc: {type: GraphQLBoolean},
            },
        });

        const recordInfo = new GraphQLObjectType({
            name: 'recordInfo',
            fields: {
                nodes: {type: new GraphQLList(GraphQLString)},
                metadata: {type: GraphQLJSON},
                count: {type: GraphQLInt},
                page: {type: GraphQLInt},
            },
        });

        // needed when a user decides they want the results as a file vs. a raw return
        const fileInfo = new GraphQLObjectType({
            name: 'fileInfo',
            fields: {
                id: {type: GraphQLString},
                file_name: {type: GraphQLString},
                file_size: {type: GraphQLFloat},
                md5hash: {type: GraphQLString},
                metadata: {type: GraphQLJSON},
                url: {type: GraphQLString},
            },
        });

        // histogram specific arguments, currently we only allow one histogram
        const histogramInputType = new GraphQLInputObjectType({
            name: 'histogram_input',
            fields: {
                column: {type: GraphQLString},
                min: {type: GraphQLInt},
                max: {type: GraphQLInt},
                nbuckets: {type: GraphQLInt},
            },
        });

        const transformationGraphQLObjects: {[key: string]: any} = {};

        // fetch all transformations that this node is associated with - these represent all timeseries tables connected
        // to the node and the data contained therein
        const nodeTransformations = await this.#nodeRepo.listTransformations(nodeID);
        if (nodeTransformations.isError) {
            return Promise.resolve(Result.Failure(`error retrieving transformations for node`));
        }

        // we don't want to fail if no tables are attached,but we can't return anything more than a blank schema basically
        if (nodeTransformations.value.length === 0) {
            return Promise.resolve(
                Result.Success(
                    new GraphQLSchema({
                        query: new GraphQLObjectType({
                            name: 'Query',
                            fields: {},
                        }),
                    }),
                ),
            );
        }

        // loop through the transformations, ignoring if they're not timeseries, and building a GraphQL object if they
        // are
        const transformations = await this.#transformationMapper.ListFromIDs(nodeTransformations.value.map((t) => t.transformation_id!));
        if (transformations.isError) {
            return Promise.resolve(Result.Failure(`unable to list transformations for node ${transformations.error?.error}`));
        }

        transformations.value.forEach((transformation, index) => {
            let name = transformation.name;
            if (!name) {
                name = `z_${transformation.id}`;
            }

            // if this object already exists, we need to append a counter onto the name, make it the index so we don't
            // have to do a recursive check
            if (transformationGraphQLObjects[stringToValidPropertyName(name)]) {
                name = `${name}_${index}`;
            }

            transformationGraphQLObjects[stringToValidPropertyName(name)] = {
                args: {
                    _record: {type: recordInputType},
                    _histogram: {type: histogramInputType},
                    ...this.inputFieldsForTransformation(transformation),
                },
                description: `Timeseries data from transformation ${name}`,
                type:(options.returnFile) ? fileInfo : new GraphQLList(
                    new GraphQLObjectType({
                        name,
                        // needed because the return type accepts an object, but throws a fit about it
                        // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                        // @ts-ignore
                        fields: () => {
                            const output: {[key: string]: {[key: string]: GraphQLNamedType | GraphQLList<any>}} = {};
                            output._record = {type: recordInfo};

                            transformation.keys.forEach((keyMapping) => {
                                // if we're not a column mapping we need to skip to so we don't pollute the object
                                if (!keyMapping.column_name || !keyMapping.value_type) {
                                    return;
                                }

                                const propertyName = stringToValidPropertyName(keyMapping.column_name);
                                switch (keyMapping.value_type) {
                                    // because we have no specification on our internal number type, we
                                    // must set this as a float for now
                                    case 'number': {
                                        output[propertyName] = {
                                            type: GraphQLInt,
                                        };
                                        break;
                                    }

                                    case 'float': {
                                        output[propertyName] = {
                                            type: GraphQLFloat,
                                        };
                                        break;
                                    }

                                    case 'number64' || 'float64': {
                                        output[propertyName] = {
                                            type: GraphQLString,
                                        };
                                        break;
                                    }

                                    case 'boolean': {
                                        output[propertyName] = {
                                            type: GraphQLBoolean,
                                        };
                                        break;
                                    }

                                    case 'string' || 'date' || 'file': {
                                        output[propertyName] = {
                                            type: GraphQLString,
                                        };
                                        break;
                                    }

                                    case 'list': {
                                        output[propertyName] = {
                                            type: new GraphQLList(GraphQLJSON),
                                        };
                                        break;
                                    }

                                    default: {
                                        output[propertyName] = {
                                            type: GraphQLString,
                                        };
                                    }
                                }
                            });

                            return output;
                        },
                    }),
                ),
                resolve: this.resolverForNode(transformation, options),
            };
        });

        return Promise.resolve(
            Result.Success(
                new GraphQLSchema({
                    query: new GraphQLObjectType({
                        name: 'Query',
                        fields: transformationGraphQLObjects,
                    }),
                }),
            ),
        );
    }
Example #28
Source File: schema.ts    From peterportal-public-api with MIT License 4 votes vote down vote up
queryType: GraphQLObjectType = new GraphQLObjectType({
  name: 'Query',
  fields: () =>  ({

    // query course by ID
    course: {
      type: courseType,

      // specify args to query by
      args: {
        id: { type: new GraphQLNonNull(GraphQLString), description: "Course Department concatenated with Course Number. Ex: COMPSCI161" }
      },

      // define function to get a course
      resolve: (_, {id}) => {
        return getCourse(id);
      },

      // documentation
      description: "Search courses by their course id. Ex: COMPSCI161"
    },

    // get instructor by ucinetid
    instructor: {
      type: instructorType,

      // specify args to query by (ucinetid)
      args: {
        ucinetid: { type: new GraphQLNonNull(GraphQLString), description: "ucinetid of a specific instructor. Ex: mikes"}
      },

      // define function to get a instructor
      resolve: (_, {ucinetid}) => {
        return getInstructor(ucinetid);
      },

      // documentation for instructor
      description: "Search instructors by their ucinetid"
    },

    // return all courses
    allCourses: {
      type: new GraphQLList(courseType),

      // get all courses from courses cache
      resolve: () => {
        return getAllCourses()
      },

      // documentation for all courses
      description: "Return all courses. Takes no arguments"
    },

    // return all instructor
    allInstructors: {
      type: new GraphQLList(instructorType),

      // get all instructors from cache
      resolve: () => {
        return getAllInstructors();
      },

      // documentation for all instructors
      description: "Return all instructors. Takes no arguments"
    },

    schedule: {
      type: new GraphQLList(courseOfferingType),

      args: {
        year: { type: new GraphQLNonNull(GraphQLFloat), description: "Year of the term. Required." },
        quarter: { type: new GraphQLNonNull(GraphQLString), description: "Quarter of the term. ['Fall'|'Winter'|'Spring'|'Summer1'|'Summer2'|'Summer10wk']. Required." },
        ge: { type: GraphQLString, description: "GE type. ['ANY'|'GE-1A'|'GE-1B'|'GE-2'|'GE-3'|'GE-4'|'GE-5A'|'GE-5B'|'GE-6'|'GE-7'|'GE-8']." },
        department: { type: GraphQLString, description: "Department Code." },
        course_number: { type: GraphQLString, description: "Course number or range. Ex: '32A' or '31-33'." },
        division: { type: GraphQLString, description: "Division level of a course. ['ALL'|'LowerDiv'|'UpperDiv'|'Graduate']. Default: 'ALL'." },
        section_codes: { type: GraphQLString, description: "5-digit course code or range. Ex: '36531' or '36520-36536'." },
        instructor: { type: GraphQLString, description: "Instructor last name or part of last name." },
        course_title: { type: GraphQLString, description: "Title of a course." },
        section_type: { type: GraphQLString, description: "Type of section. ['ALL'|'ACT'|'COL'|'DIS'|'FLD'|'LAB'|'LEC'|'QIZ'|'RES'|'SEM'|'STU'|'TAP'|'TUT']. Default: 'ALL'." },
        units: { type: GraphQLString, description: "Unit count of a course. Ex: '4' or '1.3'. Use 'VAR' to look for variable unit classes." },
        days: { type: GraphQLString, description: "Days that a course is offered. Any combination of ['Su'|'M'|'T'|'W'|'Th'|'F'|'Sa']. Ex: 'MWF'." },
        start_time: { type: GraphQLString, description: "Start time of a couse in 12 hour format. Ex: '10:00AM' or '5:00PM'" },
        end_time: { type: GraphQLString, description: "End time of a couse in 12 hour format. Ex: '10:00AM' or '5:00PM'" },
        max_capacity: { type: GraphQLString, description: "Maximum enrollment capacity of a choice. Specify a " },
        full_courses: { type: GraphQLString, description: "Status of a course's enrollment state. ['ANY'|'SkipFullWaitlist'|'FullOnly'|'OverEnrolled']. Default: 'ANY'." },
        cancelled_courses: { type: GraphQLString, description: "Indicate whether to ['Exclude'|'Include'|'Only'] cancelled courses. Default: 'EXCLUDE'." },
        building: { type: GraphQLString, description: "Building code found on https://www.reg.uci.edu/addl/campus/." },
        room: { type: GraphQLString, description: "Room number in a building. Must also specify a building code." }
      },

      resolve: async (_, args) => {
        validateScheduleArgs(args);
        const query = scheduleArgsToQuery(args);
        const results: CourseOffering[] = await getCourseSchedules(query);
        return results;
      },

      description: "Return schedule from websoc."
    },

    // return week of 'date' argument or current week if 'date' is empty.
    week: {
      type: weekType,
      //date argument
      args: {
        year: { type: GraphQLString, description: "Must be in ISO 8601 extended format `YYYY`. "},
        month: { type: GraphQLString, description: "Must be in ISO 8601 extended format `MM`. "},
        day: { type: GraphQLString, description: "Must be in ISO 8601 extended format `DD`. "}
      },

      //calls getWeek(), fetching from UCI's academic calendar
      resolve: async (_, {year, month, day}) => {
        try{
          return await getWeek(year, month, day);
        }
        catch(e) {
          throw new ValidationError("Invalid year, month or day. Must include all year, month and day or none.")
        }
      },
      description: "Returns the week and quarter, given a specific date. No parameters given uses today's date. Must include all year, month and day or none. "
    },

    grades: {
      type: gradeDistributionCollectionType,

      args: {
        year: { type: GraphQLString, description: "Must be <START_YEAR>-<END_YEAR>. Ex. 2020-2021. Multiple values in the arguments can be included by using ; as a separator."},
        quarter: { type: GraphQLString, description: "Fall | Winter | Spring | Summer Multiple values in the arguments can be included by using ; as a separator."},
        instructor: { type: GraphQLString, description: "Instructor, must following the format (<last_name>, <first_initial>.) Multiple values in the arguments can be included by using ; as a separator."},
        department: { type: GraphQLString, description: "Department short-hand. Ex. COMPSCI. Multiple values in the arguments can be included by using ; as a separator."},
        number: { type: GraphQLString, description: "Course number. Multiple values in the arguments can be included by using ; as a separator."},
        code: { type: GraphQLString, description: "5-digit course code on WebSoC. Multiple values in the arguments can be included by using ; as a separator." }, 
        division: { type: GraphQLString, description: "Filter by Course Level ('LowerDiv'|'UpperDiv')"},
        excludePNP: { type: GraphQLBoolean, description: "Exclude P/NP Only courses" }
      },

      resolve: (_, args, __, info) => {
        // Get the fields requested in the query
        // This allows us to only fetch what the client wants from sql
        const requestedFields : string[] = Object.keys(parseResolveInfo(info).fieldsByTypeName.GradeDistributionCollection)
      
        // Construct a WHERE clause from the arguments
        const where : WhereParams = parseGradesParamsToSQL(args);
        
        // If requested, retrieve the grade distributions
        let grade_distributions: GradeGQLData[];
        let rawGrades : GradeRawData = undefined;
        if (requestedFields.includes('grade_distributions')) {
          rawGrades = fetchGrades(where)

          // Format the results to GraphQL
          grade_distributions = rawGrades.map(result => {
            return {
              grade_a_count: result.gradeACount,
              grade_b_count: result.gradeBCount,
              grade_c_count: result.gradeCCount,
              grade_d_count: result.gradeDCount,
              grade_f_count: result.gradeFCount,
              grade_p_count: result.gradePCount,
              grade_np_count: result.gradeNPCount,
              grade_w_count: result.gradeWCount,
              average_gpa: result.averageGPA ?? null,
              course_offering: {
                year: result.year,
                quarter: result.quarter,
                section: {
                  code: result.code,
                  number: result.section,
                  type: result.type,
                },
                instructors: [result.instructor],
                course: {
                  id: result.department.replace(/\s/g, '')+result.number,
                  department: result.department,
                  number: result.number,
                  department_name: result.department_name,
                  title: result.title
                }
              }
            }
          })
        }
        
        // If requested, retrieve the aggregate
        let aggregate : GradeDistAggregate = undefined;
        if (requestedFields.includes('aggregate')) {
          const aggregateResult = fetchAggregatedGrades(where)
      
          // Format results to GraphQL
          aggregate = {
            sum_grade_a_count: aggregateResult['sum_grade_a_count'],
            sum_grade_b_count: aggregateResult['sum_grade_b_count'],
            sum_grade_c_count: aggregateResult['sum_grade_c_count'],
            sum_grade_d_count: aggregateResult['sum_grade_d_count'],
            sum_grade_f_count: aggregateResult['sum_grade_f_count'],
            sum_grade_p_count: aggregateResult['sum_grade_p_count'],
            sum_grade_np_count: aggregateResult['sum_grade_np_count'],
            sum_grade_w_count: aggregateResult['sum_grade_w_count'],
            average_gpa: aggregateResult['average_gpa'],
            count: aggregateResult['count']
          }
        }

        // If requested, retrieve the instructors
        let instructors : string[] = undefined;
        if (requestedFields.includes('instructors')) {
          if (rawGrades) {
            // If the grade results exist, we can get the instructors from there
            instructors = [...new Set(rawGrades.map(result => result.instructor))]
          } else {
            // Else query sql for the instructors
            instructors = fetchInstructors(where)
          }
        }
        
        // Return results
        return {
          aggregate,
          grade_distributions,
          instructors
        }
      },

      description: "Search for grade distributions. Multiple values in the arguments can be included by using ; as a separator. "
    }
  })
})
Example #29
Source File: helpers.ts    From amplify-codegen with Apache License 2.0 4 votes vote down vote up
describe('Flow typeAnnotationFromGraphQLType', () => {
  test('String', () => {
    expect(typeAnnotationFromGraphQLType(GraphQLString)).toMatchObject(t.nullableTypeAnnotation(t.stringTypeAnnotation()));
  });

  test('Int', () => {
    expect(typeAnnotationFromGraphQLType(GraphQLInt)).toMatchObject(t.nullableTypeAnnotation(t.numberTypeAnnotation()));
  });

  test('Float', () => {
    expect(typeAnnotationFromGraphQLType(GraphQLFloat)).toMatchObject(t.nullableTypeAnnotation(t.numberTypeAnnotation()));
  });

  test('Boolean', () => {
    expect(typeAnnotationFromGraphQLType(GraphQLBoolean)).toMatchObject(t.nullableTypeAnnotation(t.booleanTypeAnnotation()));
  });

  test('ID', () => {
    expect(typeAnnotationFromGraphQLType(GraphQLID)).toMatchObject(t.nullableTypeAnnotation(t.stringTypeAnnotation()));
  });

  test('String!', () => {
    expect(typeAnnotationFromGraphQLType(new GraphQLNonNull(GraphQLString))).toMatchObject(t.stringTypeAnnotation());
  });

  test('Int!', () => {
    expect(typeAnnotationFromGraphQLType(new GraphQLNonNull(GraphQLInt))).toMatchObject(t.numberTypeAnnotation());
  });

  test('Float!', () => {
    expect(typeAnnotationFromGraphQLType(new GraphQLNonNull(GraphQLFloat))).toMatchObject(t.numberTypeAnnotation());
  });

  test('Boolean!', () => {
    expect(typeAnnotationFromGraphQLType(new GraphQLNonNull(GraphQLBoolean))).toMatchObject(t.booleanTypeAnnotation());
  });

  test('ID!', () => {
    expect(typeAnnotationFromGraphQLType(new GraphQLNonNull(GraphQLID))).toMatchObject(t.stringTypeAnnotation());
  });

  // TODO: Test GenericTypeAnnotation

  test('[String]', () => {
    expect(typeAnnotationFromGraphQLType(new GraphQLList(GraphQLString))).toMatchObject(
      t.nullableTypeAnnotation(t.arrayTypeAnnotation(t.nullableTypeAnnotation(t.stringTypeAnnotation())))
    );
  });

  test('[Int]', () => {
    expect(typeAnnotationFromGraphQLType(new GraphQLList(GraphQLInt))).toMatchObject(
      t.nullableTypeAnnotation(t.arrayTypeAnnotation(t.nullableTypeAnnotation(t.numberTypeAnnotation())))
    );
  });

  test('[Float]', () => {
    expect(typeAnnotationFromGraphQLType(new GraphQLList(GraphQLFloat))).toMatchObject(
      t.nullableTypeAnnotation(t.arrayTypeAnnotation(t.nullableTypeAnnotation(t.numberTypeAnnotation())))
    );
  });

  test('[Boolean]', () => {
    expect(typeAnnotationFromGraphQLType(new GraphQLList(GraphQLBoolean))).toMatchObject(
      t.nullableTypeAnnotation(t.arrayTypeAnnotation(t.nullableTypeAnnotation(t.booleanTypeAnnotation())))
    );
  });

  test('[ID]', () => {
    expect(typeAnnotationFromGraphQLType(new GraphQLList(GraphQLID))).toMatchObject(
      t.nullableTypeAnnotation(t.arrayTypeAnnotation(t.nullableTypeAnnotation(t.stringTypeAnnotation())))
    );
  });

  test('[String]!', () => {
    expect(typeAnnotationFromGraphQLType(new GraphQLNonNull(new GraphQLList(GraphQLString)))).toMatchObject(
      t.arrayTypeAnnotation(t.nullableTypeAnnotation(t.stringTypeAnnotation()))
    );
  });

  test('[Int]!', () => {
    expect(typeAnnotationFromGraphQLType(new GraphQLNonNull(new GraphQLList(GraphQLInt)))).toMatchObject(
      t.arrayTypeAnnotation(t.nullableTypeAnnotation(t.numberTypeAnnotation()))
    );
  });
  test('[Float]!', () => {
    expect(typeAnnotationFromGraphQLType(new GraphQLNonNull(new GraphQLList(GraphQLFloat)))).toMatchObject(
      t.arrayTypeAnnotation(t.nullableTypeAnnotation(t.numberTypeAnnotation()))
    );
  });

  test('[Boolean]!', () => {
    expect(typeAnnotationFromGraphQLType(new GraphQLNonNull(new GraphQLList(GraphQLBoolean)))).toMatchObject(
      t.arrayTypeAnnotation(t.nullableTypeAnnotation(t.booleanTypeAnnotation()))
    );
  });

  test('[ID]!', () => {
    expect(typeAnnotationFromGraphQLType(new GraphQLNonNull(new GraphQLList(GraphQLID)))).toMatchObject(
      t.arrayTypeAnnotation(t.nullableTypeAnnotation(t.stringTypeAnnotation()))
    );
  });

  test('[String!]', () => {
    expect(typeAnnotationFromGraphQLType(new GraphQLList(new GraphQLNonNull(GraphQLString)))).toMatchObject(
      t.nullableTypeAnnotation(t.arrayTypeAnnotation(t.stringTypeAnnotation()))
    );
  });

  test('[Int!]', () => {
    expect(typeAnnotationFromGraphQLType(new GraphQLList(new GraphQLNonNull(GraphQLInt)))).toMatchObject(
      t.nullableTypeAnnotation(t.arrayTypeAnnotation(t.numberTypeAnnotation()))
    );
  });

  test('[Float!]', () => {
    expect(typeAnnotationFromGraphQLType(new GraphQLList(new GraphQLNonNull(GraphQLFloat)))).toMatchObject(
      t.nullableTypeAnnotation(t.arrayTypeAnnotation(t.numberTypeAnnotation()))
    );
  });

  test('[Boolean!]', () => {
    expect(typeAnnotationFromGraphQLType(new GraphQLList(new GraphQLNonNull(GraphQLBoolean)))).toMatchObject(
      t.nullableTypeAnnotation(t.arrayTypeAnnotation(t.booleanTypeAnnotation()))
    );
  });

  test('[ID!]', () => {
    expect(typeAnnotationFromGraphQLType(new GraphQLList(new GraphQLNonNull(GraphQLID)))).toMatchObject(
      t.nullableTypeAnnotation(t.arrayTypeAnnotation(t.stringTypeAnnotation()))
    );
  });

  test('[String!]!', () => {
    expect(typeAnnotationFromGraphQLType(new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(GraphQLString))))).toMatchObject(
      t.arrayTypeAnnotation(t.stringTypeAnnotation())
    );
  });

  test('[Int!]!', () => {
    expect(typeAnnotationFromGraphQLType(new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(GraphQLInt))))).toMatchObject(
      t.arrayTypeAnnotation(t.numberTypeAnnotation())
    );
  });

  test('[Float!]!', () => {
    expect(typeAnnotationFromGraphQLType(new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(GraphQLFloat))))).toMatchObject(
      t.arrayTypeAnnotation(t.numberTypeAnnotation())
    );
  });

  test('[Boolean!]!', () => {
    expect(typeAnnotationFromGraphQLType(new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(GraphQLBoolean))))).toMatchObject(
      t.arrayTypeAnnotation(t.booleanTypeAnnotation())
    );
  });

  test('[ID!]!', () => {
    expect(typeAnnotationFromGraphQLType(new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(GraphQLID))))).toMatchObject(
      t.arrayTypeAnnotation(t.stringTypeAnnotation())
    );
  });

  test('[[String]]', () => {
    expect(typeAnnotationFromGraphQLType(new GraphQLList(new GraphQLList(GraphQLString)))).toMatchObject(
      t.nullableTypeAnnotation(
        t.arrayTypeAnnotation(t.nullableTypeAnnotation(t.arrayTypeAnnotation(t.nullableTypeAnnotation(t.stringTypeAnnotation()))))
      )
    );
  });

  test('[[String]]!', () => {
    expect(typeAnnotationFromGraphQLType(new GraphQLNonNull(new GraphQLList(new GraphQLList(GraphQLString))))).toMatchObject(
      t.arrayTypeAnnotation(t.nullableTypeAnnotation(t.arrayTypeAnnotation(t.nullableTypeAnnotation(t.stringTypeAnnotation()))))
    );
  });

  test('Custom Scalar', () => {
    const OddType = new GraphQLScalarType({
      name: 'Odd',
      serialize(value) {
        return value % 2 === 1 ? value : null;
      },
    });

    expect(typeAnnotationFromGraphQLType(OddType)).toMatchObject(t.nullableTypeAnnotation(t.genericTypeAnnotation(t.identifier('Odd'))));
  });
});