Example #1
0
        private async Task <bool> HandleCreateNewRequest(
            HttpContext httpContext,
            IStateMachineContextStorage contextStorage,
            IStateMachineContentStorage contentStorage,
            IEnumerable <IStatemachineDefinition> definitions,
            ITransitionDispatcher dispatcher)
        {
            var segments = this.routeMatcher.Match(this.options.RoutePrefix + "/{name}", httpContext.Request.Path);

            if (segments?.ContainsKey("name") == true)
            {
                this.logger.LogInformation($"statemachine: create new (name={segments["name"]})");
                var definition = definitions.Safe()
                                 .FirstOrDefault(d => d.Name.Equals(segments["name"] as string, StringComparison.OrdinalIgnoreCase));
                if (definition == null)
                {
                    httpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;
                    var details = new ProblemDetails()
                    {
                        Title    = "state machine definition not found",
                        Status   = (int)HttpStatusCode.BadRequest,
                        Instance = $"{this.options.RoutePrefix}/{segments["name"]}",
                    };

                    httpContext.Response.ContentType = "application/json";
                    await httpContext.Response.WriteAsync(JsonConvert.SerializeObject(details, JsonSerializerSettings.Create()), Encoding.UTF8).ConfigureAwait(false);

                    return(true);
                }

                var context = new StateMachineContext()
                {
                    Created = DateTime.UtcNow, Updated = DateTime.UtcNow
                };
                httpContext.Request.Query.ForEach(i => context.Properties.AddOrUpdate(i.Key, i.Value));

                var instance = definition.CreateInstance(context, dispatcher);
                instance.Activate();
                await this.StoreContent(httpContext, "_current", context, contextStorage, contentStorage).ConfigureAwait(false);

                await this.StoreContent(httpContext, context.State, context, contextStorage, contentStorage).ConfigureAwait(false);

                httpContext.Response.StatusCode  = (int)HttpStatusCode.Created;
                httpContext.Response.ContentType = "application/json";
                httpContext.Response.Headers.Add("Location", $"{this.options.RoutePrefix}/{segments["name"]}/{context.Id}");
                await httpContext.Response.WriteAsync(JsonConvert.SerializeObject(context, JsonSerializerSettings.Create()), Encoding.UTF8).ConfigureAwait(false);

                return(true);
            }

            return(false);
        }
Example #2
0
        public async Task Invoke(
            HttpContext httpContext,
            IStateMachineContextStorage contextStorage,
            IStateMachineContentStorage contentStorage,
            IEnumerable <IStatemachineDefinition> definitions)
        {
            if (httpContext.Request.Path.StartsWithSegments(this.options.RoutePrefix, StringComparison.OrdinalIgnoreCase))
            {
                if (httpContext.Request.Method.SafeEquals("get"))
                {
                    if (await this.HandleGetAllRequest(httpContext, contextStorage).ConfigureAwait(false))
                    {
                        return;
                    }

                    if (await this.HandleGetByIdRequest(httpContext, contextStorage).ConfigureAwait(false))
                    {
                        return;
                    }

                    if (await this.HandleGetTriggersRequest(httpContext, contextStorage, definitions, httpContext.RequestServices.GetService <ITransitionDispatcher>()).ConfigureAwait(false))
                    {
                        return;
                    }
                }
                else if (httpContext.Request.Method.SafeEquals("post"))
                {
                    if (await this.HandleCreateNewRequest(httpContext, contextStorage, contentStorage, definitions, httpContext.RequestServices.GetService <ITransitionDispatcher>()).ConfigureAwait(false))
                    {
                        return;
                    }
                }
                else if (httpContext.Request.Method.SafeEquals("put"))
                {
                    if (await this.HandleFireTriggerRequest(httpContext, contextStorage, contentStorage, definitions, httpContext.RequestServices.GetService <ITransitionDispatcher>()).ConfigureAwait(false))
                    {
                        return;
                    }
                }
            }

            await this.next(httpContext).ConfigureAwait(false);
        }
