public ClusterFactory(IList <TNode> allNodes, CostAnalyzer <double> costanalyzer)
 {
     this.allNodes     = allNodes;
     this.costanalyzer = costanalyzer;
     // We work with the indexes. Nodes will be flagged as part of cluster by removing them from this list.
     NodesAsIndexes = Enumerable.Range(0, allNodes.Count).ToList();
     ListOfClusters = new List <Cluster <TNode, double> >();
 }
Beispiel #2
0
    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers()
        .AddJsonOptions(options =>
        {
            // required to serialize
            options.JsonSerializerOptions.Converters
            .Add(new ObjectDictionaryConverter());
            options.JsonSerializerOptions.IgnoreNullValues = true;
        });

        // graphql
        services.AddSingleton <IChat, Data.Chat>();
        services.AddSingleton <IChatResolverService, ChatResolverService>();
        services.AddSingleton <ChatSchemas>();
        services.AddSingleton(provider => provider.GetRequiredService <ChatSchemas>().Chat);

        // configure execution options
        var tanka = services.AddTankaGraphQL()
                    .ConfigureRules(rules => rules.Concat(new[]
        {
            CostAnalyzer.MaxCost(100, 1, true)
        }).ToArray())
                    .ConfigureSchema <ISchema>(schema => new ValueTask <ISchema>(schema))
                    .ConfigureWebSockets();

        //if (Env.IsDevelopment()) tanka.AddExtension<TraceExtension>();

        // signalr server
        services.AddSignalR(options => options.EnableDetailedErrors = true)
        .AddTankaGraphQL();

        // graphql-ws websocket server
        // web socket server
        services.AddWebSockets(options =>
        {
            options.AllowedOrigins.Add("http://localhost:5000");
            options.AllowedOrigins.Add("http://localhost:3000");
        });

        // CORS is required for the graphql.samples.chat.ui React App
        services.AddCors(options =>
        {
            options.AddDefaultPolicy(policy =>
            {
                policy.WithOrigins("http://localhost:3000");
                policy.AllowAnyHeader();
                policy.AllowAnyMethod();
                policy.AllowCredentials();
                //policy.WithHeaders("X-Requested-With", "authorization");
            });
        });
    }
        private static List <int> CalculateCluster(IList <int> nodesAsIndexes, CostAnalyzer <double> costanalyzer, CostAnalyzer <double> transposedCostanalyzer, double maxCostOfNearestNeighbor, int maxNodesPerCluster)
        {
            var constructingCluster = new List <int>();

            int startIndex = nodesAsIndexes[randomizer.Next(0, nodesAsIndexes.Count - 1)];

            ExpandClusterFromStartPoint(startIndex, constructingCluster, true, nodesAsIndexes, costanalyzer, maxCostOfNearestNeighbor, maxNodesPerCluster);

            ExpandClusterFromStartPoint(startIndex, constructingCluster, false, nodesAsIndexes, transposedCostanalyzer, maxCostOfNearestNeighbor, maxNodesPerCluster);

            return(constructingCluster);
        }
