Merge pull request #42 from frikky/dev

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