Merge pull request #42 from frikky/dev
OpenAPI creator with Curl generator
This commit is contained in:
@@ -13,3 +13,4 @@ BACKEND_PORT=5001
|
||||
FRONTEND_PORT=3001
|
||||
FRONTEND_PORT_HTTPS=3443
|
||||
OUTER_HOSTNAME=shuffle-backend
|
||||
DB_LOCATION=/etc/shuffle
|
||||
|
||||
@@ -588,6 +588,7 @@ class AppBase:
|
||||
logging.basicConfig(format="{asctime} - {name} - {levelname}:{message}", style='{')
|
||||
logger = logging.getLogger(f"{cls.__name__}")
|
||||
logger.setLevel(logging.DEBUG)
|
||||
print("Started execution!!")
|
||||
|
||||
app = cls(redis=None, logger=logger, console_logger=logger)
|
||||
|
||||
|
||||
+272
-97
@@ -4,17 +4,19 @@ import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"cloud.google.com/go/storage"
|
||||
"github.com/getkin/kin-openapi/openapi3"
|
||||
"github.com/satori/go.uuid"
|
||||
//"github.com/satori/go.uuid"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
@@ -277,7 +279,13 @@ func makePythoncode(swagger *openapi3.Swagger, name, url, method string, paramet
|
||||
if swagger.Components.SecuritySchemes["ApiKeyAuth"].Value.In == "header" {
|
||||
authenticationSetup = fmt.Sprintf("headers[\"%s\"] = apikey", swagger.Components.SecuritySchemes["ApiKeyAuth"].Value.Name)
|
||||
} else if swagger.Components.SecuritySchemes["ApiKeyAuth"].Value.In == "query" {
|
||||
authenticationSetup = fmt.Sprintf("url+=f\"?%s={apikey}\"", swagger.Components.SecuritySchemes["ApiKeyAuth"].Value.Name)
|
||||
// This might suck lol
|
||||
key := "?"
|
||||
if strings.Contains(url, "?") {
|
||||
key = "&"
|
||||
}
|
||||
|
||||
authenticationSetup = fmt.Sprintf("url+=f\"%s%s={apikey}\"", key, swagger.Components.SecuritySchemes["ApiKeyAuth"].Value.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -306,46 +314,75 @@ func makePythoncode(swagger *openapi3.Swagger, name, url, method string, paramet
|
||||
|
||||
bodyParameter := ""
|
||||
bodyAddin := ""
|
||||
bodyFormatter := ""
|
||||
postParameters := []string{"post", "patch", "put"}
|
||||
for _, item := range postParameters {
|
||||
if method == item {
|
||||
bodyParameter = ", body=\"\""
|
||||
bodyAddin = ", json=body"
|
||||
bodyAddin = ", data=body"
|
||||
|
||||
// FIXME: Does JSON data work?
|
||||
bodyFormatter = `
|
||||
if (body.startswith("{") and body.endswith("}")) or (body.startswith("[") and body.endswith("]")):
|
||||
try:
|
||||
body = json.dumps(body)
|
||||
except:
|
||||
pass
|
||||
`
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Extra param for url if it's changeable
|
||||
// Extra param for authentication scheme(s)
|
||||
|
||||
data := fmt.Sprintf(` async def %s(self%s%s%s%s%s):
|
||||
headers={}
|
||||
url=f"%s%s"
|
||||
%s
|
||||
%s
|
||||
%s
|
||||
return requests.%s(url, headers=headers%s%s).text
|
||||
`, functionname, authenticationParameter, urlParameter, parameterData, queryString, bodyParameter, urlInline, url, authenticationSetup, queryData, method, authenticationAddin, bodyAddin)
|
||||
`,
|
||||
functionname,
|
||||
authenticationParameter,
|
||||
urlParameter,
|
||||
parameterData,
|
||||
queryString,
|
||||
bodyParameter,
|
||||
urlInline,
|
||||
url,
|
||||
authenticationSetup,
|
||||
queryData,
|
||||
bodyFormatter,
|
||||
method,
|
||||
authenticationAddin,
|
||||
bodyAddin,
|
||||
)
|
||||
|
||||
//log.Println(data)
|
||||
//log.Println(functionname)
|
||||
return functionname, data
|
||||
}
|
||||
|
||||
func generateYaml(swagger *openapi3.Swagger, newmd5 string) (WorkflowApp, []string, error) {
|
||||
func generateYaml(swagger *openapi3.Swagger, newmd5 string) (*openapi3.Swagger, WorkflowApp, []string, error) {
|
||||
api := WorkflowApp{}
|
||||
//log.Printf("%#v", swagger.Info)
|
||||
|
||||
if len(swagger.Info.Title) == 0 {
|
||||
return WorkflowApp{}, []string{}, errors.New("Swagger.Info.Title can't be empty.")
|
||||
return swagger, WorkflowApp{}, []string{}, errors.New("Swagger.Info.Title can't be empty.")
|
||||
}
|
||||
|
||||
if len(swagger.Servers) == 0 {
|
||||
return WorkflowApp{}, []string{}, errors.New("Swagger.Servers can't be empty. Add 'servers':[{'url':'hostname.com'}'")
|
||||
return swagger, WorkflowApp{}, []string{}, errors.New("Swagger.Servers can't be empty. Add 'servers':[{'url':'hostname.com'}'")
|
||||
}
|
||||
|
||||
api.Name = swagger.Info.Title
|
||||
api.Description = swagger.Info.Description
|
||||
api.ID = uuid.NewV4().String()
|
||||
|
||||
// FIXME: Versioning issue?
|
||||
api.ID = newmd5
|
||||
//uuid.NewV4().String()
|
||||
|
||||
api.IsValid = true
|
||||
api.Link = swagger.Servers[0].URL // host doesnt exist lol
|
||||
if strings.HasSuffix(api.Link, "/") {
|
||||
@@ -365,6 +402,20 @@ func generateYaml(swagger *openapi3.Swagger, newmd5 string) (WorkflowApp, []stri
|
||||
// Setting up security schemes
|
||||
extraParameters := []WorkflowAppActionParameter{}
|
||||
|
||||
if val, ok := swagger.Info.ExtensionProps.Extensions["x-logo"]; ok {
|
||||
j, err := json.Marshal(&val)
|
||||
if err == nil {
|
||||
if j[0] == 0x22 && j[len(j)-1] == 0x22 {
|
||||
j = j[1 : len(j)-1]
|
||||
}
|
||||
|
||||
//log.Printf("%s", j)
|
||||
api.SmallImage = string(j)
|
||||
api.LargeImage = string(j)
|
||||
log.Printf("Set images!")
|
||||
}
|
||||
}
|
||||
|
||||
securitySchemes := swagger.Components.SecuritySchemes
|
||||
if securitySchemes != nil {
|
||||
//log.Printf("%#v", securitySchemes)
|
||||
@@ -456,53 +507,57 @@ func generateYaml(swagger *openapi3.Swagger, newmd5 string) (WorkflowApp, []stri
|
||||
// This is the python code to be generated
|
||||
// Could just as well be go at this point lol
|
||||
pythonFunctions := []string{}
|
||||
|
||||
for actualPath, path := range swagger.Paths {
|
||||
|
||||
//log.Printf("%#v", path)
|
||||
//log.Printf("%#v", actualPath)
|
||||
|
||||
// FIXME: Add everything from here:
|
||||
// https://godoc.org/github.com/getkin/kin-openapi/openapi3#PathItem
|
||||
firstQuery := true
|
||||
if path.Get != nil {
|
||||
action, curCode := handleGet(swagger, api, extraParameters, path, actualPath, firstQuery)
|
||||
action, curCode := handleGet(swagger, api, extraParameters, path, actualPath)
|
||||
api.Actions = append(api.Actions, action)
|
||||
pythonFunctions = append(pythonFunctions, curCode)
|
||||
}
|
||||
if path.Connect != nil {
|
||||
action, curCode := handleConnect(swagger, api, extraParameters, path, actualPath, firstQuery)
|
||||
action, curCode := handleConnect(swagger, api, extraParameters, path, actualPath)
|
||||
api.Actions = append(api.Actions, action)
|
||||
pythonFunctions = append(pythonFunctions, curCode)
|
||||
}
|
||||
if path.Head != nil {
|
||||
action, curCode := handleHead(swagger, api, extraParameters, path, actualPath, firstQuery)
|
||||
action, curCode := handleHead(swagger, api, extraParameters, path, actualPath)
|
||||
api.Actions = append(api.Actions, action)
|
||||
pythonFunctions = append(pythonFunctions, curCode)
|
||||
}
|
||||
if path.Delete != nil {
|
||||
action, curCode := handleDelete(swagger, api, extraParameters, path, actualPath, firstQuery)
|
||||
action, curCode := handleDelete(swagger, api, extraParameters, path, actualPath)
|
||||
api.Actions = append(api.Actions, action)
|
||||
pythonFunctions = append(pythonFunctions, curCode)
|
||||
}
|
||||
if path.Post != nil {
|
||||
action, curCode := handlePost(swagger, api, extraParameters, path, actualPath, firstQuery)
|
||||
action, curCode := handlePost(swagger, api, extraParameters, path, actualPath)
|
||||
api.Actions = append(api.Actions, action)
|
||||
pythonFunctions = append(pythonFunctions, curCode)
|
||||
}
|
||||
if path.Patch != nil {
|
||||
action, curCode := handlePatch(swagger, api, extraParameters, path, actualPath, firstQuery)
|
||||
action, curCode := handlePatch(swagger, api, extraParameters, path, actualPath)
|
||||
api.Actions = append(api.Actions, action)
|
||||
pythonFunctions = append(pythonFunctions, curCode)
|
||||
}
|
||||
if path.Put != nil {
|
||||
action, curCode := handlePut(swagger, api, extraParameters, path, actualPath, firstQuery)
|
||||
action, curCode := handlePut(swagger, api, extraParameters, path, actualPath)
|
||||
api.Actions = append(api.Actions, action)
|
||||
pythonFunctions = append(pythonFunctions, curCode)
|
||||
}
|
||||
|
||||
// Has to be here because its used differently above.
|
||||
// FIXING this is done during export instead?
|
||||
//log.Printf("OLDPATH: %s", actualPath)
|
||||
//if strings.Contains(actualPath, "?") {
|
||||
// actualPath = strings.Split(actualPath, "?")[0]
|
||||
//}
|
||||
|
||||
//log.Printf("NEWPATH: %s", actualPath)
|
||||
//newPaths[actualPath] = path
|
||||
}
|
||||
|
||||
return api, pythonFunctions, nil
|
||||
return swagger, api, pythonFunctions, nil
|
||||
}
|
||||
|
||||
// FIXME - have this give a real version?
|
||||
@@ -637,6 +692,7 @@ func getRunner(classname string) string {
|
||||
return fmt.Sprintf(`
|
||||
# Run the actual thing after we've checked params
|
||||
def run(request):
|
||||
print("Started execution!")
|
||||
action = request.get_json()
|
||||
print(action)
|
||||
print(type(action))
|
||||
@@ -679,7 +735,7 @@ func fixFunctionName(functionName, actualPath string) string {
|
||||
return functionName
|
||||
}
|
||||
|
||||
func handleConnect(swagger *openapi3.Swagger, api WorkflowApp, extraParameters []WorkflowAppActionParameter, path *openapi3.PathItem, actualPath string, firstQuery bool) (WorkflowAppAction, string) {
|
||||
func handleConnect(swagger *openapi3.Swagger, api WorkflowApp, extraParameters []WorkflowAppActionParameter, path *openapi3.PathItem, actualPath string) (WorkflowAppAction, string) {
|
||||
// What to do with this, hmm
|
||||
functionName := fixFunctionName(path.Connect.Summary, actualPath)
|
||||
|
||||
@@ -699,13 +755,13 @@ func handleConnect(swagger *openapi3.Swagger, api WorkflowApp, extraParameters [
|
||||
|
||||
// Parameters: []WorkflowAppActionParameter{},
|
||||
// FIXME - add data for POST stuff
|
||||
firstQuery = true
|
||||
firstQuery := true
|
||||
optionalQueries := []string{}
|
||||
parameters := []string{}
|
||||
optionalParameters := []WorkflowAppActionParameter{}
|
||||
if len(path.Connect.Parameters) > 0 {
|
||||
for _, param := range path.Connect.Parameters {
|
||||
if param.Value.Schema == nil {
|
||||
if param.Value.Schema == nil || param.Value.In == "header" {
|
||||
continue
|
||||
}
|
||||
curParam := WorkflowAppActionParameter{
|
||||
@@ -718,6 +774,24 @@ func handleConnect(swagger *openapi3.Swagger, api WorkflowApp, extraParameters [
|
||||
},
|
||||
}
|
||||
|
||||
// FIXME: Example & Multiline
|
||||
if param.Value.Example != nil {
|
||||
curParam.Example = param.Value.Example.(string)
|
||||
|
||||
if param.Value.Name == "body" {
|
||||
curParam.Value = param.Value.Example.(string)
|
||||
}
|
||||
}
|
||||
if val, ok := param.Value.ExtensionProps.Extensions["multiline"]; ok {
|
||||
j, err := json.Marshal(&val)
|
||||
if err == nil {
|
||||
b, err := strconv.ParseBool(string(j))
|
||||
if err == nil {
|
||||
curParam.Multiline = b
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if param.Value.Required {
|
||||
action.Parameters = append(action.Parameters, curParam)
|
||||
} else {
|
||||
@@ -737,13 +811,16 @@ func handleConnect(swagger *openapi3.Swagger, api WorkflowApp, extraParameters [
|
||||
|
||||
parameters = append(parameters, param.Value.Name)
|
||||
|
||||
if strings.Contains(baseUrl, fmt.Sprintf("%s={%s}", param.Value.Name, param.Value.Name)) {
|
||||
continue
|
||||
}
|
||||
|
||||
if firstQuery {
|
||||
baseUrl = fmt.Sprintf("%s?%s={%s}", baseUrl, param.Value.Name, param.Value.Name)
|
||||
firstQuery = false
|
||||
} else {
|
||||
baseUrl = fmt.Sprintf("%s&%s={%s}", baseUrl, param.Value.Name, param.Value.Name)
|
||||
firstQuery = false
|
||||
}
|
||||
firstQuery = false
|
||||
}
|
||||
|
||||
}
|
||||
@@ -764,7 +841,7 @@ func handleConnect(swagger *openapi3.Swagger, api WorkflowApp, extraParameters [
|
||||
return action, curCode
|
||||
}
|
||||
|
||||
func handleGet(swagger *openapi3.Swagger, api WorkflowApp, extraParameters []WorkflowAppActionParameter, path *openapi3.PathItem, actualPath string, firstQuery bool) (WorkflowAppAction, string) {
|
||||
func handleGet(swagger *openapi3.Swagger, api WorkflowApp, extraParameters []WorkflowAppActionParameter, path *openapi3.PathItem, actualPath string) (WorkflowAppAction, string) {
|
||||
// What to do with this, hmm
|
||||
functionName := fixFunctionName(path.Get.Summary, actualPath)
|
||||
|
||||
@@ -784,7 +861,7 @@ func handleGet(swagger *openapi3.Swagger, api WorkflowApp, extraParameters []Wor
|
||||
|
||||
// Parameters: []WorkflowAppActionParameter{},
|
||||
// FIXME - add data for POST stuff
|
||||
firstQuery = true
|
||||
firstQuery := true
|
||||
optionalQueries := []string{}
|
||||
|
||||
// FIXME - remove this when authentication is properly introduced
|
||||
@@ -793,8 +870,7 @@ func handleGet(swagger *openapi3.Swagger, api WorkflowApp, extraParameters []Wor
|
||||
optionalParameters := []WorkflowAppActionParameter{}
|
||||
if len(path.Get.Parameters) > 0 {
|
||||
for _, param := range path.Get.Parameters {
|
||||
//log.Printf("TYPE: %#v", param.Value.Schema)
|
||||
if param.Value.Schema == nil {
|
||||
if param.Value.Schema == nil || param.Value.In == "header" {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -808,6 +884,24 @@ func handleGet(swagger *openapi3.Swagger, api WorkflowApp, extraParameters []Wor
|
||||
},
|
||||
}
|
||||
|
||||
// FIXME: Example & Multiline
|
||||
if param.Value.Example != nil {
|
||||
curParam.Example = param.Value.Example.(string)
|
||||
|
||||
if param.Value.Name == "body" {
|
||||
curParam.Value = param.Value.Example.(string)
|
||||
}
|
||||
}
|
||||
if val, ok := param.Value.ExtensionProps.Extensions["multiline"]; ok {
|
||||
j, err := json.Marshal(&val)
|
||||
if err == nil {
|
||||
b, err := strconv.ParseBool(string(j))
|
||||
if err == nil {
|
||||
curParam.Multiline = b
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if param.Value.Required {
|
||||
action.Parameters = append(action.Parameters, curParam)
|
||||
} else {
|
||||
@@ -827,13 +921,17 @@ func handleGet(swagger *openapi3.Swagger, api WorkflowApp, extraParameters []Wor
|
||||
|
||||
parameters = append(parameters, param.Value.Name)
|
||||
|
||||
// Skipping simial
|
||||
if strings.Contains(baseUrl, fmt.Sprintf("%s={%s}", param.Value.Name, param.Value.Name)) {
|
||||
continue
|
||||
}
|
||||
|
||||
if firstQuery {
|
||||
baseUrl = fmt.Sprintf("%s?%s={%s}", baseUrl, param.Value.Name, param.Value.Name)
|
||||
firstQuery = false
|
||||
} else {
|
||||
baseUrl = fmt.Sprintf("%s&%s={%s}", baseUrl, param.Value.Name, param.Value.Name)
|
||||
firstQuery = false
|
||||
}
|
||||
firstQuery = false
|
||||
}
|
||||
|
||||
}
|
||||
@@ -854,7 +952,7 @@ func handleGet(swagger *openapi3.Swagger, api WorkflowApp, extraParameters []Wor
|
||||
return action, curCode
|
||||
}
|
||||
|
||||
func handleHead(swagger *openapi3.Swagger, api WorkflowApp, extraParameters []WorkflowAppActionParameter, path *openapi3.PathItem, actualPath string, firstQuery bool) (WorkflowAppAction, string) {
|
||||
func handleHead(swagger *openapi3.Swagger, api WorkflowApp, extraParameters []WorkflowAppActionParameter, path *openapi3.PathItem, actualPath string) (WorkflowAppAction, string) {
|
||||
// What to do with this, hmm
|
||||
functionName := fixFunctionName(path.Head.Summary, actualPath)
|
||||
|
||||
@@ -874,13 +972,13 @@ func handleHead(swagger *openapi3.Swagger, api WorkflowApp, extraParameters []Wo
|
||||
|
||||
// Parameters: []WorkflowAppActionParameter{},
|
||||
// FIXME - add data for POST stuff
|
||||
firstQuery = true
|
||||
firstQuery := true
|
||||
optionalQueries := []string{}
|
||||
parameters := []string{}
|
||||
optionalParameters := []WorkflowAppActionParameter{}
|
||||
if len(path.Head.Parameters) > 0 {
|
||||
for _, param := range path.Head.Parameters {
|
||||
if param.Value.Schema == nil {
|
||||
if param.Value.Schema == nil || param.Value.In == "header" {
|
||||
continue
|
||||
}
|
||||
curParam := WorkflowAppActionParameter{
|
||||
@@ -893,6 +991,24 @@ func handleHead(swagger *openapi3.Swagger, api WorkflowApp, extraParameters []Wo
|
||||
},
|
||||
}
|
||||
|
||||
// FIXME: Example & Multiline
|
||||
if param.Value.Example != nil {
|
||||
curParam.Example = param.Value.Example.(string)
|
||||
|
||||
if param.Value.Name == "body" {
|
||||
curParam.Value = param.Value.Example.(string)
|
||||
}
|
||||
}
|
||||
if val, ok := param.Value.ExtensionProps.Extensions["multiline"]; ok {
|
||||
j, err := json.Marshal(&val)
|
||||
if err == nil {
|
||||
b, err := strconv.ParseBool(string(j))
|
||||
if err == nil {
|
||||
curParam.Multiline = b
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if param.Value.Required {
|
||||
action.Parameters = append(action.Parameters, curParam)
|
||||
} else {
|
||||
@@ -912,13 +1028,16 @@ func handleHead(swagger *openapi3.Swagger, api WorkflowApp, extraParameters []Wo
|
||||
|
||||
parameters = append(parameters, param.Value.Name)
|
||||
|
||||
if strings.Contains(baseUrl, fmt.Sprintf("%s={%s}", param.Value.Name, param.Value.Name)) {
|
||||
continue
|
||||
}
|
||||
|
||||
if firstQuery {
|
||||
baseUrl = fmt.Sprintf("%s?%s={%s}", baseUrl, param.Value.Name, param.Value.Name)
|
||||
firstQuery = false
|
||||
} else {
|
||||
baseUrl = fmt.Sprintf("%s&%s={%s}", baseUrl, param.Value.Name, param.Value.Name)
|
||||
firstQuery = false
|
||||
}
|
||||
firstQuery = false
|
||||
}
|
||||
|
||||
}
|
||||
@@ -939,7 +1058,7 @@ func handleHead(swagger *openapi3.Swagger, api WorkflowApp, extraParameters []Wo
|
||||
return action, curCode
|
||||
}
|
||||
|
||||
func handleDelete(swagger *openapi3.Swagger, api WorkflowApp, extraParameters []WorkflowAppActionParameter, path *openapi3.PathItem, actualPath string, firstQuery bool) (WorkflowAppAction, string) {
|
||||
func handleDelete(swagger *openapi3.Swagger, api WorkflowApp, extraParameters []WorkflowAppActionParameter, path *openapi3.PathItem, actualPath string) (WorkflowAppAction, string) {
|
||||
// What to do with this, hmm
|
||||
functionName := fixFunctionName(path.Delete.Summary, actualPath)
|
||||
|
||||
@@ -959,13 +1078,13 @@ func handleDelete(swagger *openapi3.Swagger, api WorkflowApp, extraParameters []
|
||||
|
||||
// Parameters: []WorkflowAppActionParameter{},
|
||||
// FIXME - add data for POST stuff
|
||||
firstQuery = true
|
||||
firstQuery := true
|
||||
optionalQueries := []string{}
|
||||
parameters := []string{}
|
||||
optionalParameters := []WorkflowAppActionParameter{}
|
||||
if len(path.Delete.Parameters) > 0 {
|
||||
for _, param := range path.Delete.Parameters {
|
||||
if param.Value.Schema == nil {
|
||||
if param.Value.Schema == nil || param.Value.In == "header" {
|
||||
continue
|
||||
}
|
||||
curParam := WorkflowAppActionParameter{
|
||||
@@ -978,6 +1097,24 @@ func handleDelete(swagger *openapi3.Swagger, api WorkflowApp, extraParameters []
|
||||
},
|
||||
}
|
||||
|
||||
// FIXME: Example & Multiline
|
||||
if param.Value.Example != nil {
|
||||
curParam.Example = param.Value.Example.(string)
|
||||
|
||||
if param.Value.Name == "body" {
|
||||
curParam.Value = param.Value.Example.(string)
|
||||
}
|
||||
}
|
||||
if val, ok := param.Value.ExtensionProps.Extensions["multiline"]; ok {
|
||||
j, err := json.Marshal(&val)
|
||||
if err == nil {
|
||||
b, err := strconv.ParseBool(string(j))
|
||||
if err == nil {
|
||||
curParam.Multiline = b
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if param.Value.Required {
|
||||
action.Parameters = append(action.Parameters, curParam)
|
||||
} else {
|
||||
@@ -997,13 +1134,16 @@ func handleDelete(swagger *openapi3.Swagger, api WorkflowApp, extraParameters []
|
||||
|
||||
parameters = append(parameters, param.Value.Name)
|
||||
|
||||
if strings.Contains(baseUrl, fmt.Sprintf("%s={%s}", param.Value.Name, param.Value.Name)) {
|
||||
continue
|
||||
}
|
||||
|
||||
if firstQuery {
|
||||
baseUrl = fmt.Sprintf("%s?%s={%s}", baseUrl, param.Value.Name, param.Value.Name)
|
||||
firstQuery = false
|
||||
} else {
|
||||
baseUrl = fmt.Sprintf("%s&%s={%s}", baseUrl, param.Value.Name, param.Value.Name)
|
||||
firstQuery = false
|
||||
}
|
||||
firstQuery = false
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1024,7 +1164,7 @@ func handleDelete(swagger *openapi3.Swagger, api WorkflowApp, extraParameters []
|
||||
return action, curCode
|
||||
}
|
||||
|
||||
func handlePost(swagger *openapi3.Swagger, api WorkflowApp, extraParameters []WorkflowAppActionParameter, path *openapi3.PathItem, actualPath string, firstQuery bool) (WorkflowAppAction, string) {
|
||||
func handlePost(swagger *openapi3.Swagger, api WorkflowApp, extraParameters []WorkflowAppActionParameter, path *openapi3.PathItem, actualPath string) (WorkflowAppAction, string) {
|
||||
// What to do with this, hmm
|
||||
//log.Printf("PATH: %s", actualPath)
|
||||
functionName := fixFunctionName(path.Post.Summary, actualPath)
|
||||
@@ -1038,33 +1178,26 @@ func handlePost(swagger *openapi3.Swagger, api WorkflowApp, extraParameters []Wo
|
||||
Parameters: extraParameters,
|
||||
}
|
||||
|
||||
if path.Post.RequestBody != nil {
|
||||
log.Printf("RequestBody: %#v", path.Post.RequestBody)
|
||||
}
|
||||
|
||||
action.Returns.Schema.Type = "string"
|
||||
baseUrl := fmt.Sprintf("%s%s", api.Link, actualPath)
|
||||
|
||||
//log.Println(path.Parameters)
|
||||
|
||||
// Parameters: []WorkflowAppActionParameter{},
|
||||
// FIXME - add data for POST stuff
|
||||
firstQuery = true
|
||||
firstQuery := true
|
||||
optionalQueries := []string{}
|
||||
parameters := []string{}
|
||||
optionalParameters := []WorkflowAppActionParameter{
|
||||
WorkflowAppActionParameter{
|
||||
Name: "body",
|
||||
Description: "The body to use",
|
||||
Multiline: true,
|
||||
Required: false,
|
||||
Example: `{"username": "test"}`,
|
||||
Schema: SchemaDefinition{
|
||||
Type: "string",
|
||||
},
|
||||
},
|
||||
}
|
||||
optionalParameters := []WorkflowAppActionParameter{}
|
||||
|
||||
if len(path.Post.Parameters) > 0 {
|
||||
for _, param := range path.Post.Parameters {
|
||||
if param.Value.Schema == nil {
|
||||
if param.Value.Schema == nil || param.Value.In == "header" {
|
||||
continue
|
||||
}
|
||||
|
||||
curParam := WorkflowAppActionParameter{
|
||||
Name: param.Value.Name,
|
||||
Description: param.Value.Description,
|
||||
@@ -1075,6 +1208,24 @@ func handlePost(swagger *openapi3.Swagger, api WorkflowApp, extraParameters []Wo
|
||||
},
|
||||
}
|
||||
|
||||
// FIXME: Example & Multiline
|
||||
if param.Value.Example != nil {
|
||||
curParam.Example = param.Value.Example.(string)
|
||||
|
||||
if param.Value.Name == "body" {
|
||||
curParam.Value = param.Value.Example.(string)
|
||||
}
|
||||
}
|
||||
if val, ok := param.Value.ExtensionProps.Extensions["multiline"]; ok {
|
||||
j, err := json.Marshal(&val)
|
||||
if err == nil {
|
||||
b, err := strconv.ParseBool(string(j))
|
||||
if err == nil {
|
||||
curParam.Multiline = b
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if param.Value.Required {
|
||||
action.Parameters = append(action.Parameters, curParam)
|
||||
} else {
|
||||
@@ -1094,13 +1245,16 @@ func handlePost(swagger *openapi3.Swagger, api WorkflowApp, extraParameters []Wo
|
||||
|
||||
parameters = append(parameters, param.Value.Name)
|
||||
|
||||
if strings.Contains(baseUrl, fmt.Sprintf("%s={%s}", param.Value.Name, param.Value.Name)) {
|
||||
continue
|
||||
}
|
||||
|
||||
if firstQuery {
|
||||
baseUrl = fmt.Sprintf("%s?%s={%s}", baseUrl, param.Value.Name, param.Value.Name)
|
||||
firstQuery = false
|
||||
} else {
|
||||
baseUrl = fmt.Sprintf("%s&%s={%s}", baseUrl, param.Value.Name, param.Value.Name)
|
||||
firstQuery = false
|
||||
}
|
||||
firstQuery = false
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1121,7 +1275,7 @@ func handlePost(swagger *openapi3.Swagger, api WorkflowApp, extraParameters []Wo
|
||||
return action, curCode
|
||||
}
|
||||
|
||||
func handlePatch(swagger *openapi3.Swagger, api WorkflowApp, extraParameters []WorkflowAppActionParameter, path *openapi3.PathItem, actualPath string, firstQuery bool) (WorkflowAppAction, string) {
|
||||
func handlePatch(swagger *openapi3.Swagger, api WorkflowApp, extraParameters []WorkflowAppActionParameter, path *openapi3.PathItem, actualPath string) (WorkflowAppAction, string) {
|
||||
// What to do with this, hmm
|
||||
functionName := fixFunctionName(path.Patch.Summary, actualPath)
|
||||
|
||||
@@ -1141,24 +1295,13 @@ func handlePatch(swagger *openapi3.Swagger, api WorkflowApp, extraParameters []W
|
||||
|
||||
// Parameters: []WorkflowAppActionParameter{},
|
||||
// FIXME - add data for POST stuff
|
||||
firstQuery = true
|
||||
firstQuery := true
|
||||
optionalQueries := []string{}
|
||||
parameters := []string{}
|
||||
optionalParameters := []WorkflowAppActionParameter{
|
||||
WorkflowAppActionParameter{
|
||||
Name: "body",
|
||||
Description: "The body to use",
|
||||
Multiline: true,
|
||||
Required: false,
|
||||
Example: `{"username": "test"}`,
|
||||
Schema: SchemaDefinition{
|
||||
Type: "string",
|
||||
},
|
||||
},
|
||||
}
|
||||
optionalParameters := []WorkflowAppActionParameter{}
|
||||
if len(path.Patch.Parameters) > 0 {
|
||||
for _, param := range path.Patch.Parameters {
|
||||
if param.Value.Schema == nil {
|
||||
if param.Value.Schema == nil || param.Value.In == "header" {
|
||||
continue
|
||||
}
|
||||
curParam := WorkflowAppActionParameter{
|
||||
@@ -1171,6 +1314,24 @@ func handlePatch(swagger *openapi3.Swagger, api WorkflowApp, extraParameters []W
|
||||
},
|
||||
}
|
||||
|
||||
// FIXME: Example & Multiline
|
||||
if param.Value.Example != nil {
|
||||
curParam.Example = param.Value.Example.(string)
|
||||
|
||||
if param.Value.Name == "body" {
|
||||
curParam.Value = param.Value.Example.(string)
|
||||
}
|
||||
}
|
||||
if val, ok := param.Value.ExtensionProps.Extensions["multiline"]; ok {
|
||||
j, err := json.Marshal(&val)
|
||||
if err == nil {
|
||||
b, err := strconv.ParseBool(string(j))
|
||||
if err == nil {
|
||||
curParam.Multiline = b
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if param.Value.Required {
|
||||
action.Parameters = append(action.Parameters, curParam)
|
||||
} else {
|
||||
@@ -1190,13 +1351,16 @@ func handlePatch(swagger *openapi3.Swagger, api WorkflowApp, extraParameters []W
|
||||
|
||||
parameters = append(parameters, param.Value.Name)
|
||||
|
||||
if strings.Contains(baseUrl, fmt.Sprintf("%s={%s}", param.Value.Name, param.Value.Name)) {
|
||||
continue
|
||||
}
|
||||
|
||||
if firstQuery {
|
||||
baseUrl = fmt.Sprintf("%s?%s={%s}", baseUrl, param.Value.Name, param.Value.Name)
|
||||
firstQuery = false
|
||||
} else {
|
||||
baseUrl = fmt.Sprintf("%s&%s={%s}", baseUrl, param.Value.Name, param.Value.Name)
|
||||
firstQuery = false
|
||||
}
|
||||
firstQuery = false
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1217,7 +1381,7 @@ func handlePatch(swagger *openapi3.Swagger, api WorkflowApp, extraParameters []W
|
||||
return action, curCode
|
||||
}
|
||||
|
||||
func handlePut(swagger *openapi3.Swagger, api WorkflowApp, extraParameters []WorkflowAppActionParameter, path *openapi3.PathItem, actualPath string, firstQuery bool) (WorkflowAppAction, string) {
|
||||
func handlePut(swagger *openapi3.Swagger, api WorkflowApp, extraParameters []WorkflowAppActionParameter, path *openapi3.PathItem, actualPath string) (WorkflowAppAction, string) {
|
||||
// What to do with this, hmm
|
||||
functionName := fixFunctionName(path.Put.Summary, actualPath)
|
||||
|
||||
@@ -1237,24 +1401,14 @@ func handlePut(swagger *openapi3.Swagger, api WorkflowApp, extraParameters []Wor
|
||||
|
||||
// Parameters: []WorkflowAppActionParameter{},
|
||||
// FIXME - add data for POST stuff
|
||||
firstQuery = true
|
||||
firstQuery := true
|
||||
optionalQueries := []string{}
|
||||
parameters := []string{}
|
||||
optionalParameters := []WorkflowAppActionParameter{
|
||||
WorkflowAppActionParameter{
|
||||
Name: "body",
|
||||
Description: "The body to use",
|
||||
Multiline: true,
|
||||
Required: false,
|
||||
Example: `{"username": "test"}`,
|
||||
Schema: SchemaDefinition{
|
||||
Type: "string",
|
||||
},
|
||||
},
|
||||
}
|
||||
optionalParameters := []WorkflowAppActionParameter{}
|
||||
|
||||
if len(path.Put.Parameters) > 0 {
|
||||
for _, param := range path.Put.Parameters {
|
||||
if param.Value.Schema == nil {
|
||||
if param.Value.Schema == nil || param.Value.In == "header" {
|
||||
continue
|
||||
}
|
||||
curParam := WorkflowAppActionParameter{
|
||||
@@ -1267,6 +1421,24 @@ func handlePut(swagger *openapi3.Swagger, api WorkflowApp, extraParameters []Wor
|
||||
},
|
||||
}
|
||||
|
||||
// FIXME: Example & Multiline
|
||||
if param.Value.Example != nil {
|
||||
curParam.Example = param.Value.Example.(string)
|
||||
|
||||
if param.Value.Name == "body" {
|
||||
curParam.Value = param.Value.Example.(string)
|
||||
}
|
||||
}
|
||||
if val, ok := param.Value.ExtensionProps.Extensions["multiline"]; ok {
|
||||
j, err := json.Marshal(&val)
|
||||
if err == nil {
|
||||
b, err := strconv.ParseBool(string(j))
|
||||
if err == nil {
|
||||
curParam.Multiline = b
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if param.Value.Required {
|
||||
action.Parameters = append(action.Parameters, curParam)
|
||||
} else {
|
||||
@@ -1286,13 +1458,16 @@ func handlePut(swagger *openapi3.Swagger, api WorkflowApp, extraParameters []Wor
|
||||
|
||||
parameters = append(parameters, param.Value.Name)
|
||||
|
||||
if strings.Contains(baseUrl, fmt.Sprintf("%s={%s}", param.Value.Name, param.Value.Name)) {
|
||||
continue
|
||||
}
|
||||
|
||||
if firstQuery {
|
||||
baseUrl = fmt.Sprintf("%s?%s={%s}", baseUrl, param.Value.Name, param.Value.Name)
|
||||
firstQuery = false
|
||||
} else {
|
||||
baseUrl = fmt.Sprintf("%s&%s={%s}", baseUrl, param.Value.Name, param.Value.Name)
|
||||
firstQuery = false
|
||||
}
|
||||
firstQuery = false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+45
-20
@@ -1906,6 +1906,7 @@ func checkAdminLogin(resp http.ResponseWriter, request *http.Request) {
|
||||
if err != nil {
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err)))
|
||||
return
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
@@ -2112,7 +2113,6 @@ func SetSession(ctx context.Context, Userdata User, value string) error {
|
||||
func setOpenApiDatastore(ctx context.Context, id string, data ParsedOpenApi) error {
|
||||
k := datastore.NameKey("openapi3", id, nil)
|
||||
if _, err := dbclient.Put(ctx, k, &data); err != nil {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -5075,6 +5075,8 @@ func getOpenapi(resp http.ResponseWriter, request *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("API LENGTH GET: %d, ID: %s", len(parsedApi.Body), id)
|
||||
|
||||
parsedApi.Success = true
|
||||
data, err := json.Marshal(parsedApi)
|
||||
if err != nil {
|
||||
@@ -5505,13 +5507,6 @@ func verifySwagger(resp http.ResponseWriter, request *http.Request) {
|
||||
// Test = client side with fetch?
|
||||
|
||||
ctx := context.Background()
|
||||
//client, err := storage.NewClient(ctx)
|
||||
//if err != nil {
|
||||
// log.Printf("Failed to create client (storage): %v", err)
|
||||
// resp.WriteHeader(401)
|
||||
// resp.Write([]byte(`{"success": false, "reason": "Failed creating client"}`))
|
||||
// return
|
||||
//}
|
||||
|
||||
swagger, err := openapi3.NewSwaggerLoader().LoadSwaggerFromData(body)
|
||||
if err != nil {
|
||||
@@ -5534,7 +5529,7 @@ func verifySwagger(resp http.ResponseWriter, request *http.Request) {
|
||||
}
|
||||
|
||||
//log.Printf("Should generate yaml")
|
||||
api, pythonfunctions, err := generateYaml(swagger, newmd5)
|
||||
swagger, api, pythonfunctions, err := generateYaml(swagger, newmd5)
|
||||
if err != nil {
|
||||
log.Printf("Failed building and generating yaml: %s", err)
|
||||
resp.WriteHeader(500)
|
||||
@@ -5542,12 +5537,28 @@ func verifySwagger(resp http.ResponseWriter, request *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
api.Owner = user.Id
|
||||
if len(test.Image) > 0 {
|
||||
api.SmallImage = test.Image
|
||||
api.LargeImage = test.Image
|
||||
// FIXME: CHECK IF SAME NAME AS NORMAL APP
|
||||
// Can't overwrite existing normal app
|
||||
workflowApps, err := getAllWorkflowApps(ctx)
|
||||
if err != nil {
|
||||
log.Printf("Failed getting all workflow apps from database to verify: %s", err)
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(`{"success": false, "reason": "Failed to verify existence"}`))
|
||||
return
|
||||
}
|
||||
|
||||
// Same name only?
|
||||
lowerName := strings.ToLower(swagger.Info.Title)
|
||||
for _, app := range workflowApps {
|
||||
if app.Downloaded && !app.Generated && strings.ToLower(app.Name) == lowerName {
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Normal app with name %s already exists. Delete it first."}`, swagger.Info.Title)))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
api.Owner = user.Id
|
||||
|
||||
err = dumpApi(basePath, api)
|
||||
if err != nil {
|
||||
log.Printf("Failed dumping yaml: %s", err)
|
||||
@@ -5569,7 +5580,7 @@ func verifySwagger(resp http.ResponseWriter, request *http.Request) {
|
||||
|
||||
identifier = strings.Replace(identifier, " ", "-", -1)
|
||||
identifier = strings.Replace(identifier, "_", "-", -1)
|
||||
log.Printf("Successfully uploaded %s to bucket. Proceeding to cloud function", identifier)
|
||||
log.Printf("Successfully parsed %s. Proceeding to docker container", identifier)
|
||||
|
||||
// Now that the baseline is setup, we need to make it into a cloud function
|
||||
// 1. Upload the API to datastore for use
|
||||
@@ -5684,24 +5695,38 @@ func verifySwagger(resp http.ResponseWriter, request *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("DO I REACH HERE WHEN SAVING?")
|
||||
parsed := ParsedOpenApi{
|
||||
ID: api.ID,
|
||||
ID: newmd5,
|
||||
Body: string(body),
|
||||
}
|
||||
|
||||
log.Printf("API LENGTH: %d, ID: %s", len(parsed.Body), newmd5)
|
||||
// FIXME: Might cause versioning issues if we re-use the same!!
|
||||
// FIXME: Need a way to track different versions of the same app properly.
|
||||
// Hint: Save API.id somewhere, and use newmd5 to save latest version
|
||||
err = setOpenApiDatastore(ctx, newmd5, parsed)
|
||||
if err != nil {
|
||||
log.Printf("Failed saving to datastore: %s", err)
|
||||
resp.WriteHeader(500)
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": true, "reason": "%"}`, err)))
|
||||
}
|
||||
|
||||
// Backup every single one
|
||||
setOpenApiDatastore(ctx, api.ID, parsed)
|
||||
err = increaseStatisticsField(ctx, "total_apps_created", api.ID, 1)
|
||||
|
||||
err = increaseStatisticsField(ctx, "total_apps_created", newmd5, 1)
|
||||
if err != nil {
|
||||
log.Printf("Failed to increase success execution stats: %s", err)
|
||||
}
|
||||
|
||||
err = increaseStatisticsField(ctx, "openapi_apps_created", api.ID, 1)
|
||||
err = increaseStatisticsField(ctx, "openapi_apps_created", newmd5, 1)
|
||||
if err != nil {
|
||||
log.Printf("Failed to increase success execution stats: %s", err)
|
||||
}
|
||||
|
||||
resp.WriteHeader(200)
|
||||
resp.Write([]byte(`{"success": true}`))
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": true, "id": "%s"}`, api.ID)))
|
||||
}
|
||||
|
||||
func healthCheckHandler(resp http.ResponseWriter, request *http.Request) {
|
||||
@@ -5748,10 +5773,10 @@ func runInit(ctx context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
//log.Printf("Schedule time: every %d seconds", schedule.Seconds)
|
||||
jobret, err := newscheduler.Every(schedule.Seconds).Seconds().NotImmediately().Run(job)
|
||||
if err != nil {
|
||||
log.Printf("Failed to schedule workflow: %s", err)
|
||||
// FIXME: what now? lol:w
|
||||
}
|
||||
|
||||
scheduledJobs[schedule.Id] = jobret
|
||||
@@ -5837,7 +5862,7 @@ func init() {
|
||||
log.Printf("Running INIT process")
|
||||
dbclient, err = datastore.NewClient(ctx, gceProject)
|
||||
if err != nil {
|
||||
log.Printf("DBclient error during init: %s", err)
|
||||
panic(fmt.Sprintf("DBclient error during init: %s", err))
|
||||
}
|
||||
|
||||
go runInit(ctx)
|
||||
|
||||
@@ -1360,10 +1360,11 @@ func saveWorkflow(resp http.ResponseWriter, request *http.Request) {
|
||||
break
|
||||
}
|
||||
|
||||
if app.Name == action.AppName && app.AppVersion == action.AppVersion {
|
||||
curapp = app
|
||||
break
|
||||
}
|
||||
// Has to NOT be generated
|
||||
//if app.Name == action.AppName && app.AppVersion == action.AppVersion {
|
||||
// curapp = app
|
||||
// break
|
||||
//}
|
||||
}
|
||||
|
||||
// Check to see if the whole app is valid
|
||||
@@ -1381,7 +1382,6 @@ func saveWorkflow(resp http.ResponseWriter, request *http.Request) {
|
||||
curappaction = curAction
|
||||
break
|
||||
}
|
||||
log.Println(action.Name, curAction.Name)
|
||||
}
|
||||
|
||||
// Check to see if the action is valid
|
||||
@@ -2340,7 +2340,16 @@ func scheduleWorkflow(resp http.ResponseWriter, request *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("Schedulearg: %s", string(scheduleArg))
|
||||
// Clean up garbage. This might be wrong in some very specific use-cases
|
||||
parsedBody := string(scheduleArg)
|
||||
parsedBody = strings.Replace(parsedBody, "\\\"", "\"", -1)
|
||||
if len(parsedBody) > 0 {
|
||||
if string(parsedBody[0]) == `"` && string(parsedBody[len(parsedBody)-1]) == "\"" {
|
||||
parsedBody = parsedBody[1 : len(parsedBody)-1]
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("Schedulearg: %s", parsedBody)
|
||||
|
||||
err = createSchedule(
|
||||
ctx,
|
||||
@@ -2348,7 +2357,7 @@ func scheduleWorkflow(resp http.ResponseWriter, request *http.Request) {
|
||||
workflow.ID,
|
||||
schedule.Name,
|
||||
schedule.Frequency,
|
||||
scheduleArg,
|
||||
[]byte(parsedBody),
|
||||
)
|
||||
|
||||
// FIXME - real error message lol
|
||||
@@ -2736,6 +2745,9 @@ func getWorkflowAppConfig(resp http.ResponseWriter, request *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
//log.Printf("%#v", parsedApi)
|
||||
log.Printf("API LEN: %d, ID: %s", len(parsedApi.Body), fileId)
|
||||
|
||||
//log.Printf("Parsed API: %#v", parsedApi)
|
||||
if len(parsedApi.ID) > 0 {
|
||||
parsedApi.Success = true
|
||||
@@ -3325,7 +3337,6 @@ func iterateOpenApiGithub(fs billy.Filesystem, dir []os.FileInfo, extra string,
|
||||
// Check the file
|
||||
filename := file.Name()
|
||||
if strings.Contains(filename, "yaml") || strings.Contains(filename, "yml") {
|
||||
appCounter += 1
|
||||
//log.Printf("File: %s", filename)
|
||||
//log.Printf("Found file: %s", filename)
|
||||
tmpExtra := fmt.Sprintf("%s%s/", extra, file.Name())
|
||||
@@ -3365,7 +3376,7 @@ func iterateOpenApiGithub(fs billy.Filesystem, dir []os.FileInfo, extra string,
|
||||
}
|
||||
|
||||
//log.Printf("Should generate yaml")
|
||||
api, _, err := generateYaml(swagger, parsedOpenApi.ID)
|
||||
swagger, api, _, err := generateYaml(swagger, parsedOpenApi.ID)
|
||||
if err != nil {
|
||||
log.Printf("Failed building and generating yaml in loop (%s): %s", filename, err)
|
||||
continue
|
||||
@@ -3395,6 +3406,7 @@ func iterateOpenApiGithub(fs billy.Filesystem, dir []os.FileInfo, extra string,
|
||||
log.Printf("Failed setting workflowapp in loop: %s", err)
|
||||
continue
|
||||
} else {
|
||||
appCounter += 1
|
||||
log.Printf("Added %s:%s to the database from OpenAPI repo", api.Name, api.AppVersion)
|
||||
|
||||
// Set OpenAPI datastore
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ services:
|
||||
- shuffle
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- /etc/shuffle:/etc/shuffle
|
||||
- ${DB_LOCATION}:/etc/shuffle
|
||||
backend:
|
||||
#build: ./backend
|
||||
image: frikky/shuffle:backend
|
||||
|
||||
Generated
+294
-255
@@ -960,9 +960,9 @@
|
||||
"integrity": "sha512-b0JQb10Lie07iW2/9uKCQSrXif262d6zfYBstCLLJUk0JVA+7o/yLDg5p2+GkjgJbmodjHozIXs4Bi34RRhL8Q=="
|
||||
},
|
||||
"@emotion/hash": {
|
||||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.7.3.tgz",
|
||||
"integrity": "sha512-14ZVlsB9akwvydAdaEnVnvqu6J2P6ySv39hYyl/aoB6w/V+bXX0tay8cF6paqbgZsN2n5Xh15uF4pE+GvE+itw=="
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz",
|
||||
"integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow=="
|
||||
},
|
||||
"@emotion/is-prop-valid": {
|
||||
"version": "0.8.3",
|
||||
@@ -988,37 +988,87 @@
|
||||
"integrity": "sha512-XiUPoS79r1G7PcpnNtq85TJ7inJWe0v+b5oZJZKb0pGHNIV6+UiNeQWiFGmuQ0aj7GEhnD/v9iqxIsjuRKtEnQ=="
|
||||
},
|
||||
"@material-ui/core": {
|
||||
"version": "3.9.3",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/core/-/core-3.9.3.tgz",
|
||||
"integrity": "sha512-REIj62+zEvTgI/C//YL4fZxrCVIySygmpZglsu/Nl5jPqy3CDjZv1F9ubBYorHqmRgeVPh64EghMMWqk4egmfg==",
|
||||
"version": "4.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.10.0.tgz",
|
||||
"integrity": "sha512-yVlHe4b8AaoiTHhCOZeszHZ+T2iHU5DncdMGeNcQaaaO+q/Qrq0hxP3iFzTbgjRWnWwftEVQL668GRxcPJVRaQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.2.0",
|
||||
"@material-ui/system": "^3.0.0-alpha.0",
|
||||
"@material-ui/utils": "^3.0.0-alpha.2",
|
||||
"@types/jss": "^9.5.6",
|
||||
"@types/react-transition-group": "^2.0.8",
|
||||
"brcast": "^3.0.1",
|
||||
"classnames": "^2.2.5",
|
||||
"csstype": "^2.5.2",
|
||||
"debounce": "^1.1.0",
|
||||
"deepmerge": "^3.0.0",
|
||||
"dom-helpers": "^3.2.1",
|
||||
"hoist-non-react-statics": "^3.2.1",
|
||||
"is-plain-object": "^2.0.4",
|
||||
"jss": "^9.8.7",
|
||||
"jss-camel-case": "^6.0.0",
|
||||
"jss-default-unit": "^8.0.2",
|
||||
"jss-global": "^3.0.0",
|
||||
"jss-nested": "^6.0.1",
|
||||
"jss-props-sort": "^6.0.0",
|
||||
"jss-vendor-prefixer": "^7.0.0",
|
||||
"normalize-scroll-left": "^0.1.2",
|
||||
"popper.js": "^1.14.1",
|
||||
"prop-types": "^15.6.0",
|
||||
"react-event-listener": "^0.6.2",
|
||||
"react-transition-group": "^2.2.1",
|
||||
"recompose": "0.28.0 - 0.30.0",
|
||||
"warning": "^4.0.1"
|
||||
"@babel/runtime": "^7.4.4",
|
||||
"@material-ui/styles": "^4.10.0",
|
||||
"@material-ui/system": "^4.9.14",
|
||||
"@material-ui/types": "^5.1.0",
|
||||
"@material-ui/utils": "^4.9.12",
|
||||
"@types/react-transition-group": "^4.2.0",
|
||||
"clsx": "^1.0.4",
|
||||
"hoist-non-react-statics": "^3.3.2",
|
||||
"popper.js": "^1.16.1-lts",
|
||||
"prop-types": "^15.7.2",
|
||||
"react-is": "^16.8.0",
|
||||
"react-transition-group": "^4.4.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"csstype": {
|
||||
"version": "2.6.10",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.10.tgz",
|
||||
"integrity": "sha512-D34BqZU4cIlMCY93rZHbrq9pjTAQJ3U8S8rfBqjwHxkGPThWFjzZDQpgMJY0QViLxth6ZKYiwFBo14RdN44U/w=="
|
||||
},
|
||||
"dom-helpers": {
|
||||
"version": "5.1.4",
|
||||
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.1.4.tgz",
|
||||
"integrity": "sha512-TjMyeVUvNEnOnhzs6uAn9Ya47GmMo3qq7m+Lr/3ON0Rs5kHvb8I+SQYjLUSYn7qhEm0QjW0yrBkvz9yOrwwz1A==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.8.7",
|
||||
"csstype": "^2.6.7"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": {
|
||||
"version": "7.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.10.1.tgz",
|
||||
"integrity": "sha512-nQbbCbQc9u/rpg1XCxoMYQTbSMVZjCDxErQ1ClCn9Pvcmv1lGads19ep0a2VsEiIJeHqjZley6EQGEC3Yo1xMA==",
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.13.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"hoist-non-react-statics": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
|
||||
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
|
||||
"requires": {
|
||||
"react-is": "^16.7.0"
|
||||
}
|
||||
},
|
||||
"popper.js": {
|
||||
"version": "1.16.1",
|
||||
"resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
|
||||
"integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ=="
|
||||
},
|
||||
"react-transition-group": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz",
|
||||
"integrity": "sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"dom-helpers": "^5.0.1",
|
||||
"loose-envify": "^1.4.0",
|
||||
"prop-types": "^15.6.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": {
|
||||
"version": "7.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.10.1.tgz",
|
||||
"integrity": "sha512-nQbbCbQc9u/rpg1XCxoMYQTbSMVZjCDxErQ1ClCn9Pvcmv1lGads19ep0a2VsEiIJeHqjZley6EQGEC3Yo1xMA==",
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.13.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"regenerator-runtime": {
|
||||
"version": "0.13.5",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
|
||||
"integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@material-ui/icons": {
|
||||
@@ -1030,84 +1080,62 @@
|
||||
}
|
||||
},
|
||||
"@material-ui/styles": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.5.0.tgz",
|
||||
"integrity": "sha512-O0NSAECHK9f3DZK6wy56PZzp8b/7KSdfpJs8DSC7vnXUAoMPCTtchBKLzMtUsNlijiJFeJjSxNdQfjWXgyur5A==",
|
||||
"version": "4.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.10.0.tgz",
|
||||
"integrity": "sha512-XPwiVTpd3rlnbfrgtEJ1eJJdFCXZkHxy8TrdieaTvwxNYj42VnnCyFzxYeNW9Lhj4V1oD8YtQ6S5Gie7bZDf7Q==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.4.4",
|
||||
"@emotion/hash": "^0.7.1",
|
||||
"@material-ui/types": "^4.1.1",
|
||||
"@material-ui/utils": "^4.1.0",
|
||||
"clsx": "^1.0.2",
|
||||
"@emotion/hash": "^0.8.0",
|
||||
"@material-ui/types": "^5.1.0",
|
||||
"@material-ui/utils": "^4.9.6",
|
||||
"clsx": "^1.0.4",
|
||||
"csstype": "^2.5.2",
|
||||
"deepmerge": "^4.0.0",
|
||||
"hoist-non-react-statics": "^3.2.1",
|
||||
"jss": "^10.0.0",
|
||||
"jss-plugin-camel-case": "^10.0.0",
|
||||
"jss-plugin-default-unit": "^10.0.0",
|
||||
"jss-plugin-global": "^10.0.0",
|
||||
"jss-plugin-nested": "^10.0.0",
|
||||
"jss-plugin-props-sort": "^10.0.0",
|
||||
"jss-plugin-rule-value-function": "^10.0.0",
|
||||
"jss-plugin-vendor-prefixer": "^10.0.0",
|
||||
"hoist-non-react-statics": "^3.3.2",
|
||||
"jss": "^10.0.3",
|
||||
"jss-plugin-camel-case": "^10.0.3",
|
||||
"jss-plugin-default-unit": "^10.0.3",
|
||||
"jss-plugin-global": "^10.0.3",
|
||||
"jss-plugin-nested": "^10.0.3",
|
||||
"jss-plugin-props-sort": "^10.0.3",
|
||||
"jss-plugin-rule-value-function": "^10.0.3",
|
||||
"jss-plugin-vendor-prefixer": "^10.0.3",
|
||||
"prop-types": "^15.7.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@material-ui/utils": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.4.0.tgz",
|
||||
"integrity": "sha512-UXoQVwArQEQWXxf2FPs0iJGT+MePQpKr0Qh0CPoLc1OdF0GSMTmQczcqCzwZkeHxHAOq/NkIKM1Pb/ih1Avicg==",
|
||||
"hoist-non-react-statics": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
|
||||
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.4.4",
|
||||
"prop-types": "^15.7.2",
|
||||
"react-is": "^16.8.6"
|
||||
}
|
||||
},
|
||||
"deepmerge": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.0.tgz",
|
||||
"integrity": "sha512-/pED+kD8V9n15L1lon8DXEiWLQMW4tTiegn1kIWIQ+DBudOkFitz1cfjWQiSeKMPBQOknT3LpueyAmMVJ1Ho2g=="
|
||||
},
|
||||
"jss": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jss/-/jss-10.0.0.tgz",
|
||||
"integrity": "sha512-TPpDFsiBjuERiL+dFDq8QCdiF9oDasPcNqCKLGCo/qED3fNYOQ8PX2lZhknyTiAt3tZrfOFbb0lbQ9lTjPZxsQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"csstype": "^2.6.5",
|
||||
"is-in-browser": "^1.1.3",
|
||||
"tiny-warning": "^1.0.2"
|
||||
"react-is": "^16.7.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@material-ui/system": {
|
||||
"version": "3.0.0-alpha.2",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/system/-/system-3.0.0-alpha.2.tgz",
|
||||
"integrity": "sha512-odmxQ0peKpP7RQBQ8koly06YhsPzcoVib1vByVPBH4QhwqBXuYoqlCjt02846fYspAqkrWzjxnWUD311EBbxOA==",
|
||||
"version": "4.9.14",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.9.14.tgz",
|
||||
"integrity": "sha512-oQbaqfSnNlEkXEziDcJDDIy8pbvwUmZXWNqlmIwDqr/ZdCK8FuV3f4nxikUh7hvClKV2gnQ9djh5CZFTHkZj3w==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.2.0",
|
||||
"deepmerge": "^3.0.0",
|
||||
"prop-types": "^15.6.0",
|
||||
"warning": "^4.0.1"
|
||||
"@babel/runtime": "^7.4.4",
|
||||
"@material-ui/utils": "^4.9.6",
|
||||
"csstype": "^2.5.2",
|
||||
"prop-types": "^15.7.2"
|
||||
}
|
||||
},
|
||||
"@material-ui/types": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/types/-/types-4.1.1.tgz",
|
||||
"integrity": "sha512-AN+GZNXytX9yxGi0JOfxHrRTbhFybjUJ05rnsBVjcB+16e466Z0Xe5IxawuOayVZgTBNDxmPKo5j4V6OnMtaSQ==",
|
||||
"requires": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/types/-/types-5.1.0.tgz",
|
||||
"integrity": "sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A=="
|
||||
},
|
||||
"@material-ui/utils": {
|
||||
"version": "3.0.0-alpha.3",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-3.0.0-alpha.3.tgz",
|
||||
"integrity": "sha512-rwMdMZptX0DivkqBuC+Jdq7BYTXwqKai5G5ejPpuEDKpWzi1Oxp+LygGw329FrKpuKeiqpcymlqJTjmy+quWng==",
|
||||
"version": "4.9.12",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.9.12.tgz",
|
||||
"integrity": "sha512-/0rgZPEOcZq5CFA4+4n6Q6zk7fi8skHhH2Bcra8R3epoJEYy5PL55LuMazPtPH1oKeRausDV/Omz4BbgFsn1HQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.2.0",
|
||||
"prop-types": "^15.6.0",
|
||||
"react-is": "^16.6.3"
|
||||
"@babel/runtime": "^7.4.4",
|
||||
"prop-types": "^15.7.2",
|
||||
"react-is": "^16.8.0"
|
||||
}
|
||||
},
|
||||
"@mrmlnc/readdir-enhanced": {
|
||||
@@ -1337,9 +1365,9 @@
|
||||
}
|
||||
},
|
||||
"@types/react-transition-group": {
|
||||
"version": "2.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-2.9.2.tgz",
|
||||
"integrity": "sha512-5Fv2DQNO+GpdPZcxp2x/OQG/H19A01WlmpjVD9cKvVFmoVLOZ9LvBgSWG6pSXIU4og5fgbvGPaCV5+VGkWAEHA==",
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.0.tgz",
|
||||
"integrity": "sha512-/QfLHGpu+2fQOqQaXh8MG9q03bFENooTb/it4jr5kKaZlDQfWvjqWZg48AwzPVMBHlRuTRAY7hRHCEOXz5kV6w==",
|
||||
"requires": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
@@ -4788,11 +4816,27 @@
|
||||
"integrity": "sha1-g4NCMMyfdMRX3lnuvRVD/uuDt+w="
|
||||
},
|
||||
"css-vendor": {
|
||||
"version": "0.3.8",
|
||||
"resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-0.3.8.tgz",
|
||||
"integrity": "sha1-ZCHP0wNM5mT+dnOXL9ARn8KJQfo=",
|
||||
"version": "2.0.8",
|
||||
"resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz",
|
||||
"integrity": "sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.8.3",
|
||||
"is-in-browser": "^1.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": {
|
||||
"version": "7.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.10.1.tgz",
|
||||
"integrity": "sha512-nQbbCbQc9u/rpg1XCxoMYQTbSMVZjCDxErQ1ClCn9Pvcmv1lGads19ep0a2VsEiIJeHqjZley6EQGEC3Yo1xMA==",
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.13.4"
|
||||
}
|
||||
},
|
||||
"regenerator-runtime": {
|
||||
"version": "0.13.5",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
|
||||
"integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"css-what": {
|
||||
@@ -5504,9 +5548,9 @@
|
||||
"integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ="
|
||||
},
|
||||
"deepmerge": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-3.2.1.tgz",
|
||||
"integrity": "sha512-+hbDSzTqEW0fWgnlKksg7XAOtT+ddZS5lHZJ6f6MdixRs9wQy+50fm1uUCVb1IkvjLUYX/SfFO021ZNwriURTw=="
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-3.3.0.tgz",
|
||||
"integrity": "sha512-GRQOafGHwMHpjPx9iCvTgpu9NojZ49q794EEL94JVEw6VaeA8XTUyBKvAkOOjBX9oJNiV6G3P+T+tihFjo2TqA=="
|
||||
},
|
||||
"default-gateway": {
|
||||
"version": "2.7.2",
|
||||
@@ -11052,23 +11096,14 @@
|
||||
}
|
||||
},
|
||||
"jss": {
|
||||
"version": "9.8.7",
|
||||
"resolved": "https://registry.npmjs.org/jss/-/jss-9.8.7.tgz",
|
||||
"integrity": "sha512-awj3XRZYxbrmmrx9LUSj5pXSUfm12m8xzi/VKeqI1ZwWBtQ0kVPTs3vYs32t4rFw83CgFDukA8wKzOE9sMQnoQ==",
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jss/-/jss-10.1.1.tgz",
|
||||
"integrity": "sha512-Xz3qgRUFlxbWk1czCZibUJqhVPObrZHxY3FPsjCXhDld4NOj1BgM14Ir5hVm+Qr6OLqVljjGvoMcCdXNOAbdkQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"csstype": "^2.6.5",
|
||||
"is-in-browser": "^1.1.3",
|
||||
"symbol-observable": "^1.1.0",
|
||||
"warning": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"warning": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz",
|
||||
"integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=",
|
||||
"requires": {
|
||||
"loose-envify": "^1.0.0"
|
||||
}
|
||||
}
|
||||
"tiny-warning": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"jss-camel-case": {
|
||||
@@ -11108,179 +11143,69 @@
|
||||
}
|
||||
},
|
||||
"jss-plugin-camel-case": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.0.0.tgz",
|
||||
"integrity": "sha512-yALDL00+pPR4FJh+k07A8FeDvfoPPuXU48HLy63enAubcVd3DnS+2rgqPXglHDGixIDVkCSXecl/l5GAMjzIbA==",
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.1.1.tgz",
|
||||
"integrity": "sha512-MDIaw8FeD5uFz1seQBKz4pnvDLnj5vIKV5hXSVdMaAVq13xR6SVTVWkIV/keyTs5txxTvzGJ9hXoxgd1WTUlBw==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"hyphenate-style-name": "^1.0.3",
|
||||
"jss": "10.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"jss": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jss/-/jss-10.0.0.tgz",
|
||||
"integrity": "sha512-TPpDFsiBjuERiL+dFDq8QCdiF9oDasPcNqCKLGCo/qED3fNYOQ8PX2lZhknyTiAt3tZrfOFbb0lbQ9lTjPZxsQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"csstype": "^2.6.5",
|
||||
"is-in-browser": "^1.1.3",
|
||||
"tiny-warning": "^1.0.2"
|
||||
}
|
||||
}
|
||||
"jss": "10.1.1"
|
||||
}
|
||||
},
|
||||
"jss-plugin-default-unit": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.0.0.tgz",
|
||||
"integrity": "sha512-sURozIOdCtGg9ap18erQ+ijndAfEGtTaetxfU3H4qwC18Bi+fdvjlY/ahKbuu0ASs7R/+WKCP7UaRZOjUDMcdQ==",
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.1.1.tgz",
|
||||
"integrity": "sha512-UkeVCA/b3QEA4k0nIKS4uWXDCNmV73WLHdh2oDGZZc3GsQtlOCuiH3EkB/qI60v2MiCq356/SYWsDXt21yjwdg==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"jss": "10.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"jss": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jss/-/jss-10.0.0.tgz",
|
||||
"integrity": "sha512-TPpDFsiBjuERiL+dFDq8QCdiF9oDasPcNqCKLGCo/qED3fNYOQ8PX2lZhknyTiAt3tZrfOFbb0lbQ9lTjPZxsQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"csstype": "^2.6.5",
|
||||
"is-in-browser": "^1.1.3",
|
||||
"tiny-warning": "^1.0.2"
|
||||
}
|
||||
}
|
||||
"jss": "10.1.1"
|
||||
}
|
||||
},
|
||||
"jss-plugin-global": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.0.0.tgz",
|
||||
"integrity": "sha512-80ofWKSQUo62bxLtRoTNe0kFPtHgUbAJeOeR36WEGgWIBEsXLyXOnD5KNnjPqG4heuEkz9eSLccjYST50JnI7Q==",
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.1.1.tgz",
|
||||
"integrity": "sha512-VBG3wRyi3Z8S4kMhm8rZV6caYBegsk+QnQZSVmrWw6GVOT/Z4FA7eyMu5SdkorDlG/HVpHh91oFN56O4R9m2VA==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"jss": "10.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"jss": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jss/-/jss-10.0.0.tgz",
|
||||
"integrity": "sha512-TPpDFsiBjuERiL+dFDq8QCdiF9oDasPcNqCKLGCo/qED3fNYOQ8PX2lZhknyTiAt3tZrfOFbb0lbQ9lTjPZxsQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"csstype": "^2.6.5",
|
||||
"is-in-browser": "^1.1.3",
|
||||
"tiny-warning": "^1.0.2"
|
||||
}
|
||||
}
|
||||
"jss": "10.1.1"
|
||||
}
|
||||
},
|
||||
"jss-plugin-nested": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.0.0.tgz",
|
||||
"integrity": "sha512-waxxwl/po1hN3azTyixKnr8ReEqUv5WK7WsO+5AWB0bFndML5Yqnt8ARZ90HEg8/P6WlqE/AB2413TkCRZE8bA==",
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.1.1.tgz",
|
||||
"integrity": "sha512-ozEu7ZBSVrMYxSDplPX3H82XHNQk2DQEJ9TEyo7OVTPJ1hEieqjDFiOQOxXEj9z3PMqkylnUbvWIZRDKCFYw5Q==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"jss": "10.0.0",
|
||||
"jss": "10.1.1",
|
||||
"tiny-warning": "^1.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"jss": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jss/-/jss-10.0.0.tgz",
|
||||
"integrity": "sha512-TPpDFsiBjuERiL+dFDq8QCdiF9oDasPcNqCKLGCo/qED3fNYOQ8PX2lZhknyTiAt3tZrfOFbb0lbQ9lTjPZxsQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"csstype": "^2.6.5",
|
||||
"is-in-browser": "^1.1.3",
|
||||
"tiny-warning": "^1.0.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"jss-plugin-props-sort": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.0.0.tgz",
|
||||
"integrity": "sha512-41mf22CImjwNdtOG3r+cdC8+RhwNm616sjHx5YlqTwtSJLyLFinbQC/a4PIFk8xqf1qpFH1kEAIw+yx9HaqZ3g==",
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.1.1.tgz",
|
||||
"integrity": "sha512-g/joK3eTDZB4pkqpZB38257yD4LXB0X15jxtZAGbUzcKAVUHPl9Jb47Y7lYmiGsShiV4YmQRqG1p2DHMYoK91g==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"jss": "10.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"jss": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jss/-/jss-10.0.0.tgz",
|
||||
"integrity": "sha512-TPpDFsiBjuERiL+dFDq8QCdiF9oDasPcNqCKLGCo/qED3fNYOQ8PX2lZhknyTiAt3tZrfOFbb0lbQ9lTjPZxsQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"csstype": "^2.6.5",
|
||||
"is-in-browser": "^1.1.3",
|
||||
"tiny-warning": "^1.0.2"
|
||||
}
|
||||
}
|
||||
"jss": "10.1.1"
|
||||
}
|
||||
},
|
||||
"jss-plugin-rule-value-function": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.0.0.tgz",
|
||||
"integrity": "sha512-Jw+BZ8JIw1f12V0SERqGlBT1JEPWax3vuZpMym54NAXpPb7R1LYHiCTIlaJUyqvIfEy3kiHMtgI+r2whGgRIxQ==",
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.1.1.tgz",
|
||||
"integrity": "sha512-ClV1lvJ3laU9la1CUzaDugEcwnpjPTuJ0yGy2YtcU+gG/w9HMInD5vEv7xKAz53Bk4WiJm5uLOElSEshHyhKNw==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"jss": "10.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"jss": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jss/-/jss-10.0.0.tgz",
|
||||
"integrity": "sha512-TPpDFsiBjuERiL+dFDq8QCdiF9oDasPcNqCKLGCo/qED3fNYOQ8PX2lZhknyTiAt3tZrfOFbb0lbQ9lTjPZxsQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"csstype": "^2.6.5",
|
||||
"is-in-browser": "^1.1.3",
|
||||
"tiny-warning": "^1.0.2"
|
||||
}
|
||||
}
|
||||
"jss": "10.1.1"
|
||||
}
|
||||
},
|
||||
"jss-plugin-vendor-prefixer": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.0.0.tgz",
|
||||
"integrity": "sha512-qslqvL0MUbWuzXJWdUxpj6mdNUX8jr4FFTo3aZnAT65nmzWL7g8oTr9ZxmTXXgdp7ANhS1QWE7036/Q2isFBpw==",
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.1.1.tgz",
|
||||
"integrity": "sha512-09MZpQ6onQrhaVSF6GHC4iYifQ7+4YC/tAP6D4ZWeZotvCMq1mHLqNKRIaqQ2lkgANjlEot2JnVi1ktu4+L4pw==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"css-vendor": "^2.0.6",
|
||||
"jss": "10.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"css-vendor": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.7.tgz",
|
||||
"integrity": "sha512-VS9Rjt79+p7M0WkPqcAza4Yq1ZHrsHrwf7hPL/bjQB+c1lwmAI+1FXxYTYt818D/50fFVflw0XKleiBN5RITkg==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.6.2",
|
||||
"is-in-browser": "^1.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": {
|
||||
"version": "7.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.3.tgz",
|
||||
"integrity": "sha512-kq6anf9JGjW8Nt5rYfEuGRaEAaH1mkv3Bbu6rYvLOpPh/RusSJXuKPEAoZ7L7gybZkchE8+NV5g9vKF4AGAtsA==",
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.13.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"jss": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jss/-/jss-10.0.0.tgz",
|
||||
"integrity": "sha512-TPpDFsiBjuERiL+dFDq8QCdiF9oDasPcNqCKLGCo/qED3fNYOQ8PX2lZhknyTiAt3tZrfOFbb0lbQ9lTjPZxsQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"csstype": "^2.6.5",
|
||||
"is-in-browser": "^1.1.3",
|
||||
"tiny-warning": "^1.0.2"
|
||||
}
|
||||
}
|
||||
"css-vendor": "^2.0.7",
|
||||
"jss": "10.1.1"
|
||||
}
|
||||
},
|
||||
"jss-props-sort": {
|
||||
@@ -11294,6 +11219,16 @@
|
||||
"integrity": "sha512-Agd+FKmvsI0HLcYXkvy8GYOw3AAASBUpsmIRvVQheps+JWaN892uFOInTr0DRydwaD91vSSUCU4NssschvF7MA==",
|
||||
"requires": {
|
||||
"css-vendor": "^0.3.8"
|
||||
},
|
||||
"dependencies": {
|
||||
"css-vendor": {
|
||||
"version": "0.3.8",
|
||||
"resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-0.3.8.tgz",
|
||||
"integrity": "sha1-ZCHP0wNM5mT+dnOXL9ARn8KJQfo=",
|
||||
"requires": {
|
||||
"is-in-browser": "^1.0.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"jsx-ast-utils": {
|
||||
@@ -11753,6 +11688,102 @@
|
||||
"react-transition-group": "4.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@material-ui/core": {
|
||||
"version": "3.9.3",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/core/-/core-3.9.3.tgz",
|
||||
"integrity": "sha512-REIj62+zEvTgI/C//YL4fZxrCVIySygmpZglsu/Nl5jPqy3CDjZv1F9ubBYorHqmRgeVPh64EghMMWqk4egmfg==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.2.0",
|
||||
"@material-ui/system": "^3.0.0-alpha.0",
|
||||
"@material-ui/utils": "^3.0.0-alpha.2",
|
||||
"@types/jss": "^9.5.6",
|
||||
"@types/react-transition-group": "^2.0.8",
|
||||
"brcast": "^3.0.1",
|
||||
"classnames": "^2.2.5",
|
||||
"csstype": "^2.5.2",
|
||||
"debounce": "^1.1.0",
|
||||
"deepmerge": "^3.0.0",
|
||||
"dom-helpers": "^3.2.1",
|
||||
"hoist-non-react-statics": "^3.2.1",
|
||||
"is-plain-object": "^2.0.4",
|
||||
"jss": "^9.8.7",
|
||||
"jss-camel-case": "^6.0.0",
|
||||
"jss-default-unit": "^8.0.2",
|
||||
"jss-global": "^3.0.0",
|
||||
"jss-nested": "^6.0.1",
|
||||
"jss-props-sort": "^6.0.0",
|
||||
"jss-vendor-prefixer": "^7.0.0",
|
||||
"normalize-scroll-left": "^0.1.2",
|
||||
"popper.js": "^1.14.1",
|
||||
"prop-types": "^15.6.0",
|
||||
"react-event-listener": "^0.6.2",
|
||||
"react-transition-group": "^2.2.1",
|
||||
"recompose": "0.28.0 - 0.30.0",
|
||||
"warning": "^4.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"react-transition-group": {
|
||||
"version": "2.9.0",
|
||||
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz",
|
||||
"integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==",
|
||||
"requires": {
|
||||
"dom-helpers": "^3.4.0",
|
||||
"loose-envify": "^1.4.0",
|
||||
"prop-types": "^15.6.2",
|
||||
"react-lifecycles-compat": "^3.0.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@material-ui/system": {
|
||||
"version": "3.0.0-alpha.2",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/system/-/system-3.0.0-alpha.2.tgz",
|
||||
"integrity": "sha512-odmxQ0peKpP7RQBQ8koly06YhsPzcoVib1vByVPBH4QhwqBXuYoqlCjt02846fYspAqkrWzjxnWUD311EBbxOA==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.2.0",
|
||||
"deepmerge": "^3.0.0",
|
||||
"prop-types": "^15.6.0",
|
||||
"warning": "^4.0.1"
|
||||
}
|
||||
},
|
||||
"@material-ui/utils": {
|
||||
"version": "3.0.0-alpha.3",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-3.0.0-alpha.3.tgz",
|
||||
"integrity": "sha512-rwMdMZptX0DivkqBuC+Jdq7BYTXwqKai5G5ejPpuEDKpWzi1Oxp+LygGw329FrKpuKeiqpcymlqJTjmy+quWng==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.2.0",
|
||||
"prop-types": "^15.6.0",
|
||||
"react-is": "^16.6.3"
|
||||
}
|
||||
},
|
||||
"@types/react-transition-group": {
|
||||
"version": "2.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-2.9.2.tgz",
|
||||
"integrity": "sha512-5Fv2DQNO+GpdPZcxp2x/OQG/H19A01WlmpjVD9cKvVFmoVLOZ9LvBgSWG6pSXIU4og5fgbvGPaCV5+VGkWAEHA==",
|
||||
"requires": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"jss": {
|
||||
"version": "9.8.7",
|
||||
"resolved": "https://registry.npmjs.org/jss/-/jss-9.8.7.tgz",
|
||||
"integrity": "sha512-awj3XRZYxbrmmrx9LUSj5pXSUfm12m8xzi/VKeqI1ZwWBtQ0kVPTs3vYs32t4rFw83CgFDukA8wKzOE9sMQnoQ==",
|
||||
"requires": {
|
||||
"is-in-browser": "^1.1.3",
|
||||
"symbol-observable": "^1.1.0",
|
||||
"warning": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"warning": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz",
|
||||
"integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=",
|
||||
"requires": {
|
||||
"loose-envify": "^1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"moment": {
|
||||
"version": "2.24.0",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
|
||||
@@ -18083,6 +18114,14 @@
|
||||
"pure-color": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"react-beforeunload": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/react-beforeunload/-/react-beforeunload-2.2.1.tgz",
|
||||
"integrity": "sha512-qydpYQuGUaB1C5EP2EFN0YjPo9kgV10Z4suIZ79GIhp0DWNJxHyuR8BiTViOBltTIn9PwlpSqX4fE3uprsdqKA==",
|
||||
"requires": {
|
||||
"prop-types": "^15.7.2"
|
||||
}
|
||||
},
|
||||
"react-chartjs-2": {
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-2.8.0.tgz",
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
"version": "0.3.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@material-ui/core": "^3.9.3",
|
||||
"@material-ui/core": "^4.5.2",
|
||||
"@material-ui/icons": "^4.5.1",
|
||||
"@material-ui/styles": "^4.5.0",
|
||||
"@material-ui/styles": "^4.5.2",
|
||||
"@use-it/interval": "^0.1.3",
|
||||
"class-transformer": "^0.2.0",
|
||||
"create-react-app": "^2.0.3",
|
||||
@@ -32,6 +32,7 @@
|
||||
"react": "^16.10.2",
|
||||
"react-alert": "^5.5.0",
|
||||
"react-alert-template-basic": "^1.0.0",
|
||||
"react-beforeunload": "^2.2.1",
|
||||
"react-chartjs-2": "^2.8.0",
|
||||
"react-cookie": "^4.0.1",
|
||||
"react-cytoscapejs": "^1.2.0",
|
||||
@@ -50,6 +51,7 @@
|
||||
"react-router-dom": "^4.3.1",
|
||||
"react-scripts": "^2.1.8",
|
||||
"reactstrap": "^7.1.0",
|
||||
"shellwords": "^0.1.1",
|
||||
"simplebar": "^4.2.3",
|
||||
"styled-components": "^4.4.0",
|
||||
"websocket": "^1.0.30",
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useInterval } from 'react-powerhooks';
|
||||
import uuid from "uuid";
|
||||
|
||||
import {Link} from 'react-router-dom';
|
||||
import { Prompt } from 'react-router'
|
||||
import TextField from '@material-ui/core/TextField';
|
||||
import Drawer from '@material-ui/core/Drawer';
|
||||
import Button from '@material-ui/core/Button';
|
||||
@@ -28,6 +29,7 @@ import Checkbox from '@material-ui/core/Checkbox';
|
||||
import Breadcrumbs from '@material-ui/core/Breadcrumbs';
|
||||
import CircularProgress from '@material-ui/core/CircularProgress';
|
||||
import ReactJson from 'react-json-view'
|
||||
import { useBeforeunload } from 'react-beforeunload';
|
||||
|
||||
import DirectionsRunIcon from '@material-ui/icons/DirectionsRun';
|
||||
import PolymerIcon from '@material-ui/icons/Polymer';
|
||||
@@ -169,6 +171,9 @@ const AngularWorkflow = (props) => {
|
||||
const [update, setUpdate] = useState("");
|
||||
const [workflowExecutions, setWorkflowExecutions] = React.useState([]);
|
||||
|
||||
const unloadText = 'Are you sure you want to leave?'
|
||||
useBeforeunload(() => unloadText)
|
||||
|
||||
const [elements, setElements] = useState([])
|
||||
const { start, stop } = useInterval({
|
||||
duration: 2500,
|
||||
@@ -176,7 +181,7 @@ const AngularWorkflow = (props) => {
|
||||
callback: () => {
|
||||
fetchUpdates()
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
const getWorkflowExecution = (id) => {
|
||||
fetch(globalUrl+"/api/v1/workflows/"+id+"/executions", {
|
||||
@@ -798,16 +803,17 @@ const AngularWorkflow = (props) => {
|
||||
}
|
||||
|
||||
const onUnselect = (event) => {
|
||||
//console.log("Unselect?")
|
||||
console.time("UNSELECT")
|
||||
|
||||
// FIXME - check if they have value before overriding like this for no reason.
|
||||
// Would save a lot of time (400~ ms -> 30ms)
|
||||
//setSelectedActionName({})
|
||||
//console.log("ACTION: ", selectedAction)
|
||||
//console.log("APP: ", selectedApp)
|
||||
setSelectedAction({})
|
||||
setSelectedApp({})
|
||||
setSelectedTrigger({})
|
||||
setSelectedEdge({})
|
||||
//setSelectedApp({})
|
||||
//setSelectedTrigger({})
|
||||
//setSelectedEdge({})
|
||||
|
||||
// setSelectedTriggerIndex(-1)
|
||||
//setSelectedActionEnvironment({})
|
||||
//setSelectedEdge({})
|
||||
@@ -821,10 +827,15 @@ const AngularWorkflow = (props) => {
|
||||
}
|
||||
|
||||
const onEdgeSelect = (event) => {
|
||||
setRightSideBarOpen(true)
|
||||
|
||||
const triggercheck = workflow.triggers.find(trigger => trigger.id === event.target.data()["source"])
|
||||
if (triggercheck === undefined) {
|
||||
setSelectedEdgeIndex(workflow.branches.findIndex(data => data.id === event.target.data()["id"]))
|
||||
setSelectedEdge(event.target.data())
|
||||
|
||||
setSelectedAction({})
|
||||
setSelectedTrigger({})
|
||||
} else {
|
||||
alert.info("Can't edit branches from triggers")
|
||||
}
|
||||
@@ -837,7 +848,7 @@ const AngularWorkflow = (props) => {
|
||||
if (data.type === "ACTION") {
|
||||
// FIXME - unselect
|
||||
//console.log(cy.elements('[_id!="${data._id}"]`))
|
||||
console.time('ACTIONSTART');
|
||||
// Does it choose the wrong action?
|
||||
const curaction = workflow.actions.find(a => a.id === data.id)
|
||||
if (!curaction || curaction === undefined) {
|
||||
//console.log("Action not found error")
|
||||
@@ -855,25 +866,24 @@ const AngularWorkflow = (props) => {
|
||||
env = environments[0]
|
||||
}
|
||||
|
||||
console.log("Selected: ", data.id)
|
||||
console.log(curaction)
|
||||
|
||||
setRequiresAuthentication(curapp.authentication.required)
|
||||
setSelectedApp(curapp)
|
||||
setSelectedAction(curaction)
|
||||
setSelectedActionEnvironment(env)
|
||||
setSelectedActionName(curaction.name)
|
||||
setSelectedAction(curaction)
|
||||
setRequiresAuthentication(curapp.authentication.required)
|
||||
} else if (data.type === "TRIGGER") {
|
||||
//console.log("Should handle trigger "+data.triggertype)
|
||||
//console.log(data)
|
||||
|
||||
setSelectedTriggerIndex(workflow.triggers.findIndex(a => a.id === data.id))
|
||||
setSelectedTrigger(data)
|
||||
setSelectedActionEnvironment(data.env)
|
||||
setSelectedActionName(data.name)
|
||||
setSelectedActionEnvironment(data.env)
|
||||
} else {
|
||||
console.log("Should handle type "+data.type)
|
||||
alert.error("Can't handle "+data.type)
|
||||
}
|
||||
|
||||
setRightSideBarOpen(true)
|
||||
}
|
||||
|
||||
const onEdgeAdded = (event) => {
|
||||
@@ -2103,8 +2113,9 @@ const AngularWorkflow = (props) => {
|
||||
const setNewSelectedAction = (e) => {
|
||||
const newaction = selectedApp.actions.find(a => a.name === e.target.value)
|
||||
|
||||
// Does this one find the wrong one?
|
||||
selectedAction.name = newaction.name
|
||||
selectedAction.parameters = newaction.parameters
|
||||
selectedAction.parameters = JSON.parse(JSON.stringify(newaction.parameters))
|
||||
|
||||
// FIXME - this is broken sometimes lol
|
||||
//var env = environments.find(a => a.name === newaction.environment)
|
||||
@@ -2180,7 +2191,7 @@ const AngularWorkflow = (props) => {
|
||||
// Dropdown -> static, action, local env, global env
|
||||
// VALUE (JSON)
|
||||
// {data.name}, {data.description}, {data.required}, {data.schema.type}
|
||||
const AppActionArguments = () => {
|
||||
const AppActionArguments = (props) => {
|
||||
const [selectedActionParameters, setSelectedActionParameters] = React.useState([])
|
||||
const [selectedVariableParameter, setSelectedVariableParameter] = React.useState()
|
||||
|
||||
@@ -2189,7 +2200,10 @@ const AngularWorkflow = (props) => {
|
||||
if (requiresAuthentication) {
|
||||
console.log("ADD AUTHENTICATION FIELDS")
|
||||
}
|
||||
setSelectedActionParameters(selectedAction.parameters)
|
||||
|
||||
if (selectedAction.parameters !== null && selectedAction.parameters.length > 0) {
|
||||
setSelectedActionParameters(selectedAction.parameters)
|
||||
}
|
||||
}
|
||||
|
||||
if ((selectedVariableParameter === null || selectedVariableParameter === undefined) && (workflow.workflow_variables !== null && workflow.workflow_variables.length > 0)) {
|
||||
@@ -2201,20 +2215,20 @@ const AngularWorkflow = (props) => {
|
||||
|
||||
const changeActionParameter = (event, count) => {
|
||||
selectedActionParameters[count].value = event.target.value
|
||||
selectedAction.parameters = selectedActionParameters
|
||||
selectedAction.parameters[count].value = event.target.value
|
||||
setSelectedAction(selectedAction)
|
||||
//setUpdate(event.target.value)
|
||||
}
|
||||
|
||||
|
||||
const changeActionParameterVariable = (fieldvalue, count) => {
|
||||
setSelectedVariableParameter(fieldvalue)
|
||||
|
||||
// this isn't updated anywhere in the workflow
|
||||
setSelectedActionName({})
|
||||
setSelectedAction({})
|
||||
setSelectedTrigger({})
|
||||
setSelectedApp({})
|
||||
setSelectedEdge({})
|
||||
// setSelectedActionName({})
|
||||
// setSelectedAction({})
|
||||
// setSelectedTrigger({})
|
||||
// setSelectedApp({})
|
||||
// setSelectedEdge({})
|
||||
|
||||
selectedActionParameters[count].action_field = fieldvalue
|
||||
selectedAction.parameters = selectedActionParameters
|
||||
@@ -2222,6 +2236,7 @@ const AngularWorkflow = (props) => {
|
||||
setSelectedActionName(selectedActionName)
|
||||
setSelectedApp(selectedApp)
|
||||
setSelectedAction(selectedAction)
|
||||
setUpdate(fieldvalue)
|
||||
}
|
||||
|
||||
// Sets ACTION_RESULT things
|
||||
@@ -2236,11 +2251,6 @@ const AngularWorkflow = (props) => {
|
||||
selectedActionParameters[count].action_field = fieldvalue
|
||||
selectedAction.parameters = selectedActionParameters
|
||||
|
||||
setSelectedActionName({})
|
||||
setSelectedAction({})
|
||||
setSelectedTrigger({})
|
||||
setSelectedApp({})
|
||||
setSelectedEdge({})
|
||||
// FIXME - check if startnode
|
||||
|
||||
// Set value
|
||||
@@ -2248,6 +2258,7 @@ const AngularWorkflow = (props) => {
|
||||
setSelectedApp(selectedApp)
|
||||
|
||||
setSelectedAction(selectedAction)
|
||||
setUpdate(fieldvalue)
|
||||
}
|
||||
|
||||
const changeActionParameterVariant = (data, count) => {
|
||||
@@ -2283,7 +2294,8 @@ const AngularWorkflow = (props) => {
|
||||
setSelectedAction(selectedAction)
|
||||
}
|
||||
|
||||
if (Object.getOwnPropertyNames(selectedAction).length > 0 && selectedActionParameters) {
|
||||
// FIXME: Issue #40 - selectedActionParameters not reset
|
||||
if (Object.getOwnPropertyNames(selectedAction).length > 0 && selectedActionParameters.length > 0) {
|
||||
return (
|
||||
<div style={{marginTop: "30px"}}>
|
||||
<b>Arguments</b>
|
||||
@@ -2300,7 +2312,7 @@ const AngularWorkflow = (props) => {
|
||||
multiline = true
|
||||
}
|
||||
|
||||
if (data.value.startsWith("{") && data.value.endsWith("}")) {
|
||||
if (data.value !== undefined && data.value.startsWith("{") && data.value.endsWith("}")) {
|
||||
multiline = true
|
||||
}
|
||||
|
||||
@@ -2577,7 +2589,7 @@ const AngularWorkflow = (props) => {
|
||||
/>
|
||||
|
||||
<div style={{marginTop: "20px"}}>
|
||||
Environment:
|
||||
Environment
|
||||
<Select
|
||||
value={selectedActionEnvironment === undefined || selectedActionEnvironment.Name === undefined ? "" : selectedActionEnvironment.Name}
|
||||
PaperProps={{
|
||||
@@ -2624,10 +2636,11 @@ const AngularWorkflow = (props) => {
|
||||
value={selectedActionName}
|
||||
fullWidth
|
||||
onChange={setNewSelectedAction}
|
||||
style={{backgroundColor: inputColor, color: "white", height: "50px"}}
|
||||
style={{backgroundColor: inputColor, color: "white", height: 50}}
|
||||
SelectDisplayProps={{
|
||||
style: {
|
||||
marginLeft: 10,
|
||||
maxHeight: 200,
|
||||
}
|
||||
}}
|
||||
>
|
||||
@@ -2651,7 +2664,7 @@ const AngularWorkflow = (props) => {
|
||||
})}
|
||||
</Select>
|
||||
<div style={{marginTop: "10px", borderColor: "white", borderWidth: "2px", marginBottom: 200}}>
|
||||
<AppActionArguments key={"hey"} />
|
||||
<AppActionArguments key={selectedAction.id} selectedAction={selectedAction} />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@@ -4118,7 +4131,7 @@ const AngularWorkflow = (props) => {
|
||||
<div style={{marginTop: "20px", marginBottom: "7px", display: "flex"}}>
|
||||
<div style={{width: "17px", height: "17px", borderRadius: 17 / 2, backgroundColor: "#f85a3e", marginRight: "10px"}}/>
|
||||
<div style={{flex: "10"}}>
|
||||
<b>Run how often (seconds)? </b>
|
||||
<b>Interval (seconds) </b>
|
||||
</div>
|
||||
</div>
|
||||
<TextField
|
||||
@@ -4323,9 +4336,12 @@ const AngularWorkflow = (props) => {
|
||||
}
|
||||
|
||||
const RightSideBar = () => {
|
||||
if (!rightSideBarOpen) {
|
||||
return null
|
||||
}
|
||||
|
||||
setLastSaved(false)
|
||||
if (Object.getOwnPropertyNames(selectedAction).length > 0 && Object.getOwnPropertyNames(selectedApp).length > 0) {
|
||||
setRightSideBarOpen(true)
|
||||
//console.time('ACTIONSTART');
|
||||
return(
|
||||
<div style={rightsidebarStyle}>
|
||||
@@ -4334,7 +4350,6 @@ const AngularWorkflow = (props) => {
|
||||
)
|
||||
} else if (Object.getOwnPropertyNames(selectedTrigger).length > 0) {
|
||||
if (selectedTrigger.trigger_type === "SCHEDULE") {
|
||||
setRightSideBarOpen(true)
|
||||
console.log("SCHEDULE")
|
||||
return(
|
||||
<div style={rightsidebarStyle}>
|
||||
@@ -4342,24 +4357,18 @@ const AngularWorkflow = (props) => {
|
||||
</div>
|
||||
)
|
||||
} else if (selectedTrigger.trigger_type === "WEBHOOK") {
|
||||
setRightSideBarOpen(true)
|
||||
console.log("WEBHOOK")
|
||||
return(
|
||||
<div style={rightsidebarStyle}>
|
||||
<WebhookSidebar />
|
||||
</div>
|
||||
)
|
||||
} else if (selectedTrigger.trigger_type === "EMAIL") {
|
||||
setRightSideBarOpen(true)
|
||||
console.log("EMAIL")
|
||||
return(
|
||||
<div style={rightsidebarStyle}>
|
||||
<EmailSidebar />
|
||||
</div>
|
||||
)
|
||||
} else if (selectedTrigger.trigger_type === "USERINPUT") {
|
||||
setRightSideBarOpen(true)
|
||||
console.log("USER INPUT SIDEBAR")
|
||||
return(
|
||||
<div style={rightsidebarStyle}>
|
||||
<UserinputSidebar />
|
||||
@@ -4372,7 +4381,6 @@ const AngularWorkflow = (props) => {
|
||||
return null
|
||||
}
|
||||
} else if (Object.getOwnPropertyNames(selectedEdge).length > 0) {
|
||||
setRightSideBarOpen(true)
|
||||
return(
|
||||
<div style={rightsidebarStyle}>
|
||||
<EdgeSidebar />
|
||||
@@ -4478,7 +4486,7 @@ const AngularWorkflow = (props) => {
|
||||
}
|
||||
</div>
|
||||
:
|
||||
<div style={{padding: 25, }}>
|
||||
<div style={{padding: 25, maxWidth: 365, overflowX: "hidden",}}>
|
||||
<Breadcrumbs aria-label="breadcrumb" separator="›" style={{color: "white", fontSize: 16}}>
|
||||
<h2 style={{color: "rgba(255,255,255,0.5)", cursor: "pointer"}} onClick={() => {setExecutionModalView(0)}}>
|
||||
<DirectionsRunIcon style={{marginRight: 10}} />
|
||||
@@ -4489,7 +4497,7 @@ const AngularWorkflow = (props) => {
|
||||
<h2>Executing Workflow</h2>
|
||||
{executionData.execution_argument !== undefined && executionData.execution_argument.length > 0 ?
|
||||
<div>
|
||||
<h3>Execution Argument: </h3>{executionText}
|
||||
<h3>Execution Argument: </h3>{executionData.execution_argument}
|
||||
</div>
|
||||
: null }
|
||||
{executionData.status !== undefined && executionData.status.length > 0 ?
|
||||
@@ -4514,7 +4522,7 @@ const AngularWorkflow = (props) => {
|
||||
<div style={{display: "flex", marginTop: 10, marginBottom: 30,}}>
|
||||
<b>Actions</b>
|
||||
<div>
|
||||
{executionData.status !== undefined && executionData.status !== "ABORTED" && executionData.status !== "FINISHED" ? <CircularProgress style={{marginLeft: 20}}/> : null}
|
||||
{executionData.status !== undefined && executionData.status !== "ABORTED" && executionData.status !== "FINISHED" && executionData.status !== "FAILURE" ? <CircularProgress style={{marginLeft: 20}}/> : null}
|
||||
</div>
|
||||
</div>
|
||||
{executionData.results === undefined || executionData.results === null || executionData.results.length === 0 && executionData.status === "EXECUTING" ?
|
||||
@@ -4841,6 +4849,10 @@ const AngularWorkflow = (props) => {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Prompt
|
||||
when={true}
|
||||
message={unloadText}
|
||||
/>
|
||||
{loadedCheck}
|
||||
</div>
|
||||
)
|
||||
|
||||
+460
-96
@@ -2,6 +2,7 @@ import React, {useState, useEffect} from 'react';
|
||||
import { makeStyles } from '@material-ui/styles';
|
||||
import {BrowserView, MobileView} from "react-device-detect";
|
||||
|
||||
import {Link} from 'react-router-dom';
|
||||
import Paper from '@material-ui/core/Paper';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import Divider from '@material-ui/core/Divider';
|
||||
@@ -15,9 +16,12 @@ import DialogActions from '@material-ui/core/DialogActions';
|
||||
import TextField from '@material-ui/core/TextField';
|
||||
import Tooltip from '@material-ui/core/Tooltip';
|
||||
import CheckCircleIcon from '@material-ui/icons/CheckCircle';
|
||||
import Breadcrumbs from '@material-ui/core/Breadcrumbs';
|
||||
import AppsIcon from '@material-ui/icons/Apps';
|
||||
|
||||
import ErrorOutline from '@material-ui/icons/ErrorOutline';
|
||||
import { useAlert } from "react-alert";
|
||||
import words from "shellwords"
|
||||
|
||||
const surfaceColor = "#27292D"
|
||||
const inputColor = "#383B40"
|
||||
@@ -39,6 +43,7 @@ const actionListStyle = {
|
||||
}
|
||||
|
||||
const boxStyle = {
|
||||
color: "white",
|
||||
flex: "1",
|
||||
marginLeft: "10px",
|
||||
marginRight: "10px",
|
||||
@@ -55,7 +60,119 @@ const useStyles = makeStyles({
|
||||
notchedOutline: {
|
||||
borderColor: "#f85a3e !important"
|
||||
},
|
||||
});
|
||||
})
|
||||
|
||||
|
||||
const rewrite = (args) => {
|
||||
return args.reduce(function(args, a){
|
||||
if (0 == a.indexOf('-X')) {
|
||||
args.push('-X')
|
||||
args.push(a.slice(2))
|
||||
} else {
|
||||
args.push(a)
|
||||
}
|
||||
|
||||
return args
|
||||
}, [])
|
||||
}
|
||||
|
||||
const parseField = (s) => {
|
||||
return s.split(/: (.+)/)
|
||||
}
|
||||
|
||||
const isURL = (s) => {
|
||||
return /^https?:\/\//.test(s)
|
||||
}
|
||||
|
||||
// Parses CURL to a real request
|
||||
const parseCurl = (s) => {
|
||||
//console.log("CURL: ", s)
|
||||
|
||||
if (0 != s.indexOf('curl ')) {
|
||||
console.log("Not curl start")
|
||||
return ""
|
||||
}
|
||||
|
||||
var args = rewrite(words.split(s))
|
||||
var out = { method: 'GET', header: {} }
|
||||
var state = ''
|
||||
|
||||
args.forEach(function(arg){
|
||||
switch (true) {
|
||||
case isURL(arg):
|
||||
out.url = arg
|
||||
break;
|
||||
|
||||
case arg == '-A' || arg == '--user-agent':
|
||||
state = 'user-agent'
|
||||
break;
|
||||
|
||||
case arg == '-H' || arg == '--header':
|
||||
state = 'header'
|
||||
break;
|
||||
|
||||
case arg == '-d' || arg == '--data' || arg == '--data-ascii':
|
||||
state = 'data'
|
||||
break;
|
||||
|
||||
case arg == '-u' || arg == '--user':
|
||||
state = 'user'
|
||||
break;
|
||||
|
||||
case arg == '-I' || arg == '--head':
|
||||
out.method = 'HEAD'
|
||||
break;
|
||||
|
||||
case arg == '-X' || arg == '--request':
|
||||
state = 'method'
|
||||
break;
|
||||
|
||||
case arg == '-b' || arg =='--cookie':
|
||||
state = 'cookie'
|
||||
break;
|
||||
|
||||
case arg == '--compressed':
|
||||
out.header['Accept-Encoding'] = out.header['Accept-Encoding'] || 'deflate, gzip'
|
||||
break;
|
||||
|
||||
case !!arg:
|
||||
switch (state) {
|
||||
case 'header':
|
||||
var field = parseField(arg)
|
||||
out.header[field[0]] = field[1]
|
||||
state = ''
|
||||
break;
|
||||
case 'user-agent':
|
||||
out.header['User-Agent'] = arg
|
||||
state = ''
|
||||
break;
|
||||
case 'data':
|
||||
if (out.method == 'GET' || out.method == 'HEAD') out.method = 'POST'
|
||||
out.header['Content-Type'] = out.header['Content-Type'] || 'application/x-www-form-urlencoded'
|
||||
out.body = out.body
|
||||
? out.body + '&' + arg
|
||||
: arg
|
||||
state = ''
|
||||
break;
|
||||
case 'user':
|
||||
out.header['Authorization'] = 'Basic ' + btoa(arg)
|
||||
state = ''
|
||||
break;
|
||||
case 'method':
|
||||
out.method = arg
|
||||
state = ''
|
||||
break;
|
||||
case 'cookie':
|
||||
out.header['Set-Cookie'] = arg
|
||||
state = ''
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
})
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// Should be different if logged in :|
|
||||
const AppCreator = (props) => {
|
||||
@@ -117,16 +234,16 @@ const AppCreator = (props) => {
|
||||
|
||||
const [currentActionMethod, setCurrentActionMethod] = useState(actionNonBodyRequest[0])
|
||||
const [currentAction, setCurrentAction] = useState({
|
||||
"name": "",
|
||||
"description": "",
|
||||
"url": "",
|
||||
"headers": "",
|
||||
"paths": [],
|
||||
"queries": [],
|
||||
"body": "",
|
||||
"errors": [],
|
||||
"method": actionNonBodyRequest[0],
|
||||
});
|
||||
"name": "",
|
||||
"description": "",
|
||||
"url": "",
|
||||
"headers": "",
|
||||
"paths": [],
|
||||
"queries": [],
|
||||
"body": "",
|
||||
"errors": [],
|
||||
"method": actionNonBodyRequest[0],
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -144,13 +261,13 @@ const AppCreator = (props) => {
|
||||
|
||||
const handleEditApp = () => {
|
||||
fetch(globalUrl+"/api/v1/apps/"+props.match.params.appid+"/config", {
|
||||
method: 'GET',
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
window.location.pathname = "/apps"
|
||||
@@ -159,14 +276,12 @@ const AppCreator = (props) => {
|
||||
return response.json()
|
||||
})
|
||||
.then((responseJson) => {
|
||||
setIsAppLoaded(true)
|
||||
setIsAppLoaded(true)
|
||||
if (!responseJson.success) {
|
||||
alert.error("Failed to get the app")
|
||||
} else {
|
||||
const data = JSON.parse(responseJson.body)
|
||||
console.log("LOADED IMAGE: ", data.image)
|
||||
setFileBase64(data.image)
|
||||
parseOpenapiData(data)
|
||||
parseIncomingOpenapiData(data)
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
@@ -184,13 +299,13 @@ const AppCreator = (props) => {
|
||||
}
|
||||
|
||||
fetch(globalUrl+"/api/v1/get_openapi/"+urlParams.get("id"), {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
throw new Error("NOT 200 :O")
|
||||
@@ -199,12 +314,12 @@ const AppCreator = (props) => {
|
||||
return response.json()
|
||||
})
|
||||
.then((responseJson) => {
|
||||
setIsAppLoaded(true)
|
||||
setIsAppLoaded(true)
|
||||
if (!responseJson.success) {
|
||||
alert.error("Failed to verify")
|
||||
} else {
|
||||
const data = JSON.parse(responseJson.body)
|
||||
parseOpenapiData(data)
|
||||
parseIncomingOpenapiData(data)
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
@@ -227,14 +342,18 @@ const AppCreator = (props) => {
|
||||
}
|
||||
|
||||
// Sets the data up as it should be at later points
|
||||
const parseOpenapiData = (data) => {
|
||||
// This is the data FROM the database, not what's being saved
|
||||
const parseIncomingOpenapiData = (data) => {
|
||||
setBasedata(data)
|
||||
|
||||
|
||||
setName(data.info.title)
|
||||
setDescription(data.info.description)
|
||||
document.title = "Apps - "+data.info.title
|
||||
|
||||
if (data.info !== null && data.info !== undefined && data.info["x-logo"] !== undefined) {
|
||||
setFileBase64(data.info["x-logo"])
|
||||
}
|
||||
|
||||
if (data.info.contact != undefined) {
|
||||
setContact(data.info.contact)
|
||||
}
|
||||
@@ -243,8 +362,6 @@ const AppCreator = (props) => {
|
||||
setBaseUrl(data.servers[0].url)
|
||||
}
|
||||
|
||||
console.log(data)
|
||||
|
||||
// This is annoying (:
|
||||
var securitySchemes = data.components.securityDefinitions
|
||||
if (securitySchemes === undefined) {
|
||||
@@ -255,6 +372,7 @@ const AppCreator = (props) => {
|
||||
securitySchemes = data.components.securitySchemes
|
||||
}
|
||||
|
||||
// FIXME: Have multiple authentication options?
|
||||
if (securitySchemes !== undefined) {
|
||||
console.log("Am I in here?")
|
||||
for (const [key, value] of Object.entries(securitySchemes)) {
|
||||
@@ -263,7 +381,6 @@ const AppCreator = (props) => {
|
||||
break
|
||||
} else if (value.type === "apiKey") {
|
||||
setAuthenticationOption("API key")
|
||||
setParameterName(value.name)
|
||||
|
||||
value.in = value.in.charAt(0).toUpperCase() + value.in.slice(1);
|
||||
setParameterLocation(value.in)
|
||||
@@ -271,6 +388,8 @@ const AppCreator = (props) => {
|
||||
console.log("APIKEY SELECT: ", apikeySelection)
|
||||
alert.error("Might be error in setting up API key authentication")
|
||||
}
|
||||
|
||||
setParameterName(value.name)
|
||||
break
|
||||
} else if (value.scheme === "basic") {
|
||||
setAuthenticationOption("Basic auth")
|
||||
@@ -279,6 +398,8 @@ const AppCreator = (props) => {
|
||||
}
|
||||
}
|
||||
|
||||
console.log(data)
|
||||
|
||||
// FIXME - headers?
|
||||
var newActions = []
|
||||
for (let [path, pathvalue] of Object.entries(data.paths)) {
|
||||
@@ -295,8 +416,6 @@ const AppCreator = (props) => {
|
||||
"errors": [],
|
||||
}
|
||||
|
||||
//console.log(`${path}: ${method}`);
|
||||
//console.log(methodvalue)
|
||||
|
||||
for (var key in methodvalue.parameters) {
|
||||
const parameter = methodvalue.parameters[key]
|
||||
@@ -316,6 +435,16 @@ const AppCreator = (props) => {
|
||||
} else if (parameter.in === "path") {
|
||||
// FIXME - parse this to the URL too
|
||||
newaction.paths.push(parameter.name)
|
||||
|
||||
// FIXME: This doesn't follow OpenAPI3 exactly.
|
||||
// https://swagger.io/docs/specification/describing-request-body/
|
||||
// https://swagger.io/docs/specification/describing-parameters/
|
||||
// Need to split the data.
|
||||
} else if (parameter.in === "body") {
|
||||
console.log("BODY: ", parameter)
|
||||
newaction.body = parameter.example
|
||||
} else if (parameter.in === "header") {
|
||||
newaction.headers += `${parameter.name}=${parameter.example}\n`
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,12 +452,12 @@ const AppCreator = (props) => {
|
||||
}
|
||||
}
|
||||
|
||||
console.log(newActions)
|
||||
setActions(newActions)
|
||||
}
|
||||
|
||||
// Saving the app that's been configured.
|
||||
const submitApp = () => {
|
||||
alert.info("Uploading private app " + name)
|
||||
alert.info("Uploading and building app " + name)
|
||||
setErrorCode("")
|
||||
|
||||
// Format the information
|
||||
@@ -343,6 +472,7 @@ const AppCreator = (props) => {
|
||||
"title": name,
|
||||
"description": description,
|
||||
"version": "1.0",
|
||||
"x-logo": fileBase64,
|
||||
},
|
||||
"servers": [{"url": baseUrl}],
|
||||
"host": host,
|
||||
@@ -353,7 +483,6 @@ const AppCreator = (props) => {
|
||||
"components": {
|
||||
"securitySchemes": {},
|
||||
},
|
||||
"image": fileBase64,
|
||||
"id": props.match.params.appid,
|
||||
"securityDefinitions": {},
|
||||
}
|
||||
@@ -368,7 +497,7 @@ const AppCreator = (props) => {
|
||||
data.info["contact"] = contact
|
||||
}
|
||||
|
||||
console.log("LOADED IMAGE: ", data.image)
|
||||
//console.log("LOADED IMAGE: ", data.image)
|
||||
|
||||
for (var key in actions) {
|
||||
const item = actions[key]
|
||||
@@ -440,6 +569,68 @@ const AppCreator = (props) => {
|
||||
//console.log(queryitem)
|
||||
}
|
||||
}
|
||||
|
||||
if (item.body.length > 0) {
|
||||
const required = false
|
||||
newitem = {
|
||||
"in": "body",
|
||||
"name": "body",
|
||||
"multiline": true,
|
||||
"description": "Generated by shuffler.io OpenAPI",
|
||||
"required": required,
|
||||
"example": item.body,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
},
|
||||
}
|
||||
|
||||
// FIXME - add application/json if JSON example?
|
||||
data.paths[item.url][item.method.toLowerCase()]["requestBody"] = {
|
||||
"description": "Generated by Shuffler.io",
|
||||
"required": required,
|
||||
"content": {
|
||||
"example": {
|
||||
"example": item.body,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
data.paths[item.url][item.method.toLowerCase()].parameters.push(newitem)
|
||||
}
|
||||
|
||||
if (item.headers.length > 0) {
|
||||
const required = false
|
||||
|
||||
const headersSplit = item.headers.split("\n")
|
||||
for (var key in headersSplit) {
|
||||
const header = headersSplit[key]
|
||||
var key = ""
|
||||
var value = ""
|
||||
if (header.length > 0 && header.includes("=")) {
|
||||
const headersplit = header.split("=")
|
||||
key = headersplit[0]
|
||||
value = headersplit[1]
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
|
||||
if (key.length > 0 && value.length > 0) {
|
||||
newitem = {
|
||||
"in": "header",
|
||||
"name": key,
|
||||
"multiline": false,
|
||||
"description": "Header generated by shuffler.io OpenAPI",
|
||||
"required": false,
|
||||
"example": value,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
},
|
||||
}
|
||||
|
||||
data.paths[item.url][item.method.toLowerCase()].parameters.push(newitem)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (authenticationOption === "API key") {
|
||||
@@ -461,8 +652,10 @@ const AppCreator = (props) => {
|
||||
}
|
||||
}
|
||||
|
||||
console.log("ACTIONS: ", data.paths)
|
||||
|
||||
fetch(globalUrl+"/api/v1/verify_openapi", {
|
||||
method: 'POST',
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
@@ -480,12 +673,17 @@ const AppCreator = (props) => {
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (!responseJson.success) {
|
||||
setErrorCode(responseJson.reason)
|
||||
alert.error("Failed to verify: ")
|
||||
if (responseJson.reason !== undefined) {
|
||||
setErrorCode(responseJson.reason)
|
||||
alert.error("Failed to verify: "+responseJson.reason)
|
||||
}
|
||||
} else {
|
||||
// Return?
|
||||
alert.success("Successfully uploaded openapi")
|
||||
//window.location = "/apps"
|
||||
if (window.location.pathname.includes("/new")) {
|
||||
if (responseJson.id !== undefined && responseJson.id !== null) {
|
||||
window.location = `/apps/edit/${responseJson.id}`
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
@@ -495,9 +693,9 @@ const AppCreator = (props) => {
|
||||
}
|
||||
|
||||
const bearerAuth = authenticationOption === "Bearer auth" ?
|
||||
<div>
|
||||
<div style={{color: "white"}}>
|
||||
<h4>
|
||||
<a href="https://swagger.io/docs/specification/authentication/bearer-authentication/" style={{textDecoriation: "none", color: "#f85a3e"}}>
|
||||
<a target="_blank" href="https://swagger.io/docs/specification/authentication/bearer-authentication/" style={{textDecoriation: "none", color: "#f85a3e"}}>
|
||||
Bearer auth
|
||||
</a>
|
||||
</h4>
|
||||
@@ -508,9 +706,9 @@ const AppCreator = (props) => {
|
||||
|
||||
// Basicauth
|
||||
const basicAuth = authenticationOption === "Basic auth" ?
|
||||
<div>
|
||||
<div style={{color: "white"}}>
|
||||
<h4>
|
||||
<a href="https://swagger.io/docs/specification/authentication/basic-authentication/" style={{textDecoriation: "none", color: "#f85a3e"}}>
|
||||
<a target="_blank" href="https://swagger.io/docs/specification/authentication/basic-authentication/" style={{textDecoriation: "none", color: "#f85a3e"}}>
|
||||
Basic authentication
|
||||
</a>
|
||||
</h4>
|
||||
@@ -596,11 +794,11 @@ const AppCreator = (props) => {
|
||||
setActions(actions)
|
||||
}
|
||||
|
||||
console.log("Option: ", authenticationOption)
|
||||
console.log("Location: ", parameterLocation)
|
||||
console.log("Name: ", parameterName)
|
||||
//console.log("Option: ", authenticationOption)
|
||||
//console.log("Location: ", parameterLocation)
|
||||
//console.log("Name: ", parameterName)
|
||||
const apiKey = authenticationOption === "API key" ?
|
||||
<div>
|
||||
<div style={{color: "white"}}>
|
||||
<h4>API key</h4>
|
||||
<TextField
|
||||
required
|
||||
@@ -745,11 +943,14 @@ const AppCreator = (props) => {
|
||||
const setActionField = (field, value) => {
|
||||
currentAction[field] = value
|
||||
setCurrentAction(currentAction)
|
||||
//if (updater !== value) {
|
||||
// setUpdater(value)
|
||||
//}
|
||||
}
|
||||
|
||||
const bodyInfo = actionBodyRequest.includes(currentActionMethod) ?
|
||||
<div>
|
||||
Body
|
||||
Body - used as example in action argument
|
||||
<TextField
|
||||
required
|
||||
style={{flex: "1", marginRight: "15px", backgroundColor: inputColor}}
|
||||
@@ -780,10 +981,7 @@ const AppCreator = (props) => {
|
||||
currentAction.queries = urlPathQueries
|
||||
setUrlPathQueries([])
|
||||
|
||||
console.log(actions)
|
||||
console.log(currentAction.name)
|
||||
const actionIndex = actions.findIndex(data => data.name === currentAction.name)
|
||||
console.log(actionIndex)
|
||||
if (actionIndex < 0) {
|
||||
actions.push(currentAction)
|
||||
} else {
|
||||
@@ -811,20 +1009,34 @@ const AppCreator = (props) => {
|
||||
errormessage.push("All queries must have a value")
|
||||
}
|
||||
|
||||
console.log(urlPathParameters)
|
||||
// const [urlPathParameters, setUrlPathParameters] = useState([]);
|
||||
|
||||
return errormessage
|
||||
}
|
||||
|
||||
const UrlPathParameters = () => {
|
||||
var paths = []
|
||||
var queries = []
|
||||
|
||||
if (urlPath.includes("{") && urlPath.includes("}")) {
|
||||
var values = []
|
||||
var tmpWord = ""
|
||||
var record = false
|
||||
|
||||
var query = false
|
||||
for (var key in urlPath) {
|
||||
if (urlPath[key] === "?") {
|
||||
query = true
|
||||
}
|
||||
|
||||
if (urlPath[key] === "}") {
|
||||
values.push(tmpWord)
|
||||
if (tmpWord === parameterName) {
|
||||
tmpWord = ""
|
||||
record = false
|
||||
continue
|
||||
} else if (query) {
|
||||
queries.push(tmpWord)
|
||||
} else {
|
||||
paths.push(tmpWord)
|
||||
}
|
||||
|
||||
tmpWord = ""
|
||||
record = false
|
||||
}
|
||||
@@ -833,24 +1045,74 @@ const AppCreator = (props) => {
|
||||
tmpWord += urlPath[key]
|
||||
}
|
||||
|
||||
if (urlPath[key] === "{" && urlPath[key-1] === "/") {
|
||||
//if (urlPath[key] === "{" && urlPath[key-1] === "/") {
|
||||
if (urlPath[key] === "{") {
|
||||
record = true
|
||||
}
|
||||
}
|
||||
|
||||
if (!currentAction.paths === values) {
|
||||
currentAction.paths = values
|
||||
setCurrentAction(currentAction)
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
Required parameters: {values.join(", ")}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
if (urlPath.includes("<") && urlPath.includes(">")) {
|
||||
var tmpWord = ""
|
||||
var record = false
|
||||
|
||||
var query = false
|
||||
for (var key in urlPath) {
|
||||
if (urlPath[key] === "?") {
|
||||
query = true
|
||||
}
|
||||
|
||||
if (urlPath[key] === ">") {
|
||||
if (tmpWord === parameterName) {
|
||||
tmpWord = ""
|
||||
record = false
|
||||
continue
|
||||
} else if (query) {
|
||||
queries.push(tmpWord)
|
||||
} else {
|
||||
paths.push(tmpWord)
|
||||
}
|
||||
|
||||
tmpWord = ""
|
||||
record = false
|
||||
}
|
||||
|
||||
if (record) {
|
||||
tmpWord += urlPath[key]
|
||||
}
|
||||
|
||||
//if (urlPath[key] === "{" && urlPath[key-1] === "/") {
|
||||
if (urlPath[key] === "<") {
|
||||
record = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (currentAction.paths !== paths) {
|
||||
setActionField("paths", paths)
|
||||
}
|
||||
|
||||
var tmpQueries = []
|
||||
|
||||
// No overlapping of names
|
||||
for (var key in queries) {
|
||||
const tmpquery = queries[key]
|
||||
const found = tmpQueries.find(query => query.name === tmpquery)
|
||||
if (found === undefined) {
|
||||
tmpQueries.push({"name": queries[key], required: true})
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Frontend isn't updating..
|
||||
if (JSON.stringify(tmpQueries) !== JSON.stringify(urlPathQueries)) {
|
||||
setUrlPathQueries(tmpQueries)
|
||||
}
|
||||
|
||||
return paths.length > 0 ?
|
||||
<div>
|
||||
Required parameters: {paths.join(", ")}
|
||||
</div>
|
||||
: null
|
||||
}
|
||||
|
||||
const newActionModal =
|
||||
@@ -858,6 +1120,7 @@ const AppCreator = (props) => {
|
||||
open={actionsModalOpen}
|
||||
fullWidth
|
||||
onClose={() => {
|
||||
console.log("CLOSED?")
|
||||
setUrlPath("")
|
||||
setCurrentAction({
|
||||
"name": "",
|
||||
@@ -878,12 +1141,12 @@ const AppCreator = (props) => {
|
||||
<FormControl style={{backgroundColor: surfaceColor, color: "white",}}>
|
||||
<DialogTitle><div style={{color: "white"}}>New action</div></DialogTitle>
|
||||
<DialogContent>
|
||||
<a href="/docs/apps#actions" style={{textDecoration: "none", color: "#f85a3e"}}>Learn more about app creation</a>
|
||||
<Link target="_blank" to="/docs/apps#actions" style={{textDecoration: "none", color: "#f85a3e"}}>Learn more about actions</Link>
|
||||
<div style={{marginTop: "15px"}}/>
|
||||
Name
|
||||
<TextField
|
||||
required
|
||||
style={{flex: "1", marginTop: "5px", marginRight: "15px", backgroundColor: inputColor}}
|
||||
style={{flex: "1", marginTop: 5, marginRight: 15, backgroundColor: inputColor}}
|
||||
fullWidth={true}
|
||||
placeholder="Name"
|
||||
type="name"
|
||||
@@ -891,7 +1154,19 @@ const AppCreator = (props) => {
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
defaultValue={currentAction["name"]}
|
||||
onChange={e => setActionField("name", e.target.value)}
|
||||
onChange={e => {
|
||||
setActionField("name", e.target.value)
|
||||
}}
|
||||
onBlur={e => {
|
||||
// Fix basic issues in frontend. Python functions run a-zA-Z0-9_
|
||||
console.log(e.target.value)
|
||||
const regex = /[A-Za-z0-9 _]/g;
|
||||
const found = e.target.value.match(regex);
|
||||
console.log("FOUND: ", found)
|
||||
if (found !== null) {
|
||||
setActionField("name", found.join(""))
|
||||
}
|
||||
}}
|
||||
key={currentAction}
|
||||
InputProps={{
|
||||
classes: {
|
||||
@@ -902,7 +1177,7 @@ const AppCreator = (props) => {
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<div style={{marginTop: "10px"}}/>
|
||||
<div style={{marginTop: 10}}/>
|
||||
Description
|
||||
<TextField
|
||||
required
|
||||
@@ -951,7 +1226,7 @@ const AppCreator = (props) => {
|
||||
))}
|
||||
</Select>
|
||||
<div style={{marginTop: "15px"}} />
|
||||
URL path
|
||||
URL path / Curl statement
|
||||
<TextField
|
||||
required
|
||||
style={{flex: "1", marginRight: "15px", marginTop: "5px", backgroundColor: inputColor}}
|
||||
@@ -966,7 +1241,7 @@ const AppCreator = (props) => {
|
||||
setUrlPath(e.target.value)
|
||||
console.log(e.target.value)
|
||||
}}
|
||||
helperText={<div style={{color:"white", marginBottom: "2px",}}>The path to use. Must start with /. Add {"{variable}"} to have path variables</div>}
|
||||
helperText={<div style={{color:"white", marginBottom: "2px",}}>The path to use. Must start with /. Use {"{variablename}"} to have path variables</div>}
|
||||
InputProps={{
|
||||
classes: {
|
||||
notchedOutline: classes.notchedOutline,
|
||||
@@ -976,6 +1251,61 @@ const AppCreator = (props) => {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
onBlur={event => {
|
||||
var parsedurl = event.target.value
|
||||
if (parsedurl.startsWith("curl")) {
|
||||
const request = parseCurl(event.target.value)
|
||||
console.log(request)
|
||||
if (request.method.toUpperCase() !== currentAction.Method) {
|
||||
setCurrentActionMethod(request.method.toUpperCase())
|
||||
setActionField("method", request.method.toUpperCase())
|
||||
}
|
||||
|
||||
if (request.header !== undefined && request.header !== null) {
|
||||
var headers = []
|
||||
for (let [key, value] of Object.entries(request.header)) {
|
||||
headers += key+"="+value+"\n"
|
||||
}
|
||||
|
||||
setActionField("headers", headers)
|
||||
}
|
||||
|
||||
if (request.body !== undefined && request.body !== null) {
|
||||
setActionField("body", request.body)
|
||||
}
|
||||
|
||||
// Parse URL
|
||||
parsedurl = request.url
|
||||
}
|
||||
|
||||
if (parsedurl.includes("<") && parsedurl.includes(">")) {
|
||||
parsedurl = parsedurl.split("<").join("{")
|
||||
parsedurl = parsedurl.split(">").join("}")
|
||||
}
|
||||
|
||||
if (parsedurl.startsWith("http") || parsedurl.startsWith("ftp")) {
|
||||
if (parsedurl !== undefined && parsedurl.includes(parameterName)) {
|
||||
// Remove <> etc.
|
||||
//
|
||||
|
||||
console.log("IT HAS THE PARAM NAME!")
|
||||
const newurl = new URL(encodeURI(parsedurl))
|
||||
newurl.searchParams.delete(parameterName)
|
||||
parsedurl = decodeURI(newurl.href)
|
||||
}
|
||||
|
||||
// Remove the base URL itself
|
||||
if (parsedurl !== undefined && baseUrl !== undefined && baseUrl.length > 0 && parsedurl.includes(baseUrl)) {
|
||||
parsedurl = parsedurl.replace(baseUrl, "")
|
||||
}
|
||||
|
||||
// Check URL query && headers
|
||||
setActionField("url", parsedurl)
|
||||
setUrlPath(parsedurl)
|
||||
}
|
||||
|
||||
//console.log("URL: ", request.url)
|
||||
}}
|
||||
/>
|
||||
<UrlPathParameters />
|
||||
{loopQueries}
|
||||
@@ -983,7 +1313,7 @@ const AppCreator = (props) => {
|
||||
addPathQuery()
|
||||
}}>New query</Button>
|
||||
<div/>
|
||||
Headers
|
||||
Headers - static for the action
|
||||
<TextField
|
||||
required
|
||||
style={{flex: "1", marginRight: "15px", marginTop: "5px", backgroundColor: inputColor}}
|
||||
@@ -1009,15 +1339,16 @@ const AppCreator = (props) => {
|
||||
{bodyInfo}
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button style={{borderRadius: "0px"}} onClick={() => {
|
||||
setActionsModalOpen(false)}}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button color="primary" variant="outlined" style={{borderRadius: "0px"}} onClick={() => {
|
||||
<Button style={{borderRadius: "0px"}} onClick={() => {
|
||||
setActionsModalOpen(false)}}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button color="primary" variant="outlined" style={{borderRadius: "0px"}} onClick={() => {
|
||||
const errors = getActionErrors()
|
||||
addActionToView(errors)
|
||||
setActionsModalOpen(false)
|
||||
setUrlPathQueries([])
|
||||
setUrlPath("")
|
||||
}}>
|
||||
Submit
|
||||
</Button>
|
||||
@@ -1029,7 +1360,7 @@ const AppCreator = (props) => {
|
||||
<div style={{color: "white"}}>
|
||||
<h2>Actions</h2>
|
||||
Actions are the tasks performed by an app. Read more about actions and apps
|
||||
<a href="/docs/apps#actions" style={{textDecoration: "none", color: "#f85a3e"}}> here</a>.
|
||||
<Link target="_blank" to="/docs/apps#actions" style={{textDecoration: "none", color: "#f85a3e"}}> here</Link>.
|
||||
<div>
|
||||
{loopActions}
|
||||
<Button color="primary" style={{marginTop: "20px", borderRadius: "0px"}} variant="outlined" onClick={() => {
|
||||
@@ -1044,7 +1375,7 @@ const AppCreator = (props) => {
|
||||
"errors": [],
|
||||
"method": actionNonBodyRequest[0],
|
||||
})
|
||||
setCurrentActionMethod(actionNonBodyRequest[0])
|
||||
setCurrentActionMethod(actionNonBodyRequest[0])
|
||||
setActionsModalOpen(true)
|
||||
}}>New action</Button>
|
||||
</div>
|
||||
@@ -1054,7 +1385,7 @@ const AppCreator = (props) => {
|
||||
<div style={{color: "white"}}>
|
||||
<h2>Test</h2>
|
||||
Test an action to see whether it performs in an expected way.
|
||||
<a href="/docs/apps#testing" style={{textDecoration: "none", color: "#f85a3e"}}> Click here to learn more about testing</a>.
|
||||
<Link target="_blank" to="/docs/apps#testing" style={{textDecoration: "none", color: "#f85a3e"}}> TBD: Click here to learn more about testing</Link>.
|
||||
<div>
|
||||
Test :)
|
||||
</div>
|
||||
@@ -1071,14 +1402,23 @@ const AppCreator = (props) => {
|
||||
if (file !== "") {
|
||||
const img = document.getElementById('logo')
|
||||
var canvas = document.createElement('canvas')
|
||||
canvas.width = 174
|
||||
canvas.height = 174
|
||||
var ctx = canvas.getContext('2d')
|
||||
|
||||
img.onload = function() {
|
||||
// img, x, y, width, height
|
||||
ctx.drawImage(img, 0, 0)
|
||||
//ctx.drawImage(img, 174, 174)
|
||||
console.log("IMG natural: ", img.naturalWidth, img.naturalHeight)
|
||||
//ctx.drawImage(img, 0, 0, 174, 174)
|
||||
ctx.drawImage(img,
|
||||
0, 0, img.width, img.height,
|
||||
0, 0, canvas.width, canvas.height
|
||||
)
|
||||
|
||||
const canvasUrl = canvas.toDataURL()
|
||||
console.log(canvasUrl)
|
||||
if (canvasUrl !== fileBase64) {
|
||||
console.log("SET URL TO: ", canvasUrl)
|
||||
setFileBase64(canvasUrl)
|
||||
}
|
||||
}
|
||||
@@ -1096,14 +1436,25 @@ const AppCreator = (props) => {
|
||||
// <img src={file} id="logo" style={{width: "100%", height: "100%"}} />
|
||||
|
||||
const imageData = file.length > 0 ? file : fileBase64
|
||||
const imageInfo = <img src={imageData} alt="Click to upload an image" id="logo" style={{width: 174, height: 174}} />
|
||||
const imageInfo = <img src={imageData} alt="Click to upload an image" id="logo" style={{maxWidth: 174, maxHeight: 174,}} />
|
||||
|
||||
// Random names for type & autoComplete. Didn't research :^)
|
||||
const landingpageDataBrowser =
|
||||
<div style={{paddingBottom: 100, color: "white",}}>
|
||||
<Breadcrumbs aria-label="breadcrumb" separator="›" style={{color: "white",}}>
|
||||
<Link to="/apps" style={{textDecoration: "none", color: "inherit",}}>
|
||||
<h2 style={{color: "rgba(255,255,255,0.5)"}}>
|
||||
<AppsIcon style={{marginRight: 10}} />
|
||||
Apps
|
||||
</h2>
|
||||
</Link>
|
||||
<h2>
|
||||
{name}
|
||||
</h2>
|
||||
</Breadcrumbs>
|
||||
<Paper style={boxStyle}>
|
||||
<h2 style={{marginBottom: "10px", color: "white"}}>General information</h2>
|
||||
<a href="/docs/apps#create" style={{textDecoration: "none", color: "#f85a3e"}}>Click here to learn more about app creation</a>
|
||||
<Link target="_blank" to="/docs/apps#create_openapi_app" style={{textDecoration: "none", color: "#f85a3e"}}>Click here to learn more about app creation</Link>
|
||||
<div style={{color: "white", flex: "1", display: "flex", flexDirection: "row"}}>
|
||||
<Tooltip title="Click to edit the app's image" placement="bottom">
|
||||
<div style={{flex: "1", margin: 10, border: "1px solid #f85a3e", cursor: "pointer", backgroundColor: inputColor, maxWidth: 174, maxHeight: 174}} onClick={() => {upload.click()}}>
|
||||
@@ -1120,11 +1471,11 @@ const AppCreator = (props) => {
|
||||
fullWidth={true}
|
||||
placeholder="Name"
|
||||
type="name"
|
||||
id="standard-required"
|
||||
id="standard-required"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
value={name}
|
||||
onChange={e => setName(e.target.value)}
|
||||
onChange={e => setName(e.target.value)}
|
||||
color="primary"
|
||||
InputProps={{
|
||||
style:{
|
||||
@@ -1187,6 +1538,19 @@ const AppCreator = (props) => {
|
||||
helperText={<div style={{color:"white", marginBottom: "2px",}}>Must start with http(s):// and CANT end with /. </div>}
|
||||
placeholder="https://api.example.com"
|
||||
onChange={e => setBaseUrl(e.target.value)}
|
||||
onBlur={(event) => {
|
||||
var tmpstring = event.target.value.trim()
|
||||
if (tmpstring.endsWith("/")) {
|
||||
tmpstring = tmpstring.slice(0, -1)
|
||||
}
|
||||
if (tmpstring.length > 4 && !tmpstring.startsWith("http") && !tmpstring.startsWith("ftp")) {
|
||||
alert.error("URL must start with http(s)://")
|
||||
}
|
||||
|
||||
//if (authenticationOption === "No authentication" &&
|
||||
|
||||
setBaseUrl(tmpstring)
|
||||
}}
|
||||
/>
|
||||
<FormControl style={{marginTop: "15px",}} variant="outlined">
|
||||
<h5 style={{marginBottom: "10px", color: "white",}}>Authentication</h5>
|
||||
@@ -1196,7 +1560,7 @@ const AppCreator = (props) => {
|
||||
setAuthenticationOption(e.target.value)
|
||||
}}
|
||||
value={authenticationOption}
|
||||
style={{backgroundColor: inputColor, paddingLeft: "10px", color: "white", height: "50px"}}
|
||||
style={{backgroundColor: inputColor, color: "white", height: "50px"}}
|
||||
>
|
||||
{authenticationOptions.map(data => (
|
||||
<MenuItem style={{backgroundColor: inputColor, color: "white"}} value={data}>
|
||||
@@ -1219,7 +1583,7 @@ const AppCreator = (props) => {
|
||||
}}>
|
||||
Save
|
||||
</Button>
|
||||
{errorCode}
|
||||
{errorCode.length > 0 ? `Error: ${errorCode}` : null}
|
||||
</Paper>
|
||||
</div>
|
||||
|
||||
|
||||
+112
-60
@@ -2,6 +2,7 @@ import React, { useEffect} from 'react';
|
||||
|
||||
import { useInterval } from 'react-powerhooks';
|
||||
|
||||
import AppsIcon from '@material-ui/icons/Apps';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import Select from '@material-ui/core/Select';
|
||||
import Paper from '@material-ui/core/Paper';
|
||||
@@ -17,8 +18,12 @@ import Switch from '@material-ui/core/Switch';
|
||||
import Input from '@material-ui/core/Input';
|
||||
import YAML from 'yaml'
|
||||
import {Link} from 'react-router-dom';
|
||||
import Breadcrumbs from '@material-ui/core/Breadcrumbs';
|
||||
|
||||
import CloudDownload from '@material-ui/icons/CloudDownload';
|
||||
import EditIcon from '@material-ui/icons/Edit';
|
||||
import DeleteIcon from '@material-ui/icons/Delete';
|
||||
|
||||
import { useAlert } from "react-alert";
|
||||
|
||||
import Dialog from '@material-ui/core/Dialog';
|
||||
@@ -117,10 +122,12 @@ const Apps = (props) => {
|
||||
setFilteredApps(responseJson)
|
||||
if (responseJson.length > 0) {
|
||||
setSelectedApp(responseJson[0])
|
||||
if (responseJson[0].actions.length > 0) {
|
||||
if (responseJson[0].actions !== null && responseJson[0].actions.length > 0) {
|
||||
setSelectedAction(responseJson[0].actions[0])
|
||||
} else {
|
||||
setSelectedAction({})
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
alert.error(error.toString())
|
||||
@@ -130,15 +137,15 @@ const Apps = (props) => {
|
||||
const downloadApp = (inputdata) => {
|
||||
const id = inputdata.id
|
||||
|
||||
alert.info("Preparing download.")
|
||||
alert.info("Downloading..")
|
||||
fetch(globalUrl+"/api/v1/apps/"+id+"/config", {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
window.location.pathname = "/apps"
|
||||
@@ -150,12 +157,22 @@ const Apps = (props) => {
|
||||
if (!responseJson.success) {
|
||||
alert.error("Failed to download file")
|
||||
} else {
|
||||
const data = YAML.stringify(YAML.parse(responseJson.body))
|
||||
const inputdata = YAML.parse(responseJson.body)
|
||||
const newpaths = {}
|
||||
Object.keys(inputdata["paths"]).forEach(function(key) {
|
||||
newpaths[key.split("?")[0]] = inputdata.paths[key]
|
||||
})
|
||||
|
||||
var name = inputdata.name
|
||||
inputdata.paths = newpaths
|
||||
console.log("INPUT: ", inputdata)
|
||||
var name = inputdata.info.title
|
||||
name = name.replace(/ /g, "_", -1)
|
||||
name = name.toLowerCase()
|
||||
|
||||
delete inputdata.id
|
||||
delete inputdata.editing
|
||||
|
||||
const data = YAML.stringify(inputdata)
|
||||
var blob = new Blob( [ data ], {
|
||||
type: 'application/octet-stream'
|
||||
})
|
||||
@@ -222,9 +239,11 @@ const Apps = (props) => {
|
||||
<Paper square style={paperAppStyle} onClick={() => {
|
||||
if (selectedApp.id !== data.id) {
|
||||
setSelectedApp(data)
|
||||
if (data.actions.length > 0) {
|
||||
if (data.actions !== undefined && data.actions !== null && data.actions.length > 0) {
|
||||
console.log(data.actions[0])
|
||||
setSelectedAction(data.actions[0])
|
||||
} else {
|
||||
setSelectedAction({})
|
||||
}
|
||||
}
|
||||
}}>
|
||||
@@ -250,14 +269,15 @@ const Apps = (props) => {
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{data.activated && data.private_id !== undefined && data.private_id.length > 0 && data.generated ?
|
||||
<Grid container style={{margin: "10px 10px 10px 10px", flex: "1"}} onClick={() => {downloadApp(data)}}>
|
||||
{/*
|
||||
<Tooltip title={"Download"} style={{marginTop: "28px", width: "100%"}} aria-label={data.name}>
|
||||
<CloudDownload />
|
||||
</Tooltip>
|
||||
*/}
|
||||
</Grid>
|
||||
: null}
|
||||
</Paper>
|
||||
)
|
||||
}
|
||||
@@ -290,15 +310,28 @@ const Apps = (props) => {
|
||||
|
||||
const editUrl = "/apps/edit/"+selectedApp.id
|
||||
const activateUrl = "/apps/new?id="+selectedApp.id
|
||||
|
||||
var downloadButton = selectedApp.activated && selectedApp.private_id !== undefined && selectedApp.private_id.length > 0 && selectedApp.generated ?
|
||||
<Button
|
||||
onClick={() => {downloadApp(selectedApp)}}
|
||||
variant="outlined"
|
||||
component="label"
|
||||
color="primary"
|
||||
style={{marginTop: 10, marginRight: 8}}
|
||||
>
|
||||
<CloudDownload />
|
||||
</Button>
|
||||
: null
|
||||
|
||||
var editButton = selectedApp.activated && selectedApp.private_id !== undefined && selectedApp.private_id.length > 0 && selectedApp.generated ?
|
||||
<Link to={editUrl} style={{textDecoration: "none"}}>
|
||||
<Button
|
||||
variant="outlined"
|
||||
component="label"
|
||||
color="primary"
|
||||
style={{marginTop: "10px"}}
|
||||
style={{marginTop: 10, marginRight: 10,}}
|
||||
>
|
||||
Edit app
|
||||
<EditIcon />
|
||||
</Button></Link> : null
|
||||
|
||||
var activateButton = selectedApp.generated && !selectedApp.activated ?
|
||||
@@ -307,7 +340,7 @@ const Apps = (props) => {
|
||||
variant="contained"
|
||||
component="label"
|
||||
color="primary"
|
||||
style={{marginTop: "10px"}}
|
||||
style={{marginTop: 10}}
|
||||
>
|
||||
Activate App
|
||||
</Button></Link> : null
|
||||
@@ -322,7 +355,7 @@ const Apps = (props) => {
|
||||
deleteApp(selectedApp.id)
|
||||
}}
|
||||
>
|
||||
Delete app
|
||||
<DeleteIcon />
|
||||
</Button> : null
|
||||
|
||||
var imageline = selectedApp.large_image === undefined || selectedApp.large_image.length === 0 ?
|
||||
@@ -343,6 +376,7 @@ const Apps = (props) => {
|
||||
</div>
|
||||
</div>
|
||||
{activateButton}
|
||||
{downloadButton}
|
||||
{editButton}
|
||||
{deleteButton}
|
||||
<Divider style={{marginBottom: "10px", marginTop: "10px", backgroundColor: dividerColor}}/>
|
||||
@@ -352,36 +386,42 @@ const Apps = (props) => {
|
||||
|
||||
<div style={{marginTop: 15, marginBottom: 15}}>
|
||||
<b>Actions</b>
|
||||
<Select
|
||||
fullWidth
|
||||
value={selectedAction}
|
||||
onChange={(event) => {
|
||||
setSelectedAction(event.target.value)
|
||||
}}
|
||||
style={{backgroundColor: inputColor, color: "white", height: "50px"}}
|
||||
SelectDisplayProps={{
|
||||
style: {
|
||||
marginLeft: 10,
|
||||
}
|
||||
}}
|
||||
>
|
||||
{selectedApp.actions.map(data => {
|
||||
var newActionname = data.label !== undefined && data.label.length > 0 ? data.label : data.name
|
||||
{selectedApp.actions !== null && selectedApp.actions.length > 0 ?
|
||||
<Select
|
||||
fullWidth
|
||||
value={selectedAction}
|
||||
onChange={(event) => {
|
||||
setSelectedAction(event.target.value)
|
||||
}}
|
||||
style={{backgroundColor: inputColor, color: "white", height: "50px"}}
|
||||
SelectDisplayProps={{
|
||||
style: {
|
||||
marginLeft: 10,
|
||||
}
|
||||
}}
|
||||
>
|
||||
{selectedApp.actions.map(data => {
|
||||
var newActionname = data.label !== undefined && data.label.length > 0 ? data.label : data.name
|
||||
|
||||
// ROFL FIXME - loop
|
||||
newActionname = newActionname.replace("_", " ")
|
||||
newActionname = newActionname.replace("_", " ")
|
||||
newActionname = newActionname.replace("_", " ")
|
||||
newActionname = newActionname.replace("_", " ")
|
||||
newActionname = newActionname.charAt(0).toUpperCase()+newActionname.substring(1)
|
||||
return (
|
||||
<MenuItem style={{backgroundColor: inputColor, color: "white"}} value={data}>
|
||||
{newActionname}
|
||||
// ROFL FIXME - loop
|
||||
newActionname = newActionname.replace("_", " ")
|
||||
newActionname = newActionname.replace("_", " ")
|
||||
newActionname = newActionname.replace("_", " ")
|
||||
newActionname = newActionname.replace("_", " ")
|
||||
newActionname = newActionname.charAt(0).toUpperCase()+newActionname.substring(1)
|
||||
return (
|
||||
<MenuItem style={{backgroundColor: inputColor, color: "white"}} value={data}>
|
||||
{newActionname}
|
||||
|
||||
</MenuItem>
|
||||
)
|
||||
})}
|
||||
</Select>
|
||||
</MenuItem>
|
||||
)
|
||||
})}
|
||||
</Select>
|
||||
:
|
||||
<div style={{marginTop: 10}}>
|
||||
There are no actions defined for this app.
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
{selectedAction.parameters !== undefined && selectedAction.parameters !== null ?
|
||||
@@ -453,8 +493,8 @@ const Apps = (props) => {
|
||||
)
|
||||
}
|
||||
|
||||
const handleSearchChange = (event) => {
|
||||
const searchfield = event.target.value.toLowerCase()
|
||||
const handleSearchChange = (search) => {
|
||||
const searchfield = search.toLowerCase()
|
||||
const newapps = apps.filter(data => data.name.toLowerCase().includes(searchfield) || data.description.toLowerCase().includes(searchfield))
|
||||
|
||||
if ((newapps.length === 0 || searchBackend) && !appSearchLoading) {
|
||||
@@ -469,9 +509,22 @@ const Apps = (props) => {
|
||||
const appView = isLoggedIn ?
|
||||
<div style={{maxWidth: 1366, margin: "auto",}}>
|
||||
<div style={appViewStyle}>
|
||||
<div style={{flex: "1", marginLeft: 10, marginRight: 10}}>
|
||||
<h2>Upload</h2>
|
||||
<div style={{marginTop: 20}}/>
|
||||
<div>
|
||||
<Breadcrumbs aria-label="breadcrumb" separator="›" style={{color: "white",}}>
|
||||
<Link to="/apps" style={{textDecoration: "none", color: "inherit",}}>
|
||||
<h2 style={{color: "rgba(255,255,255,0.5)"}}>
|
||||
<AppsIcon style={{marginRight: 10}} />
|
||||
App upload
|
||||
</h2>
|
||||
</Link>
|
||||
{selectedApp.activated && selectedApp.private_id !== undefined && selectedApp.private_id.length > 0 && selectedApp.generated ?
|
||||
<Link to={`/apps/edit/${selectedApp.id}`} style={{textDecoration: "none", color: "inherit",}}>
|
||||
<h2>
|
||||
{selectedApp.name}
|
||||
</h2>
|
||||
</Link>
|
||||
: null}
|
||||
</Breadcrumbs>
|
||||
<UploadView/>
|
||||
</div>
|
||||
<Divider style={{marginBottom: "10px", marginTop: "10px", height: "100%", width: "1px", backgroundColor: dividerColor}}/>
|
||||
@@ -484,7 +537,10 @@ const Apps = (props) => {
|
||||
<FormControlLabel
|
||||
style={{color: "white", marginBottom: "0px", marginTop: "10px"}}
|
||||
label=<div style={{color: "white"}}>Search OpenAPI</div>
|
||||
control={<Switch checked={searchBackend} onChange={() => {setSearchBackend(!searchBackend)}} />}
|
||||
control={<Switch checked={searchBackend} onChange={() => {
|
||||
handleSearchChange("")
|
||||
setSearchBackend(!searchBackend)}
|
||||
} />}
|
||||
/>
|
||||
<Button
|
||||
variant="outlined"
|
||||
@@ -514,7 +570,7 @@ const Apps = (props) => {
|
||||
color="primary"
|
||||
placeholder={"Search apps"}
|
||||
onChange={(event) => {
|
||||
handleSearchChange(event)
|
||||
handleSearchChange(event.target.value)
|
||||
}}
|
||||
/>
|
||||
<div style={{marginTop: 15}}>
|
||||
@@ -956,11 +1012,7 @@ const Apps = (props) => {
|
||||
</div>
|
||||
|
||||
// Maybe use gridview or something, idk
|
||||
return (
|
||||
<div>
|
||||
{loadedCheck}
|
||||
</div>
|
||||
)
|
||||
return loadedCheck
|
||||
}
|
||||
|
||||
export default Apps
|
||||
|
||||
@@ -886,15 +886,15 @@ const Workflows = (props) => {
|
||||
<div style={emptyWorkflowStyle}>
|
||||
<Paper style={boxStyle}>
|
||||
<div>
|
||||
<h2>Welcome to Shuffle!</h2>
|
||||
<h2>Welcome to Shuffle</h2>
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
<b>Shuffle</b> is a flexible, easy to use, automation framework allowing users to integrate their services and devices to reduce the amount of manual labor required for those tasks. <Link to="/docs/workflows" style={{textDecoration: "none", color: "#f85a3e"}}>Click here for more information.</Link>
|
||||
<b>Shuffle</b> is a flexible, easy to use, automation platform allowing users to integrate their services and devices freely. It's made to significantly reduce the amount of manual labor, and is focused on security applications. <Link to="/docs/about" style={{textDecoration: "none", color: "#f85a3e"}}>Click here to learn more.</Link>
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
If you want to jump straight into it, click the following button to create your first workflow:
|
||||
If you want to jump straight into it, click here to create your first workflow:
|
||||
</div>
|
||||
<div>
|
||||
<Button color="primary" style={{marginTop: "20px",}} variant="outlined" onClick={() => setModalOpen(true)}>New workflow</Button>
|
||||
|
||||
@@ -35,6 +35,8 @@ const data = [{
|
||||
'shape': 'square',
|
||||
'background-color': '#213243',
|
||||
'border-color': '#81c784',
|
||||
'background-width': '100%',
|
||||
'background-height': '100%',
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user