Beispiel #4
0
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

            // graphql
            services.AddSingleton <IChat, Chat>();
            services.AddSingleton <IChatResolverService, ChatResolverService>();
            services.AddSingleton <ChatSchemas>();
            services.AddSingleton(provider => provider.GetRequiredService <ChatSchemas>().Chat);

            // configure execution options
            services.AddTankaSchemaOptions()
            .Configure <ISchema>((options, schema) =>
            {
                options.ValidationRules = ExecutionRules.All
                                          .Concat(new[]
                {
                    CostAnalyzer.MaxCost(maxCost: 100, defaultFieldComplexity: 1, addExtensionData: true)
                }).ToArray();

                options.GetSchema = query => new ValueTask <ISchema>(schema);
            });

            // signalr server
            services.AddSignalR(options => options.EnableDetailedErrors = true)
            // add GraphQL query streaming hub
            .AddTankaServerHubWithTracing();

            // graphql-ws websocket server
            // web socket server
            services.AddWebSockets(options =>
            {
                options.AllowedOrigins.Add("https://localhost:5000");
                options.AllowedOrigins.Add("https://localhost:3000");
            });
            services.AddTankaWebSocketServerWithTracing();

            // CORS is required for the graphql.samples.chat.ui React App
            services.AddCors(options =>
            {
                options.AddDefaultPolicy(policy =>
                {
                    policy.WithOrigins("http://localhost:3000");
                    policy.AllowAnyHeader();
                    policy.AllowAnyMethod();
                    policy.AllowCredentials();
                    policy.WithHeaders("X-Requested-With", "authorization");
                });
            });
        }
        public IList <Cluster <TNode, double> > Solve([NotNull] IList <TNode> Nodes, [NotNull] CostAnalyzer <double> costanalyzer, double maxCostOfNearestNeighbor, int maxNodesPerCluster = -1)
        {
            var clusterFactory = new ClusterFactory(Nodes, costanalyzer);

            var transposedCostMatrix = CostMatrix <double> .CreateTransposed(costanalyzer.CostMatrix);

            var transposedCostanalyzer = new CostAnalyzer <double>(transposedCostMatrix);


            // While unhandled indexes available and the loop runs for at most a certain amount of times, continue the algorithm
            for (var counter = 0; counter < Nodes.Count * maxFactorOfLoopCount && clusterFactory.NodesAsIndexes.Count > 0; counter++)
            {
                // set the current pointer to a random node index among the nodes not yet classified in a cluster
                int currentPointer = clusterFactory.NodesAsIndexes[randomizer.Next(0, clusterFactory.NodesAsIndexes.Count - 1)];

                var candidateCluster = CalculateCluster(clusterFactory.NodesAsIndexes, costanalyzer, transposedCostanalyzer, maxCostOfNearestNeighbor, maxNodesPerCluster);

                if (candidateCluster.Count > 1)
                {
                    // loop over the cluster again maybe to cut the cluster in pieces. Clusters with one item are thrown back in the pool
                    for (int indexInCluster = 0; indexInCluster < candidateCluster.Count - 1; indexInCluster++)
                    {
                        var maxCostInCluster = indexInCluster == 0 ?
                                               costanalyzer.AllNeighborsOrdered(candidateCluster[0])[candidateCluster[1]]
                            : candidateCluster.Take(indexInCluster + 1).Tuples().Max(t => costanalyzer.AllNeighborsOrdered(t.Item1)[t.Item2]);

                        var nodesOutsideClusterQuiteCloseToCluster = costanalyzer.AllNeighborsOrdered(candidateCluster[indexInCluster])
                                                                     .Where(neighbor =>
                                                                            !candidateCluster.Contains(neighbor.Key) && neighbor.Value <= maxCostInCluster);

                        if (nodesOutsideClusterQuiteCloseToCluster.Count() > 1) // Split the cluster
                        {
                            var firstPart = candidateCluster.GetRange(0, indexInCluster + 1);

                            clusterFactory.CreateCluster(firstPart);

                            candidateCluster.RemoveRange(0, indexInCluster + 1);
                            indexInCluster = -1;
                        }
                    }
                }

                clusterFactory.CreateCluster(candidateCluster);
            }

            // Here we add the singleton clusters from the remaining nodes
            clusterFactory.CreateSingletonClusters();

            return(clusterFactory.ListOfClusters);
        }
Beispiel #6
0
        public void Cost_below_max_cost_with_with_costDirective_and_multiplier()
        {
            /* Given */
            var document = Parser.ParseDocument(
                @"{
                    withMultiplier
                }");

            /* When */
            var result = Validate(
                document,
                CostAnalyzer.MaxCost(
                    maxCost: 3,
                    defaultFieldComplexity: 0)
                );

            /* Then */
            Assert.True(result.IsValid);
        }
Beispiel #7
0
        public void Cost_below_max_cost_with_defaultComplexity()
        {
            /* Given */
            var document = Parser.ParseDocument(
                @"{
                    default
                }");

            /* When */
            var result = Validate(
                document,
                CostAnalyzer.MaxCost(
                    maxCost: 1,
                    defaultFieldComplexity: 1)
                );

            /* Then */
            Assert.True(result.IsValid);
        }
Beispiel #8
0
    public void Cost_below_max_cost_with_with_costDirective_and_multiplier()
    {
        /* Given */
        var document =
            @"{
                    withMultiplier
                }";

        /* When */
        var result = Validate(
            document,
            CostAnalyzer.MaxCost(
                3,
                0)
            );

        /* Then */
        Assert.True(result.IsValid);
    }
Beispiel #9
0
    public void Cost_below_max_cost_with_defaultComplexity()
    {
        /* Given */
        var document =
            @"{
                    default
                }";

        /* When */
        var result = Validate(
            document,
            CostAnalyzer.MaxCost(
                1,
                1)
            );

        /* Then */
        Assert.True(result.IsValid);
    }
