Added sharing and fixed OpenAPI issue
This commit is contained in:
@@ -459,7 +459,7 @@ func generateYaml(swagger *openapi3.Swagger, newmd5 string) (*openapi3.Swagger,
|
|||||||
// Jesus what a clusterfuck.
|
// Jesus what a clusterfuck.
|
||||||
// Handles parsing of categories from OpenApi3 custom field
|
// Handles parsing of categories from OpenApi3 custom field
|
||||||
if val, ok := swagger.Info.ExtensionProps.Extensions["x-categories"]; ok {
|
if val, ok := swagger.Info.ExtensionProps.Extensions["x-categories"]; ok {
|
||||||
log.Printf("Categories: %#v", val)
|
//log.Printf("Categories: %#v", val)
|
||||||
j, err := json.Marshal(&val)
|
j, err := json.Marshal(&val)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if j[0] == 0x22 && j[len(j)-1] == 0x22 {
|
if j[0] == 0x22 && j[len(j)-1] == 0x22 {
|
||||||
|
|||||||
+17
-15
@@ -141,7 +141,7 @@ type UserLimits struct {
|
|||||||
|
|
||||||
// Saves some data, not sure what to have here lol
|
// Saves some data, not sure what to have here lol
|
||||||
type UserAuth struct {
|
type UserAuth struct {
|
||||||
Description string `json:"description" datastore:"description" yaml:"description"`
|
Description string `json:"description" datastore:"description,noindex" yaml:"description"`
|
||||||
Name string `json:"name" datastore:"name" yaml:"name"`
|
Name string `json:"name" datastore:"name" yaml:"name"`
|
||||||
Workflows []string `json:"workflows" datastore:"workflows"`
|
Workflows []string `json:"workflows" datastore:"workflows"`
|
||||||
Username string `json:"username" datastore:"username"`
|
Username string `json:"username" datastore:"username"`
|
||||||
@@ -210,7 +210,7 @@ type Translator struct {
|
|||||||
Src struct {
|
Src struct {
|
||||||
Name string `json:"name" datastore:"name"`
|
Name string `json:"name" datastore:"name"`
|
||||||
Value string `json:"value" datastore:"value"`
|
Value string `json:"value" datastore:"value"`
|
||||||
Description string `json:"description" datastore:"description"`
|
Description string `json:"description" datastore:"description,noindex"`
|
||||||
Required string `json:"required" datastore:"required"`
|
Required string `json:"required" datastore:"required"`
|
||||||
Type string `json:"type" datastore:"type"`
|
Type string `json:"type" datastore:"type"`
|
||||||
Schema struct {
|
Schema struct {
|
||||||
@@ -221,7 +221,7 @@ type Translator struct {
|
|||||||
Name string `json:"name" datastore:"name"`
|
Name string `json:"name" datastore:"name"`
|
||||||
Value string `json:"value" datastore:"value"`
|
Value string `json:"value" datastore:"value"`
|
||||||
Type string `json:"type" datastore:"type"`
|
Type string `json:"type" datastore:"type"`
|
||||||
Description string `json:"description" datastore:"description"`
|
Description string `json:"description" datastore:"description,noindex"`
|
||||||
Required string `json:"required" datastore:"required"`
|
Required string `json:"required" datastore:"required"`
|
||||||
Schema struct {
|
Schema struct {
|
||||||
Type string `json:"type" datastore:"type"`
|
Type string `json:"type" datastore:"type"`
|
||||||
@@ -284,7 +284,7 @@ type ApiYaml struct {
|
|||||||
Name string `json:"name" yaml:"name" required:"true datastore:"name"`
|
Name string `json:"name" yaml:"name" required:"true datastore:"name"`
|
||||||
Foldername string `json:"foldername" yaml:"foldername" required:"true datastore:"foldername"`
|
Foldername string `json:"foldername" yaml:"foldername" required:"true datastore:"foldername"`
|
||||||
Id string `json:"id" yaml:"id",required:"true, datastore:"id"`
|
Id string `json:"id" yaml:"id",required:"true, datastore:"id"`
|
||||||
Description string `json:"description" datastore:"description" yaml:"description"`
|
Description string `json:"description" datastore:"description,noindex" yaml:"description"`
|
||||||
AppVersion string `json:"app_version" yaml:"app_version",datastore:"app_version"`
|
AppVersion string `json:"app_version" yaml:"app_version",datastore:"app_version"`
|
||||||
ContactInfo struct {
|
ContactInfo struct {
|
||||||
Name string `json:"name" datastore:"name" yaml:"name"`
|
Name string `json:"name" datastore:"name" yaml:"name"`
|
||||||
@@ -293,10 +293,10 @@ type ApiYaml struct {
|
|||||||
Types []string `json:"types" datastore:"types" yaml:"types"`
|
Types []string `json:"types" datastore:"types" yaml:"types"`
|
||||||
Input []struct {
|
Input []struct {
|
||||||
Name string `json:"name" datastore:"name" yaml:"name"`
|
Name string `json:"name" datastore:"name" yaml:"name"`
|
||||||
Description string `json:"description" datastore:"description" yaml:"description"`
|
Description string `json:"description" datastore:"description,noindex" yaml:"description"`
|
||||||
InputParameters []struct {
|
InputParameters []struct {
|
||||||
Name string `json:"name" datastore:"name" yaml:"name"`
|
Name string `json:"name" datastore:"name" yaml:"name"`
|
||||||
Description string `json:"description" datastore:"description" yaml:"description"`
|
Description string `json:"description" datastore:"description,noindex" yaml:"description"`
|
||||||
Required string `json:"required" datastore:"required" yaml:"required"`
|
Required string `json:"required" datastore:"required" yaml:"required"`
|
||||||
Schema struct {
|
Schema struct {
|
||||||
Type string `json:"type" datastore:"type" yaml:"type"`
|
Type string `json:"type" datastore:"type" yaml:"type"`
|
||||||
@@ -304,7 +304,7 @@ type ApiYaml struct {
|
|||||||
} `json:"inputparameters" datastore:"inputparameters" yaml:"inputparameters"`
|
} `json:"inputparameters" datastore:"inputparameters" yaml:"inputparameters"`
|
||||||
OutputParameters []struct {
|
OutputParameters []struct {
|
||||||
Name string `json:"name" datastore:"name" yaml:"name"`
|
Name string `json:"name" datastore:"name" yaml:"name"`
|
||||||
Description string `json:"description" datastore:"description" yaml:"description"`
|
Description string `json:"description" datastore:"description,noindex" yaml:"description"`
|
||||||
Required string `json:"required" datastore:"required" yaml:"required"`
|
Required string `json:"required" datastore:"required" yaml:"required"`
|
||||||
Schema struct {
|
Schema struct {
|
||||||
Type string `json:"type" datastore:"type" yaml:"type"`
|
Type string `json:"type" datastore:"type" yaml:"type"`
|
||||||
@@ -312,7 +312,7 @@ type ApiYaml struct {
|
|||||||
} `json:"outputparameters" datastore:"outputparameters" yaml:"outputparameters"`
|
} `json:"outputparameters" datastore:"outputparameters" yaml:"outputparameters"`
|
||||||
Config []struct {
|
Config []struct {
|
||||||
Name string `json:"name" datastore:"name" yaml:"name"`
|
Name string `json:"name" datastore:"name" yaml:"name"`
|
||||||
Description string `json:"description" datastore:"description" yaml:"description"`
|
Description string `json:"description" datastore:"description,noindex" yaml:"description"`
|
||||||
Required string `json:"required" datastore:"required" yaml:"required"`
|
Required string `json:"required" datastore:"required" yaml:"required"`
|
||||||
Schema struct {
|
Schema struct {
|
||||||
Type string `json:"type" datastore:"type" yaml:"type"`
|
Type string `json:"type" datastore:"type" yaml:"type"`
|
||||||
@@ -321,10 +321,10 @@ type ApiYaml struct {
|
|||||||
} `json:"input" datastore:"input" yaml:"input"`
|
} `json:"input" datastore:"input" yaml:"input"`
|
||||||
Output []struct {
|
Output []struct {
|
||||||
Name string `json:"name" datastore:"name" yaml:"name"`
|
Name string `json:"name" datastore:"name" yaml:"name"`
|
||||||
Description string `json:"description" datastore:"description" yaml:"description"`
|
Description string `json:"description" datastore:"description,noindex" yaml:"description"`
|
||||||
Config []struct {
|
Config []struct {
|
||||||
Name string `json:"name" datastore:"name" yaml:"name"`
|
Name string `json:"name" datastore:"name" yaml:"name"`
|
||||||
Description string `json:"description" datastore:"description" yaml:"description"`
|
Description string `json:"description" datastore:"description,noindex" yaml:"description"`
|
||||||
Required string `json:"required" datastore:"required" yaml:"required"`
|
Required string `json:"required" datastore:"required" yaml:"required"`
|
||||||
Schema struct {
|
Schema struct {
|
||||||
Type string `json:"type" datastore:"type" yaml:"type"`
|
Type string `json:"type" datastore:"type" yaml:"type"`
|
||||||
@@ -332,7 +332,7 @@ type ApiYaml struct {
|
|||||||
} `json:"config" datastore:"config" yaml:"config"`
|
} `json:"config" datastore:"config" yaml:"config"`
|
||||||
InputParameters []struct {
|
InputParameters []struct {
|
||||||
Name string `json:"name" datastore:"name" yaml:"name"`
|
Name string `json:"name" datastore:"name" yaml:"name"`
|
||||||
Description string `json:"description" datastore:"description" yaml:"description"`
|
Description string `json:"description" datastore:"description,noindex" yaml:"description"`
|
||||||
Required string `json:"required" datastore:"required" yaml:"required"`
|
Required string `json:"required" datastore:"required" yaml:"required"`
|
||||||
Schema struct {
|
Schema struct {
|
||||||
Type string `json:"type" datastore:"type" yaml:"type"`
|
Type string `json:"type" datastore:"type" yaml:"type"`
|
||||||
@@ -340,7 +340,7 @@ type ApiYaml struct {
|
|||||||
} `json:"inputparameters" datastore:"inputparameters" yaml:"inputparameters"`
|
} `json:"inputparameters" datastore:"inputparameters" yaml:"inputparameters"`
|
||||||
OutputParameters []struct {
|
OutputParameters []struct {
|
||||||
Name string `json:"name" datastore:"name" yaml:"name"`
|
Name string `json:"name" datastore:"name" yaml:"name"`
|
||||||
Description string `json:"description" datastore:"description" yaml:"description"`
|
Description string `json:"description" datastore:"description,noindex" yaml:"description"`
|
||||||
Required string `json:"required" datastore:"required" yaml:"required"`
|
Required string `json:"required" datastore:"required" yaml:"required"`
|
||||||
Schema struct {
|
Schema struct {
|
||||||
Type string `json:"type" datastore:"type" yaml:"type"`
|
Type string `json:"type" datastore:"type" yaml:"type"`
|
||||||
@@ -357,7 +357,7 @@ type Hooks struct {
|
|||||||
type Info struct {
|
type Info struct {
|
||||||
Url string `json:"url" datastore:"url"`
|
Url string `json:"url" datastore:"url"`
|
||||||
Name string `json:"name" datastore:"name"`
|
Name string `json:"name" datastore:"name"`
|
||||||
Description string `json:"description" datastore:"description"`
|
Description string `json:"description" datastore:"description,noindex"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actions to be done by webhooks etc
|
// Actions to be done by webhooks etc
|
||||||
@@ -1697,10 +1697,11 @@ func handleInfo(resp http.ResponseWriter, request *http.Request) {
|
|||||||
"success": true,
|
"success": true,
|
||||||
"admin": %s,
|
"admin": %s,
|
||||||
"tutorials": [],
|
"tutorials": [],
|
||||||
|
"id": "%s",
|
||||||
"orgs": [{"name": "Shuffle", "id": "123", "role": "admin"}],
|
"orgs": [{"name": "Shuffle", "id": "123", "role": "admin"}],
|
||||||
"selected_org": {"name": "Shuffle", "id": "123", "role": "admin"},
|
"selected_org": {"name": "Shuffle", "id": "123", "role": "admin"},
|
||||||
"cookies": [{"key": "session_token", "value": "%s", "expiration": %d}]
|
"cookies": [{"key": "session_token", "value": "%s", "expiration": %d}]
|
||||||
}`, parsedAdmin, userInfo.Session, expiration.Unix())
|
}`, parsedAdmin, userInfo.Id, userInfo.Session, expiration.Unix())
|
||||||
|
|
||||||
resp.WriteHeader(200)
|
resp.WriteHeader(200)
|
||||||
resp.Write([]byte(returnData))
|
resp.Write([]byte(returnData))
|
||||||
@@ -2505,7 +2506,7 @@ func handleCors(resp http.ResponseWriter, request *http.Request) bool {
|
|||||||
|
|
||||||
resp.Header().Set("Vary", "Origin")
|
resp.Header().Set("Vary", "Origin")
|
||||||
resp.Header().Set("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With, remember-me")
|
resp.Header().Set("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With, remember-me")
|
||||||
resp.Header().Set("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE")
|
resp.Header().Set("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE, PATCH")
|
||||||
resp.Header().Set("Access-Control-Allow-Credentials", "true")
|
resp.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||||
resp.Header().Set("Access-Control-Allow-Origin", allowedOrigins)
|
resp.Header().Set("Access-Control-Allow-Origin", allowedOrigins)
|
||||||
|
|
||||||
@@ -6497,6 +6498,7 @@ func init() {
|
|||||||
r.HandleFunc("/api/v1/apps/run_hotload", handleAppHotloadRequest).Methods("GET", "OPTIONS")
|
r.HandleFunc("/api/v1/apps/run_hotload", handleAppHotloadRequest).Methods("GET", "OPTIONS")
|
||||||
r.HandleFunc("/api/v1/apps/get_existing", loadSpecificApps).Methods("POST", "OPTIONS")
|
r.HandleFunc("/api/v1/apps/get_existing", loadSpecificApps).Methods("POST", "OPTIONS")
|
||||||
r.HandleFunc("/api/v1/apps/download_remote", loadSpecificApps).Methods("POST", "OPTIONS")
|
r.HandleFunc("/api/v1/apps/download_remote", loadSpecificApps).Methods("POST", "OPTIONS")
|
||||||
|
r.HandleFunc("/api/v1/apps/{appId}", updateWorkflowAppConfig).Methods("PATCH", "OPTIONS")
|
||||||
r.HandleFunc("/api/v1/apps/validate", validateAppInput).Methods("POST", "OPTIONS")
|
r.HandleFunc("/api/v1/apps/validate", validateAppInput).Methods("POST", "OPTIONS")
|
||||||
r.HandleFunc("/api/v1/apps/{appId}", deleteWorkflowApp).Methods("DELETE", "OPTIONS")
|
r.HandleFunc("/api/v1/apps/{appId}", deleteWorkflowApp).Methods("DELETE", "OPTIONS")
|
||||||
r.HandleFunc("/api/v1/apps/{appId}/config", getWorkflowAppConfig).Methods("GET", "OPTIONS")
|
r.HandleFunc("/api/v1/apps/{appId}/config", getWorkflowAppConfig).Methods("GET", "OPTIONS")
|
||||||
|
|||||||
+139
-38
@@ -96,25 +96,26 @@ type AuthenticationUsage struct {
|
|||||||
// An app inside Shuffle
|
// An app inside Shuffle
|
||||||
// Source string `json:"source" datastore:"soure" yaml:"source"` - downloadlocation
|
// Source string `json:"source" datastore:"soure" yaml:"source"` - downloadlocation
|
||||||
type WorkflowApp struct {
|
type WorkflowApp struct {
|
||||||
Name string `json:"name" yaml:"name" required:true datastore:"name"`
|
Name string `json:"name" yaml:"name" required:true datastore:"name"`
|
||||||
IsValid bool `json:"is_valid" yaml:"is_valid" required:true datastore:"is_valid"`
|
IsValid bool `json:"is_valid" yaml:"is_valid" required:true datastore:"is_valid"`
|
||||||
ID string `json:"id" yaml:"id,omitempty" required:false datastore:"id"`
|
ID string `json:"id" yaml:"id,omitempty" required:false datastore:"id"`
|
||||||
Link string `json:"link" yaml:"link" required:false datastore:"link,noindex"`
|
Link string `json:"link" yaml:"link" required:false datastore:"link,noindex"`
|
||||||
AppVersion string `json:"app_version" yaml:"app_version" required:true datastore:"app_version"`
|
AppVersion string `json:"app_version" yaml:"app_version" required:true datastore:"app_version"`
|
||||||
Generated bool `json:"generated" yaml:"generated" required:false datastore:"generated"`
|
SharingConfig string `json:"sharing_config" yaml:"sharing_config" datastore:"sharing_config"`
|
||||||
Downloaded bool `json:"downloaded" yaml:"downloaded" required:false datastore:"downloaded"`
|
Generated bool `json:"generated" yaml:"generated" required:false datastore:"generated"`
|
||||||
Sharing bool `json:"sharing" yaml:"sharing" required:false datastore:"sharing"`
|
Downloaded bool `json:"downloaded" yaml:"downloaded" required:false datastore:"downloaded"`
|
||||||
Verified bool `json:"verified" yaml:"verified" required:false datastore:"verified"`
|
Sharing bool `json:"sharing" yaml:"sharing" required:false datastore:"sharing"`
|
||||||
Activated bool `json:"activated" yaml:"activated" required:false datastore:"activated"`
|
Verified bool `json:"verified" yaml:"verified" required:false datastore:"verified"`
|
||||||
Tested bool `json:"tested" yaml:"tested" required:false datastore:"tested"`
|
Activated bool `json:"activated" yaml:"activated" required:false datastore:"activated"`
|
||||||
Owner string `json:"owner" datastore:"owner" yaml:"owner"`
|
Tested bool `json:"tested" yaml:"tested" required:false datastore:"tested"`
|
||||||
Hash string `json:"hash" datastore:"hash" yaml:"hash"` // api.yaml+dockerfile+src/app.py for apps
|
Owner string `json:"owner" datastore:"owner" yaml:"owner"`
|
||||||
PrivateID string `json:"private_id" yaml:"private_id" required:false datastore:"private_id"`
|
Hash string `json:"hash" datastore:"hash" yaml:"hash"` // api.yaml+dockerfile+src/app.py for apps
|
||||||
Description string `json:"description" datastore:"description,noindex" required:false yaml:"description"`
|
PrivateID string `json:"private_id" yaml:"private_id" required:false datastore:"private_id"`
|
||||||
Environment string `json:"environment" datastore:"environment" required:true yaml:"environment"`
|
Description string `json:"description" datastore:"description,noindex" required:false yaml:"description"`
|
||||||
SmallImage string `json:"small_image" datastore:"small_image,noindex" required:false yaml:"small_image"`
|
Environment string `json:"environment" datastore:"environment" required:true yaml:"environment"`
|
||||||
LargeImage string `json:"large_image" datastore:"large_image,noindex" yaml:"large_image" required:false`
|
SmallImage string `json:"small_image" datastore:"small_image,noindex" required:false yaml:"small_image"`
|
||||||
ContactInfo struct {
|
LargeImage string `json:"large_image" datastore:"large_image,noindex" yaml:"large_image" required:false`
|
||||||
|
ContactInfo struct {
|
||||||
Name string `json:"name" datastore:"name" yaml:"name"`
|
Name string `json:"name" datastore:"name" yaml:"name"`
|
||||||
Url string `json:"url" datastore:"url" yaml:"url"`
|
Url string `json:"url" datastore:"url" yaml:"url"`
|
||||||
} `json:"contact_info" datastore:"contact_info" yaml:"contact_info" required:false`
|
} `json:"contact_info" datastore:"contact_info" yaml:"contact_info" required:false`
|
||||||
@@ -144,7 +145,7 @@ type SchemaDefinition struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type WorkflowAppAction struct {
|
type WorkflowAppAction struct {
|
||||||
Description string `json:"description" datastore:"description"`
|
Description string `json:"description" datastore:"description,noindex"`
|
||||||
ID string `json:"id" datastore:"id" yaml:"id,omitempty"`
|
ID string `json:"id" datastore:"id" yaml:"id,omitempty"`
|
||||||
Name string `json:"name" datastore:"name"`
|
Name string `json:"name" datastore:"name"`
|
||||||
Label string `json:"label" datastore:"label"`
|
Label string `json:"label" datastore:"label"`
|
||||||
@@ -157,7 +158,7 @@ type WorkflowAppAction struct {
|
|||||||
Tested bool `json:"tested" datastore:"tested" yaml:"tested"`
|
Tested bool `json:"tested" datastore:"tested" yaml:"tested"`
|
||||||
Parameters []WorkflowAppActionParameter `json:"parameters" datastore: "parameters"`
|
Parameters []WorkflowAppActionParameter `json:"parameters" datastore: "parameters"`
|
||||||
ExecutionVariable struct {
|
ExecutionVariable struct {
|
||||||
Description string `json:"description" datastore:"description"`
|
Description string `json:"description" datastore:"description,noindex"`
|
||||||
ID string `json:"id" datastore:"id"`
|
ID string `json:"id" datastore:"id"`
|
||||||
Name string `json:"name" datastore:"name"`
|
Name string `json:"name" datastore:"name"`
|
||||||
Value string `json:"value" datastore:"value"`
|
Value string `json:"value" datastore:"value"`
|
||||||
@@ -192,7 +193,7 @@ type WorkflowExecution struct {
|
|||||||
Workflow Workflow `json:"workflow" datastore:"workflow,noindex"`
|
Workflow Workflow `json:"workflow" datastore:"workflow,noindex"`
|
||||||
Results []ActionResult `json:"results" datastore:"results,noindex"`
|
Results []ActionResult `json:"results" datastore:"results,noindex"`
|
||||||
ExecutionVariables []struct {
|
ExecutionVariables []struct {
|
||||||
Description string `json:"description" datastore:"description"`
|
Description string `json:"description" datastore:"description,noindex"`
|
||||||
ID string `json:"id" datastore:"id"`
|
ID string `json:"id" datastore:"id"`
|
||||||
Name string `json:"name" datastore:"name"`
|
Name string `json:"name" datastore:"name"`
|
||||||
Value string `json:"value" datastore:"value,noindex"`
|
Value string `json:"value" datastore:"value,noindex"`
|
||||||
@@ -217,7 +218,7 @@ type Action struct {
|
|||||||
Name string `json:"name" datastore:"name"`
|
Name string `json:"name" datastore:"name"`
|
||||||
Parameters []WorkflowAppActionParameter `json:"parameters" datastore: "parameters,noindex"`
|
Parameters []WorkflowAppActionParameter `json:"parameters" datastore: "parameters,noindex"`
|
||||||
ExecutionVariable struct {
|
ExecutionVariable struct {
|
||||||
Description string `json:"description" datastore:"description"`
|
Description string `json:"description" datastore:"description,noindex"`
|
||||||
ID string `json:"id" datastore:"id"`
|
ID string `json:"id" datastore:"id"`
|
||||||
Name string `json:"name" datastore:"name"`
|
Name string `json:"name" datastore:"name"`
|
||||||
Value string `json:"value" datastore:"value,noindex"`
|
Value string `json:"value" datastore:"value,noindex"`
|
||||||
@@ -235,7 +236,7 @@ type Action struct {
|
|||||||
// Added environment for location to execute
|
// Added environment for location to execute
|
||||||
type Trigger struct {
|
type Trigger struct {
|
||||||
AppName string `json:"app_name" datastore:"app_name"`
|
AppName string `json:"app_name" datastore:"app_name"`
|
||||||
Description string `json:"description" datastore:"description"`
|
Description string `json:"description" datastore:"description,noindex"`
|
||||||
LongDescription string `json:"long_description" datastore:"long_description"`
|
LongDescription string `json:"long_description" datastore:"long_description"`
|
||||||
Status string `json:"status" datastore:"status"`
|
Status string `json:"status" datastore:"status"`
|
||||||
AppVersion string `json:"app_version" datastore:"app_version"`
|
AppVersion string `json:"app_version" datastore:"app_version"`
|
||||||
@@ -294,20 +295,20 @@ type Workflow struct {
|
|||||||
ID string `json:"id" datastore:"id"`
|
ID string `json:"id" datastore:"id"`
|
||||||
IsValid bool `json:"is_valid" datastore:"is_valid"`
|
IsValid bool `json:"is_valid" datastore:"is_valid"`
|
||||||
Name string `json:"name" datastore:"name"`
|
Name string `json:"name" datastore:"name"`
|
||||||
Description string `json:"description" datastore:"description"`
|
Description string `json:"description" datastore:"description,noindex"`
|
||||||
Start string `json:"start" datastore:"start"`
|
Start string `json:"start" datastore:"start"`
|
||||||
Owner string `json:"owner" datastore:"owner"`
|
Owner string `json:"owner" datastore:"owner"`
|
||||||
Sharing string `json:"sharing" datastore:"sharing"`
|
Sharing string `json:"sharing" datastore:"sharing"`
|
||||||
Org []Org `json:"org,omitempty" datastore:"org"`
|
Org []Org `json:"org,omitempty" datastore:"org"`
|
||||||
ExecutingOrg Org `json:"execution_org,omitempty" datastore:"execution_org"`
|
ExecutingOrg Org `json:"execution_org,omitempty" datastore:"execution_org"`
|
||||||
WorkflowVariables []struct {
|
WorkflowVariables []struct {
|
||||||
Description string `json:"description" datastore:"description"`
|
Description string `json:"description" datastore:"description,noindex"`
|
||||||
ID string `json:"id" datastore:"id"`
|
ID string `json:"id" datastore:"id"`
|
||||||
Name string `json:"name" datastore:"name"`
|
Name string `json:"name" datastore:"name"`
|
||||||
Value string `json:"value" datastore:"value"`
|
Value string `json:"value" datastore:"value"`
|
||||||
} `json:"workflow_variables" datastore:"workflow_variables"`
|
} `json:"workflow_variables" datastore:"workflow_variables"`
|
||||||
ExecutionVariables []struct {
|
ExecutionVariables []struct {
|
||||||
Description string `json:"description" datastore:"description"`
|
Description string `json:"description" datastore:"description,noindex"`
|
||||||
ID string `json:"id" datastore:"id"`
|
ID string `json:"id" datastore:"id"`
|
||||||
Name string `json:"name" datastore:"name"`
|
Name string `json:"name" datastore:"name"`
|
||||||
Value string `json:"value" datastore:"value,noindex"`
|
Value string `json:"value" datastore:"value,noindex"`
|
||||||
@@ -330,7 +331,7 @@ type Authentication struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type AuthenticationParams struct {
|
type AuthenticationParams struct {
|
||||||
Description string `json:"description" datastore:"description" yaml:"description"`
|
Description string `json:"description" datastore:"description,noindex" yaml:"description"`
|
||||||
ID string `json:"id" datastore:"id" yaml:"id"`
|
ID string `json:"id" datastore:"id" yaml:"id"`
|
||||||
Name string `json:"name" datastore:"name" yaml:"name"`
|
Name string `json:"name" datastore:"name" yaml:"name"`
|
||||||
Example string `json:"example" datastore:"example" yaml:"example"`
|
Example string `json:"example" datastore:"example" yaml:"example"`
|
||||||
@@ -3408,9 +3409,6 @@ 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
|
||||||
@@ -3629,6 +3627,90 @@ func getAppAuthentication(resp http.ResponseWriter, request *http.Request) {
|
|||||||
}`
|
}`
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
func updateWorkflowAppConfig(resp http.ResponseWriter, request *http.Request) {
|
||||||
|
cors := handleCors(resp, request)
|
||||||
|
if cors {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
user, userErr := handleApiAuthentication(resp, request)
|
||||||
|
if userErr != nil {
|
||||||
|
log.Printf("Api authentication failed in get all apps: %s", userErr)
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
location := strings.Split(request.URL.String(), "/")
|
||||||
|
var fileId string
|
||||||
|
if location[1] == "api" {
|
||||||
|
if len(location) <= 4 {
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fileId = location[4]
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
app, err := getApp(ctx, fileId)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error getting app: %s (update app)", app.Name)
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.Id != app.Owner && user.Role != "admin" {
|
||||||
|
log.Printf("Wrong user (%s) for app %s in update app", user.Username, app.Name)
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(request.Body)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error with body read in update app: %s", err)
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type updatefields struct {
|
||||||
|
Sharing bool `json:"sharing"`
|
||||||
|
SharingConfig string `json:"sharing_config"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var tmpfields updatefields
|
||||||
|
err = json.Unmarshal(body, &tmpfields)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error with unmarshal body in update app: %s\n%s", err, string(body))
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if tmpfields.Sharing != app.Sharing {
|
||||||
|
app.Sharing = tmpfields.Sharing
|
||||||
|
}
|
||||||
|
|
||||||
|
if tmpfields.SharingConfig != app.SharingConfig {
|
||||||
|
app.SharingConfig = tmpfields.SharingConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
err = setWorkflowAppDatastore(ctx, *app, app.ID)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed patching workflowapp: %s", err)
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Changed workflow app %s", app.ID)
|
||||||
|
resp.WriteHeader(200)
|
||||||
|
resp.Write([]byte(fmt.Sprintf(`{"success": true}`)))
|
||||||
|
}
|
||||||
|
|
||||||
func getWorkflowApps(resp http.ResponseWriter, request *http.Request) {
|
func getWorkflowApps(resp http.ResponseWriter, request *http.Request) {
|
||||||
cors := handleCors(resp, request)
|
cors := handleCors(resp, request)
|
||||||
@@ -3691,12 +3773,12 @@ func getWorkflowApps(resp http.ResponseWriter, request *http.Request) {
|
|||||||
newapps := []WorkflowApp{}
|
newapps := []WorkflowApp{}
|
||||||
baseApps := []WorkflowApp{}
|
baseApps := []WorkflowApp{}
|
||||||
|
|
||||||
if len(user.PrivateApps) > 0 {
|
|
||||||
newapps = append(newapps, user.PrivateApps...)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, workflowapp := range workflowapps {
|
for _, workflowapp := range workflowapps {
|
||||||
if !workflowapp.Sharing {
|
if !workflowapp.Activated && workflowapp.Generated {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if workflowapp.Owner != user.Id && user.Role != "admin" && !workflowapp.Sharing {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3716,6 +3798,25 @@ func getWorkflowApps(resp http.ResponseWriter, request *http.Request) {
|
|||||||
baseApps = append(baseApps, workflowapp)
|
baseApps = append(baseApps, workflowapp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(user.PrivateApps) > 0 {
|
||||||
|
found := false
|
||||||
|
for _, item := range user.PrivateApps {
|
||||||
|
for _, app := range newapps {
|
||||||
|
if item.ID == app.ID {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
newapps = append(newapps, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("User: %d", len(user.PrivateApps))
|
||||||
|
log.Printf("All: %d", len(workflowapps))
|
||||||
|
|
||||||
// Double unmarshal because of user apps
|
// Double unmarshal because of user apps
|
||||||
newbody, err := json.Marshal(newapps)
|
newbody, err := json.Marshal(newapps)
|
||||||
//newbody, err := json.Marshal(workflowapps)
|
//newbody, err := json.Marshal(workflowapps)
|
||||||
@@ -4381,14 +4482,14 @@ func iterateOpenApiGithub(fs billy.Filesystem, dir []os.FileInfo, extra string,
|
|||||||
|
|
||||||
readFile, err := ioutil.ReadAll(fileReader)
|
readFile, err := ioutil.ReadAll(fileReader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Filereader error yaml: %s", err)
|
log.Printf("Filereader error yaml for %s: %s", filename, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. This parses OpenAPI v2 to v3 etc, for use.
|
// 1. This parses OpenAPI v2 to v3 etc, for use.
|
||||||
parsedOpenApi, err := handleSwaggerValidation(readFile)
|
parsedOpenApi, err := handleSwaggerValidation(readFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Validation error: %s", err)
|
log.Printf("Validation error for %s: %s", filename, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -129,13 +129,14 @@ const App = (message, props) => {
|
|||||||
<Route exact path="/contact" render={props => <Contact isLoaded={isLoaded} globalUrl={globalUrl} {...props} />} />
|
<Route exact path="/contact" render={props => <Contact isLoaded={isLoaded} globalUrl={globalUrl} {...props} />} />
|
||||||
<Route exact path="/login" render={props => <LoginPage isLoggedIn={isLoggedIn} setIsLoggedIn={setIsLoggedIn} register={true} isLoaded={isLoaded} globalUrl={globalUrl} setCookie={setCookie} cookies={cookies} {...props} />} />
|
<Route exact path="/login" render={props => <LoginPage isLoggedIn={isLoggedIn} setIsLoggedIn={setIsLoggedIn} register={true} isLoaded={isLoaded} globalUrl={globalUrl} setCookie={setCookie} cookies={cookies} {...props} />} />
|
||||||
<Route exact path="/admin" render={props => <Admin isLoggedIn={isLoggedIn} setIsLoggedIn={setIsLoggedIn} register={true} isLoaded={isLoaded} globalUrl={globalUrl} setCookie={setCookie} cookies={cookies} {...props} />} />
|
<Route exact path="/admin" render={props => <Admin isLoggedIn={isLoggedIn} setIsLoggedIn={setIsLoggedIn} register={true} isLoaded={isLoaded} globalUrl={globalUrl} setCookie={setCookie} cookies={cookies} {...props} />} />
|
||||||
|
<Route exact path="/admin/:key" render={props => <Admin isLoggedIn={isLoggedIn} setIsLoggedIn={setIsLoggedIn} register={true} isLoaded={isLoaded} globalUrl={globalUrl} setCookie={setCookie} cookies={cookies} {...props} />} />
|
||||||
<Route exact path="/settings" render={props => <SettingsPage isLoaded={isLoaded} userdata={userdata} globalUrl={globalUrl} {...props} />} />
|
<Route exact path="/settings" render={props => <SettingsPage isLoaded={isLoaded} userdata={userdata} globalUrl={globalUrl} {...props} />} />
|
||||||
<Route exact path="/AdminSetup" render={props => <AdminSetup isLoaded={isLoaded} userdata={userdata} globalUrl={globalUrl} {...props} />} />
|
<Route exact path="/AdminSetup" render={props => <AdminSetup isLoaded={isLoaded} userdata={userdata} globalUrl={globalUrl} {...props} />} />
|
||||||
<Route exact path="/webhooks" render={props => <Webhooks isLoaded={isLoaded} globalUrl={globalUrl} {...props} />} />
|
<Route exact path="/webhooks" render={props => <Webhooks isLoaded={isLoaded} globalUrl={globalUrl} {...props} />} />
|
||||||
<Route exact path="/webhooks/:key" render={props => <EditWebhook isLoaded={isLoaded} globalUrl={globalUrl} {...props} />} />
|
<Route exact path="/webhooks/:key" render={props => <EditWebhook isLoaded={isLoaded} globalUrl={globalUrl} {...props} />} />
|
||||||
<Route exact path="/schedules" render={props => <Schedules globalUrl={globalUrl} {...props} />} />
|
<Route exact path="/schedules" render={props => <Schedules globalUrl={globalUrl} {...props} />} />
|
||||||
<Route exact path="/dashboard" render={props => <Dashboard isLoaded={isLoaded} isLoggedIn={isLoggedIn} globalUrl={globalUrl} {...props} />} />
|
<Route exact path="/dashboard" render={props => <Dashboard isLoaded={isLoaded} isLoggedIn={isLoggedIn} globalUrl={globalUrl} {...props} />} />
|
||||||
<Route exact path="/apps" render={props => <Apps isLoaded={isLoaded} isLoggedIn={isLoggedIn} globalUrl={globalUrl} {...props} />} />
|
<Route exact path="/apps" render={props => <Apps isLoaded={isLoaded} isLoggedIn={isLoggedIn} globalUrl={globalUrl} userdata={userdata} {...props} />} />
|
||||||
<Route exact path="/apps/new" render={props => <AppCreator isLoaded={isLoaded} isLoggedIn={isLoggedIn} globalUrl={globalUrl} {...props} />} />
|
<Route exact path="/apps/new" render={props => <AppCreator isLoaded={isLoaded} isLoggedIn={isLoggedIn} globalUrl={globalUrl} {...props} />} />
|
||||||
<Route exact path="/apps/edit/:appid" render={props => <AppCreator isLoaded={isLoaded} isLoggedIn={isLoggedIn} globalUrl={globalUrl} {...props} />} />
|
<Route exact path="/apps/edit/:appid" render={props => <AppCreator isLoaded={isLoaded} isLoggedIn={isLoggedIn} globalUrl={globalUrl} {...props} />} />
|
||||||
<Route exact path="/schedules/:key" render={props => <EditSchedule globalUrl={globalUrl} {...props} />} />
|
<Route exact path="/schedules/:key" render={props => <EditSchedule globalUrl={globalUrl} {...props} />} />
|
||||||
|
|||||||
@@ -49,6 +49,71 @@ const Admin = (props) => {
|
|||||||
const [selectedAuthenticationModalOpen, setSelectedAuthenticationModalOpen] = React.useState(false)
|
const [selectedAuthenticationModalOpen, setSelectedAuthenticationModalOpen] = React.useState(false)
|
||||||
const [showArchived, setShowArchived] = React.useState(false)
|
const [showArchived, setShowArchived] = React.useState(false)
|
||||||
|
|
||||||
|
const getApps = () => {
|
||||||
|
fetch(globalUrl+"/api/v1/workflows/apps", {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json',
|
||||||
|
},
|
||||||
|
credentials: "include",
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
if (response.status !== 200) {
|
||||||
|
console.log("Status not 200 for apps :O!")
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.json()
|
||||||
|
})
|
||||||
|
.then((responseJson) => {
|
||||||
|
console.log("apps: ", responseJson)
|
||||||
|
//setApps(responseJson)
|
||||||
|
//setFilteredApps(responseJson)
|
||||||
|
//if (responseJson.length > 0) {
|
||||||
|
// setSelectedApp(responseJson[0])
|
||||||
|
// if (responseJson[0].actions !== null && responseJson[0].actions.length > 0) {
|
||||||
|
// setSelectedAction(responseJson[0].actions[0])
|
||||||
|
// } else {
|
||||||
|
// setSelectedAction({})
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
alert.error(error.toString())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const categories = [
|
||||||
|
{
|
||||||
|
"name": "Ticketing",
|
||||||
|
"apps": [
|
||||||
|
"TheHive",
|
||||||
|
"Service-Now",
|
||||||
|
"SecureWorks",
|
||||||
|
],
|
||||||
|
"categories": ["tickets", "ticket", "ticketing"]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
/*
|
||||||
|
"SIEM",
|
||||||
|
"Active Directory",
|
||||||
|
"Firewalls",
|
||||||
|
"Proxies web",
|
||||||
|
"SIEM",
|
||||||
|
"SOAR",
|
||||||
|
"Mail",
|
||||||
|
"EDR",
|
||||||
|
"AV",
|
||||||
|
"MDM/MAM",
|
||||||
|
"DNS",
|
||||||
|
"Ticketing platform",
|
||||||
|
"TIP",
|
||||||
|
"Communication",
|
||||||
|
"DDOS protection",
|
||||||
|
"VMS",
|
||||||
|
]
|
||||||
|
*/
|
||||||
|
|
||||||
const alert = useAlert()
|
const alert = useAlert()
|
||||||
|
|
||||||
const deleteAuthentication = (data) => {
|
const deleteAuthentication = (data) => {
|
||||||
@@ -905,6 +970,82 @@ const Admin = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
: null
|
: null
|
||||||
|
|
||||||
|
const appCategoryView = curTab === 6 ?
|
||||||
|
<div>
|
||||||
|
<div style={{marginTop: 20, marginBottom: 20,}}>
|
||||||
|
<h2 style={{display: "inline",}}>Categories</h2>
|
||||||
|
<span style={{marginLeft: 25}}>
|
||||||
|
Categories are the categories supported by Shuffle, which are mapped to apps and workflows
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<Divider style={{marginTop: 20, marginBottom: 20, backgroundColor: theme.palette.inputColor}}/>
|
||||||
|
<List>
|
||||||
|
<ListItem>
|
||||||
|
<ListItemText
|
||||||
|
primary="Category"
|
||||||
|
style={{minWidth: 150, maxWidth: 150}}
|
||||||
|
/>
|
||||||
|
<ListItemText
|
||||||
|
primary="Apps"
|
||||||
|
style={{minWidth: 250, maxWidth: 250}}
|
||||||
|
/>
|
||||||
|
<ListItemText
|
||||||
|
primary="Workflows"
|
||||||
|
style={{minWidth: 150, maxWidth: 150}}
|
||||||
|
/>
|
||||||
|
<ListItemText
|
||||||
|
primary="Authentication"
|
||||||
|
style={{minWidth: 150, maxWidth: 150, overflow: "hidden"}}
|
||||||
|
/>
|
||||||
|
<ListItemText
|
||||||
|
primary="Actions"
|
||||||
|
style={{minWidth: 150, maxWidth: 150, overflow: "hidden"}}
|
||||||
|
/>
|
||||||
|
</ListItem>
|
||||||
|
{categories.map(data => {
|
||||||
|
if (data.apps.length === 0) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ListItem>
|
||||||
|
<ListItemText
|
||||||
|
primary={data.name}
|
||||||
|
style={{minWidth: 150, maxWidth: 150}}
|
||||||
|
/>
|
||||||
|
<ListItemText
|
||||||
|
primary={""}
|
||||||
|
style={{minWidth: 250, maxWidth: 250}}
|
||||||
|
/>
|
||||||
|
<ListItemText
|
||||||
|
primary={""}
|
||||||
|
style={{minWidth: 150, maxWidth: 150}}
|
||||||
|
/>
|
||||||
|
<ListItemText
|
||||||
|
primary={""}
|
||||||
|
style={{minWidth: 150, maxWidth: 150, overflow: "hidden"}}
|
||||||
|
/>
|
||||||
|
<ListItemText
|
||||||
|
style={{minWidth: 150, maxWidth: 150, overflow: "hidden"}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
style={{}}
|
||||||
|
variant="outlined"
|
||||||
|
color="primary"
|
||||||
|
onClick={() => {
|
||||||
|
console.log("Show apps with this category")
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Find app ({data.apps.length})
|
||||||
|
</Button>
|
||||||
|
</ListItemText>
|
||||||
|
</ListItem>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</List>
|
||||||
|
</div>
|
||||||
|
: null
|
||||||
|
|
||||||
const authenticationView = curTab === 1 ?
|
const authenticationView = curTab === 1 ?
|
||||||
<div>
|
<div>
|
||||||
<div style={{marginTop: 20, marginBottom: 20,}}>
|
<div style={{marginTop: 20, marginBottom: 20,}}>
|
||||||
@@ -1182,6 +1323,10 @@ const Admin = (props) => {
|
|||||||
getSchedules()
|
getSchedules()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (newValue === 6) {
|
||||||
|
console.log("Should get apps for categories.")
|
||||||
|
}
|
||||||
|
|
||||||
setModalUser({})
|
setModalUser({})
|
||||||
setCurTab(newValue)
|
setCurTab(newValue)
|
||||||
}
|
}
|
||||||
@@ -1202,10 +1347,12 @@ const Admin = (props) => {
|
|||||||
<Tab label=<span><ScheduleIcon style={iconStyle} />Schedules</span> />
|
<Tab label=<span><ScheduleIcon style={iconStyle} />Schedules</span> />
|
||||||
{window.location.protocol == "http:" && window.location.port === "3000" ? <Tab label=<span><CloudIcon style={iconStyle} /> Hybrid</span>/> : null}
|
{window.location.protocol == "http:" && window.location.port === "3000" ? <Tab label=<span><CloudIcon style={iconStyle} /> Hybrid</span>/> : null}
|
||||||
{window.location.protocol == "http:" && window.location.port === "3000" ? <Tab label=<span><BusinessIcon style={iconStyle} /> Organizations</span>/> : null}
|
{window.location.protocol == "http:" && window.location.port === "3000" ? <Tab label=<span><BusinessIcon style={iconStyle} /> Organizations</span>/> : null}
|
||||||
|
{window.location.protocol === "http:" && window.location.port === "3000" ? <Tab label=<span><LockIcon style={iconStyle} />Categories</span>/> : null}
|
||||||
</Tabs>
|
</Tabs>
|
||||||
<Divider style={{marginTop: 0, marginBottom: 10, backgroundColor: "rgb(91, 96, 100)"}} />
|
<Divider style={{marginTop: 0, marginBottom: 10, backgroundColor: "rgb(91, 96, 100)"}} />
|
||||||
<div style={{padding: 15}}>
|
<div style={{padding: 15}}>
|
||||||
{authenticationView}
|
{authenticationView}
|
||||||
|
{appCategoryView}
|
||||||
{usersView}
|
{usersView}
|
||||||
{environmentView}
|
{environmentView}
|
||||||
{schedulesView}
|
{schedulesView}
|
||||||
|
|||||||
+132
-16
@@ -112,6 +112,7 @@ const Apps = (props) => {
|
|||||||
const [openApiError, setOpenApiError] = React.useState("")
|
const [openApiError, setOpenApiError] = React.useState("")
|
||||||
const [field1, setField1] = React.useState("")
|
const [field1, setField1] = React.useState("")
|
||||||
const [field2, setField2] = React.useState("")
|
const [field2, setField2] = React.useState("")
|
||||||
|
const [sharingConfiguration, setSharingConfiguration] = React.useState("you")
|
||||||
|
|
||||||
const { start, stop } = useInterval({
|
const { start, stop } = useInterval({
|
||||||
duration: 5000,
|
duration: 5000,
|
||||||
@@ -134,6 +135,24 @@ const Apps = (props) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function sortByKey(array, key) {
|
||||||
|
return array.sort(function(a, b) {
|
||||||
|
var x = a[key];
|
||||||
|
var y = b[key];
|
||||||
|
|
||||||
|
if (typeof x == "string")
|
||||||
|
{
|
||||||
|
x = (""+x).toLowerCase();
|
||||||
|
}
|
||||||
|
if (typeof y == "string")
|
||||||
|
{
|
||||||
|
y = (""+y).toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ((x < y) ? 1 : ((x > y) ? -1 : 0));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const appViewStyle = {
|
const appViewStyle = {
|
||||||
color: "#ffffff",
|
color: "#ffffff",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
@@ -169,6 +188,8 @@ const Apps = (props) => {
|
|||||||
return response.json()
|
return response.json()
|
||||||
})
|
})
|
||||||
.then((responseJson) => {
|
.then((responseJson) => {
|
||||||
|
responseJson = sortByKey(responseJson, "large_image")
|
||||||
|
|
||||||
setApps(responseJson)
|
setApps(responseJson)
|
||||||
setFilteredApps(responseJson)
|
setFilteredApps(responseJson)
|
||||||
if (responseJson.length > 0) {
|
if (responseJson.length > 0) {
|
||||||
@@ -290,12 +311,18 @@ const Apps = (props) => {
|
|||||||
<Paper square key={data.id} style={paperAppStyle} onClick={() => {
|
<Paper square key={data.id} style={paperAppStyle} onClick={() => {
|
||||||
if (selectedApp.id !== data.id) {
|
if (selectedApp.id !== data.id) {
|
||||||
setSelectedApp(data)
|
setSelectedApp(data)
|
||||||
|
|
||||||
console.log(data)
|
console.log(data)
|
||||||
if (data.actions !== undefined && data.actions !== null && data.actions.length > 0) {
|
if (data.actions !== undefined && data.actions !== null && data.actions.length > 0) {
|
||||||
setSelectedAction(data.actions[0])
|
setSelectedAction(data.actions[0])
|
||||||
} else {
|
} else {
|
||||||
setSelectedAction({})
|
setSelectedAction({})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("Sharing: ", data.sharing_config)
|
||||||
|
if (data.sharing) {
|
||||||
|
setSharingConfiguration("everyone")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
<Grid container style={{margin: 10, flex: "10"}}>
|
<Grid container style={{margin: 10, flex: "10"}}>
|
||||||
@@ -314,9 +341,20 @@ const Apps = (props) => {
|
|||||||
{description}
|
{description}
|
||||||
</Grid>
|
</Grid>
|
||||||
</div>
|
</div>
|
||||||
<Grid item style={{flex: "1", justifyContent: "center"}}>
|
<Grid item style={{flex: "1", justifyContent: "center", marginTop: 5}}>
|
||||||
Sharing: {sharing}
|
{data.tags === null || data.tags === undefined ? null : data.tags.map((tag, index) => {
|
||||||
, Valid: {valid}
|
if (index >= 3) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Chip
|
||||||
|
style={{height: 25, marginRight: 5, cursor: "pointer",}}
|
||||||
|
label={tag}
|
||||||
|
color="primary"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})}
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
@@ -324,7 +362,7 @@ const Apps = (props) => {
|
|||||||
|
|
||||||
{data.activated && data.private_id !== undefined && data.private_id.length > 0 && data.generated ?
|
{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 OpenAPI"} style={{marginTop: "28px", width: "100%"}} aria-label={data.name}>
|
||||||
<CloudDownload />
|
<CloudDownload />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Grid>
|
</Grid>
|
||||||
@@ -450,6 +488,7 @@ const Apps = (props) => {
|
|||||||
</MenuItem>
|
</MenuItem>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
{/*
|
||||||
<ReactJson
|
<ReactJson
|
||||||
src={JSON.parse(showResult)}
|
src={JSON.parse(showResult)}
|
||||||
theme="solarized"
|
theme="solarized"
|
||||||
@@ -457,19 +496,26 @@ const Apps = (props) => {
|
|||||||
displayDataTypes={true}
|
displayDataTypes={true}
|
||||||
name={"Example return value"}
|
name={"Example return value"}
|
||||||
/>
|
/>
|
||||||
|
*/}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div style={{marginTop: 10}}>
|
||||||
<b>Example return</b><div/>
|
<b>Example return</b><div/>
|
||||||
{selectedAction.returns.example}
|
{selectedAction.returns.example}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
//fetch(globalUrl+"/api/v1/get_openapi/"+urlParams.get("id"), {
|
const userRoles = [
|
||||||
|
"you",
|
||||||
|
"everyone",
|
||||||
|
]
|
||||||
|
|
||||||
|
//fetch(globalUrl+"/api/v1/get_openapi/"+urlParams.get("id"),
|
||||||
|
console.log("User: ", props.userdata)
|
||||||
var baseInfo = newAppname.length > 0 ?
|
var baseInfo = newAppname.length > 0 ?
|
||||||
<div>
|
<div>
|
||||||
<div style={{display: "flex"}}>
|
<div style={{display: "flex"}}>
|
||||||
@@ -482,15 +528,23 @@ const Apps = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{activateButton}
|
{activateButton}
|
||||||
{downloadButton}
|
{props.userdata.role === "admin" || props.userdata.id === selectedApp.owner ?
|
||||||
{editButton}
|
<div>
|
||||||
{deleteButton}
|
{downloadButton}
|
||||||
|
{editButton}
|
||||||
|
{deleteButton}
|
||||||
|
</div>
|
||||||
|
: null}
|
||||||
{selectedApp.tags !== undefined && selectedApp.tags !== null ?
|
{selectedApp.tags !== undefined && selectedApp.tags !== null ?
|
||||||
<div style={{display: "inline-block", marginLeft: 15, float: "right",}}>
|
<div style={{display: "inline-block", marginLeft: 15, float: "right",}}>
|
||||||
{selectedApp.tags.map(tag => {
|
{selectedApp.tags.map((tag, index) => {
|
||||||
|
if (index >= 3) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Chip
|
<Chip
|
||||||
style={{marginRight: 5, marginTop: 7, cursor: "pointer",}}
|
style={{height: 25, marginRight: 5, marginTop: 7, cursor: "pointer",}}
|
||||||
label={tag}
|
label={tag}
|
||||||
color="primary"
|
color="primary"
|
||||||
/>
|
/>
|
||||||
@@ -498,11 +552,41 @@ const Apps = (props) => {
|
|||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
: null}
|
: null}
|
||||||
<Divider style={{marginBottom: "10px", marginTop: "10px", backgroundColor: dividerColor}}/>
|
{props.userdata.id === selectedApp.owner ?
|
||||||
{selectedApp.link.length > 0 ? <p><b>URL:</b> {selectedApp.link}</p> : null}
|
<div style={{marginTop: 15}}>
|
||||||
<p><b>ID:</b> {selectedApp.id}</p>
|
{/*<p><b>ID:</b> {selectedApp.id}</p>*/}
|
||||||
|
<b style={{marginRight: 15}}>Sharing:</b>
|
||||||
|
<Select
|
||||||
|
value={sharingConfiguration}
|
||||||
|
onChange={(event) => {
|
||||||
|
setSharingConfiguration(event.target.value)
|
||||||
|
alert.info("Changed sharing to "+event.target.value)
|
||||||
|
|
||||||
|
updateAppField(selectedApp.id, "sharing", !selectedApp.sharing)
|
||||||
|
//setSelectedAction(event.target.value)
|
||||||
|
}}
|
||||||
|
style={{width: 150, backgroundColor: inputColor, color: "white", height: 35, marginleft: 10,}}
|
||||||
|
SelectDisplayProps={{
|
||||||
|
style: {
|
||||||
|
marginLeft: 10,
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{userRoles.map(data => {
|
||||||
|
return (
|
||||||
|
<MenuItem key={data} style={{backgroundColor: inputColor, color: "white"}} value={data}>
|
||||||
|
{data}
|
||||||
|
|
||||||
|
</MenuItem>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
: null}
|
||||||
|
{/*<p><b>Owner:</b> {selectedApp.owner}</p>*/}
|
||||||
{selectedApp.privateId !== undefined && selectedApp.privateId.length > 0 ? <p><b>PrivateID:</b> {selectedApp.privateId}</p> : null}
|
{selectedApp.privateId !== undefined && selectedApp.privateId.length > 0 ? <p><b>PrivateID:</b> {selectedApp.privateId}</p> : null}
|
||||||
|
<Divider style={{marginBottom: 10, marginTop: 10, backgroundColor: dividerColor}}/>
|
||||||
|
{selectedApp.link.length > 0 ? <p><b>URL:</b> {selectedApp.link}</p> : null}
|
||||||
<div style={{marginTop: 15, marginBottom: 15}}>
|
<div style={{marginTop: 15, marginBottom: 15}}>
|
||||||
<b>Actions</b>
|
<b>Actions</b>
|
||||||
{selectedApp.actions !== null && selectedApp.actions.length > 0 ?
|
{selectedApp.actions !== null && selectedApp.actions.length > 0 ?
|
||||||
@@ -884,6 +968,36 @@ const Apps = (props) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const updateAppField = (app_id, fieldname, fieldvalue) => {
|
||||||
|
const data = {}
|
||||||
|
data[fieldname] = fieldvalue
|
||||||
|
|
||||||
|
fetch(globalUrl+"/api/v1/apps/"+app_id, {
|
||||||
|
method: 'PATCH',
|
||||||
|
headers: {
|
||||||
|
'Accept': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
credentials: "include",
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
//setAppSearchLoading(false)
|
||||||
|
return response.json()
|
||||||
|
})
|
||||||
|
.then((responseJson) => {
|
||||||
|
//console.log(responseJson)
|
||||||
|
//alert.info(responseJson)
|
||||||
|
if (responseJson.success) {
|
||||||
|
alert.info("Success")
|
||||||
|
} else {
|
||||||
|
alert.error("Error updating app")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
alert.error(error.toString())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const runAppSearch = (searchterm) => {
|
const runAppSearch = (searchterm) => {
|
||||||
const data = {"search": searchterm}
|
const data = {"search": searchterm}
|
||||||
|
|
||||||
@@ -1165,11 +1279,13 @@ const Apps = (props) => {
|
|||||||
placeholder="OpenAPI URI"
|
placeholder="OpenAPI URI"
|
||||||
fullWidth
|
fullWidth
|
||||||
/>
|
/>
|
||||||
|
{/*
|
||||||
<div style={{marginTop: "15px"}}/>
|
<div style={{marginTop: "15px"}}/>
|
||||||
Example:
|
Example:
|
||||||
<div />
|
<div />
|
||||||
https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/examples/v2.0/json/uber.json
|
https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/examples/v2.0/json/uber.json
|
||||||
<h4>or paste the yaml/JSON directly below</h4>
|
*/}
|
||||||
|
<h4>Or paste the yaml/JSON directly below</h4>
|
||||||
<TextField
|
<TextField
|
||||||
style={{backgroundColor: inputColor}}
|
style={{backgroundColor: inputColor}}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import MenuItem from '@material-ui/core/MenuItem';
|
|||||||
import FormControlLabel from '@material-ui/core/FormControlLabel';
|
import FormControlLabel from '@material-ui/core/FormControlLabel';
|
||||||
import Chip from '@material-ui/core/Chip';
|
import Chip from '@material-ui/core/Chip';
|
||||||
import Switch from '@material-ui/core/Switch';
|
import Switch from '@material-ui/core/Switch';
|
||||||
|
import Typography from '@material-ui/core/Typography';
|
||||||
|
|
||||||
import CircularProgress from '@material-ui/core/CircularProgress';
|
import CircularProgress from '@material-ui/core/CircularProgress';
|
||||||
import CachedIcon from '@material-ui/icons/Cached';
|
import CachedIcon from '@material-ui/icons/Cached';
|
||||||
@@ -201,7 +202,7 @@ const Workflows = (props) => {
|
|||||||
marginTop: "10px",
|
marginTop: "10px",
|
||||||
overflow: "scroll",
|
overflow: "scroll",
|
||||||
height: "90%",
|
height: "90%",
|
||||||
overflowX: "auto",
|
overflowX: "hidden",
|
||||||
overflowY: "auto",
|
overflowY: "auto",
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,6 +213,8 @@ const Workflows = (props) => {
|
|||||||
marginTop: "5px",
|
marginTop: "5px",
|
||||||
color: "white",
|
color: "white",
|
||||||
backgroundColor: surfaceColor,
|
backgroundColor: surfaceColor,
|
||||||
|
borderRadius: 5,
|
||||||
|
padding: 10,
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
}
|
}
|
||||||
@@ -241,6 +244,8 @@ const Workflows = (props) => {
|
|||||||
setWorkflowExecutions(responseJson)
|
setWorkflowExecutions(responseJson)
|
||||||
} else {
|
} else {
|
||||||
alert.info("Couldn't find executions for the workflow")
|
alert.info("Couldn't find executions for the workflow")
|
||||||
|
setSelectedExecution({})
|
||||||
|
setWorkflowExecutions([])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -393,6 +398,7 @@ const Workflows = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// dropdown with copy etc I guess
|
// dropdown with copy etc I guess
|
||||||
|
console.log("Why reset?")
|
||||||
const WorkflowPaper = (props) => {
|
const WorkflowPaper = (props) => {
|
||||||
const { data } = props;
|
const { data } = props;
|
||||||
const [open, setOpen] = React.useState(false);
|
const [open, setOpen] = React.useState(false);
|
||||||
@@ -419,7 +425,6 @@ const Workflows = (props) => {
|
|||||||
var webhookImg = ""
|
var webhookImg = ""
|
||||||
var scheduleImg = ""
|
var scheduleImg = ""
|
||||||
if (data.triggers !== undefined && data.triggers !== null && data.triggers.length > 0) {
|
if (data.triggers !== undefined && data.triggers !== null && data.triggers.length > 0) {
|
||||||
console.log("Triggers: ", data.triggers)
|
|
||||||
for (var key in data.triggers) {
|
for (var key in data.triggers) {
|
||||||
if (data.triggers[key].app_name === "Webhook") {
|
if (data.triggers[key].app_name === "Webhook") {
|
||||||
webhooks += 1
|
webhooks += 1
|
||||||
@@ -434,14 +439,9 @@ const Workflows = (props) => {
|
|||||||
const imgSize = 25
|
const imgSize = 25
|
||||||
return (
|
return (
|
||||||
<Paper square style={paperAppStyle} onClick={(e) => {
|
<Paper square style={paperAppStyle} onClick={(e) => {
|
||||||
}}>
|
}}>
|
||||||
<Grid container style={{margin: "10px 0px 10px 5px", maxWidth: 35,}}>
|
<div style={{width: boxWidth, backgroundColor: boxColor}} />
|
||||||
<AppsIcon style={{width: imgSize, height: imgSize}} />
|
<Grid container style={{margin: "0px 10px 0px 10px", flex: 10}}>
|
||||||
{webhooks > 0 ? <img alt={data.title} style={{width: imgSize, height: imgSize, marginTop: 5}} src={webhookImg} /> : null}
|
|
||||||
{schedules > 0 ? <img alt={data.title} style={{width: imgSize, height: imgSize, marginTop: 5}} src={scheduleImg} /> : null}
|
|
||||||
</Grid>
|
|
||||||
<div style={{marginTop: 5, marginBottom: 5, width: boxWidth, backgroundColor: boxColor}} />
|
|
||||||
<Grid container style={{margin: "10px 10px 10px 10px", flex: 10}}>
|
|
||||||
<Grid style={{display: "flex", flexDirection: "column", width: "100%"}}>
|
<Grid style={{display: "flex", flexDirection: "column", width: "100%"}}>
|
||||||
<Grid item style={{flex: 1, display: "flex"}}>
|
<Grid item style={{flex: 1, display: "flex"}}>
|
||||||
<div style={{flex: "10",}} onClick={() => {
|
<div style={{flex: "10",}} onClick={() => {
|
||||||
@@ -450,9 +450,11 @@ const Workflows = (props) => {
|
|||||||
getWorkflowExecution(data.id)
|
getWorkflowExecution(data.id)
|
||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
<h3 style={{marginBottom: "0px", marginTop: "10px"}}>{data.name}</h3>
|
<Typography variant="h6" style={{marginTop: 10, marginBottom: 0, }}>
|
||||||
|
{data.name}
|
||||||
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
<div style={{flex: "1"}}>
|
<div style={{flex: 1, }}>
|
||||||
<IconButton
|
<IconButton
|
||||||
aria-label="more"
|
aria-label="more"
|
||||||
aria-controls="long-menu"
|
aria-controls="long-menu"
|
||||||
@@ -505,7 +507,7 @@ const Workflows = (props) => {
|
|||||||
getWorkflowExecution(data.id)
|
getWorkflowExecution(data.id)
|
||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
<Grid item style={{flex: "1", justifyContent: "center", overflow: "hidden"}}>
|
<Grid item style={{flex: "1", justifyContent: "center", overflow: "hidden", float: "bottom",}}>
|
||||||
<Link to={"/workflows/"+data.id}>
|
<Link to={"/workflows/"+data.id}>
|
||||||
<Tooltip color="primary" title="Edit workflow" placement="bottom">
|
<Tooltip color="primary" title="Edit workflow" placement="bottom">
|
||||||
<Button style={{}} color="secondary" variant="text" style={{marginRight: 10}} onClick={() => {}}>
|
<Button style={{}} color="secondary" variant="text" style={{marginRight: 10}} onClick={() => {}}>
|
||||||
@@ -514,9 +516,11 @@ const Workflows = (props) => {
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Link>
|
</Link>
|
||||||
<Tooltip color="primary" title="Execute workflow" placement="bottom">
|
<Tooltip color="primary" title="Execute workflow" placement="bottom">
|
||||||
<Button style={{}} color="secondary" variant="text" onClick={() => executeWorkflow(data.id)}>
|
<span>
|
||||||
<PlayArrowIcon />
|
<Button style={{}} disabled={!data.is_valid} color="secondary" variant="text" onClick={() => executeWorkflow(data.id)}>
|
||||||
</Button>
|
<PlayArrowIcon />
|
||||||
|
</Button>
|
||||||
|
</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
{data.tags !== undefined ?
|
{data.tags !== undefined ?
|
||||||
data.tags.map(tag => {
|
data.tags.map(tag => {
|
||||||
@@ -533,6 +537,22 @@ const Workflows = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
<Grid container style={{maxWidth: 35, marginRight: 10,}}>
|
||||||
|
<Tooltip title={data.actions.length} placement="right">
|
||||||
|
<AppsIcon style={{width: imgSize, height: imgSize}} />
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
{webhooks > 0 ?
|
||||||
|
<Tooltip title={webhooks} placement="right">
|
||||||
|
<img alt={data.title} style={{width: imgSize, height: imgSize, marginTop: 5}} src={webhookImg} />
|
||||||
|
</Tooltip>
|
||||||
|
: null}
|
||||||
|
{schedules > 0 ?
|
||||||
|
<Tooltip title={webhooks} placement="right">
|
||||||
|
<img alt={data.title} style={{width: imgSize, height: imgSize, marginTop: 5}} src={scheduleImg} />
|
||||||
|
</Tooltip>
|
||||||
|
: null}
|
||||||
|
</Grid>
|
||||||
</Paper>
|
</Paper>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -938,7 +958,6 @@ const Workflows = (props) => {
|
|||||||
setLoadWorkflowsModalOpen(false)
|
setLoadWorkflowsModalOpen(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("WOrkflowtags: ", newWorkflowTags)
|
|
||||||
const modalView = modalOpen ?
|
const modalView = modalOpen ?
|
||||||
<Dialog
|
<Dialog
|
||||||
open={modalOpen}
|
open={modalOpen}
|
||||||
|
|||||||
Reference in New Issue
Block a user