/// <summary> /// Searches current api for true mutation container /// </summary> /// <param name="apiMutation">The mutation</param> /// <param name="apiRoot">The api root</param> /// <returns>The mutation container</returns> private static MergedField FindContainer(ApiMutation apiMutation, MergedApiRoot apiRoot) { var path = apiMutation.Name.Split('.').ToList(); path.RemoveAt(path.Count - 1); MergedObjectType type = apiRoot; MergedField field = null; var queue = new Queue <string>(path); while (queue.Count > 0) { if (type == null) { return(null); } var fieldName = queue.Dequeue(); if (!type.Fields.TryGetValue(fieldName, out field)) { return(null); } type = field.Type as MergedObjectType; } return(field); }
/// <summary> /// Merges schemes from multiple APIs /// </summary> /// <param name="providers"> /// The API providers descriptions /// </param> /// <param name="createdTypes"> /// The list of created types. /// <remarks> /// This is the mutation dictionary and will be filled during creation process /// </remarks> /// </param> /// <returns> /// Merged API /// </returns> private static MergedApiRoot MergeApis(List <ApiProvider> providers, Dictionary <string, MergedType> createdTypes) { var apiRoot = new MergedApiRoot("api"); apiRoot.AddProviders(providers.Select(p => new FieldProvider { Provider = p, FieldType = p.Description })); apiRoot.Category = providers.Count > 1 ? MergedObjectType.EnCategory.MultipleApiType : MergedObjectType.EnCategory.SingleApiType; foreach (var provider in providers) { MergeFields(apiRoot, provider.Description.Fields, provider, new List <string>(), false, createdTypes); foreach (var apiMutation in provider.Description.Mutations) { var mutationName = $"{MergedType.EscapeName(provider.Description.ApiName)}_{MergedType.EscapeName(apiMutation.Name)}"; MergedField mutation = null; switch (apiMutation.Type) { case ApiMutation.EnType.ConnectionCreate: case ApiMutation.EnType.ConnectionUpdate: case ApiMutation.EnType.ConnectionDelete: case ApiMutation.EnType.Connection: mutation = RegisterConnectionMutation(provider, apiMutation, apiRoot, createdTypes); break; case ApiMutation.EnType.Untyped: mutation = RegisterUntypedMutation(provider, apiMutation, apiRoot, createdTypes); break; } if (mutation != null) { apiRoot.Mutations[mutationName] = mutation; } } } var nodeSearcher = new NodeSearcher(apiRoot); apiRoot.NodeSearher = nodeSearcher; return(apiRoot); }
/// <summary> /// Sets the field arguments from the field metadata /// </summary> /// <param name="field">The field</param> /// <param name="fieldDescription">The field description</param> /// <param name="graphTypes">The list of end-types</param> private static void SetFieldArguments( FieldType field, MergedField fieldDescription, Dictionary <string, IGraphType> graphTypes) { var typeArguments = fieldDescription.Type.GenerateArguments(graphTypes) ?? new QueryArguments(); var fieldArguments = fieldDescription.Arguments.Select( p => new QueryArgument(typeof(VirtualInputGraphType)) { Name = p.Key, ResolvedType = GetTypeForField(p.Value, graphTypes), Description = p.Value.Description }); var resultingArguments = typeArguments.Union(fieldArguments).ToList(); if (resultingArguments.Any()) { field.Arguments = new QueryArguments(resultingArguments); } }
/// <summary> /// Insert new fields from new provider into current type /// </summary> /// <param name="parentType"> /// Field to update /// </param> /// <param name="apiFields"> /// The list of subfields from api /// </param> /// <param name="provider"> /// The api provider /// </param> /// <param name="path"> /// The types names path to avoid circular references. /// </param> /// <param name="createAsInput">A value indicating that an input type is assembled</param> /// <param name="typesCreated">The list of previously created types</param> private static void MergeFields( MergedObjectType parentType, IEnumerable <ApiField> apiFields, ApiProvider provider, ICollection <string> path, bool createAsInput, Dictionary <string, MergedType> typesCreated) { var fields = createAsInput ? apiFields.Where( f => f.Flags.HasFlag(EnFieldFlags.CanBeUsedInInput) && f.Arguments.All(a => a.Flags.HasFlag(EnFieldFlags.IsTypeArgument))) : apiFields.Where(f => f.Flags.HasFlag(EnFieldFlags.Queryable)); foreach (var apiField in fields) { MergedField complexField; if (parentType.Fields.TryGetValue(apiField.Name, out complexField)) { if (apiField.ScalarType != EnScalarType.None || createAsInput || apiField.Flags.HasFlag(EnFieldFlags.IsConnection) || apiField.Flags.HasFlag(EnFieldFlags.IsArray) || !(complexField.Type is MergedObjectType) || complexField.Arguments.Any() || apiField.Arguments.Any()) { // todo: write merge error continue; } } var flags = apiField.Flags; if (createAsInput && flags.HasFlag(EnFieldFlags.IsConnection)) { flags &= ~EnFieldFlags.IsConnection; flags |= EnFieldFlags.IsArray; } var cloneField = apiField.Clone(); cloneField.Flags = flags; var fieldType = CreateMergedType(provider, cloneField, complexField, path, createAsInput, typesCreated); if (fieldType == null) { continue; } var fieldArguments = new Dictionary <string, MergedField>(); if (!createAsInput) { foreach (var argument in apiField.Arguments) { var fieldArgumentType = CreateMergedType(provider, argument, null, path, true, typesCreated); fieldArguments[argument.Name] = new MergedField( argument.Name, fieldArgumentType, provider, apiField, argument.Flags, description: argument.Description); } } var description = string.Join( "\n", new[] { complexField?.Description, apiField.Description }.Where(s => !string.IsNullOrWhiteSpace(s))); var field = new MergedField( apiField.Name, fieldType, provider, cloneField, flags, fieldArguments, string.IsNullOrWhiteSpace(description) ? null : description); if (complexField != null) { foreach (var complexFieldProvider in complexField.Providers) { field.AddProvider( complexFieldProvider, complexField.OriginalFields[complexFieldProvider.Description.ApiName]); } } parentType.Fields[apiField.Name] = field; } }
/// <summary> /// Creates field from api description /// </summary> /// <param name="provider">The api provider</param> /// <param name="apiField">The api field description</param> /// <param name="complexField">The same field merged from previous api descriptions</param> /// <param name="path">The list of processed types</param> /// <param name="createAsInput">A value indicating that an input type is assembled</param> /// <param name="typesCreated">The list of already created types</param> /// <returns>The field description</returns> private static MergedType CreateMergedType( ApiProvider provider, ApiField apiField, MergedField complexField, ICollection <string> path, bool createAsInput, Dictionary <string, MergedType> typesCreated) { MergedType createdType; if (apiField.ScalarType != EnScalarType.None) { return(CreateScalarType(apiField.ScalarType, typesCreated)); } var apiType = provider.Description.Types.FirstOrDefault(t => t.TypeName == apiField.TypeName); if (apiType == null) { throw new Exception("type was not found"); } var apiEnumType = apiType as ApiEnumType; if (apiEnumType != null) { return(CreateEnumType(apiEnumType, provider, typesCreated)); } var apiObjectType = (ApiObjectType)apiType; if (apiField.Flags.HasFlag(EnFieldFlags.IsConnection)) { var typedArgumentNames = apiField.Arguments.Where(a => a.Flags.HasFlag(EnFieldFlags.IsTypeArgument)) .Select(f => f.Name) .ToList(); return(CreateConnectionType(apiObjectType, provider, typesCreated, typedArgumentNames)); } var objectType = (complexField?.Type as MergedObjectType)?.Clone() ?? (createAsInput ? new MergedInputType($"{provider.Description.ApiName}_{apiField.TypeName}") : new MergedObjectType($"{provider.Description.ApiName}_{apiField.TypeName}")); objectType.AddProvider(new FieldProvider { FieldType = apiObjectType, Provider = provider }); if (complexField != null) { objectType.Category = MergedObjectType.EnCategory.MultipleApiType; } if (typesCreated.TryGetValue(objectType.ComplexTypeName, out createdType)) { return(createdType); } typesCreated[objectType.ComplexTypeName] = objectType; MergeFields( objectType, apiObjectType.Fields, provider, path.Union(new[] { apiObjectType.TypeName }).ToList(), createAsInput, typesCreated); objectType.Initialize(); return(objectType); }
/// <summary> /// Gets the end-type for field /// </summary> /// <param name="field">The field description</param> /// <param name="graphTypes">The list of defined types</param> /// <returns>The field resolved type</returns> private static IGraphType GetTypeForField(MergedField field, Dictionary <string, IGraphType> graphTypes) { return(field.Flags.HasFlag(EnFieldFlags.IsArray) ? new ListGraphType(graphTypes[field.Type.ComplexTypeName]) : graphTypes[field.Type.ComplexTypeName]); }
/// <summary> /// Register the mutation /// </summary> /// <param name="provider">The mutation api provider</param> /// <param name="apiMutation">The mutation description</param> /// <param name="apiRoot">The api root</param> /// <param name="typesCreated">The list of created types</param> /// <returns>The mutation as merged field </returns> private static MergedField RegisterUntypedMutation( ApiProvider provider, ApiMutation apiMutation, MergedApiRoot apiRoot, Dictionary <string, MergedType> typesCreated) { var returnType = CreateMergedType(provider, apiMutation, null, new List <string>(), false, typesCreated); var inputType = new MergedInputType(apiMutation.Name); inputType.AddProvider( new FieldProvider { Provider = provider, FieldType = new ApiObjectType(apiMutation.Name) }); typesCreated[inputType.ComplexTypeName] = inputType; foreach (var apiField in apiMutation.Arguments) { inputType.Fields.Add( apiField.Name, new MergedField( apiField.Name, CreateMergedType(provider, apiField, null, new List <string>(), true, typesCreated), provider, apiMutation.Clone(), apiMutation.Flags, description: apiField.Description)); } inputType.Fields["clientMutationId"] = new MergedField( "clientMutationId", CreateScalarType(EnScalarType.String, typesCreated), provider, apiMutation); var arguments = new Dictionary <string, MergedField> { { "input", new MergedField( "input", inputType, provider, apiMutation) } }; var payload = new MergedUntypedMutationResult(returnType, apiRoot, provider, apiMutation); typesCreated[payload.ComplexTypeName] = payload; var untypedMutation = new MergedField( apiMutation.Name, payload, provider, apiMutation, apiMutation.Flags, arguments, apiMutation.Description); return(untypedMutation); }