Beispiel #10
0
        public void Cost_above_max_cost_with_costDirective_and_multiplier()
        {
            /* Given */
            var document = Parser.ParseDocument(
                @"{
                    withMultiplier(count: 5)
                }");

            /* When */
            var result = Validate(
                document,
                CostAnalyzer.MaxCost(
                    maxCost: 5,
                    defaultFieldComplexity: 0)
                );

            /* Then */
            Assert.False(result.IsValid);
            Assert.Single(
                result.Errors,
                error => error.Code == "MAX_COST");
        }
Beispiel #11
0
        public void Cost_above_max_cost_with_defaultComplexity()
        {
            /* Given */
            var document = Parser.ParseDocument(
                @"{
                    default
                }");

            /* When */
            var result = Validate(
                document,
                CostAnalyzer.MaxCost(
                    maxCost: 0,
                    defaultFieldComplexity: 1)
                );

            /* Then */
            Assert.False(result.IsValid);
            Assert.Single(
                result.Errors,
                error => error.Code == "MAX_COST");
        }
Beispiel #12
0
    public void Cost_above_max_cost_with_costDirective()
    {
        /* Given */
        var document =
            @"{
                    withCost
                }";

        /* When */
        var result = Validate(
            document,
            CostAnalyzer.MaxCost(
                0,
                0)
            );

        /* Then */
        Assert.False(result.IsValid);
        Assert.Single(
            result.Errors,
            error => error.Code == "MAX_COST");
    }
Beispiel #13
0
        public void ConfigureServices(IServiceCollection services)
        {
            JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
            JwtSecurityTokenHandler.DefaultOutboundClaimTypeMap.Clear();
            AddForwardedHeaders(services);

            services.AddMemoryCache();

            // signalr authentication
            services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme    = JwtBearerDefaults.AuthenticationScheme;
            }).AddJwtBearer(options =>
            {
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    NameClaimType = "sub",
                    RoleClaimType = "role"
                };
                options.Authority = Configuration["JWT:Authority"];
                options.Audience  = Configuration["JWT:Audience"];
                options.SaveToken = true;

                // We have to hook the OnMessageReceived event in order to
                // allow the JWT authentication handler to read the access
                // token from the query string when a WebSocket or
                // Server-Sent Events request comes in.
                options.Events = new JwtBearerEvents
                {
                    OnMessageReceived = context =>
                    {
                        var accessToken = context.Request.Query["access_token"];

                        // Read the token out of the query string
                        context.Token = accessToken;
                        return(Task.CompletedTask);
                    },
                    OnTokenValidated = async context =>
                    {
                        if (context.SecurityToken is JwtSecurityToken jwt)
                        {
                            /*
                             *  PERFORMANCE/SECURITY
                             *
                             *  For performance reasons we're caching the results of the userInfo request.
                             *
                             *  This is NOT production quality and you should carefully think when using
                             *  it in your application
                             */
                            var cache = context.HttpContext.RequestServices.GetRequiredService <IMemoryCache>();
                            var key   = jwt.Claims.Single(c => c.Type == "sub").Value;
                            var userInfoClaimsIdentity = await cache
                                                         .GetOrCreateAsync(key, async entry =>
                            {
                                entry.SetAbsoluteExpiration(jwt.ValidTo - TimeSpan.FromSeconds(5));

                                var configuration = await context.Options.ConfigurationManager
                                                    .GetConfigurationAsync(CancellationToken.None);

                                var userInfoEndpoint = configuration.UserInfoEndpoint;
                                var client           = new HttpClient();
                                var userInfo         = await client.GetUserInfoAsync(new UserInfoRequest
                                {
                                    Address = userInfoEndpoint,
                                    Token   = jwt.RawData
                                });

                                if (userInfo.IsError)
                                {
                                    return(new ClaimsIdentity());
                                }

                                return(new ClaimsIdentity(userInfo.Claims));
                            });

                            context.Principal.AddIdentity(userInfoClaimsIdentity);
                        }
                    }
                };
            });

            services.AddAuthorization(options =>
                                      options.AddPolicy("authorize", policy => policy.RequireAuthenticatedUser()));

            services.AddHttpContextAccessor();

            // add domain
            services.AddSingleton <Messages>();

            // add schema
            services.AddSchemaControllers()
            .AddQueryController <QueryController>()
            .AddMutationController <MutationController>()
            .AddSubscriptionController <SubscriptionController>()
            .AddMessageController <MessageController>();

            services.AddMemoryCache();
            services.AddSingleton <SchemaCache>();

            services.AddTankaGraphQL()
            .ConfigureSchema <SchemaCache>(async cache => await cache.GetOrAdd())
            .ConfigureRules(rules => rules.Concat(new[]
            {
                CostAnalyzer.MaxCost(
                    100
                    )
            }).ToArray());

            // add signalr
            services.AddSignalR(options => { options.EnableDetailedErrors = true; })
            .AddTankaGraphQL();
        }