Example #3
0
        private async Task StoreContent(HttpContext httpContext, string key, StateMachineContext context, IStateMachineContextStorage contextStorage, IStateMachineContentStorage contentStorage)
        {
            var contentLength = httpContext.Request.ContentLength ?? 0;

            if (httpContext.Request.Body != null && contentLength > 0)
            {
                httpContext.Request.EnableBuffering(); // allow multiple reads
                httpContext.Request.Body.Position = 0;
                try
                {
                    using (var stream = new MemoryStream())
                    {
                        await httpContext.Request.Body.CopyToAsync(stream).ConfigureAwait(false);

                        this.logger.LogInformation($"statemachine: store content (name={context.Name}, id={context.Id}, key={key}, size={stream.Length}, type={httpContext.Request.ContentType})");
                        contentStorage.Save(context, $"{key}", stream, httpContext.Request.ContentType);
                        context.AddContent(key, httpContext.Request.ContentType, stream.Length);
                    }
                }
                catch (Exception ex)
                {
                    this.logger.LogWarning(ex, $"save content failed: {ex.Message}");
                }

                httpContext.Request.Body.Position = 0;
            }

            contextStorage.Save(context);
        }
Example #4
0
        private async Task <bool> HandleFireTriggerRequest(
            HttpContext httpContext,
            IStateMachineContextStorage contextStorage,
            IStateMachineContentStorage contentStorage,
            IEnumerable <IStatemachineDefinition> definitions,
            ITransitionDispatcher dispatcher)
        {
            var segments = this.routeMatcher.Match(this.options.RoutePrefix + "/{name}/{id}/triggers/{trigger}", httpContext.Request.Path);

            if (segments?.ContainsKey("name") == true && segments?.ContainsKey("id") == true && segments?.ContainsKey("trigger") == true)
            {
                this.logger.LogInformation($"statemachine: fire trigger (name={segments["name"]}, id={segments["id"]}, trigger={segments["trigger"]})");

                var definition = definitions.Safe()
                                 .FirstOrDefault(d => d.Name.Equals(segments["name"] as string, StringComparison.OrdinalIgnoreCase));
                if (definition == null)
                {
                    httpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;
                    var details = new ProblemDetails()
                    {
                        Title    = "state machine definition not found",
                        Status   = (int)HttpStatusCode.BadRequest,
                        Instance = $"{this.options.RoutePrefix}/{segments["name"]}/{segments["id"]}",
                    };

                    httpContext.Response.ContentType = "application/json";
                    await httpContext.Response.WriteAsync(JsonConvert.SerializeObject(details, JsonSerializerSettings.Create()), Encoding.UTF8).ConfigureAwait(false);

                    return(true);
                }

                var context = contextStorage.FindById(segments["id"] as string);
                if (context == null)
                {
                    httpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
                    return(true);
                }

                if (context.IsExpired())
                {
                    httpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;
                    var details = new ProblemDetails()
                    {
                        Title    = "state machine is expired",
                        Status   = (int)HttpStatusCode.BadRequest,
                        Instance = $"{this.options.RoutePrefix}/{context.Name}/{context.Id}",
                    };

                    httpContext.Response.ContentType = "application/json";
                    await httpContext.Response.WriteAsync(JsonConvert.SerializeObject(details, JsonSerializerSettings.Create()), Encoding.UTF8).ConfigureAwait(false);

                    return(true);
                }

                if (!context.Name.Equals(segments["name"] as string, StringComparison.OrdinalIgnoreCase))
                {
                    httpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;
                    return(true);
                }

                httpContext.Request.Query.ForEach(i => context.Properties.AddOrUpdate(i.Key, i.Value));
                var instance = definition.CreateInstance(context, dispatcher);
                try
                {
                    await this.StoreContent(httpContext, "_current", context, contextStorage, contentStorage).ConfigureAwait(false);

                    if (await instance.FireAsync(segments["trigger"] as string).ConfigureAwait(false))
                    {
                        await this.StoreContent(httpContext, context.State, context, contextStorage, contentStorage).ConfigureAwait(false);

                        this.logger.LogInformation($"statemachine: trigger successfull (name={segments["name"]}, id={segments["id"]}, trigger={segments["trigger"]})");
                        contextStorage.Save(context);

                        httpContext.Response.StatusCode = (int)HttpStatusCode.OK;
                    }
                    else
                    {
                        this.logger.LogError($"statemachine: trigger invalid (name={segments["name"]}, id={segments["id"]}, trigger={segments["trigger"]})");
                        httpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;
                    }
                }
                catch (Exception ex)
                {
                    this.logger.LogCritical(ex, $"statemachine: trigger failed (name={segments["name"]}, id={segments["id"]}, trigger={segments["trigger"]}) {ex.Message}");
                    httpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
                }
                finally
                {
                    this.logger.LogInformation($"statemachine: permitted triggers={string.Join("|", instance.PermittedTriggers)}");
                    contextStorage.Save(context);
                }

                return(true);
            }

            return(false);
        }