Beispiel #14
0
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers()
            .AddJsonOptions(options =>
            {
                // required to serialize
                options.JsonSerializerOptions.Converters
                .Add(new ObjectDictionaryConverter());
            });

            AddForwardedHeaders(services);

            // signalr authentication
            services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme    = JwtBearerDefaults.AuthenticationScheme;
            }).AddJwtBearer(options =>
            {
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    NameClaimType = "sub",
                    RoleClaimType = "role"
                };
                options.Authority = Configuration["JWT:Authority"];
                options.Audience  = Configuration["JWT:Audience"];
                options.SaveToken = true;

                // We have to hook the OnMessageReceived event in order to
                // allow the JWT authentication handler to read the access
                // token from the query string when a WebSocket or
                // Server-Sent Events request comes in.
                options.Events = new JwtBearerEvents
                {
                    OnMessageReceived = context =>
                    {
                        var accessToken = context.Request.Query["access_token"]
                                          .ToString();

                        if (string.IsNullOrEmpty(accessToken))
                        {
                            accessToken = context.Request
                                          .Headers["Authorization"]
                                          .ToString()
                                          .Replace("Bearer ", string.Empty);
                        }


                        // Read the token out of the query string
                        context.Token = accessToken;
                        return(Task.CompletedTask);
                    }
                };
            });

            services.AddAuthorization(options =>
                                      options.AddPolicy("authorize", policy => policy.RequireAuthenticatedUser()));

            services.AddHttpContextAccessor();

            // add domain
            services.AddSingleton <Channels>();

            // add schema
            services.AddSchemaControllers()
            .AddQueryController <QueryController>()
            .AddChannelController <ChannelController>();

            services.AddMemoryCache();
            services.AddSingleton <SchemaCache>();

            services.AddTankaGraphQL()
            .ConfigureSchema <SchemaCache>(async cache => await cache.GetOrAdd())
            .ConfigureRules(rules => rules.Concat(new[]
            {
                CostAnalyzer.MaxCost(
                    100
                    )
            }).ToArray());

            // add signalr
            services.AddSignalR(options => { options.EnableDetailedErrors = true; })
            .AddTankaGraphQL();
        }
Beispiel #15
0
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            AddForwardedHeaders(services);

            JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
            JwtSecurityTokenHandler.DefaultOutboundClaimTypeMap.Clear();

            services.AddCors(cors =>
            {
                cors.AddDefaultPolicy(policy =>
                {
                    policy.SetIsOriginAllowed(origin => true);
                    policy.AllowAnyHeader();
                    policy.AllowAnyMethod();
                    policy.AllowCredentials();
                });
            });

            // signalr authentication
            services.AddAuthentication()
            .AddJwtBearer(options =>
            {
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    NameClaimType = "sub",
                    RoleClaimType = "role"
                };

                options.Authority = Configuration["JWT:Authority"];
                options.Audience  = Configuration["JWT:Audience"];
                options.SaveToken = false;

                // We have to hook the OnMessageReceived event in order to
                // allow the JWT authentication handler to read the access
                // token from the query string when a WebSocket or
                // Server-Sent Events request comes in.
                options.Events = new JwtBearerEvents
                {
                    OnMessageReceived = context =>
                    {
                        var accessToken = context.Request.Query["access_token"].ToString();

                        if (string.IsNullOrEmpty(accessToken))
                        {
                            var messageContext = context.HttpContext
                                                 .RequestServices.GetRequiredService <IMessageContextAccessor>();

                            var message = messageContext.Context?.Message;

                            if (message?.Type == MessageType.GQL_CONNECTION_INIT)
                            {
                                if (messageContext.Context?.Message.Payload is Dictionary <string, object> payload &&
                                    payload.ContainsKey("authorization"))
                                {
                                    accessToken = payload["authorization"].ToString() !;
                                }
                            }
                        }

                        // Read the token out of the query string
                        context.Token = accessToken;
                        return(Task.CompletedTask);
                    },
                    OnTokenValidated = context =>
                    {
                        if (context.SecurityToken is JwtSecurityToken jwt)
                        {
                            var tokenIdentity = new ClaimsIdentity(
                                new[]
                            {
                                new Claim("access_token", jwt.RawData)
                            });

                            context.Principal?.AddIdentity(tokenIdentity);
                        }

                        return(Task.CompletedTask);
                    }
                };
            });

            services.AddAuthorization(options =>
                                      options.AddPolicy("authorize",
                                                        policy => policy
                                                        .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
                                                        .RequireAuthenticatedUser()));

            services.AddHttpContextAccessor();

            // add schema cache
            services.AddSingleton <SchemaCache>();
            services.AddMemoryCache();

            // add execution options
            services.AddTankaGraphQL()
            .ConfigureSchema <SchemaCache>(async cache => await cache.GetOrAdd())
            .ConfigureRules <ILogger <Startup> >((rules, logger) => rules.Concat(new[]
            {
                CostAnalyzer.MaxCost(
                    100,
                    1,
                    onCalculated: operation =>
                {
                    logger.LogInformation(
                        $"Operation '{operation.Operation.ToGraphQL()}' costs " +
                        $"'{operation.Cost}' (max: '{operation.MaxCost}')");
                }
                    )
            }).ToArray())
            .ConfigureWebSockets <IHttpContextAccessor>(
                async(context, accessor) =>
            {
                var succeeded = await AuthorizeHelper.AuthorizeAsync(
                    accessor.HttpContext,
                    new List <IAuthorizeData>
                {
                    new AuthorizeAttribute("authorize")
                });

                if (succeeded)
                {
                    await context.Output.WriteAsync(new OperationMessage
                    {
                        Type = MessageType.GQL_CONNECTION_ACK
                    });
                }
                else
                {
                    // you must decide what kind of message to send back to the client
                    // in case the connection is not accepted.
                    await context.Output.WriteAsync(new OperationMessage
                    {
                        Type = MessageType.GQL_CONNECTION_ERROR,
                        Id   = context.Message.Id
                    });

                    // complete the output forcing the server to disconnect
                    context.Output.Complete();
                }
            });


            // add signalr
            services.AddSignalR(options => { options.EnableDetailedErrors = true; })
            .AddTankaGraphQL();
        }
        private static void ExpandClusterFromStartPoint(int startIndex, List <int> constructingCluster, bool atTail, IList <int> nodesAsIndexes, CostAnalyzer <double> costanalyzer, double maxCostOfNearestNeighbor, int maxNodesPerCluster)
        {
            int currentPointer = startIndex;

            bool foundNextNodeInChain;

            if (maxNodesPerCluster < 0 || constructingCluster.Count < maxNodesPerCluster - 1)
            {
                do
                {
                    foundNextNodeInChain = false;

                    var myNeighborsNotAlreadyInConstructingCluster =
                        costanalyzer.AllNeighborsOrdered(currentPointer).Where(neighbor => currentPointer != neighbor.Key && !constructingCluster.Contains(neighbor.Key));

                    if (myNeighborsNotAlreadyInConstructingCluster.Any())
                    {
                        var nearestNeighbor = myNeighborsNotAlreadyInConstructingCluster.FirstOrDefault(); // The closest neighbor

                        if (nearestNeighbor.Value <= maxCostOfNearestNeighbor &&
                            nodesAsIndexes.Contains(nearestNeighbor.Key))    // No overlapping with other clusters. We stop if the next one is in another cluster.
                        {
                            if (constructingCluster.Count == 0)
                            {
                                constructingCluster.Add(currentPointer); // If we start construct a cluster don't forget to add the beginning node.
                            }
                            if (atTail)
                            {
                                constructingCluster.Add(nearestNeighbor.Key);
                            }
                            else
                            {
                                constructingCluster.Insert(0, nearestNeighbor.Key);
                            }

                            currentPointer = nearestNeighbor.Key; // Move the pointer to the neighbor

                            foundNextNodeInChain = true;
                        }
                    }
                }while (foundNextNodeInChain && (maxNodesPerCluster < 0 || constructingCluster.Count < maxNodesPerCluster));
            }
        }