Merge branch '2.0.0' of github.com:Shuffle/Shuffle into 2.0.0
This commit is contained in:
@@ -11,11 +11,13 @@ Shuffle Automation
|
|||||||
|
|
||||||
[Shuffle](https://shuffler.io) is an automation platform for and by the community, focusing on accessibility for anyone to automate. Security operations is complex, but it doesn't have to be.
|
[Shuffle](https://shuffler.io) is an automation platform for and by the community, focusing on accessibility for anyone to automate. Security operations is complex, but it doesn't have to be.
|
||||||
|
|
||||||
|
[ Get training ](https://shuffler.io/training)
|
||||||
[_Key Features_](https://shuffler.io/docs/features) —
|
[_Key Features_](https://shuffler.io/docs/features) —
|
||||||
[_Community & Support_](https://discord.gg/B2CBzUm) —
|
[_Community & Support_](https://discord.gg/B2CBzUm) —
|
||||||
[_Documentation_](https://shuffler.io/docs) —
|
[_Documentation_](https://shuffler.io/docs) —
|
||||||
[_Getting Started_](https://shuffler.io/docs/getting_started) —
|
[_Getting Started_](https://shuffler.io/docs/getting_started) —
|
||||||
[_Development_](https://github.com/shuffle/Shuffle/blob/master/.github/CONTRIBUTING.md)
|
[_Development_](https://github.com/shuffle/Shuffle/blob/master/.github/CONTRIBUTING.md)
|
||||||
|
[ Set up a demo call ](https://shuffler.io/contact)
|
||||||
|
|
||||||
Follow us on Twitter at [@shuffleio](https://twitter.com/shuffleio).
|
Follow us on Twitter at [@shuffleio](https://twitter.com/shuffleio).
|
||||||
|
|
||||||
|
|||||||
@@ -98,6 +98,7 @@ def md5_base64(a):
|
|||||||
foundhash = hashlib.md5(a.encode('utf-8')).hexdigest()
|
foundhash = hashlib.md5(a.encode('utf-8')).hexdigest()
|
||||||
return base64.b64encode(foundhash.encode('utf-8'))
|
return base64.b64encode(foundhash.encode('utf-8'))
|
||||||
|
|
||||||
|
|
||||||
@shuffle_filters.register
|
@shuffle_filters.register
|
||||||
def base64_encode(a):
|
def base64_encode(a):
|
||||||
a = str(a)
|
a = str(a)
|
||||||
@@ -107,6 +108,17 @@ def base64_encode(a):
|
|||||||
except:
|
except:
|
||||||
return base64.b64encode(a).decode()
|
return base64.b64encode(a).decode()
|
||||||
|
|
||||||
|
@shuffle_filters.register
|
||||||
|
def random_element(a):
|
||||||
|
# Choose a random item from an array
|
||||||
|
a = list(a)
|
||||||
|
|
||||||
|
if len(a) == 0:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
return random.choice(a)
|
||||||
|
|
||||||
|
|
||||||
@shuffle_filters.register
|
@shuffle_filters.register
|
||||||
def base64_decode(a):
|
def base64_decode(a):
|
||||||
a = str(a)
|
a = str(a)
|
||||||
|
|||||||
@@ -20,15 +20,15 @@ require (
|
|||||||
github.com/gorilla/mux v1.8.1
|
github.com/gorilla/mux v1.8.1
|
||||||
github.com/h2non/filetype v1.1.3
|
github.com/h2non/filetype v1.1.3
|
||||||
github.com/satori/go.uuid v1.2.0
|
github.com/satori/go.uuid v1.2.0
|
||||||
github.com/shuffle/shuffle-shared v0.6.40
|
github.com/shuffle/shuffle-shared v0.6.50
|
||||||
golang.org/x/crypto v0.22.0
|
golang.org/x/crypto v0.22.0
|
||||||
google.golang.org/api v0.176.1
|
google.golang.org/api v0.176.1
|
||||||
google.golang.org/grpc v1.63.2
|
google.golang.org/grpc v1.63.2
|
||||||
gopkg.in/src-d/go-git.v4 v4.13.1
|
gopkg.in/src-d/go-git.v4 v4.13.1
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
k8s.io/api v0.30.0
|
k8s.io/api v0.30.2
|
||||||
k8s.io/apimachinery v0.30.0
|
k8s.io/apimachinery v0.30.2
|
||||||
k8s.io/client-go v0.30.0
|
k8s.io/client-go v0.30.2
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -58,7 +58,7 @@ require (
|
|||||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||||
github.com/emirpasic/gods v1.18.1 // indirect
|
github.com/emirpasic/gods v1.18.1 // indirect
|
||||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
github.com/frikky/schemaless v0.0.11 // indirect
|
github.com/frikky/schemaless v0.0.13 // indirect
|
||||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||||
github.com/go-logr/logr v1.4.1 // indirect
|
github.com/go-logr/logr v1.4.1 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
@@ -100,6 +100,8 @@ require (
|
|||||||
github.com/pjbgf/sha1cd v0.3.0 // indirect
|
github.com/pjbgf/sha1cd v0.3.0 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/sashabaranov/go-openai v1.19.2 // indirect
|
github.com/sashabaranov/go-openai v1.19.2 // indirect
|
||||||
|
github.com/sendgrid/rest v2.6.9+incompatible // indirect
|
||||||
|
github.com/sendgrid/sendgrid-go v3.14.0+incompatible // indirect
|
||||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
|
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
|
||||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||||
github.com/skeema/knownhosts v1.2.2 // indirect
|
github.com/skeema/knownhosts v1.2.2 // indirect
|
||||||
|
|||||||
@@ -160,6 +160,8 @@ github.com/frikky/schemaless v0.0.9 h1:RzNLPkJq5c4nlm5iLiTndFcbeQxdMGJIj266wSGt2
|
|||||||
github.com/frikky/schemaless v0.0.9/go.mod h1:mooDxY+D6weHjhKvjy3+IE9S7P4g4cpNnidkdRv/cHQ=
|
github.com/frikky/schemaless v0.0.9/go.mod h1:mooDxY+D6weHjhKvjy3+IE9S7P4g4cpNnidkdRv/cHQ=
|
||||||
github.com/frikky/schemaless v0.0.11 h1:c4r6CJX30XI+SoJdT9RlUd9qYSQlx6hvwGRtsypu+uM=
|
github.com/frikky/schemaless v0.0.11 h1:c4r6CJX30XI+SoJdT9RlUd9qYSQlx6hvwGRtsypu+uM=
|
||||||
github.com/frikky/schemaless v0.0.11/go.mod h1:mooDxY+D6weHjhKvjy3+IE9S7P4g4cpNnidkdRv/cHQ=
|
github.com/frikky/schemaless v0.0.11/go.mod h1:mooDxY+D6weHjhKvjy3+IE9S7P4g4cpNnidkdRv/cHQ=
|
||||||
|
github.com/frikky/schemaless v0.0.13 h1:ARiN9V7wr2VZXAr9JK5wvTbyPgpGrgeiL1VhR5MlgaQ=
|
||||||
|
github.com/frikky/schemaless v0.0.13/go.mod h1:mooDxY+D6weHjhKvjy3+IE9S7P4g4cpNnidkdRv/cHQ=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
github.com/fsouza/go-dockerclient v1.11.0 h1:4ZAk6W7rPAtPXm7198EFqA5S68rwnNQORxlOA5OurCA=
|
github.com/fsouza/go-dockerclient v1.11.0 h1:4ZAk6W7rPAtPXm7198EFqA5S68rwnNQORxlOA5OurCA=
|
||||||
@@ -303,6 +305,8 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
|
|||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
|
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
|
||||||
|
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||||
@@ -427,6 +431,10 @@ github.com/sashabaranov/go-openai v1.19.2 h1:+dkuCADSnwXV02YVJkdphY8XD9AyHLUWwk6
|
|||||||
github.com/sashabaranov/go-openai v1.19.2/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg=
|
github.com/sashabaranov/go-openai v1.19.2/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg=
|
||||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||||
|
github.com/sendgrid/rest v2.6.9+incompatible h1:1EyIcsNdn9KIisLW50MKwmSRSK+ekueiEMJ7NEoxJo0=
|
||||||
|
github.com/sendgrid/rest v2.6.9+incompatible/go.mod h1:kXX7q3jZtJXK5c5qK83bSGMdV6tsOE70KbHoqJls4lE=
|
||||||
|
github.com/sendgrid/sendgrid-go v3.14.0+incompatible h1:KDSasSTktAqMJCYClHVE94Fcif2i7P7wzISv1sU6DUA=
|
||||||
|
github.com/sendgrid/sendgrid-go v3.14.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8=
|
||||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
|
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
|
||||||
@@ -443,6 +451,12 @@ github.com/shuffle/shuffle-shared v0.6.27 h1:q4qZD6bGZFIvZ5Y10unGr3N3rZ7OryWyvva
|
|||||||
github.com/shuffle/shuffle-shared v0.6.27/go.mod h1:rWkh1eWdIx7OqQzJ1+JzF3Hck1X/Ty1WkUtjLrp+CU4=
|
github.com/shuffle/shuffle-shared v0.6.27/go.mod h1:rWkh1eWdIx7OqQzJ1+JzF3Hck1X/Ty1WkUtjLrp+CU4=
|
||||||
github.com/shuffle/shuffle-shared v0.6.31 h1:MK1SW1pwjIP7hznq+mMlTPM1R3LIOfi1/bUL5xFSg/8=
|
github.com/shuffle/shuffle-shared v0.6.31 h1:MK1SW1pwjIP7hznq+mMlTPM1R3LIOfi1/bUL5xFSg/8=
|
||||||
github.com/shuffle/shuffle-shared v0.6.31/go.mod h1:rWkh1eWdIx7OqQzJ1+JzF3Hck1X/Ty1WkUtjLrp+CU4=
|
github.com/shuffle/shuffle-shared v0.6.31/go.mod h1:rWkh1eWdIx7OqQzJ1+JzF3Hck1X/Ty1WkUtjLrp+CU4=
|
||||||
|
github.com/shuffle/shuffle-shared v0.6.46 h1:v/IXc+4V8DCWflGKGgaI37uuGnsylMjqKRylwNoXVrA=
|
||||||
|
github.com/shuffle/shuffle-shared v0.6.46/go.mod h1:rWkh1eWdIx7OqQzJ1+JzF3Hck1X/Ty1WkUtjLrp+CU4=
|
||||||
|
github.com/shuffle/shuffle-shared v0.6.47 h1:EOalfIIBX97uGkgRRPsdUApts6yCCcXf1iSphm6UoE0=
|
||||||
|
github.com/shuffle/shuffle-shared v0.6.47/go.mod h1:XVIcR2/GyIk+6qmlpOG3NvIao6kCOma36EdMym4AeSY=
|
||||||
|
github.com/shuffle/shuffle-shared v0.6.50 h1:MBeGAiBNkw9Eg+3YTJIlBOuskWntGvT0uefFUYOBhbY=
|
||||||
|
github.com/shuffle/shuffle-shared v0.6.50/go.mod h1:RAJiSFjmuKmijKTbbEf9A6Ojb+3/te7g71lED7JjPus=
|
||||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
@@ -977,10 +991,16 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9
|
|||||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||||
k8s.io/api v0.30.0 h1:siWhRq7cNjy2iHssOB9SCGNCl2spiF1dO3dABqZ8niA=
|
k8s.io/api v0.30.0 h1:siWhRq7cNjy2iHssOB9SCGNCl2spiF1dO3dABqZ8niA=
|
||||||
k8s.io/api v0.30.0/go.mod h1:OPlaYhoHs8EQ1ql0R/TsUgaRPhpKNxIMrKQfWUp8QSE=
|
k8s.io/api v0.30.0/go.mod h1:OPlaYhoHs8EQ1ql0R/TsUgaRPhpKNxIMrKQfWUp8QSE=
|
||||||
|
k8s.io/api v0.30.2 h1:+ZhRj+28QT4UOH+BKznu4CBgPWgkXO7XAvMcMl0qKvI=
|
||||||
|
k8s.io/api v0.30.2/go.mod h1:ULg5g9JvOev2dG0u2hig4Z7tQ2hHIuS+m8MNZ+X6EmI=
|
||||||
k8s.io/apimachinery v0.30.0 h1:qxVPsyDM5XS96NIh9Oj6LavoVFYff/Pon9cZeDIkHHA=
|
k8s.io/apimachinery v0.30.0 h1:qxVPsyDM5XS96NIh9Oj6LavoVFYff/Pon9cZeDIkHHA=
|
||||||
k8s.io/apimachinery v0.30.0/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc=
|
k8s.io/apimachinery v0.30.0/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc=
|
||||||
|
k8s.io/apimachinery v0.30.2 h1:fEMcnBj6qkzzPGSVsAZtQThU62SmQ4ZymlXRC5yFSCg=
|
||||||
|
k8s.io/apimachinery v0.30.2/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc=
|
||||||
k8s.io/client-go v0.30.0 h1:sB1AGGlhY/o7KCyCEQ0bPWzYDL0pwOZO4vAtTSh/gJQ=
|
k8s.io/client-go v0.30.0 h1:sB1AGGlhY/o7KCyCEQ0bPWzYDL0pwOZO4vAtTSh/gJQ=
|
||||||
k8s.io/client-go v0.30.0/go.mod h1:g7li5O5256qe6TYdAMyX/otJqMhIiGgTapdLchhmOaY=
|
k8s.io/client-go v0.30.0/go.mod h1:g7li5O5256qe6TYdAMyX/otJqMhIiGgTapdLchhmOaY=
|
||||||
|
k8s.io/client-go v0.30.2 h1:sBIVJdojUNPDU/jObC+18tXWcTJVcwyqS9diGdWHk50=
|
||||||
|
k8s.io/client-go v0.30.2/go.mod h1:JglKSWULm9xlJLx4KCkfLLQ7XwtlbflV6uFFSHTMgVs=
|
||||||
k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
|
k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
|
||||||
k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag=
|
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag=
|
||||||
|
|||||||
@@ -3616,7 +3616,7 @@ func remoteOrgJobHandler(org shuffle.Org, interval int) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if org.SyncConfig.WorkflowBackup {
|
if org.SyncConfig.WorkflowBackup {
|
||||||
workflows, err := shuffle.GetAllWorkflowsByQuery(ctx, foundUser)
|
workflows, err := shuffle.GetAllWorkflowsByQuery(ctx, foundUser, 250, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("[ERROR] Failed getting backup workflows for org %s: %s", org.Id, err)
|
log.Printf("[ERROR] Failed getting backup workflows for org %s: %s", org.Id, err)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1076,7 +1076,7 @@ func handleExecution(id string, workflow shuffle.Workflow, request *http.Request
|
|||||||
// return workflowExecution, fmt.Sprintf("%s", err), nil
|
// return workflowExecution, fmt.Sprintf("%s", err), nil
|
||||||
} else {
|
} else {
|
||||||
log.Printf("[ERROR] Failed in prepareExecution: '%s'", err)
|
log.Printf("[ERROR] Failed in prepareExecution: '%s'", err)
|
||||||
return shuffle.WorkflowExecution{}, fmt.Sprintf("Failed starting workflow: %s", err), err
|
return shuffle.WorkflowExecution{}, fmt.Sprintf("Failed running: %s", err), err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3382,6 +3382,8 @@ func executeSingleAction(resp http.ResponseWriter, request *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debugUrl := fmt.Sprintf("/workflows/%s?execution_id=%s", workflowExecution.Workflow.ID, workflowExecution.ExecutionId)
|
||||||
|
resp.Header().Add("X-Debug-Url", debugUrl)
|
||||||
|
|
||||||
workflowExecution.Priority = 11
|
workflowExecution.Priority = 11
|
||||||
environments, err := shuffle.GetEnvironments(ctx, user.ActiveOrg.Id)
|
environments, err := shuffle.GetEnvironments(ctx, user.ActiveOrg.Id)
|
||||||
|
|||||||
@@ -232,6 +232,11 @@ const AppGrid = (props) => {
|
|||||||
removeQuery("q");
|
removeQuery("q");
|
||||||
refine(event.currentTarget.value);
|
refine(event.currentTarget.value);
|
||||||
}}
|
}}
|
||||||
|
onKeyDown={(event) => {
|
||||||
|
if(event.key === "Enter") {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
}}
|
||||||
limit={5}
|
limit={5}
|
||||||
/>
|
/>
|
||||||
{/*isSearchStalled ? 'My search is stalled' : ''*/}
|
{/*isSearchStalled ? 'My search is stalled' : ''*/}
|
||||||
@@ -994,6 +999,11 @@ const AppGrid = (props) => {
|
|||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
setSearchQuery(event.currentTarget.value);
|
setSearchQuery(event.currentTarget.value);
|
||||||
}}
|
}}
|
||||||
|
onKeyDown={(event) => {
|
||||||
|
if(event.key === "Enter") {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
}}
|
||||||
limit={5}
|
limit={5}
|
||||||
/>
|
/>
|
||||||
{/*isSearchStalled ? 'My search is stalled' : ''*/}
|
{/*isSearchStalled ? 'My search is stalled' : ''*/}
|
||||||
|
|||||||
+1152
-690
File diff suppressed because it is too large
Load Diff
@@ -134,6 +134,9 @@ const AppStats = (defaultprops) => {
|
|||||||
Accept: "application/json",
|
Accept: "application/json",
|
||||||
},
|
},
|
||||||
credentials: "include",
|
credentials: "include",
|
||||||
|
}).catch((error) => {
|
||||||
|
console.log("Error getting workflow stats: " + error);
|
||||||
|
return workflow
|
||||||
})
|
})
|
||||||
|
|
||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
|
|||||||
@@ -428,7 +428,6 @@ const ConfigureWorkflow = (props) => {
|
|||||||
console.log("Found webhook: ", trigger)
|
console.log("Found webhook: ", trigger)
|
||||||
|
|
||||||
if (trigger.app_association !== undefined && trigger.app_association.name !== null && trigger.app_association.name !== "") {
|
if (trigger.app_association !== undefined && trigger.app_association.name !== null && trigger.app_association.name !== "") {
|
||||||
console.log("Actions: ", newactions)
|
|
||||||
const findapp = trigger.app_association.name.toLowerCase()
|
const findapp = trigger.app_association.name.toLowerCase()
|
||||||
const foundindex = newactions.findIndex(action => action.app_name.toLowerCase() === findapp)
|
const foundindex = newactions.findIndex(action => action.app_name.toLowerCase() === findapp)
|
||||||
|
|
||||||
@@ -449,9 +448,6 @@ const ConfigureWorkflow = (props) => {
|
|||||||
|
|
||||||
newactions[foundindex].show_steps = true
|
newactions[foundindex].show_steps = true
|
||||||
|
|
||||||
console.log("CHANGED ACTION: ", newactions[foundindex])
|
|
||||||
//console.log("Index: ", newactions[foundindex])
|
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1321,7 +1317,6 @@ const ConfigureWorkflow = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (step.type === "authenticate") {
|
if (step.type === "authenticate") {
|
||||||
console.log("AUTH STEP: ", step)
|
|
||||||
if (data.must_authenticate === true ) {
|
if (data.must_authenticate === true ) {
|
||||||
filled = false
|
filled = false
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -136,6 +136,11 @@ const CreatorGrid = props => {
|
|||||||
removeQuery("q")
|
removeQuery("q")
|
||||||
refine(event.currentTarget.value)
|
refine(event.currentTarget.value)
|
||||||
}}
|
}}
|
||||||
|
onKeyDown={(event) => {
|
||||||
|
if(event.key === "Enter") {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
{/*isSearchStalled ? 'My search is stalled' : ''*/}
|
{/*isSearchStalled ? 'My search is stalled' : ''*/}
|
||||||
</form>
|
</form>
|
||||||
@@ -147,6 +152,7 @@ const CreatorGrid = props => {
|
|||||||
flexWrap: "wrap",
|
flexWrap: "wrap",
|
||||||
alignContent: "space-between",
|
alignContent: "space-between",
|
||||||
marginTop: 5,
|
marginTop: 5,
|
||||||
|
padding:"0px 180px",
|
||||||
}
|
}
|
||||||
|
|
||||||
const Hits = ({ hits }) => {
|
const Hits = ({ hits }) => {
|
||||||
|
|||||||
@@ -75,7 +75,12 @@ const DiscordChat = props => {
|
|||||||
fullWidth
|
fullWidth
|
||||||
value={currentRefinement}
|
value={currentRefinement}
|
||||||
onChange={(event) => refine(event.currentTarget.value)}
|
onChange={(event) => refine(event.currentTarget.value)}
|
||||||
placeholder="Search Discord Chats"
|
onKeyDown={(event) => {
|
||||||
|
if(event.key === "Enter") {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
placeholder="Search Discord Chats..."
|
||||||
style={{ backgroundColor: theme.palette.inputColor, borderRadius: borderRadius, margin: 10, width: "100%", }}
|
style={{ backgroundColor: theme.palette.inputColor, borderRadius: borderRadius, margin: 10, width: "100%", }}
|
||||||
InputProps={{
|
InputProps={{
|
||||||
style: {
|
style: {
|
||||||
@@ -143,7 +148,7 @@ const DiscordChat = props => {
|
|||||||
const CustomHits = connectHits(Hits);
|
const CustomHits = connectHits(Hits);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ width: "100%", textAlign:"center", position: "relative", height: "100%", }}>
|
<div style={{textAlign:"center", position: "relative", height: "100%", padding:"0px 240px" }}>
|
||||||
<InstantSearch searchClient={searchClient} indexName="discord_chat">
|
<InstantSearch searchClient={searchClient} indexName="discord_chat">
|
||||||
<div style={{ maxWidth: 450, margin: "auto", marginTop: 15, marginBottom: 5, }}>
|
<div style={{ maxWidth: 450, margin: "auto", marginTop: 15, marginBottom: 5, }}>
|
||||||
<CustomSearchBox />
|
<CustomSearchBox />
|
||||||
|
|||||||
@@ -124,6 +124,11 @@ const DocsGrid = props => {
|
|||||||
removeQuery("q")
|
removeQuery("q")
|
||||||
refine(event.currentTarget.value)
|
refine(event.currentTarget.value)
|
||||||
}}
|
}}
|
||||||
|
onKeyDown={(event) => {
|
||||||
|
if(event.key === "Enter") {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
}}
|
||||||
limit={5}
|
limit={5}
|
||||||
/>
|
/>
|
||||||
{/*isSearchStalled ? 'My search is stalled' : ''*/}
|
{/*isSearchStalled ? 'My search is stalled' : ''*/}
|
||||||
@@ -274,7 +279,7 @@ const DocsGrid = props => {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
*/}
|
*/}
|
||||||
<div style={{width: "100%", position: "relative", height: "100%",}}>
|
<div style={{width: "100%", position: "relative", height: "100%", padding: "0px 180px"}}>
|
||||||
<InstantSearch searchClient={searchClient} indexName="documentation">
|
<InstantSearch searchClient={searchClient} indexName="documentation">
|
||||||
<div style={{maxWidth: 450, margin: "auto", marginTop: 15, marginBottom: 15, }}>
|
<div style={{maxWidth: 450, margin: "auto", marginTop: 15, marginBottom: 15, }}>
|
||||||
<CustomSearchBox />
|
<CustomSearchBox />
|
||||||
|
|||||||
@@ -78,7 +78,6 @@ const EditWorkflow = (props) => {
|
|||||||
const [name, setName] = React.useState(workflow.name !== undefined ? workflow.name : "")
|
const [name, setName] = React.useState(workflow.name !== undefined ? workflow.name : "")
|
||||||
const [dueDate, setDueDate] = React.useState(workflow.due_date !== undefined && workflow.due_date !== null && workflow.due_date !== 0 ? dayjs(workflow.due_date*1000) : dayjs().subtract(1, 'day'))
|
const [dueDate, setDueDate] = React.useState(workflow.due_date !== undefined && workflow.due_date !== null && workflow.due_date !== 0 ? dayjs(workflow.due_date*1000) : dayjs().subtract(1, 'day'))
|
||||||
|
|
||||||
console.log("WORKFLOW: ", workflow)
|
|
||||||
const [inputQuestions, setInputQuestions] = React.useState(workflow.input_questions !== undefined && workflow.input_questions !== null ? JSON.parse(JSON.stringify(workflow.input_questions)) : [])
|
const [inputQuestions, setInputQuestions] = React.useState(workflow.input_questions !== undefined && workflow.input_questions !== null ? JSON.parse(JSON.stringify(workflow.input_questions)) : [])
|
||||||
|
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
@@ -235,7 +234,7 @@ const EditWorkflow = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<div style={{width: 600, position: "fixed", left: 0, bottom: 0, zIndex: 1002, backgroundColor: "rgba(53,53,53,1)", height: 75, paddingTop: 20, paddingLeft: 75, }}>
|
<div style={{borderTop: "1px solid rgba(255,255,255,0.5)", width: 600, position: "fixed", left: 0, bottom: 0, zIndex: 1002, backgroundColor: "rgba(53,53,53,1)", height: 75, paddingTop: 20, paddingLeft: 75, }}>
|
||||||
{/*
|
{/*
|
||||||
<Button
|
<Button
|
||||||
style={{}}
|
style={{}}
|
||||||
@@ -315,7 +314,7 @@ const EditWorkflow = (props) => {
|
|||||||
}}
|
}}
|
||||||
color="primary"
|
color="primary"
|
||||||
>
|
>
|
||||||
{submitLoading ? <CircularProgress color="secondary" /> : "Done"}
|
{submitLoading ? <CircularProgress color="secondary" /> : "Save Changes"}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -339,26 +338,6 @@ const EditWorkflow = (props) => {
|
|||||||
autoFocus
|
autoFocus
|
||||||
fullWidth
|
fullWidth
|
||||||
/>
|
/>
|
||||||
<div style={{display: "flex", }}>
|
|
||||||
<TextField
|
|
||||||
onBlur={(event) => {
|
|
||||||
setDescription(event.target.value)
|
|
||||||
}}
|
|
||||||
InputProps={{
|
|
||||||
style: {
|
|
||||||
color: "white",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
maxRows={4}
|
|
||||||
color="primary"
|
|
||||||
defaultValue={innerWorkflow.description}
|
|
||||||
placeholder="Description"
|
|
||||||
multiline
|
|
||||||
label="Description"
|
|
||||||
margin="dense"
|
|
||||||
fullWidth
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div style={{display: "flex", marginTop: 10, }}>
|
<div style={{display: "flex", marginTop: 10, }}>
|
||||||
{usecases !== null && usecases !== undefined && usecases.length > 0 ?
|
{usecases !== null && usecases !== undefined && usecases.length > 0 ?
|
||||||
<FormControl style={{flex: 1, marginRight: 5,}}>
|
<FormControl style={{flex: 1, marginRight: 5,}}>
|
||||||
@@ -426,27 +405,56 @@ const EditWorkflow = (props) => {
|
|||||||
fullWidth
|
fullWidth
|
||||||
value={newWorkflowTags}
|
value={newWorkflowTags}
|
||||||
onChange={(chip) => {
|
onChange={(chip) => {
|
||||||
console.log("Chip: ", chip)
|
|
||||||
//newWorkflowTags.push(chip);
|
|
||||||
setNewWorkflowTags(chip);
|
setNewWorkflowTags(chip);
|
||||||
}}
|
}}
|
||||||
|
onBlur={(event) => {
|
||||||
|
if (event.target.value.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newWorkflowTags.includes(event.target.value)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
newWorkflowTags.push(event.target.value)
|
||||||
|
setNewWorkflowTags(newWorkflowTags)
|
||||||
|
|
||||||
|
setUpdate(Math.random())
|
||||||
|
}}
|
||||||
onAdd={(chip) => {
|
onAdd={(chip) => {
|
||||||
newWorkflowTags.push(chip);
|
newWorkflowTags.push(chip)
|
||||||
setNewWorkflowTags(newWorkflowTags);
|
setNewWorkflowTags(newWorkflowTags)
|
||||||
}}
|
}}
|
||||||
onDelete={(chip, index) => {
|
onDelete={(chip, index) => {
|
||||||
console.log("Deleting: ", chip, index)
|
console.log("Deleting: ", chip, index)
|
||||||
newWorkflowTags.splice(index, 1);
|
newWorkflowTags.splice(index, 1)
|
||||||
setNewWorkflowTags(newWorkflowTags);
|
setNewWorkflowTags(newWorkflowTags)
|
||||||
setUpdate(Math.random());
|
setUpdate(Math.random())
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{showMoreClicked === true ?
|
{showMoreClicked === true ?
|
||||||
<span style={{marginTop: 25, }}>
|
<div style={{marginTop: 50, }}>
|
||||||
|
<TextField
|
||||||
|
onBlur={(event) => {
|
||||||
|
setDescription(event.target.value)
|
||||||
|
}}
|
||||||
|
InputProps={{
|
||||||
|
style: {
|
||||||
|
color: "white",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
multiLine
|
||||||
|
rows={3}
|
||||||
|
color="primary"
|
||||||
|
defaultValue={innerWorkflow.description}
|
||||||
|
placeholder="Description"
|
||||||
|
multiline
|
||||||
|
label="Description"
|
||||||
|
margin="dense"
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
|
|
||||||
<div style={{display: "flex"}}>
|
<div style={{display: "flex"}}>
|
||||||
<FormControl style={{marginTop: 15, }}>
|
<FormControl style={{marginTop: 15, }}>
|
||||||
@@ -565,14 +573,24 @@ const EditWorkflow = (props) => {
|
|||||||
fullWidth
|
fullWidth
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Typography variant="body2" style={{marginTop: 50, }}>
|
<Divider style={{marginTop: 20, marginBottom: 20, }} />
|
||||||
MSSP Suborg Distribution (beta - contact support@shuffler.io)
|
|
||||||
|
<Typography variant="body1" style={{marginTop: 50, }}>
|
||||||
|
MSSP Suborg Distribution (beta - contact support@shuffler.io for more info)
|
||||||
</Typography>
|
</Typography>
|
||||||
{userdata !== undefined && userdata !== null && userdata.orgs !== undefined && userdata.orgs !== null && userdata.orgs.length > 0 ?
|
{userdata !== undefined && userdata !== null && userdata.orgs !== undefined && userdata.orgs !== null && userdata.orgs.length > 0 ?
|
||||||
userdata.orgs.filter(org => org.creator_org === userdata.active_org.id).length === 0 ?
|
userdata.orgs.filter(org => org.creator_org === userdata.active_org.id).length === 0 ?
|
||||||
<Typography variant="body2" style={{marginTop: 10, color: "rgba(255,255,255,0.7)"}}>
|
userdata.active_org.creator_org === undefined || userdata.active_org.creator_org === null || userdata.active_org.creator_org === "" ?
|
||||||
You can only distribute to suborgs from a parent org.
|
<Typography variant="body2" style={{marginTop: 10, color: "rgba(255,255,255,0.7)"}}>
|
||||||
</Typography>
|
Your organization does not have any suborgs yet. Please <a href="/admin?tab=suborgs" style={{textDecoration: "none", color: "#f86a3e"}} target="_blank">make one</a>, then try again.
|
||||||
|
</Typography>
|
||||||
|
:
|
||||||
|
<Typography variant="body2" style={{marginTop: 10, color: "rgba(255,255,255,0.7)"}}>
|
||||||
|
{innerWorkflow.parentorg_workflow !== undefined && innerWorkflow.parentorg_workflow !== null && innerWorkflow.parentorg_workflow.length > 0 ? <span>This workflow is distributed from <a href={`/workflows/${innerWorkflow.parentorg_workflow}`} style={{textDecoration: "none", color: "#f86a3e"}} target="_blank">your parent workflow</a> (you may not have access).</span> : null}
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
You can only distribute to suborgs from a parent org.
|
||||||
|
</Typography>
|
||||||
:
|
:
|
||||||
<Select
|
<Select
|
||||||
multiple
|
multiple
|
||||||
@@ -584,8 +602,6 @@ const EditWorkflow = (props) => {
|
|||||||
newvalue = newvalue.filter(value => value !== "none")
|
newvalue = newvalue.filter(value => value !== "none")
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("NEWVALUE: ", newvalue)
|
|
||||||
|
|
||||||
if (newvalue.includes("none")) {
|
if (newvalue.includes("none")) {
|
||||||
newvalue = ["none"]
|
newvalue = ["none"]
|
||||||
} else if (newvalue.includes("all")) {
|
} else if (newvalue.includes("all")) {
|
||||||
@@ -664,12 +680,13 @@ const EditWorkflow = (props) => {
|
|||||||
</Link>
|
</Link>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<Divider style={{marginTop: 20, marginBottom: 20, }} />
|
||||||
|
|
||||||
<Typography variant="h6" style={{marginTop: 50, }}>
|
<Typography variant="h6" style={{marginTop: 50, }}>
|
||||||
Input fields
|
Input fields
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body1" color="textSecondary" style={{marginBottom: 20, }}>
|
<Typography variant="body2" color="textSecondary" style={{marginBottom: 20, }}>
|
||||||
Input fields are fields that will be used during the startup of the workflow. These will be formatted in JSON and is most commonly used from the <a href={`/workflows/${workflow.id}/run`} rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", color: "#f86a3e" }}>workflow run page</a>.
|
Input fields are fields that will be used during the startup of the workflow. These will be formatted in JSON and is most commonly used from the <a href={`/workflows/${workflow.id}/run`} rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", color: "#f86a3e" }}>workflow run page</a>. If chosen in the User Input node, these will be required fields.
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
|
|
||||||
@@ -775,18 +792,173 @@ const EditWorkflow = (props) => {
|
|||||||
>
|
>
|
||||||
<AddIcon style={{}} />
|
<AddIcon style={{}} />
|
||||||
</Button>
|
</Button>
|
||||||
</span>
|
|
||||||
|
<Divider style={{marginTop: 20, marginBottom: 20, }} />
|
||||||
|
|
||||||
|
<Typography variant="body1" style={{marginTop: 50, }}>
|
||||||
|
Git Backup Repository
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2" style={{ textAlign: "left", marginTop: 5, }} color="textSecondary">
|
||||||
|
Decide where this workflow is backed up in a Git repository. Will create logs and notifications if upload fails. <b>The repository and branch must already have been initialized</b>. Files will show up in the root folder in the format 'orgid/workflow status/workflow id.json' without images. Overrides the <a href="/admin?admin_tab=organization" style={{textDecoration: "none", color: "#f86a3e"}} target="_blank">default backup repository</a> your org has chosen. All fields must be filled in for the backup to work.
|
||||||
|
<br />
|
||||||
|
PS: This is a beta feature, and might not work as expected. Credentials are NOT encrypted.
|
||||||
|
</Typography>
|
||||||
|
<Grid container style={{ marginTop: 10, }} spacing={2}>
|
||||||
|
<Grid item xs={6} style={{}}>
|
||||||
|
<span>
|
||||||
|
<Typography>Workflow Backup Repository</Typography>
|
||||||
|
<TextField
|
||||||
|
required
|
||||||
|
style={{
|
||||||
|
flex: "1",
|
||||||
|
marginTop: "5px",
|
||||||
|
marginRight: "15px",
|
||||||
|
}}
|
||||||
|
fullWidth={true}
|
||||||
|
type="name"
|
||||||
|
multiline={true}
|
||||||
|
rows={1}
|
||||||
|
id="outlined-with-placeholder"
|
||||||
|
margin="normal"
|
||||||
|
variant="outlined"
|
||||||
|
placeholder="github/com/shuffle/workflowbackup "
|
||||||
|
defaultValue={innerWorkflow.backup_config === undefined || innerWorkflow.backup_config.upload_repo === undefined || innerWorkflow.backup_config.upload_repo === null || innerWorkflow.backup_config.upload_repo === "" ? "" : innerWorkflow.backup_config.upload_repo}
|
||||||
|
onChange={(e) => {
|
||||||
|
//setUploadRepo(e.target.value);
|
||||||
|
innerWorkflow.backup_config.upload_repo = e.target.value
|
||||||
|
setInnerWorkflow(innerWorkflow)
|
||||||
|
}}
|
||||||
|
InputProps={{
|
||||||
|
classes: {
|
||||||
|
notchedOutline: classes.notchedOutline,
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
color: "white",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6} style={{}}>
|
||||||
|
<span>
|
||||||
|
<Typography>Branch</Typography>
|
||||||
|
<TextField
|
||||||
|
required
|
||||||
|
style={{
|
||||||
|
flex: "1",
|
||||||
|
marginTop: "5px",
|
||||||
|
marginRight: "15px",
|
||||||
|
}}
|
||||||
|
fullWidth={true}
|
||||||
|
type="name"
|
||||||
|
id="outlined-with-placeholder"
|
||||||
|
margin="normal"
|
||||||
|
variant="outlined"
|
||||||
|
multiline={true}
|
||||||
|
rows={1}
|
||||||
|
placeholder="The branch to use"
|
||||||
|
defaultValue={innerWorkflow.backup_config === undefined || innerWorkflow.backup_config.upload_branch === undefined || innerWorkflow.backup_config.upload_branch === null || innerWorkflow.backup_config.upload_branch === "" ? "" : innerWorkflow.backup_config.upload_branch}
|
||||||
|
onChange={(e) => {
|
||||||
|
innerWorkflow.backup_config.upload_branch = e.target.value
|
||||||
|
setInnerWorkflow(innerWorkflow)
|
||||||
|
}}
|
||||||
|
InputProps={{
|
||||||
|
classes: {
|
||||||
|
notchedOutline: classes.notchedOutline,
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
color: "white",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<Grid container style={{ }} spacing={2}>
|
||||||
|
<Grid item xs={6} style={{}}>
|
||||||
|
<span>
|
||||||
|
<Typography>Username</Typography>
|
||||||
|
<TextField
|
||||||
|
required
|
||||||
|
style={{
|
||||||
|
flex: "1",
|
||||||
|
marginTop: "5px",
|
||||||
|
marginRight: "15px",
|
||||||
|
}}
|
||||||
|
fullWidth={true}
|
||||||
|
type="name"
|
||||||
|
multiline={true}
|
||||||
|
rows={1}
|
||||||
|
id="outlined-with-placeholder"
|
||||||
|
margin="normal"
|
||||||
|
variant="outlined"
|
||||||
|
placeholder="The username to use"
|
||||||
|
defaultValue={innerWorkflow.backup_config === undefined || innerWorkflow.backup_config.upload_username === undefined || innerWorkflow.backup_config.upload_username === null || innerWorkflow.backup_config.upload_username === "" ? "" : innerWorkflow.backup_config.upload_username}
|
||||||
|
onChange={(e) => {
|
||||||
|
innerWorkflow.backup_config.upload_username = e.target.value
|
||||||
|
setInnerWorkflow(innerWorkflow)
|
||||||
|
}}
|
||||||
|
InputProps={{
|
||||||
|
classes: {
|
||||||
|
notchedOutline: classes.notchedOutline,
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
color: "white",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6} style={{}}>
|
||||||
|
<span>
|
||||||
|
<Typography>Git token/password</Typography>
|
||||||
|
<TextField
|
||||||
|
required
|
||||||
|
style={{
|
||||||
|
flex: "1",
|
||||||
|
marginTop: "5px",
|
||||||
|
marginRight: "15px",
|
||||||
|
}}
|
||||||
|
fullWidth={true}
|
||||||
|
id="outlined-with-placeholder"
|
||||||
|
margin="normal"
|
||||||
|
variant="outlined"
|
||||||
|
multiline={true}
|
||||||
|
rows={1}
|
||||||
|
placeholder="The API token to use"
|
||||||
|
defaultValue={innerWorkflow.backup_config === undefined || innerWorkflow.backup_config.upload_token === undefined || innerWorkflow.backup_config.upload_token === null || innerWorkflow.backup_config.upload_token === "" ? "" : innerWorkflow.backup_config.upload_token}
|
||||||
|
onChange={(e) => {
|
||||||
|
innerWorkflow.backup_config.upload_token = e.target.value
|
||||||
|
setInnerWorkflow(innerWorkflow)
|
||||||
|
}}
|
||||||
|
InputProps={{
|
||||||
|
classes: {
|
||||||
|
notchedOutline: classes.notchedOutline,
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
color: "white",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
type="password"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</div>
|
||||||
: null}
|
: null}
|
||||||
|
|
||||||
|
|
||||||
<Tooltip color="primary" title={"Add more details"} placement="top">
|
<Tooltip color="primary" title={"Add more details"} placement="top">
|
||||||
<IconButton
|
<Button
|
||||||
style={{ color: "white", margin: "auto", textAlign: "center", width: 50, marginTop: 50, }}
|
style={{ margin: "auto", marginTop: 50, textAlign: "center", }}
|
||||||
|
variant="outlined"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setShowMoreClicked(!showMoreClicked);
|
setShowMoreClicked(!showMoreClicked);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{showMoreClicked ? <ExpandLessIcon /> : <ExpandMoreIcon/>}
|
{showMoreClicked ? <ExpandLessIcon /> : <ExpandMoreIcon/>}
|
||||||
</IconButton>
|
{showMoreClicked ? "Collapse": "Expand"}
|
||||||
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -44,9 +44,9 @@ const Files = (props) => {
|
|||||||
const { globalUrl, userdata, serverside, selectedOrganization, isCloud,isSelectedFiles } = props;
|
const { globalUrl, userdata, serverside, selectedOrganization, isCloud,isSelectedFiles } = props;
|
||||||
|
|
||||||
const [files, setFiles] = React.useState([]);
|
const [files, setFiles] = React.useState([]);
|
||||||
const [selectedNamespace, setSelectedNamespace] = React.useState("default");
|
const [selectedCategory, setSelectedCategory] = React.useState("default");
|
||||||
const [openFileId, setOpenFileId] = React.useState(false);
|
const [openFileId, setOpenFileId] = React.useState(false);
|
||||||
const [fileNamespaces, setFileNamespaces] = React.useState([]);
|
const [fileCategories, setFileCategories] = React.useState([]);
|
||||||
const [fileContent, setFileContent] = React.useState("");
|
const [fileContent, setFileContent] = React.useState("");
|
||||||
const [openEditor, setOpenEditor] = React.useState(false);
|
const [openEditor, setOpenEditor] = React.useState(false);
|
||||||
const [renderTextBox, setRenderTextBox] = React.useState(false);
|
const [renderTextBox, setRenderTextBox] = React.useState(false);
|
||||||
@@ -57,6 +57,7 @@ const Files = (props) => {
|
|||||||
const [downloadUrl, setDownloadUrl] = React.useState("https://github.com/shuffle/standards")
|
const [downloadUrl, setDownloadUrl] = React.useState("https://github.com/shuffle/standards")
|
||||||
const [downloadBranch, setDownloadBranch] = React.useState("main");
|
const [downloadBranch, setDownloadBranch] = React.useState("main");
|
||||||
const [downloadFolder, setDownloadFolder] = React.useState("translation_standards");
|
const [downloadFolder, setDownloadFolder] = React.useState("translation_standards");
|
||||||
|
const [contentLoading, setContentLoading] = React.useState(false)
|
||||||
|
|
||||||
//const alert = useAlert();
|
//const alert = useAlert();
|
||||||
const allowedFileTypes = ["txt", "py", "yaml", "yml","json", "html", "js", "csv", "log", "eml", "msg", "md", "xml", "sh", "bat", "ps1", "psm1", "psd1", "ps1xml", "pssc", "psc1", "response"]
|
const allowedFileTypes = ["txt", "py", "yaml", "yml","json", "html", "js", "csv", "log", "eml", "msg", "md", "xml", "sh", "bat", "ps1", "psm1", "psd1", "ps1xml", "pssc", "psc1", "response"]
|
||||||
@@ -67,8 +68,8 @@ const Files = (props) => {
|
|||||||
|
|
||||||
console.log('do validate')
|
console.log('do validate')
|
||||||
console.log("new namespace name->",event.target.value);
|
console.log("new namespace name->",event.target.value);
|
||||||
fileNamespaces.push(event.target.value);
|
fileCategories.push(event.target.value);
|
||||||
setSelectedNamespace(event.target.value);
|
setSelectedCategory(event.target.value);
|
||||||
setRenderTextBox(false);
|
setRenderTextBox(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,12 +108,13 @@ const Files = (props) => {
|
|||||||
|
|
||||||
const getFiles = (namespace) => {
|
const getFiles = (namespace) => {
|
||||||
var parsedurl = `${globalUrl}/api/v1/files`
|
var parsedurl = `${globalUrl}/api/v1/files`
|
||||||
if (namespace === undefined || namespace === "default") {
|
|
||||||
|
if (namespace === undefined || namespace === null || namespace === "default") {
|
||||||
|
|
||||||
} else if (namespace !== undefined && namespace !== null && namespace !== "") {
|
} else if (namespace !== undefined && namespace !== null && namespace !== "") {
|
||||||
parsedurl = `${globalUrl}/api/v1/files/namespaces/${namespace}?ids=true`
|
parsedurl = `${globalUrl}/api/v1/files/namespaces/${namespace}?ids=true`
|
||||||
} else if (selectedNamespace !== undefined && selectedNamespace !== null && selectedNamespace !== "default" && selectedNamespace !== "") {
|
} else if (selectedCategory !== undefined && selectedCategory !== null && selectedCategory !== "default" && selectedCategory !== "") {
|
||||||
parsedurl = `${globalUrl}/api/v1/files/namespaces/${selectedNamespace}?ids=true`
|
parsedurl = `${globalUrl}/api/v1/files/namespaces/${selectedCategory}?ids=true`
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch(parsedurl, {
|
fetch(parsedurl, {
|
||||||
@@ -149,9 +151,11 @@ const Files = (props) => {
|
|||||||
setFiles([]);
|
setFiles([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (responseJson.namespaces !== undefined && responseJson.namespaces !== null && (fileNamespaces.length === 0 || responseJson.namespaces.length > fileNamespaces.length)) {
|
if (namespace === undefined || namespace === null || namespace === "default") {
|
||||||
setFileNamespaces(responseJson.namespaces);
|
if (responseJson.namespaces !== undefined && responseJson.namespaces !== null && (fileCategories.length === 0 || responseJson.namespaces.length > fileCategories.length)) {
|
||||||
}
|
setFileCategories(responseJson.namespaces)
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
toast(error.toString());
|
toast(error.toString());
|
||||||
@@ -159,7 +163,21 @@ const Files = (props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getFiles(selectedNamespace)
|
getFiles("default")
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
var category = selectedCategory
|
||||||
|
if (window.location.search.includes("category=")) {
|
||||||
|
const urlParams = new URLSearchParams(window.location.search)
|
||||||
|
category = urlParams.get("category")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (category !== undefined && category !== null && category.length > 0 && category !== "default") {
|
||||||
|
setSelectedCategory(category)
|
||||||
|
}
|
||||||
|
|
||||||
|
getFiles(category)
|
||||||
|
}, 1000)
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const importStandardsFromUrl = (url, folder) => {
|
const importStandardsFromUrl = (url, folder) => {
|
||||||
@@ -392,8 +410,9 @@ const Files = (props) => {
|
|||||||
) {
|
) {
|
||||||
toast("Failed to delete file: " + responseJson.reason);
|
toast("Failed to delete file: " + responseJson.reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
getFiles();
|
getFiles(selectedCategory)
|
||||||
}, 1500);
|
}, 1500);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
@@ -402,6 +421,8 @@ const Files = (props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const readFileData = (file) => {
|
const readFileData = (file) => {
|
||||||
|
setContentLoading(true)
|
||||||
|
|
||||||
fetch(globalUrl + "/api/v1/files/" + file.id + "/content", {
|
fetch(globalUrl + "/api/v1/files/" + file.id + "/content", {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
@@ -411,6 +432,7 @@ const Files = (props) => {
|
|||||||
credentials: "include",
|
credentials: "include",
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
|
setContentLoading(false)
|
||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
console.log("Status not 200 for file :O!");
|
console.log("Status not 200 for file :O!");
|
||||||
return "";
|
return "";
|
||||||
@@ -428,12 +450,12 @@ const Files = (props) => {
|
|||||||
return respdata
|
return respdata
|
||||||
})
|
})
|
||||||
.then((responseData) => {
|
.then((responseData) => {
|
||||||
|
setFileContent(responseData);
|
||||||
setFileContent(responseData);
|
//console.log("filecontent state ",fileContent);
|
||||||
//console.log("filecontent state ",fileContent);
|
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
toast(error.toString());
|
setContentLoading(false)
|
||||||
|
toast(error.toString())
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -505,12 +527,12 @@ const Files = (props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (
|
if (
|
||||||
selectedNamespace !== undefined &&
|
selectedCategory !== undefined &&
|
||||||
selectedNamespace !== null &&
|
selectedCategory !== null &&
|
||||||
selectedNamespace.length > 0 &&
|
selectedCategory.length > 0 &&
|
||||||
selectedNamespace !== "default"
|
selectedCategory !== "default"
|
||||||
) {
|
) {
|
||||||
data.namespace = selectedNamespace;
|
data.namespace = selectedCategory;
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch(globalUrl + "/api/v1/files/create", {
|
fetch(globalUrl + "/api/v1/files/create", {
|
||||||
@@ -676,22 +698,23 @@ const Files = (props) => {
|
|||||||
//setFile(fileObject)
|
//setFile(fileObject)
|
||||||
//const files = event.target.files[0]
|
//const files = event.target.files[0]
|
||||||
uploadFiles(event.target.files);
|
uploadFiles(event.target.files);
|
||||||
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
style={{ marginLeft: 5, marginRight: 15, backgroundColor:isSelectedFiles?"#2F2F2F":null,borderRadius:isSelectedFiles?200:null, width:isSelectedFiles?81:null, height:isSelectedFiles?40:null, boxShadow: isSelectedFiles?'none':null, }}
|
style={{ marginLeft: 5, marginRight: 15, backgroundColor:isSelectedFiles?"#2F2F2F":null,borderRadius:isSelectedFiles?200:null, width:isSelectedFiles?81:null, height:isSelectedFiles?40:null, boxShadow: isSelectedFiles?'none':null, }}
|
||||||
variant="contained"
|
variant="contained"
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={() => getFiles()}
|
onClick={() => getFiles(selectedCategory)}
|
||||||
>
|
>
|
||||||
<CachedIcon />
|
<CachedIcon />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{fileNamespaces !== undefined &&
|
{fileCategories !== undefined &&
|
||||||
fileNamespaces !== null &&
|
fileCategories !== null &&
|
||||||
fileNamespaces.length > 1 ? (
|
fileCategories.length > 1 ? (
|
||||||
<FormControl style={{ minWidth: 150, maxWidth: 150 }}>
|
<FormControl style={{ minWidth: 150, maxWidth: 150 }}>
|
||||||
<InputLabel id="input-namespace-label">File Category</InputLabel>
|
<InputLabel id="input-namespace-label">File Category</InputLabel>
|
||||||
<Select
|
<Select
|
||||||
@@ -703,18 +726,26 @@ const Files = (props) => {
|
|||||||
maxWidth: 150,
|
maxWidth: 150,
|
||||||
float: "right",
|
float: "right",
|
||||||
}}
|
}}
|
||||||
value={selectedNamespace}
|
value={selectedCategory}
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
setSelectedNamespace(event.target.value);
|
setSelectedCategory(event.target.value)
|
||||||
|
|
||||||
if (event.target.value === "all" || event.target.value === "default") {
|
if (event.target.value === "all" || event.target.value === "default") {
|
||||||
getFiles()
|
getFiles()
|
||||||
} else {
|
} else {
|
||||||
getFiles(event.target.value)
|
getFiles(event.target.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add it to the url as a query
|
||||||
|
if (window.location.search.includes("category=")) {
|
||||||
|
const newurl = window.location.href.replace(/category=[^&]+/, `category=${event.target.value}`)
|
||||||
|
window.history.pushState({ path: newurl }, "", newurl)
|
||||||
|
} else {
|
||||||
|
window.history.pushState({ path: window.location.href }, "", `${window.location.href}&category=${event.target.value}`)
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{fileNamespaces.map((data, index) => {
|
{fileCategories.map((data, index) => {
|
||||||
return (
|
return (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
key={index}
|
key={index}
|
||||||
@@ -731,31 +762,31 @@ const Files = (props) => {
|
|||||||
<div style={{display: "inline-flex", position:"relative"}}>
|
<div style={{display: "inline-flex", position:"relative"}}>
|
||||||
{renderTextBox ?
|
{renderTextBox ?
|
||||||
|
|
||||||
<Tooltip title={"Close"} style={{}} aria-label={""}>
|
<Tooltip title={"Close"} style={{}} aria-label={""}>
|
||||||
<Button
|
<Button
|
||||||
style={{ marginLeft: 5, marginRight: 15 }}
|
style={{ marginLeft: 5, marginRight: 15 }}
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setRenderTextBox(false);
|
setRenderTextBox(false);
|
||||||
console.log(" close clicked")
|
console.log(" close clicked")
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ClearIcon/>
|
<ClearIcon/>
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
:
|
:
|
||||||
<Tooltip title={"Add new file category"} style={{}} aria-label={""}>
|
<Tooltip title={"Add new file category"} style={{}} aria-label={""}>
|
||||||
<Button
|
<Button
|
||||||
style={{ marginLeft: 5, marginRight: 15 }}
|
style={{ marginLeft: 5, marginRight: 15 }}
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setRenderTextBox(true);
|
setRenderTextBox(true);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AddIcon/>
|
<AddIcon/>
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip> }
|
</Tooltip>
|
||||||
|
}
|
||||||
|
|
||||||
{renderTextBox && <TextField
|
{renderTextBox && <TextField
|
||||||
onKeyPress={(event)=>{
|
onKeyPress={(event)=>{
|
||||||
@@ -783,6 +814,7 @@ const Files = (props) => {
|
|||||||
isFileEditor = {true}
|
isFileEditor = {true}
|
||||||
key = {fileContent} //https://reactjs.org/docs/reconciliation.html#recursing-on-children
|
key = {fileContent} //https://reactjs.org/docs/reconciliation.html#recursing-on-children
|
||||||
runUpdateText = {runUpdateText}
|
runUpdateText = {runUpdateText}
|
||||||
|
contentLoading = {contentLoading}
|
||||||
/>
|
/>
|
||||||
{isSelectedFiles?null:
|
{isSelectedFiles?null:
|
||||||
<Divider
|
<Divider
|
||||||
@@ -795,15 +827,17 @@ const Files = (props) => {
|
|||||||
|
|
||||||
<List style={{borderRadius: isSelectedFiles?8:null, border:isSelectedFiles?"1px solid #494949":null, marginTop:isSelectedFiles?24:null}}>
|
<List style={{borderRadius: isSelectedFiles?8:null, border:isSelectedFiles?"1px solid #494949":null, marginTop:isSelectedFiles?24:null}}>
|
||||||
<ListItem style={{width:isSelectedFiles?"100%":null, borderBottom:isSelectedFiles?"1px solid #494949":null}}>
|
<ListItem style={{width:isSelectedFiles?"100%":null, borderBottom:isSelectedFiles?"1px solid #494949":null}}>
|
||||||
|
{/*
|
||||||
<ListItemText
|
<ListItemText
|
||||||
primary="Updated"
|
primary="Updated"
|
||||||
style={{ maxWidth: 185, minWidth: 185 }}
|
style={{ maxWidth: 185, minWidth: 185 }}
|
||||||
/>
|
/>
|
||||||
|
*/}
|
||||||
<ListItemText
|
<ListItemText
|
||||||
primary="Name"
|
primary="Name"
|
||||||
style={{
|
style={{
|
||||||
maxWidth: 150,
|
maxWidth: 250,
|
||||||
minWidth: 150,
|
minWidth: 250,
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
marginLeft: 10,
|
marginLeft: 10,
|
||||||
}}
|
}}
|
||||||
@@ -814,7 +848,7 @@ const Files = (props) => {
|
|||||||
/>
|
/>
|
||||||
<ListItemText
|
<ListItemText
|
||||||
primary="Md5"
|
primary="Md5"
|
||||||
style={{ minWidth: isSelectedFiles?210:250, maxWidth: isSelectedFiles?210:250, overflow: "hidden" }}
|
style={{ minWidth: 300, maxWidth: 300, overflow: "hidden" }}
|
||||||
/>
|
/>
|
||||||
<ListItemText
|
<ListItemText
|
||||||
primary="Status"
|
primary="Status"
|
||||||
@@ -832,7 +866,7 @@ const Files = (props) => {
|
|||||||
file.namespace = "default";
|
file.namespace = "default";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file.namespace !== selectedNamespace) {
|
if (file.namespace !== selectedCategory) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -853,6 +887,7 @@ const Files = (props) => {
|
|||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
{/*
|
||||||
<ListItemText
|
<ListItemText
|
||||||
style={{
|
style={{
|
||||||
maxWidth: isSelectedFiles ? 170:225,
|
maxWidth: isSelectedFiles ? 170:225,
|
||||||
@@ -861,10 +896,11 @@ const Files = (props) => {
|
|||||||
}}
|
}}
|
||||||
primary={new Date(file.updated_at * 1000).toISOString()}
|
primary={new Date(file.updated_at * 1000).toISOString()}
|
||||||
/>
|
/>
|
||||||
|
*/}
|
||||||
<ListItemText
|
<ListItemText
|
||||||
style={{
|
style={{
|
||||||
maxWidth: 150,
|
maxWidth: 250,
|
||||||
minWidth: 150,
|
minWidth: 250,
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
marginLeft: 10,
|
marginLeft: 10,
|
||||||
}}
|
}}
|
||||||
@@ -928,8 +964,8 @@ const Files = (props) => {
|
|||||||
<ListItemText
|
<ListItemText
|
||||||
primary={file.md5_sum}
|
primary={file.md5_sum}
|
||||||
style={{
|
style={{
|
||||||
minWidth: isSelectedFiles?200:300,
|
minWidth: 300,
|
||||||
maxWidth: isSelectedFiles?200:300,
|
maxWidth: 300,
|
||||||
marginLeft:isSelectedFiles? 15:null,
|
marginLeft:isSelectedFiles? 15:null,
|
||||||
overflow: isSelectedFiles?"auto":"hidden",
|
overflow: isSelectedFiles?"auto":"hidden",
|
||||||
}}
|
}}
|
||||||
@@ -1076,7 +1112,7 @@ const Files = (props) => {
|
|||||||
disabled={file.status !== "active"}
|
disabled={file.status !== "active"}
|
||||||
style = {{padding: "6px"}}
|
style = {{padding: "6px"}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
deleteFile(file);
|
deleteFile(file)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DeleteIcon
|
<DeleteIcon
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
import theme from "../theme.jsx";
|
import theme from "../theme.jsx";
|
||||||
import { BrowserView, MobileView } from "react-device-detect";
|
import { BrowserView, MobileView } from "react-device-detect";
|
||||||
@@ -84,6 +84,15 @@ const Header = (props) => {
|
|||||||
setAnchorEl(event.currentTarget);
|
setAnchorEl(event.currentTarget);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const topbar = localStorage.getItem("topbar_closed")
|
||||||
|
if (topbar === "true") {
|
||||||
|
setShowTopbar(false)
|
||||||
|
} else {
|
||||||
|
setShowTopbar(true)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
setAnchorEl(null);
|
setAnchorEl(null);
|
||||||
setAnchorElAvatar(null);
|
setAnchorElAvatar(null);
|
||||||
@@ -537,7 +546,7 @@ const Header = (props) => {
|
|||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
<Tooltip color="primary" title={"Join the Shuffle Automation Community"} placement="left">
|
<Tooltip color="primary" title={"Shuffle is Open Source, and has a thriving Discord Automation Community"} placement="left">
|
||||||
<IconButton
|
<IconButton
|
||||||
color="primary"
|
color="primary"
|
||||||
style={{}}
|
style={{}}
|
||||||
@@ -545,11 +554,8 @@ const Header = (props) => {
|
|||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
onClick={(event) => { }}
|
onClick={(event) => { }}
|
||||||
>
|
>
|
||||||
<img
|
<svg viewBox="0 0 256 199" width="256" height="199" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid" style={{height: 30, width: 30, }}><path d="M216.856 16.597A208.502 208.502 0 0 0 164.042 0c-2.275 4.113-4.933 9.645-6.766 14.046-19.692-2.961-39.203-2.961-58.533 0-1.832-4.4-4.55-9.933-6.846-14.046a207.809 207.809 0 0 0-52.855 16.638C5.618 67.147-3.443 116.4 1.087 164.956c22.169 16.555 43.653 26.612 64.775 33.193A161.094 161.094 0 0 0 79.735 175.3a136.413 136.413 0 0 1-21.846-10.632 108.636 108.636 0 0 0 5.356-4.237c42.122 19.702 87.89 19.702 129.51 0a131.66 131.66 0 0 0 5.355 4.237 136.07 136.07 0 0 1-21.886 10.653c4.006 8.02 8.638 15.67 13.873 22.848 21.142-6.58 42.646-16.637 64.815-33.213 5.316-56.288-9.08-105.09-38.056-148.36ZM85.474 135.095c-12.645 0-23.015-11.805-23.015-26.18s10.149-26.2 23.015-26.2c12.867 0 23.236 11.804 23.015 26.2.02 14.375-10.148 26.18-23.015 26.18Zm85.051 0c-12.645 0-23.014-11.805-23.014-26.18s10.148-26.2 23.014-26.2c12.867 0 23.236 11.804 23.015 26.2 0 14.375-10.148 26.18-23.015 26.18Z" fill="#dadae1"/></svg>
|
||||||
alt="Discord Community Join"
|
{/*#f865f2*/}
|
||||||
src={"/images/social/discord.png"}
|
|
||||||
style={{ height: 30, width: 30 }}
|
|
||||||
/>
|
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</a>
|
</a>
|
||||||
@@ -1590,17 +1596,17 @@ const Header = (props) => {
|
|||||||
|
|
||||||
navigate("/training")
|
navigate("/training")
|
||||||
|
|
||||||
//if (window.drift !== undefined) {
|
|
||||||
// window.drift.api.startInteraction({ interactionId: 341911 })
|
|
||||||
//} else {
|
|
||||||
// console.log("Couldn't find drift in window.drift and not .drift-open-chat with querySelector: ", window.drift)
|
|
||||||
//}
|
|
||||||
}} style={{ cursor: "pointer", textDecoration: "none", color: "rgba(255,255,255,0.8)" }}>
|
}} style={{ cursor: "pointer", textDecoration: "none", color: "rgba(255,255,255,0.8)" }}>
|
||||||
Public Training!
|
Public Training!
|
||||||
</span>
|
</span>
|
||||||
</u>
|
</u>
|
||||||
</Typography>
|
</Typography>
|
||||||
<IconButton color="secondary" style={{ position: "absolute", top: -3, right: 20, }} onClick={(event) => { setShowTopbar(false) }}>
|
<IconButton color="secondary" style={{ position: "absolute", top: -3, right: 20, }} onClick={(event) => {
|
||||||
|
setShowTopbar(false)
|
||||||
|
|
||||||
|
// Set storage that it's clicked
|
||||||
|
localStorage.setItem("topbar_closed", "true")
|
||||||
|
}}>
|
||||||
<CloseIcon />
|
<CloseIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -471,32 +471,26 @@ const AuthenticationOauth2 = (props) => {
|
|||||||
state += `%26refresh_uri%3d${authentication_url}`;
|
state += `%26refresh_uri%3d${authentication_url}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// No prompt forcing
|
// FIXME: Should this be =consent?
|
||||||
//var url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&prompt=login&scope=${resources}&state=${state}&access_type=offline`;
|
|
||||||
var defaultPrompt = "login"
|
var defaultPrompt = "login"
|
||||||
if (prompt !== undefined && prompt !== null && prompt.length > 0) {
|
if (prompt !== undefined && prompt !== null && prompt.length > 0) {
|
||||||
defaultPrompt = prompt
|
defaultPrompt = prompt
|
||||||
}
|
}
|
||||||
|
|
||||||
var url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&prompt=${defaultPrompt}&scope=${resources}&state=${state}&access_type=offline`;
|
var url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&prompt=${defaultPrompt}&scope=${resources}&state=${state}&access_type=offline`;
|
||||||
|
if (admin_consent === true) {
|
||||||
|
console.log("Running Oauth2 WITH admin consent")
|
||||||
|
//url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&prompt=consent&scope=${resources}&state=${state}&access_type=offline`;
|
||||||
|
url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&prompt=admin_consent&scope=${resources}&state=${state}&access_type=offline`;
|
||||||
|
}
|
||||||
|
|
||||||
if (admin_consent === true) {
|
console.log("OAUTH2 URL: ", url)
|
||||||
console.log("Running Oauth2 WITH admin consent")
|
|
||||||
//url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&prompt=consent&scope=${resources}&state=${state}&access_type=offline`;
|
|
||||||
url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&prompt=admin_consent&scope=${resources}&state=${state}&access_type=offline`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Force new consent
|
// Force new consent
|
||||||
//const url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&scope=${resources}&prompt=consent&state=${state}&access_type=offline`;
|
//const url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&scope=${resources}&prompt=consent&state=${state}&access_type=offline`;
|
||||||
|
|
||||||
// Admin consent
|
// Admin consent
|
||||||
//const url = `https://accounts.zoho.com/oauth/v2/auth?response_type=code&client_id=${client_id}&scope=AaaServer.profile.Read&redirect_uri=${redirectUri}&prompt=consent`
|
//const url = `https://accounts.zoho.com/oauth/v2/auth?response_type=code&client_id=${client_id}&scope=AaaServer.profile.Read&redirect_uri=${redirectUri}&prompt=consent`
|
||||||
|
|
||||||
// &resource=https%3A%2F%2Fgraph.microsoft.com&
|
|
||||||
|
|
||||||
// FIXME: Awful, but works for prototyping
|
|
||||||
// How can we get a callback properly realtime?
|
|
||||||
// How can we properly try-catch without breaks on error?
|
|
||||||
try {
|
try {
|
||||||
var newwin = window.open(url, "", "width=582,height=700");
|
var newwin = window.open(url, "", "width=582,height=700");
|
||||||
//console.log(newwin)
|
//console.log(newwin)
|
||||||
@@ -997,7 +991,6 @@ const AuthenticationOauth2 = (props) => {
|
|||||||
color: "white",
|
color: "white",
|
||||||
padding: 5,
|
padding: 5,
|
||||||
minWidth: 300,
|
minWidth: 300,
|
||||||
maxWidth: 300,
|
|
||||||
}}
|
}}
|
||||||
onChange={(e, value) => {
|
onChange={(e, value) => {
|
||||||
//handleScopeChange(e)
|
//handleScopeChange(e)
|
||||||
|
|||||||
@@ -55,6 +55,10 @@ const OrgHeaderexpanded = (props) => {
|
|||||||
globalUrl,
|
globalUrl,
|
||||||
isCloud,
|
isCloud,
|
||||||
adminTab,
|
adminTab,
|
||||||
|
|
||||||
|
selectedStatus,
|
||||||
|
setSelectedStatus,
|
||||||
|
isEditOrgTab
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
@@ -169,6 +173,11 @@ const OrgHeaderexpanded = (props) => {
|
|||||||
: selectedOrganization.sso_config.openid_token
|
: selectedOrganization.sso_config.openid_token
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const [uploadRepo, setUploadRepo] = React.useState(selectedOrganization.defaults === undefined ? "" : selectedOrganization.defaults.workflow_upload_repo === undefined || selectedOrganization.defaults.workflow_upload_repo.length === 0 ? "" : selectedOrganization.defaults.workflow_upload_repo)
|
||||||
|
const [uploadBranch, setUploadBranch] = React.useState(selectedOrganization.defaults === undefined ? defaultBranch : selectedOrganization.defaults.workflow_upload_branch === undefined || selectedOrganization.defaults.workflow_upload_branch.length === 0 ? defaultBranch : selectedOrganization.defaults.workflow_upload_branch)
|
||||||
|
const [uploadUsername, setUploadUsername] = React.useState(selectedOrganization.defaults === undefined ? "" : selectedOrganization.defaults.workflow_upload_username === undefined || selectedOrganization.defaults.workflow_upload_username.length === 0 ? "" : selectedOrganization.defaults.workflow_upload_username)
|
||||||
|
const [uploadToken, setUploadToken] = React.useState(selectedOrganization.defaults === undefined ? "" : selectedOrganization.defaults.workflow_upload_token === undefined || selectedOrganization.defaults.workflow_upload_token.length === 0 ? "" : selectedOrganization.defaults.workflow_upload_token)
|
||||||
|
|
||||||
const [workflows, setWorkflows] = React.useState([])
|
const [workflows, setWorkflows] = React.useState([])
|
||||||
const [workflow, setWorkflow] = React.useState({})
|
const [workflow, setWorkflow] = React.useState({})
|
||||||
|
|
||||||
@@ -299,7 +308,6 @@ const OrgHeaderexpanded = (props) => {
|
|||||||
client_secret: openidClientSecret,
|
client_secret: openidClientSecret,
|
||||||
openid_authorization: openidAuthorization,
|
openid_authorization: openidAuthorization,
|
||||||
openid_token: openidToken,
|
openid_token: openidToken,
|
||||||
SSORequired: SSORequired
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -311,16 +319,7 @@ const OrgHeaderexpanded = (props) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const toggleBetweenRequiredOrOptional = (event) => {
|
const toggleBetweenRequiredOrOptional = (event) => {
|
||||||
if (ssoEntrypoint === "" && openidAuthorization === "" && openidToken === "") {
|
setSSORequired(event.target.checked);
|
||||||
if (!SSORequired) {
|
|
||||||
toast.error("Please fill in fields for either OpenID connect or SSO before continuing. ")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
toast.info("Toggled SSO. Remember to save.")
|
|
||||||
}
|
|
||||||
|
|
||||||
setSSORequired(event.target.checked)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -512,16 +511,164 @@ const OrgHeaderexpanded = (props) => {
|
|||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</Grid>
|
</Grid>
|
||||||
{/* {isCloud ? null : */}
|
|
||||||
<Typography variant="h4" style={{ textAlign: "left", marginTop: 75, marginLeft: 20, }}>Single Signon - SAML/OpenID</Typography>
|
<Grid item xs={12} style={{ marginTop: 50, }}>
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', marginTop: 20, marginLeft: 10, width: '100%', borderBottom: '1px solid #414347' }}>
|
<Typography variant="h4" style={{ textAlign: "left", }}>Workflow Backup Repository</Typography>
|
||||||
<Typography variant="body1" style={{ margin: '5px 0px 5px 10px' }}>Make SAML SSO or OpenID Authentication required/optional for your organization. Tip: Do not make it required until you have tested the URL directly.</Typography>
|
<Typography variant="body2" style={{ textAlign: "left", marginTop: 5, }} color="textSecondary">
|
||||||
|
Decide where workflows are backed up in a Git repository. Will create logs and notifications if upload fails. The repository and branch must already have been initialized. Files will show up in the root folder in the format 'status_workflowid.json'
|
||||||
|
</Typography>
|
||||||
|
<Grid container style={{ marginTop: 10, }} spacing={2}>
|
||||||
|
<Grid item xs={6} style={{}}>
|
||||||
|
<span>
|
||||||
|
<Typography>Repository for workflow backup</Typography>
|
||||||
|
<TextField
|
||||||
|
required
|
||||||
|
style={{
|
||||||
|
flex: "1",
|
||||||
|
marginTop: "5px",
|
||||||
|
marginRight: "15px",
|
||||||
|
backgroundColor: isEditOrgTab ? "rgba(33, 33, 33, 1)" : theme.palette.inputColor,
|
||||||
|
}}
|
||||||
|
fullWidth={true}
|
||||||
|
type="name"
|
||||||
|
multiline={true}
|
||||||
|
rows={1}
|
||||||
|
id="outlined-with-placeholder"
|
||||||
|
margin="normal"
|
||||||
|
variant="outlined"
|
||||||
|
placeholder="Ex: github/com/shuffle/workflowbackup "
|
||||||
|
value={uploadRepo}
|
||||||
|
onChange={(e) => {
|
||||||
|
setUploadRepo(e.target.value);
|
||||||
|
}}
|
||||||
|
InputProps={{
|
||||||
|
classes: {
|
||||||
|
notchedOutline: isEditOrgTab ? null : classes.notchedOutline,
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
color: "white",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6} style={{}}>
|
||||||
|
<span>
|
||||||
|
<Typography>Branch</Typography>
|
||||||
|
<TextField
|
||||||
|
required
|
||||||
|
style={{
|
||||||
|
flex: "1",
|
||||||
|
marginTop: "5px",
|
||||||
|
marginRight: "15px",
|
||||||
|
backgroundColor: isEditOrgTab ? "rgba(33, 33, 33, 1)" : theme.palette.inputColor,
|
||||||
|
}}
|
||||||
|
fullWidth={true}
|
||||||
|
type="name"
|
||||||
|
id="outlined-with-placeholder"
|
||||||
|
margin="normal"
|
||||||
|
variant="outlined"
|
||||||
|
multiline={true}
|
||||||
|
rows={1}
|
||||||
|
placeholder="The branch to use for backup of workflows"
|
||||||
|
value={uploadBranch}
|
||||||
|
onChange={(e) => {
|
||||||
|
setUploadBranch(e.target.value);
|
||||||
|
}}
|
||||||
|
InputProps={{
|
||||||
|
classes: {
|
||||||
|
notchedOutline: isEditOrgTab ? null : classes.notchedOutline,
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
color: "white",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<Grid container style={{ marginTop: 10, }} spacing={2}>
|
||||||
|
<Grid item xs={6} style={{}}>
|
||||||
|
<span>
|
||||||
|
<Typography>Username</Typography>
|
||||||
|
<TextField
|
||||||
|
required
|
||||||
|
style={{
|
||||||
|
flex: "1",
|
||||||
|
marginTop: "5px",
|
||||||
|
marginRight: "15px",
|
||||||
|
backgroundColor: isEditOrgTab ? "rgba(33, 33, 33, 1)" : theme.palette.inputColor,
|
||||||
|
}}
|
||||||
|
fullWidth={true}
|
||||||
|
type="name"
|
||||||
|
multiline={true}
|
||||||
|
rows={1}
|
||||||
|
id="outlined-with-placeholder"
|
||||||
|
margin="normal"
|
||||||
|
variant="outlined"
|
||||||
|
placeholder="The username to use for backup of workflows"
|
||||||
|
value={uploadUsername}
|
||||||
|
onChange={(e) => {
|
||||||
|
setUploadUsername(e.target.value);
|
||||||
|
}}
|
||||||
|
InputProps={{
|
||||||
|
classes: {
|
||||||
|
notchedOutline: isEditOrgTab ? null : classes.notchedOutline,
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
color: "white",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6} style={{}}>
|
||||||
|
<span>
|
||||||
|
<Typography>Git token/password</Typography>
|
||||||
|
<TextField
|
||||||
|
required
|
||||||
|
style={{
|
||||||
|
flex: "1",
|
||||||
|
marginTop: "5px",
|
||||||
|
marginRight: "15px",
|
||||||
|
backgroundColor: isEditOrgTab ? "rgba(33, 33, 33, 1)" : theme.palette.inputColor,
|
||||||
|
}}
|
||||||
|
fullWidth={true}
|
||||||
|
id="outlined-with-placeholder"
|
||||||
|
margin="normal"
|
||||||
|
variant="outlined"
|
||||||
|
multiline={true}
|
||||||
|
rows={1}
|
||||||
|
placeholder="The token to use for backup of workflows. PS: This will be stored in cleartext in the database for now."
|
||||||
|
value={uploadToken}
|
||||||
|
onChange={(e) => {
|
||||||
|
setUploadToken(e.target.value);
|
||||||
|
}}
|
||||||
|
InputProps={{
|
||||||
|
classes: {
|
||||||
|
notchedOutline: isEditOrgTab ? null : classes.notchedOutline,
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
color: "white",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
type="password"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Typography variant="h4" style={{ marginLeft: 20, paddingTop: 100, borderTop: "1px solid rgba(255, 255, 255, 0.12)", width: "100%", marginTop: 50, }}>
|
||||||
|
SSO Configuration
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', marginLeft: 10, width: '100%', }}>
|
||||||
|
<Typography variant="body2" color="textSecondary" style={{ margin: '5px 0px 5px 10px' }}>Make SAML SSO or OpenID Authentication Required or Optional for Your Organization.</Typography>
|
||||||
<div>
|
<div>
|
||||||
<Switch
|
<Switch
|
||||||
checked={SSORequired}
|
checked={SSORequired}
|
||||||
onChange={(e) => {
|
onChange={toggleBetweenRequiredOrOptional}
|
||||||
toggleBetweenRequiredOrOptional(e)
|
|
||||||
}}
|
|
||||||
name="onOffSwitch"
|
name="onOffSwitch"
|
||||||
color="primary"
|
color="primary"
|
||||||
title="Make SAML SSO or OpenID Authentication Required or Optional for Your Organization"
|
title="Make SAML SSO or OpenID Authentication Required or Optional for Your Organization"
|
||||||
@@ -529,10 +676,8 @@ const OrgHeaderexpanded = (props) => {
|
|||||||
{SSORequired ? 'Required' : 'Optional'}
|
{SSORequired ? 'Required' : 'Optional'}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
</div>
|
|
||||||
<Grid item xs={12} style={{}}>
|
<Grid item xs={12} style={{}}>
|
||||||
<Typography variant="h6" style={{ textAlign: "left", }}>OpenID connect</Typography>
|
<Typography variant="h6" style={{ textAlign: "center", }}>OpenID connect</Typography>
|
||||||
<Grid container style={{ marginTop: 10, }}>
|
<Grid container style={{ marginTop: 10, }}>
|
||||||
<Grid item xs={6} style={{}}>
|
<Grid item xs={6} style={{}}>
|
||||||
<span>
|
<span>
|
||||||
@@ -549,6 +694,11 @@ const OrgHeaderexpanded = (props) => {
|
|||||||
type="name"
|
type="name"
|
||||||
multiline={true}
|
multiline={true}
|
||||||
rows={2}
|
rows={2}
|
||||||
|
disabled={
|
||||||
|
selectedOrganization.manager_orgs !== undefined &&
|
||||||
|
selectedOrganization.manager_orgs !== null &&
|
||||||
|
selectedOrganization.manager_orgs.length > 0
|
||||||
|
}
|
||||||
id="outlined-with-placeholder"
|
id="outlined-with-placeholder"
|
||||||
margin="normal"
|
margin="normal"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
@@ -583,6 +733,11 @@ const OrgHeaderexpanded = (props) => {
|
|||||||
type="name"
|
type="name"
|
||||||
multiline={true}
|
multiline={true}
|
||||||
rows={2}
|
rows={2}
|
||||||
|
disabled={
|
||||||
|
selectedOrganization.manager_orgs !== undefined &&
|
||||||
|
selectedOrganization.manager_orgs !== null &&
|
||||||
|
selectedOrganization.manager_orgs.length > 0
|
||||||
|
}
|
||||||
id="outlined-with-placeholder"
|
id="outlined-with-placeholder"
|
||||||
margin="normal"
|
margin="normal"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
@@ -677,7 +832,7 @@ const OrgHeaderexpanded = (props) => {
|
|||||||
{/* } */}
|
{/* } */}
|
||||||
{/*isCloud ? null : */}
|
{/*isCloud ? null : */}
|
||||||
<Grid item xs={12} style={{ marginTop: 50, }}>
|
<Grid item xs={12} style={{ marginTop: 50, }}>
|
||||||
<Typography variant="h4" style={{ textAlign: "center", }}>SAML SSO (v1.1)</Typography>
|
<Typography variant="h6" style={{ textAlign: "center", }}>SAML SSO (v1.1)</Typography>
|
||||||
<Grid container style={{ marginTop: 20, }}>
|
<Grid container style={{ marginTop: 20, }}>
|
||||||
<Grid item xs={6} style={{}}>
|
<Grid item xs={6} style={{}}>
|
||||||
<span>
|
<span>
|
||||||
@@ -694,6 +849,11 @@ const OrgHeaderexpanded = (props) => {
|
|||||||
type="name"
|
type="name"
|
||||||
multiline={true}
|
multiline={true}
|
||||||
rows={2}
|
rows={2}
|
||||||
|
disabled={
|
||||||
|
selectedOrganization.manager_orgs !== undefined &&
|
||||||
|
selectedOrganization.manager_orgs !== null &&
|
||||||
|
selectedOrganization.manager_orgs.length > 0
|
||||||
|
}
|
||||||
id="outlined-with-placeholder"
|
id="outlined-with-placeholder"
|
||||||
margin="normal"
|
margin="normal"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
|
|||||||
+1698
-1638
File diff suppressed because it is too large
Load Diff
@@ -27,8 +27,9 @@ const Priorities = (props) => {
|
|||||||
|
|
||||||
const [selectedWorkflow, setSelectedWorkflow] = React.useState("NO HIGHLIGHT");
|
const [selectedWorkflow, setSelectedWorkflow] = React.useState("NO HIGHLIGHT");
|
||||||
const [selectedExecutionId, setSelectedExecutionId] = React.useState("NO HIGHLIGHT");
|
const [selectedExecutionId, setSelectedExecutionId] = React.useState("NO HIGHLIGHT");
|
||||||
let navigate = useNavigate();
|
const [highlightKMS, setHighlightKMS] = React.useState(false)
|
||||||
|
|
||||||
|
let navigate = useNavigate();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getFramework()
|
getFramework()
|
||||||
|
|
||||||
@@ -36,6 +37,12 @@ const Priorities = (props) => {
|
|||||||
const urlParams = new URLSearchParams(window.location.search)
|
const urlParams = new URLSearchParams(window.location.search)
|
||||||
const workflow = urlParams.get("workflow")
|
const workflow = urlParams.get("workflow")
|
||||||
const execution_id = urlParams.get("execution_id")
|
const execution_id = urlParams.get("execution_id")
|
||||||
|
const kms = urlParams.get("kms")
|
||||||
|
|
||||||
|
if (kms !== null && kms !== undefined && kms.length > 0 && kms === "true") {
|
||||||
|
toast.info("KMS-related notifications are highlighted.")
|
||||||
|
setHighlightKMS(true)
|
||||||
|
}
|
||||||
|
|
||||||
if (execution_id !== null) {
|
if (execution_id !== null) {
|
||||||
setSelectedExecutionId(execution_id)
|
setSelectedExecutionId(execution_id)
|
||||||
@@ -217,7 +224,16 @@ const Priorities = (props) => {
|
|||||||
var orgId = "";
|
var orgId = "";
|
||||||
|
|
||||||
|
|
||||||
const highlighted = selectedExecutionId === "" && selectedWorkflow === "" ? false : data.reference_url === undefined || data.reference_url === null || data.reference_url.length === 0 ? false : data.reference_url.includes(selectedExecutionId) || data.reference_url.includes(selectedWorkflow)
|
var highlighted = selectedExecutionId === "" && selectedWorkflow === "" ? false : data.reference_url === undefined || data.reference_url === null || data.reference_url.length === 0 ? false : data.reference_url.includes(selectedExecutionId) || data.reference_url.includes(selectedWorkflow)
|
||||||
|
|
||||||
|
if (!highlighted && highlightKMS) {
|
||||||
|
if (data.title !== undefined && data.title !== null && data.title.toLowerCase().includes("kms")) {
|
||||||
|
highlighted = true
|
||||||
|
} else if (data.description !== undefined && data.description !== null && data.description.toLowerCase().includes("kms")) {
|
||||||
|
highlighted = true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
if (userdata.orgs !== undefined) {
|
if (userdata.orgs !== undefined) {
|
||||||
const foundOrg = userdata.orgs.find((org) => org.id === data["org_id"]);
|
const foundOrg = userdata.orgs.find((org) => org.id === data["org_id"]);
|
||||||
|
|||||||
@@ -88,12 +88,12 @@ const SearchData = props => {
|
|||||||
|
|
||||||
const textFieldRef = useRef(null);
|
const textFieldRef = useRef(null);
|
||||||
const keyPressHandler = (e) => {
|
const keyPressHandler = (e) => {
|
||||||
if (e.which === 13) {
|
if (e.key === "Enter") {
|
||||||
|
e.preventDefault();
|
||||||
// navigate(`/search?q=${currentRefinement}`, { state: value, replace: true });
|
// navigate(`/search?q=${currentRefinement}`, { state: value, replace: true });
|
||||||
// setModalOpen(false);
|
// setModalOpen(false);
|
||||||
const trimmedValue = inputValue.trim();
|
const trimmedValue = inputValue.trim();
|
||||||
if (trimmedValue !== '') {
|
if (trimmedValue !== '') {
|
||||||
e.preventDefault();
|
|
||||||
navigate(`/search?q=${trimmedValue}`, { state: trimmedValue, replace: true });
|
navigate(`/search?q=${trimmedValue}`, { state: trimmedValue, replace: true });
|
||||||
setModalOpen(false);
|
setModalOpen(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,10 +107,9 @@ const CodeEditor = (props) => {
|
|||||||
getParents,
|
getParents,
|
||||||
|
|
||||||
fieldname,
|
fieldname,
|
||||||
|
contentLoading,
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const [localcodedata, setlocalcodedata] = React.useState(codedata === undefined || codedata === null || codedata.length === 0 ? "" : codedata);
|
const [localcodedata, setlocalcodedata] = React.useState(codedata === undefined || codedata === null || codedata.length === 0 ? "" : codedata);
|
||||||
|
|
||||||
//const { setContainer } = useCodeMirror({
|
//const { setContainer } = useCodeMirror({
|
||||||
@@ -627,8 +626,8 @@ const CodeEditor = (props) => {
|
|||||||
newMarkers.push({
|
newMarkers.push({
|
||||||
startRow: i,
|
startRow: i,
|
||||||
startCol: startCh,
|
startCol: startCh,
|
||||||
endRow: i+1,
|
endRow: i,
|
||||||
endCol: endCh+1,
|
endCol: endCh,
|
||||||
className: correctVariable ? "good-marker" : "bad-marker",
|
className: correctVariable ? "good-marker" : "bad-marker",
|
||||||
type: "text",
|
type: "text",
|
||||||
})
|
})
|
||||||
@@ -989,6 +988,17 @@ const CodeEditor = (props) => {
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
||||||
|
{contentLoading === true ?
|
||||||
|
<Tooltip
|
||||||
|
color="primary"
|
||||||
|
title={`The File content is loading. Please wait a moment.`}
|
||||||
|
placement="top"
|
||||||
|
>
|
||||||
|
<CircularProgress style={{position: "absolute", right: 106, top: 6, }}/>
|
||||||
|
</Tooltip>
|
||||||
|
: null}
|
||||||
|
|
||||||
<Tooltip
|
<Tooltip
|
||||||
color="primary"
|
color="primary"
|
||||||
title={`Move window`}
|
title={`Move window`}
|
||||||
@@ -1571,7 +1581,8 @@ const CodeEditor = (props) => {
|
|||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<span style={{color: "white"}}>
|
<span style={{color: "white"}}>
|
||||||
Expected Output
|
|
||||||
|
{selectedAction === undefined ? "" : selectedAction.name === "execute_python" ? "Code to run" : "Expected Output"}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1588,7 +1599,7 @@ const CodeEditor = (props) => {
|
|||||||
border: `1px solid ${theme.palette.primary.main}`,
|
border: `1px solid ${theme.palette.primary.main}`,
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
top: 24,
|
top: 24,
|
||||||
right: 65,
|
right: 100,
|
||||||
maxHeight: 35,
|
maxHeight: 35,
|
||||||
minWidth: 70,
|
minWidth: 70,
|
||||||
}}
|
}}
|
||||||
@@ -1599,7 +1610,7 @@ const CodeEditor = (props) => {
|
|||||||
{executing ?
|
{executing ?
|
||||||
<CircularProgress style={{height: 18, width: 18, }} />
|
<CircularProgress style={{height: 18, width: 18, }} />
|
||||||
:
|
:
|
||||||
<span>Try it <PlayArrowIcon style={{height: 18, width: 18, marginBottom: -4, marginLeft: 5, }} /> </span>
|
<span>{selectedAction === undefined ? "" : selectedAction.name === "execute_python" ? "Run Python Code" : "Try it"}<PlayArrowIcon style={{height: 18, width: 18, marginBottom: -4, marginLeft: 5, }} /> </span>
|
||||||
}
|
}
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|||||||
@@ -204,6 +204,9 @@ const AppGrid = props => {
|
|||||||
style={{backgroundColor: theme.palette.inputColor, borderRadius: borderRadius, margin: 10, width: "100%",}}
|
style={{backgroundColor: theme.palette.inputColor, borderRadius: borderRadius, margin: 10, width: "100%",}}
|
||||||
InputProps={{
|
InputProps={{
|
||||||
style:{
|
style:{
|
||||||
|
color: "white",
|
||||||
|
fontSize: "1em",
|
||||||
|
height: 50,
|
||||||
},
|
},
|
||||||
startAdornment: (
|
startAdornment: (
|
||||||
<InputAdornment position="start">
|
<InputAdornment position="start">
|
||||||
@@ -221,6 +224,11 @@ const AppGrid = props => {
|
|||||||
removeQuery("q")
|
removeQuery("q")
|
||||||
refine(event.currentTarget.value)
|
refine(event.currentTarget.value)
|
||||||
}}
|
}}
|
||||||
|
onKeyDown={(event) => {
|
||||||
|
if(event.key === "Enter") {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
}}
|
||||||
limit={5}
|
limit={5}
|
||||||
/>
|
/>
|
||||||
: null}
|
: null}
|
||||||
@@ -233,6 +241,8 @@ const AppGrid = props => {
|
|||||||
flexWrap: "wrap",
|
flexWrap: "wrap",
|
||||||
alignContent: "space-between",
|
alignContent: "space-between",
|
||||||
marginTop: 5,
|
marginTop: 5,
|
||||||
|
padding: "0px 180px",
|
||||||
|
width:"auto"
|
||||||
}
|
}
|
||||||
|
|
||||||
var workflowDelay = -50
|
var workflowDelay = -50
|
||||||
|
|||||||
@@ -133,6 +133,11 @@ const data = [
|
|||||||
"background-gradient-stop-colors": "data(fillGradient)",
|
"background-gradient-stop-colors": "data(fillGradient)",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
selector: `node[?parent_controlled]`,
|
||||||
|
css: {
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
selector: `node[app_name="Testing"]`,
|
selector: `node[app_name="Testing"]`,
|
||||||
css: {
|
css: {
|
||||||
|
|||||||
@@ -18,11 +18,9 @@ code {
|
|||||||
margin-right: -13px;
|
margin-right: -13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toc:hover {
|
table th, table td {
|
||||||
background-color: gray;
|
border: 1px solid;
|
||||||
color: white;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* .cm-string{
|
/* .cm-string{
|
||||||
z-index: -1;
|
z-index: -1;
|
||||||
}
|
}
|
||||||
|
|||||||
+759
-402
File diff suppressed because it is too large
Load Diff
+2703
-1450
File diff suppressed because it is too large
Load Diff
@@ -955,7 +955,6 @@ const Apps = (props) => {
|
|||||||
</Link>
|
</Link>
|
||||||
: null
|
: null
|
||||||
|
|
||||||
console.log("Sharing config: ", sharingConfiguration);
|
|
||||||
const activateButton =
|
const activateButton =
|
||||||
selectedApp.generated && !selectedApp.activated ? (
|
selectedApp.generated && !selectedApp.activated ? (
|
||||||
<div>
|
<div>
|
||||||
@@ -1281,10 +1280,15 @@ const Apps = (props) => {
|
|||||||
{isCloud && !internalIds.includes(selectedApp.name.toLowerCase()) ?
|
{isCloud && !internalIds.includes(selectedApp.name.toLowerCase()) ?
|
||||||
<Tooltip title="Deactivates this app for the current organisation. This means the app will not be usable again until you re-activate it." placement="top">
|
<Tooltip title="Deactivates this app for the current organisation. This means the app will not be usable again until you re-activate it." placement="top">
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant={selectedApp.reference_org === userdata.active_org.id ? "outlined" : "contained"}
|
||||||
component="label"
|
component="label"
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
if (selectedApp.reference_org === userdata.active_org.id) {
|
||||||
|
toast.info("Can't deactivate apps made in this org. Please contact support if you want to deactivate this app.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const tmpurl = new URL(window.location.href);
|
const tmpurl = new URL(window.location.href);
|
||||||
const searchParams = tmpurl.searchParams;
|
const searchParams = tmpurl.searchParams;
|
||||||
const queryID = searchParams.get("queryID");
|
const queryID = searchParams.get("queryID");
|
||||||
@@ -1316,7 +1320,7 @@ const Apps = (props) => {
|
|||||||
console.log("No query to handle when activating");
|
console.log("No query to handle when activating");
|
||||||
}
|
}
|
||||||
|
|
||||||
activateApp(selectedApp.id, true)
|
activateApp(selectedApp.id, true, true)
|
||||||
}}
|
}}
|
||||||
style={{ height: 35, marginTop: 0, marginLeft: 10, }}
|
style={{ height: 35, marginTop: 0, marginLeft: 10, }}
|
||||||
>
|
>
|
||||||
@@ -1845,9 +1849,12 @@ const Apps = (props) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const activateApp = (appid, refresh) => {
|
const activateApp = (appid, refresh, deactivate) => {
|
||||||
const appExists = userdata.active_apps !== undefined && userdata.active_apps !== null && userdata.active_apps.includes(appid)
|
const appExists = userdata.active_apps !== undefined && userdata.active_apps !== null && userdata.active_apps.includes(appid)
|
||||||
const url = appExists ? `${globalUrl}/api/v1/apps/${appid}/deactivate` : `${globalUrl}/api/v1/apps/${appid}/activate`
|
const url = deactivate === true ?
|
||||||
|
`${globalUrl}/api/v1/apps/${appid}/deactivate`
|
||||||
|
:
|
||||||
|
appExists ? `${globalUrl}/api/v1/apps/${appid}/deactivate` : `${globalUrl}/api/v1/apps/${appid}/activate`
|
||||||
|
|
||||||
fetch(url, {
|
fetch(url, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
@@ -1876,7 +1883,7 @@ const Apps = (props) => {
|
|||||||
if (appExists) {
|
if (appExists) {
|
||||||
toast("App deactivated for your organization! Existing workflows with the app will continue to work.")
|
toast("App deactivated for your organization! Existing workflows with the app will continue to work.")
|
||||||
} else {
|
} else {
|
||||||
toast("App activated for your organization!")
|
toast("App activation changed for your organization!")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (refresh === true) {
|
if (refresh === true) {
|
||||||
@@ -2021,8 +2028,6 @@ const Apps = (props) => {
|
|||||||
userToken: userdata === undefined || userdata === null || userdata.id === undefined ? "unauthenticated" : userdata.id,
|
userToken: userdata === undefined || userdata === null || userdata.id === undefined ? "unauthenticated" : userdata.id,
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
} else {
|
|
||||||
console.log("No query to handle when activating")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
activateApp(hit.objectID, true)
|
activateApp(hit.objectID, true)
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
import React, { useEffect, useLayoutEffect, useRef, useState } from "react"
|
import React, { useEffect, useLayoutEffect, useRef, useState } from "react"
|
||||||
|
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import Markdown from 'react-markdown'
|
import Markdown from 'react-markdown'
|
||||||
|
|
||||||
import theme from '../theme.jsx';
|
import theme from '../theme.jsx';
|
||||||
import ReactJson from "react-json-view";
|
import ReactJson from "react-json-view";
|
||||||
import { isMobile } from "react-device-detect";
|
import { isMobile } from "react-device-detect";
|
||||||
import { BrowserView, MobileView } from "react-device-detect";
|
import { BrowserView, MobileView } from "react-device-detect";
|
||||||
import { useParams, useNavigate, Link } from "react-router-dom";
|
import { useParams, useNavigate, Link } from "react-router-dom";
|
||||||
import { validateJson, GetIconInfo } from "../views/Workflows.jsx";
|
import { validateJson, GetIconInfo } from "../views/Workflows.jsx";
|
||||||
|
import remarkGfm from 'remark-gfm'
|
||||||
import {
|
import {
|
||||||
Grid,
|
Grid,
|
||||||
TextField,
|
TextField,
|
||||||
@@ -26,7 +24,6 @@ import {
|
|||||||
ListItemButton,
|
ListItemButton,
|
||||||
ListItemText
|
ListItemText
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Link as LinkIcon,
|
Link as LinkIcon,
|
||||||
Edit as EditIcon,
|
Edit as EditIcon,
|
||||||
@@ -35,7 +32,6 @@ import {
|
|||||||
FileCopy as FileCopyIcon
|
FileCopy as FileCopyIcon
|
||||||
} from "@mui/icons-material";
|
} from "@mui/icons-material";
|
||||||
import { fontGrid } from "@mui/material/styles/cssUtils.js";
|
import { fontGrid } from "@mui/material/styles/cssUtils.js";
|
||||||
import { active } from "d3";
|
|
||||||
|
|
||||||
const Body = {
|
const Body = {
|
||||||
//maxWidth: 1000,
|
//maxWidth: 1000,
|
||||||
@@ -46,6 +42,7 @@ const Body = {
|
|||||||
height: "100%",
|
height: "100%",
|
||||||
color: "white",
|
color: "white",
|
||||||
position: "relative",
|
position: "relative",
|
||||||
|
paddingTop: 40,
|
||||||
//textAlign: "center",
|
//textAlign: "center",
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -155,9 +152,16 @@ export const OuterLink = (props) => {
|
|||||||
|
|
||||||
|
|
||||||
export const Img = (props) => {
|
export const Img = (props) => {
|
||||||
return <img style={{ borderRadius: theme.palette.borderRadius, width: 750, maxWidth: "100%", marginTop: 15, marginBottom: 15, }} alt={props.alt} src={props.src} />;
|
return(
|
||||||
|
<img
|
||||||
|
style={{border: "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette.borderRadius, width: 750, maxWidth: "100%", marginTop: 15, marginBottom: 15, }}
|
||||||
|
alt={props.alt}
|
||||||
|
src={props.src}
|
||||||
|
/>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export const CodeHandler = (props) => {
|
export const CodeHandler = (props) => {
|
||||||
const propvalue = props.value !== undefined && props.value !== null ? props.value : props.children !== undefined && props.children !== null && props.children.length > 0 ? props.children[0] : ""
|
const propvalue = props.value !== undefined && props.value !== null ? props.value : props.children !== undefined && props.children !== null && props.children.length > 0 ? props.children[0] : ""
|
||||||
|
|
||||||
@@ -369,8 +373,7 @@ const Docs = (defaultprops) => {
|
|||||||
hash = hash.split('?')[0]
|
hash = hash.split('?')[0]
|
||||||
}
|
}
|
||||||
if (hash) {
|
if (hash) {
|
||||||
console.log("HASH: ", hash)
|
const element = document.getElementById(hash.toLowerCase())
|
||||||
const element = document.getElementById(hash)
|
|
||||||
if (element) {
|
if (element) {
|
||||||
element.scrollIntoView({
|
element.scrollIntoView({
|
||||||
behavior: "instant",
|
behavior: "instant",
|
||||||
@@ -1022,8 +1025,10 @@ const Docs = (defaultprops) => {
|
|||||||
<Markdown
|
<Markdown
|
||||||
components={markdownComponents}
|
components={markdownComponents}
|
||||||
id="markdown_wrapper"
|
id="markdown_wrapper"
|
||||||
|
className={"style.reactMarkdown"}
|
||||||
escapeHtml={false}
|
escapeHtml={false}
|
||||||
skipHtml={false}
|
skipHtml={false}
|
||||||
|
remarkPlugins={[remarkGfm]}
|
||||||
style={{
|
style={{
|
||||||
maxWidth: "100%", minWidth: "100%",
|
maxWidth: "100%", minWidth: "100%",
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ const LoginDialog = (props) => {
|
|||||||
} else {
|
} else {
|
||||||
if (responseJson["reason"] === "MFA_REDIRECT") {
|
if (responseJson["reason"] === "MFA_REDIRECT") {
|
||||||
setLoginInfo(
|
setLoginInfo(
|
||||||
"MFA required. Please the 6-digit code from your authenticator"
|
"MFA required. Please enter the 6-digit code from your authenticator"
|
||||||
);
|
);
|
||||||
setMFAField(true);
|
setMFAField(true);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import React, {useState, useEffect} from 'react';
|
import React, {useState, useEffect} from 'react';
|
||||||
import ReactDOM from "react-dom"
|
import ReactDOM from "react-dom"
|
||||||
|
|
||||||
|
import { ToastContainer, toast } from "react-toastify"
|
||||||
import { useInterval } from "react-powerhooks";
|
import { useInterval } from "react-powerhooks";
|
||||||
import { makeStyles } from '@mui/material/styles';
|
import { makeStyles } from '@mui/material/styles';
|
||||||
import { useNavigate, Link, useParams } from "react-router-dom";
|
import { useNavigate, Link, useParams } from "react-router-dom";
|
||||||
@@ -43,7 +44,8 @@ const bodyDivStyle = {
|
|||||||
const RunWorkflow = (defaultprops) => {
|
const RunWorkflow = (defaultprops) => {
|
||||||
const { globalUrl, isLoaded, isLoggedIn, setIsLoggedIn, setCookie, register, serverside } = defaultprops;
|
const { globalUrl, isLoaded, isLoggedIn, setIsLoggedIn, setCookie, register, serverside } = defaultprops;
|
||||||
|
|
||||||
let navigate = useNavigate();
|
let navigate = useNavigate();
|
||||||
|
const [_, setUpdate] = useState(""); // Used to force rendring, don't remove
|
||||||
const [message, setMessage] = useState("");
|
const [message, setMessage] = useState("");
|
||||||
const [workflow, setWorkflow] = React.useState({});
|
const [workflow, setWorkflow] = React.useState({});
|
||||||
const [executionRequest, setExecutionRequest] = React.useState({});
|
const [executionRequest, setExecutionRequest] = React.useState({});
|
||||||
@@ -55,6 +57,7 @@ const RunWorkflow = (defaultprops) => {
|
|||||||
const [selectedOrganization, setSelectedOrganization] = React.useState(undefined);
|
const [selectedOrganization, setSelectedOrganization] = React.useState(undefined);
|
||||||
const [apps, setApps] = React.useState([]);
|
const [apps, setApps] = React.useState([]);
|
||||||
const [buttonClicked, setButtonClicked] = React.useState("");
|
const [buttonClicked, setButtonClicked] = React.useState("");
|
||||||
|
const [foundSourcenode, setFoundSourcenode] = React.useState(undefined);
|
||||||
|
|
||||||
const boxStyle = {
|
const boxStyle = {
|
||||||
color: "white",
|
color: "white",
|
||||||
@@ -340,6 +343,8 @@ const RunWorkflow = (defaultprops) => {
|
|||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("ONSUBMIT: ", event, execution_id, authorization, answer)
|
||||||
|
|
||||||
stop()
|
stop()
|
||||||
setMessage("")
|
setMessage("")
|
||||||
setExecutionLoading(true)
|
setExecutionLoading(true)
|
||||||
@@ -389,12 +394,27 @@ const RunWorkflow = (defaultprops) => {
|
|||||||
url += `?reference_execution=${execution_id}&authorization=${authorization}&answer=${answer}`
|
url += `?reference_execution=${execution_id}&authorization=${authorization}&answer=${answer}`
|
||||||
data = {}
|
data = {}
|
||||||
fetchBody.method = "GET"
|
fetchBody.method = "GET"
|
||||||
|
|
||||||
|
if (executionArgument !== undefined && executionArgument !== null) {
|
||||||
|
try {
|
||||||
|
if (typeof executionArgument === "string") {
|
||||||
|
url += "¬e=" + executionArgument
|
||||||
|
} else {
|
||||||
|
url += "¬e=" + JSON.stringify(executionArgument)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
url += "¬e=" + executionArgument
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
fetchBody.method = "POST"
|
fetchBody.method = "POST"
|
||||||
fetchBody.body = JSON.stringify(data)
|
fetchBody.body = JSON.stringify(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Pre request: ", url, fetchBody)
|
// IF there is an execution argument, we should use it
|
||||||
|
console.log("FULL URL: ", url)
|
||||||
|
|
||||||
fetch(url, fetchBody)
|
fetch(url, fetchBody)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response.status !== 200 && response.status !== 201) {
|
if (response.status !== 200 && response.status !== 201) {
|
||||||
@@ -408,16 +428,26 @@ const RunWorkflow = (defaultprops) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
start();
|
start();
|
||||||
return
|
return response.json()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.json();
|
return response.json()
|
||||||
})
|
})
|
||||||
.then(responseJson => {
|
.then(responseJson => {
|
||||||
setExecutionLoading(false)
|
setExecutionLoading(false)
|
||||||
if (responseJson["success"] === false) {
|
if (responseJson.success === false) {
|
||||||
console.log("Failed sending execution request")
|
console.log("Failed sending execution request")
|
||||||
|
if (responseJson.reason !== undefined && responseJson.reason !== null) {
|
||||||
|
toast.warn(responseJson.reason)
|
||||||
|
}
|
||||||
|
|
||||||
|
stop()
|
||||||
|
setMessage("")
|
||||||
|
setExecutionData({})
|
||||||
|
setExecutionInfo("")
|
||||||
|
setExecutionRunning(false)
|
||||||
|
setExecutionRequest({})
|
||||||
} else {
|
} else {
|
||||||
console.log("Started execution")
|
console.log("Started execution")
|
||||||
|
|
||||||
@@ -426,17 +456,24 @@ const RunWorkflow = (defaultprops) => {
|
|||||||
} else {
|
} else {
|
||||||
setExecutionRunning(true);
|
setExecutionRunning(true);
|
||||||
setExecutionRequest(responseJson)
|
setExecutionRequest(responseJson)
|
||||||
start();
|
start()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
//setExecutionInfo("Error in workflow startup: " + error)
|
//setExecutionInfo("Error in workflow startup: " + error)
|
||||||
|
toast.warn("Error in workflow startup: " + error)
|
||||||
|
|
||||||
|
stop()
|
||||||
|
setMessage("")
|
||||||
|
setExecutionData({})
|
||||||
|
setExecutionInfo("")
|
||||||
|
|
||||||
setExecutionLoading(false)
|
setExecutionLoading(false)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const getWorkflow = (workflow_id) => {
|
const getWorkflow = (workflow_id, selectedNode) => {
|
||||||
fetch(globalUrl + "/api/v1/workflows/" + workflow_id, {
|
fetch(globalUrl + "/api/v1/workflows/" + workflow_id, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
@@ -480,6 +517,48 @@ const RunWorkflow = (defaultprops) => {
|
|||||||
setExecutionArgument(newexec)
|
setExecutionArgument(newexec)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (selectedNode !== undefined && selectedNode !== null && selectedNode.length > 0) {
|
||||||
|
var found = false
|
||||||
|
for (var actionkey in responseJson.actions) {
|
||||||
|
if (responseJson.actions[actionkey].id === selectedNode) {
|
||||||
|
found = true
|
||||||
|
setFoundSourcenode(responseJson.actions[actionkey])
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
for (var triggerkey in responseJson.triggers) {
|
||||||
|
if (responseJson.triggers[triggerkey].id !== selectedNode) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
setFoundSourcenode(responseJson.triggers[triggerkey])
|
||||||
|
|
||||||
|
if (responseJson.input_questions !== undefined && responseJson.input_questions !== null && responseJson.input_questions.length > 0 && responseJson.triggers[triggerkey].trigger_type === "USERINPUT") {
|
||||||
|
|
||||||
|
// Look for input questions param
|
||||||
|
for (var paramkey in responseJson.triggers[triggerkey].parameters) {
|
||||||
|
if (responseJson.triggers[triggerkey].parameters[paramkey].name === "input_questions") {
|
||||||
|
|
||||||
|
var relevantquestions = []
|
||||||
|
for (var questionkey in responseJson.input_questions) {
|
||||||
|
if (responseJson.triggers[triggerkey].parameters[paramkey].value.includes(responseJson.input_questions[questionkey].name)) {
|
||||||
|
relevantquestions.push(responseJson.input_questions[questionkey])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
responseJson.input_questions = relevantquestions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
handleGetOrg(responseJson.org_id)
|
handleGetOrg(responseJson.org_id)
|
||||||
setWorkflow(responseJson);
|
setWorkflow(responseJson);
|
||||||
})
|
})
|
||||||
@@ -489,7 +568,7 @@ const RunWorkflow = (defaultprops) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const { start, stop } = useInterval({
|
const { start, stop } = useInterval({
|
||||||
duration: 3000,
|
duration: 1500,
|
||||||
startImmediate: true,
|
startImmediate: true,
|
||||||
callback: () => {
|
callback: () => {
|
||||||
fetchUpdates(executionRequest.execution_id, executionRequest.authorization)
|
fetchUpdates(executionRequest.execution_id, executionRequest.authorization)
|
||||||
@@ -526,15 +605,11 @@ const RunWorkflow = (defaultprops) => {
|
|||||||
|
|
||||||
for (var key in responseJson.results) {
|
for (var key in responseJson.results) {
|
||||||
if (responseJson.results[key].status === "WAITING") {
|
if (responseJson.results[key].status === "WAITING") {
|
||||||
console.log("Found: ", responseJson.results[key])
|
|
||||||
|
|
||||||
const validate = validateJson(responseJson.results[key].result)
|
const validate = validateJson(responseJson.results[key].result)
|
||||||
console.log("Validate: ", validate)
|
|
||||||
if (validate.valid && typeof validate.result === "string") {
|
if (validate.valid && typeof validate.result === "string") {
|
||||||
validate.result = JSON.parse(validate.result)
|
validate.result = JSON.parse(validate.result)
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Newresult: ", validate.result)
|
|
||||||
if (validate.result["information"] !== undefined && validate.result["information"] !== null) {
|
if (validate.result["information"] !== undefined && validate.result["information"] !== null) {
|
||||||
setWorkflowQuestion(validate.result["information"])
|
setWorkflowQuestion(validate.result["information"])
|
||||||
}
|
}
|
||||||
@@ -623,15 +698,15 @@ const RunWorkflow = (defaultprops) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch(globalUrl + "/api/v1/streams/results", {
|
fetch(globalUrl + "/api/v1/streams/results", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
Accept: "application/json",
|
Accept: "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify(innerRequest),
|
body: JSON.stringify(innerRequest),
|
||||||
credentials: "include",
|
credentials: "include",
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
console.log("Status not 200 for stream results :O!");
|
console.log("Status not 200 for stream results :O!");
|
||||||
@@ -651,11 +726,14 @@ const RunWorkflow = (defaultprops) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const answer = new URLSearchParams(window.location.search).get("answer")
|
const searchParams = new URLSearchParams(window.location.search)
|
||||||
const execution_id = new URLSearchParams(window.location.search).get("reference_execution")
|
const answer = searchParams.get("answer")
|
||||||
const authorization = new URLSearchParams(window.location.search).get("authorization")
|
const execution_id = searchParams.get("reference_execution")
|
||||||
|
const authorization = searchParams.get("authorization")
|
||||||
|
const sourceNode = searchParams.get("source_node")
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getWorkflow(props.match.params.key)
|
getWorkflow(props.match.params.key, sourceNode)
|
||||||
if (execution_id !== undefined && execution_id !== null && authorization !== undefined && authorization !== null) {
|
if (execution_id !== undefined && execution_id !== null && authorization !== undefined && authorization !== null) {
|
||||||
console.log("Get execution: ", execution_id)
|
console.log("Get execution: ", execution_id)
|
||||||
fetchUpdates(execution_id, authorization, true)
|
fetchUpdates(execution_id, authorization, true)
|
||||||
@@ -667,13 +745,60 @@ const RunWorkflow = (defaultprops) => {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (executionData === undefined || executionData === null || executionData === {}) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundSourcenode === undefined || foundSourcenode === null || foundSourcenode === {}) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundSourcenode.trigger_type !== "USERINPUT") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (executionData.results === undefined || executionData.results === null || executionData.results.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var resultkey in executionData.results) {
|
||||||
|
const result = executionData.results[resultkey]
|
||||||
|
if (result.action.id !== foundSourcenode.id) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var parsedresult = result.result
|
||||||
|
try {
|
||||||
|
parsedresult = JSON.parse(parsedresult)
|
||||||
|
} catch (e) {
|
||||||
|
console.log("Error parsing result: ", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.status !== "WAITING") {
|
||||||
|
if (parsedresult.click_info !== undefined && parsedresult.click_info !== null) {
|
||||||
|
if (parsedresult.click_info.user !== undefined && parsedresult.click_info.user !== null && parsedresult.click_info.user.length > 0) {
|
||||||
|
setMessage("Already answered by " + parsedresult.click_info.user)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setMessage("Answered.")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
}, [executionData, foundSourcenode])
|
||||||
|
|
||||||
const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)"
|
const buttonBackground = "linear-gradient(to right, #f86a3e, #f34079)"
|
||||||
const buttonStyle = {borderRadius: 25, height: 50, fontSize: 18, backgroundImage: handleValidateForm(executionArgument) || executionLoading ? buttonBackground : "grey", color: "white"}
|
const buttonStyle = {borderRadius: 25, height: 50, fontSize: 18, backgroundImage: handleValidateForm(executionArgument) || executionLoading ? buttonBackground : "grey", color: "white"}
|
||||||
|
|
||||||
//console.log("execdata: ", executionData)
|
|
||||||
const disabledButtons = message.length > 0 || executionData.status === "FINISHED" || executionData.status === "ABORTED"
|
const disabledButtons = message.length > 0 || executionData.status === "FINISHED" || executionData.status === "ABORTED"
|
||||||
|
|
||||||
|
//{disabledButtons ? null :
|
||||||
|
|
||||||
|
|
||||||
const organization = selectedOrganization !== undefined && selectedOrganization !== null ? selectedOrganization.name : "Unknown"
|
const organization = selectedOrganization !== undefined && selectedOrganization !== null ? selectedOrganization.name : "Unknown"
|
||||||
const contact = selectedOrganization !== undefined && selectedOrganization !== null && selectedOrganization.org !== undefined && selectedOrganization.org !== null? selectedOrganization.org : "support@shuffler.io"
|
const contact = selectedOrganization !== undefined && selectedOrganization !== null && selectedOrganization.org !== undefined && selectedOrganization.org !== null? selectedOrganization.org : "support@shuffler.io"
|
||||||
//const contact = selectedOrganization !== undefined && selectedOrganization !== null && selectedOrganization.contact !== undefined && selectedOrganization.contact !== null? selectedOrganization.contact : "support@shuffler.io"
|
//const contact = selectedOrganization !== undefined && selectedOrganization !== null && selectedOrganization.contact !== undefined && selectedOrganization.contact !== null? selectedOrganization.contact : "support@shuffler.io"
|
||||||
@@ -724,49 +849,54 @@ const RunWorkflow = (defaultprops) => {
|
|||||||
<Typography variant="h6" style={{marginBottom: 10, marginTop: 50, textAlign: "center", }}>
|
<Typography variant="h6" style={{marginBottom: 10, marginTop: 50, textAlign: "center", }}>
|
||||||
{organization}
|
{organization}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body1" color="textSecondary" style={{marginBottom: 15, marginTop: 0, textAlign: "center",}}>
|
|
||||||
{contact}
|
|
||||||
</Typography>
|
|
||||||
<Divider style={{marginTop: 20, marginBottom: 20, }}/>
|
<Divider style={{marginTop: 20, marginBottom: 20, }}/>
|
||||||
<Typography color="textSecondary">{message}</Typography>
|
|
||||||
|
{disabledButtons && message.length > 0 ? null :
|
||||||
|
<Typography color="textSecondary" style={{textAlign: "center", }}>
|
||||||
|
{message}
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
|
|
||||||
{answer !== undefined && answer !== null ? null :
|
{answer !== undefined && answer !== null ? null :
|
||||||
<Typography variant="h6" style={{marginBottom: 15, }}><b>{workflow.name}</b></Typography>
|
<Typography variant="h6" style={{marginBottom: 15, textAlign: "center", }}><b>{workflow.name}</b></Typography>
|
||||||
}
|
}
|
||||||
|
|
||||||
{workflowQuestion.length > 0 ?
|
{workflowQuestion.length > 0 ?
|
||||||
<Typography variant="body1" style={{ marginBottom: 35, marginTop: 30, marginRight: 15, textAlign: "center", whiteSpace: "pre-line", }}>
|
<div style={{
|
||||||
{workflowQuestion}
|
backgroundColor: theme.palette.inputColor,
|
||||||
</Typography>
|
padding: 20,
|
||||||
|
borderRadius: theme.palette.borderRadius,
|
||||||
|
marginBottom: 35,
|
||||||
|
marginTop: 30,
|
||||||
|
}}>
|
||||||
|
<Typography variant="body1" style={{ marginRight: 15, textAlign: "center", whiteSpace: "pre-line", }}>
|
||||||
|
{workflowQuestion}
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
: null}
|
: null}
|
||||||
|
|
||||||
{workflow.input_questions !== undefined && workflow.input_questions !== null && workflow.input_questions.length > 0 ?
|
{workflow.input_questions !== undefined && workflow.input_questions !== null && workflow.input_questions.length > 0 ?
|
||||||
<div style={{marginBottom: 5, }}>
|
<div style={{marginBottom: 5, }}>
|
||||||
{workflow.input_questions.map((question, index) => {
|
{workflow.input_questions.map((question, index) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{marginBottom: 5}}>
|
<div style={{marginBottom: 5}}>
|
||||||
{question.name}
|
{question.name}
|
||||||
<TextField
|
<TextField
|
||||||
color="primary"
|
color="primary"
|
||||||
style={{backgroundColor: theme.palette.inputColor, marginTop: 5, }}
|
style={{backgroundColor: theme.palette.inputColor, marginTop: 5, }}
|
||||||
multiLine
|
label={question.value}
|
||||||
maxRows={2}
|
required
|
||||||
InputProps={{
|
|
||||||
style:{
|
disabled={disabledButtons}
|
||||||
height: "50px",
|
|
||||||
color: "white",
|
|
||||||
fontSize: "1em",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
fullWidth={true}
|
fullWidth={true}
|
||||||
placeholder=""
|
placeholder=""
|
||||||
id="emailfield"
|
id="emailfield"
|
||||||
margin="normal"
|
margin="normal"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
onChange={(e) => {
|
onBlur={(e) => {
|
||||||
//setExecutionArgument(e.target.value)
|
//setExecutionArgument(e.target.value)
|
||||||
executionArgument[question.value] = e.target.value
|
executionArgument[question.value] = e.target.value
|
||||||
|
setUpdate(Math.random())
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -813,20 +943,27 @@ const RunWorkflow = (defaultprops) => {
|
|||||||
</Typography>
|
</Typography>
|
||||||
: null}
|
: null}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
:
|
:
|
||||||
answer !== undefined && answer !== null ?
|
((answer !== undefined && answer !== null) || (foundSourcenode !== undefined && foundSourcenode !== null)) ?
|
||||||
<span style={{marginTop: 20, }}>
|
<span style={{marginTop: 20, }}>
|
||||||
<Typography variant="body1" style={{textAlign: "center", marginTop: 30, marginBottom: 20, }}>
|
|
||||||
{disabledButtons ? "Already answered. Nothing to do." : ""}
|
{disabledButtons && message.length > 0 ?
|
||||||
</Typography>
|
<Typography variant="body1" style={{textAlign: "center", marginTop: 30, marginBottom: 20, }}>
|
||||||
|
{message}. You may close this window.
|
||||||
|
</Typography>
|
||||||
|
:
|
||||||
|
<Typography variant="body1" style={{textAlign: "center", marginTop: 30, marginBottom: 20, }}>
|
||||||
|
{disabledButtons ? "Answered. You may close this window." : ""}
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
|
|
||||||
{disabledButtons ? null :
|
{disabledButtons ? null :
|
||||||
<Typography variant="body2" color="textSecondary" style={{textAlign: "center", marginTop: 10, }}>
|
<Typography variant="body2" color="textSecondary" style={{textAlign: "center", marginTop: 10, }}>
|
||||||
What do you want to do?
|
What do you want to do?
|
||||||
</Typography>
|
</Typography>
|
||||||
}
|
}
|
||||||
<div fullWidth style={{width: "100%", marginTop: 10, marginBottom: 10, display: "flex", }}>
|
<div fullWidth style={{width: "100%", marginTop: 10, marginBottom: 10, display: "flex", }}>
|
||||||
<Button fullWidth id="continue_execution" variant="contained" disabled={disabledButtons} color="primary" style={{border: answer === "true" ? "2px solid rgba(255,255,255,0.6)" : null, flex: 1,}} onClick={() => {
|
<Button fullWidth id="continue_execution" variant="contained" disabled={disabledButtons} color="primary" style={{flex: 1,}} onClick={() => {
|
||||||
onSubmit(null, execution_id, authorization, true)
|
onSubmit(null, execution_id, authorization, true)
|
||||||
|
|
||||||
setButtonClicked("FINISHED")
|
setButtonClicked("FINISHED")
|
||||||
@@ -837,7 +974,7 @@ const RunWorkflow = (defaultprops) => {
|
|||||||
<Typography variant="body1" style={{marginLeft: 3, marginRight: 3, marginTop: 3, }}>
|
<Typography variant="body1" style={{marginLeft: 3, marginRight: 3, marginTop: 3, }}>
|
||||||
or
|
or
|
||||||
</Typography>
|
</Typography>
|
||||||
<Button fullWidth id="abort_execution" variant="contained" color="primary" disabled={disabledButtons} style={{border: answer !== "true" ? "2px solid rgba(255,255,255,0.6)" : null, flex: 1, }} onClick={() => {
|
<Button fullWidth id="abort_execution" variant="contained" color="primary" disabled={disabledButtons} style={{ flex: 1, }} onClick={() => {
|
||||||
onSubmit(null, execution_id, authorization, false)
|
onSubmit(null, execution_id, authorization, false)
|
||||||
|
|
||||||
setButtonClicked("ABORTED")
|
setButtonClicked("ABORTED")
|
||||||
@@ -856,7 +993,7 @@ const RunWorkflow = (defaultprops) => {
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
{buttonClicked !== undefined && buttonClicked !== null && buttonClicked !== "finished" && buttonClicked.length > 0 ?
|
{/*buttonClicked !== undefined && buttonClicked !== null && buttonClicked !== "finished" && buttonClicked.length > 0 ?
|
||||||
<img id="finalize_gif" src="/images/finalize.gif" alt="finalize workflow animation" style={{width: 150, marginLeft: 125, borderRadius: theme.palette.borderRadius, }}
|
<img id="finalize_gif" src="/images/finalize.gif" alt="finalize workflow animation" style={{width: 150, marginLeft: 125, borderRadius: theme.palette.borderRadius, }}
|
||||||
onLoad={() => {
|
onLoad={() => {
|
||||||
console.log("Img loaded.")
|
console.log("Img loaded.")
|
||||||
@@ -868,7 +1005,7 @@ const RunWorkflow = (defaultprops) => {
|
|||||||
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
: null}
|
: null*/}
|
||||||
|
|
||||||
<div style={{marginTop: "10px"}}>
|
<div style={{marginTop: "10px"}}>
|
||||||
{executionInfo}
|
{executionInfo}
|
||||||
|
|||||||
@@ -43,11 +43,10 @@ const Search = (props) => {
|
|||||||
const params = Object.fromEntries(urlSearchParams.entries());
|
const params = Object.fromEntries(urlSearchParams.entries());
|
||||||
const foundTab = params["tab"];
|
const foundTab = params["tab"];
|
||||||
if (foundTab !== null && foundTab !== undefined) {
|
if (foundTab !== null && foundTab !== undefined) {
|
||||||
for (var key in Object.keys(views)) {
|
for (let key in views) {
|
||||||
const value = views[key];
|
const value = views[key];
|
||||||
console.log(key, value);
|
|
||||||
if (value === foundTab) {
|
if (value === foundTab) {
|
||||||
setConfig("", key);
|
setConfig(null, key);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -130,7 +129,7 @@ const Search = (props) => {
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
overflowX: "hidden",
|
overflowX: "hidden",
|
||||||
minHeight: 400,
|
minHeight: 400
|
||||||
};
|
};
|
||||||
|
|
||||||
const views = {
|
const views = {
|
||||||
@@ -138,7 +137,9 @@ const Search = (props) => {
|
|||||||
1: "workflows",
|
1: "workflows",
|
||||||
2: "docs",
|
2: "docs",
|
||||||
3: "creators",
|
3: "creators",
|
||||||
|
4: "discord"
|
||||||
};
|
};
|
||||||
|
|
||||||
const setConfig = (event, inputValue) => {
|
const setConfig = (event, inputValue) => {
|
||||||
const newValue = parseInt(inputValue);
|
const newValue = parseInt(inputValue);
|
||||||
|
|
||||||
@@ -216,7 +217,7 @@ const Search = (props) => {
|
|||||||
<div style={boxStyle}>
|
<div style={boxStyle}>
|
||||||
<Tabs
|
<Tabs
|
||||||
style={{
|
style={{
|
||||||
width: 741,
|
width: 757,
|
||||||
margin: isHeader ? null : "auto",
|
margin: isHeader ? null : "auto",
|
||||||
marginTop: hidemargins === true ? 0 : isHeader ? null : 25,
|
marginTop: hidemargins === true ? 0 : isHeader ? null : 25,
|
||||||
backgroundColor: "rgba(33, 33, 33, 1)",
|
backgroundColor: "rgba(33, 33, 33, 1)",
|
||||||
@@ -227,8 +228,8 @@ const Search = (props) => {
|
|||||||
textColor="secondary"
|
textColor="secondary"
|
||||||
onChange={setConfig}
|
onChange={setConfig}
|
||||||
aria-label="disabled tabs example"
|
aria-label="disabled tabs example"
|
||||||
variant="scrollable"
|
// variant="scrollable"
|
||||||
scrollButtons="auto"
|
// scrollButtons="auto"
|
||||||
classes={{ indicator: classes.hideIndicator, root: classes.customTab }}
|
classes={{ indicator: classes.hideIndicator, root: classes.customTab }}
|
||||||
>
|
>
|
||||||
<StyledTab
|
<StyledTab
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
Divider,
|
Divider,
|
||||||
TextField,
|
TextField,
|
||||||
Modal,
|
Modal,
|
||||||
|
Switch,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
//import { useAlert
|
//import { useAlert
|
||||||
import { ToastContainer, toast } from "react-toastify";
|
import { ToastContainer, toast } from "react-toastify";
|
||||||
@@ -19,6 +20,9 @@ import { FileCopy, Visibility, VisibilityOff } from "@mui/icons-material";
|
|||||||
import IconButton from "@mui/material/IconButton";
|
import IconButton from "@mui/material/IconButton";
|
||||||
import { Tooltip } from "@mui/material";
|
import { Tooltip } from "@mui/material";
|
||||||
import CloseIcon from "@mui/icons-material/Close";
|
import CloseIcon from "@mui/icons-material/Close";
|
||||||
|
import { Box } from "@mui/system";
|
||||||
|
import CircularProgress from "@mui/material/CircularProgress";
|
||||||
|
|
||||||
|
|
||||||
const Settings = (props) => {
|
const Settings = (props) => {
|
||||||
const { globalUrl, isLoaded, userdata, setUserData } = props;
|
const { globalUrl, isLoaded, userdata, setUserData } = props;
|
||||||
@@ -32,6 +36,11 @@ const Settings = (props) => {
|
|||||||
const [currentPassword, setCurrentPassword] = useState("");
|
const [currentPassword, setCurrentPassword] = useState("");
|
||||||
const [newPassword, setNewPassword] = useState("");
|
const [newPassword, setNewPassword] = useState("");
|
||||||
const [newPassword2, setNewPassword2] = useState("");
|
const [newPassword2, setNewPassword2] = useState("");
|
||||||
|
const [selectedOrganization, setSelectedOrganization] = useState({});
|
||||||
|
const [MFARequired, setMFARequired] = React.useState(false);
|
||||||
|
const [image2FA, setImage2FA] = React.useState("");
|
||||||
|
const [value2FA, setValue2FA] = React.useState("");
|
||||||
|
|
||||||
// const [file, setFile] = React.useState("");
|
// const [file, setFile] = React.useState("");
|
||||||
// const [fileBase64, setFileBase64] = React.useState(
|
// const [fileBase64, setFileBase64] = React.useState(
|
||||||
// userdata.image === undefined || userdata.image === null
|
// userdata.image === undefined || userdata.image === null
|
||||||
@@ -87,6 +96,82 @@ const Settings = (props) => {
|
|||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (userdata.active_org === undefined || userdata.active_org === null || userdata.active_org.id === undefined || userdata.active_org.id === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch(`${globalUrl}/api/v1/orgs/${userdata.active_org.id}`, {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Accept: "application/json",
|
||||||
|
},
|
||||||
|
credentials: "include",
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
console.log("Received response:", response);
|
||||||
|
if (response.status !== 200) {
|
||||||
|
console.error("Status not 200:", response.status);
|
||||||
|
throw new Error(`Status not 200: ${response.status}`);
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then((responseJson) => {
|
||||||
|
setSelectedOrganization(responseJson);
|
||||||
|
setMFARequired(responseJson.mfa_required);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error("Error fetching organization:", error);
|
||||||
|
});
|
||||||
|
}, [userdata]);
|
||||||
|
|
||||||
|
const UpdateMFAInUserOrg = (org_id) => {
|
||||||
|
|
||||||
|
if (MFARequired === false) {
|
||||||
|
toast("Making MFA required for your organization. Please wait...");
|
||||||
|
} else {
|
||||||
|
toast("Making MFA optional for your organization. Please wait...");
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
mfa_required: !selectedOrganization.mfa_required,
|
||||||
|
org_id: selectedOrganization.id,
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = globalUrl + `/api/v1/orgs/${selectedOrganization.id}`;
|
||||||
|
fetch(url, {
|
||||||
|
mode: "cors",
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
credentials: "include",
|
||||||
|
crossDomain: true,
|
||||||
|
withCredentials: true,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json; charset=utf-8",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((response) =>
|
||||||
|
response.json().then((responseJson) => {
|
||||||
|
console.log(responseJson)
|
||||||
|
if (responseJson["success"] === false) {
|
||||||
|
toast.error("Failed updating org: ", responseJson.reason);
|
||||||
|
} else {
|
||||||
|
if (MFARequired === false) {
|
||||||
|
setMFARequired(true)
|
||||||
|
toast.success("Successfully make MFA required for your organization!");
|
||||||
|
} else {
|
||||||
|
setMFARequired(false)
|
||||||
|
toast.success("Successfully make MFA optional for your organization!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.catch((error) => {
|
||||||
|
toast("Err: " + error.toString());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const checkOwner = (data, userdata) => {
|
const checkOwner = (data, userdata) => {
|
||||||
var currentOwner = false;
|
var currentOwner = false;
|
||||||
if (data.owner.address === userdata.eth_info.account) {
|
if (data.owner.address === userdata.eth_info.account) {
|
||||||
@@ -940,7 +1025,6 @@ const Settings = (props) => {
|
|||||||
</Button>
|
</Button>
|
||||||
<h3>{passwordFormMessage}</h3>
|
<h3>{passwordFormMessage}</h3>
|
||||||
|
|
||||||
|
|
||||||
{isCloud && (
|
{isCloud && (
|
||||||
<>
|
<>
|
||||||
<Divider style={{ marginTop: "40px" }} />
|
<Divider style={{ marginTop: "40px" }} />
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ import {
|
|||||||
ArrowLeft as ArrowLeftIcon,
|
ArrowLeft as ArrowLeftIcon,
|
||||||
ArrowRight as ArrowRightIcon,
|
ArrowRight as ArrowRightIcon,
|
||||||
QueryStats as QueryStatsIcon,
|
QueryStats as QueryStatsIcon,
|
||||||
|
Visibility as VisibilityIcon,
|
||||||
} from "@mui/icons-material";
|
} from "@mui/icons-material";
|
||||||
|
|
||||||
import { DataGrid, GridToolbar } from "@mui/x-data-grid";
|
import { DataGrid, GridToolbar } from "@mui/x-data-grid";
|
||||||
@@ -129,7 +130,6 @@ export const GetIconInfo = (action) => {
|
|||||||
// Finds the icon based on the action. Should be verbs.
|
// Finds the icon based on the action. Should be verbs.
|
||||||
const iconList = [
|
const iconList = [
|
||||||
{ key: "cases", values: ["cases"] },
|
{ key: "cases", values: ["cases"] },
|
||||||
{ key: "communication", values: ["communication", "comms", "email",] },
|
|
||||||
{ key: "cache_add", values: ["set_cache"] },
|
{ key: "cache_add", values: ["set_cache"] },
|
||||||
{ key: "cache_get", values: ["get_cache"] },
|
{ key: "cache_get", values: ["get_cache"] },
|
||||||
{ key: "filter", values: ["filter"] },
|
{ key: "filter", values: ["filter"] },
|
||||||
@@ -199,6 +199,7 @@ export const GetIconInfo = (action) => {
|
|||||||
values: ["compare", "convert", "to", "filter", "translate", "parse"],
|
values: ["compare", "convert", "to", "filter", "translate", "parse"],
|
||||||
},
|
},
|
||||||
{ key: "close", values: ["close", "stop", "cancel", "block"] },
|
{ key: "close", values: ["close", "stop", "cancel", "block"] },
|
||||||
|
{ key: "communication", values: ["communication", "comms", "email", "mail",] },
|
||||||
];
|
];
|
||||||
|
|
||||||
var selectedKey = ""
|
var selectedKey = ""
|
||||||
@@ -614,6 +615,7 @@ const Workflows = (props) => {
|
|||||||
const [videoViewOpen, setVideoViewOpen] = React.useState(false)
|
const [videoViewOpen, setVideoViewOpen] = React.useState(false)
|
||||||
const [gettingStartedItems, setGettingStartedItems] = React.useState([])
|
const [gettingStartedItems, setGettingStartedItems] = React.useState([])
|
||||||
const [selectedWorkflowIndexes, setSelectedWorkflowIndexes] = React.useState([])
|
const [selectedWorkflowIndexes, setSelectedWorkflowIndexes] = React.useState([])
|
||||||
|
const [highlightIds, setHighlightIds] = React.useState([])
|
||||||
|
|
||||||
const [apps, setApps] = React.useState([]);
|
const [apps, setApps] = React.useState([]);
|
||||||
|
|
||||||
@@ -974,15 +976,16 @@ const Workflows = (props) => {
|
|||||||
}, 1000);
|
}, 1000);
|
||||||
} else if (selectedWorkflowIndexes.length > 0) {
|
} else if (selectedWorkflowIndexes.length > 0) {
|
||||||
// Do backwards so it doesn't change
|
// Do backwards so it doesn't change
|
||||||
|
toast("Starting deletion of workflows. This might take a while.")
|
||||||
for (var i = selectedWorkflowIndexes.length - 1; i >= 0; i--) {
|
for (var i = selectedWorkflowIndexes.length - 1; i >= 0; i--) {
|
||||||
const workflow = filteredWorkflows[selectedWorkflowIndexes[i]-1]
|
const workflow = filteredWorkflows[selectedWorkflowIndexes[i]-1]
|
||||||
if (workflow !== undefined && workflow !== null && workflow.id !== undefined && workflow.id !== null) {
|
if (workflow !== undefined && workflow !== null && workflow.id !== undefined && workflow.id !== null) {
|
||||||
deleteWorkflow(workflow.id);
|
deleteWorkflow(workflow.id, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
getAvailableWorkflows();
|
getAvailableWorkflows()
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
setSelectedWorkflowIndexes([]);
|
setSelectedWorkflowIndexes([]);
|
||||||
@@ -1113,7 +1116,7 @@ const Workflows = (props) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const getAvailableWorkflows = () => {
|
const getAvailableWorkflows = (amount) => {
|
||||||
var storageWorkflows = []
|
var storageWorkflows = []
|
||||||
try {
|
try {
|
||||||
const storagewf = localStorage.getItem("workflows")
|
const storagewf = localStorage.getItem("workflows")
|
||||||
@@ -1130,7 +1133,12 @@ const Workflows = (props) => {
|
|||||||
//console.log("Failed to get workflows from localstorage: ", e)
|
//console.log("Failed to get workflows from localstorage: ", e)
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch(globalUrl + "/api/v1/workflows", {
|
var url = `${globalUrl}/api/v1/workflows`
|
||||||
|
if (amount !== undefined && amount !== null) {
|
||||||
|
url += `?top=${amount}`
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch(url, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
@@ -1154,10 +1162,11 @@ const Workflows = (props) => {
|
|||||||
})
|
})
|
||||||
.then((responseJson) => {
|
.then((responseJson) => {
|
||||||
if (responseJson !== undefined) {
|
if (responseJson !== undefined) {
|
||||||
|
|
||||||
var newarray = []
|
var newarray = []
|
||||||
for (var key in responseJson) {
|
for (var wfkey in responseJson) {
|
||||||
const wf = responseJson[key]
|
const wf = responseJson[wfkey]
|
||||||
if (wf.public === true) {
|
if (wf.public === true || wf.hidden === true) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1170,9 +1179,9 @@ const Workflows = (props) => {
|
|||||||
var parsedactionlist = [];
|
var parsedactionlist = [];
|
||||||
for (var key in newarray) {
|
for (var key in newarray) {
|
||||||
const workflow = newarray[key]
|
const workflow = newarray[key]
|
||||||
if (workflow.status === "production") {
|
//if (workflow.status === "production") {
|
||||||
setProdFilter = true
|
// setProdFilter = true
|
||||||
}
|
//}
|
||||||
|
|
||||||
for (var actionkey in newarray[key].actions) {
|
for (var actionkey in newarray[key].actions) {
|
||||||
const action = newarray[key].actions[actionkey];
|
const action = newarray[key].actions[actionkey];
|
||||||
@@ -1213,7 +1222,24 @@ const Workflows = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setFirstLoad(false)
|
setFirstLoad(false)
|
||||||
}, 100)
|
}, 250)
|
||||||
|
|
||||||
|
/*
|
||||||
|
setTimeout(() => {
|
||||||
|
var timeout = 0
|
||||||
|
for (var key in newarray) {
|
||||||
|
const wf = newarray[key]
|
||||||
|
if (wf.actions === undefined || wf.actions === null || wf.actions.length === 0) {
|
||||||
|
setTimeout(() => {
|
||||||
|
sideloadWorkflow(wf.id, false)
|
||||||
|
}, timeout)
|
||||||
|
|
||||||
|
timeout += 1000
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}, 1000)
|
||||||
|
*/
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (isLoggedIn) {
|
if (isLoggedIn) {
|
||||||
@@ -1319,8 +1345,21 @@ const Workflows = (props) => {
|
|||||||
setView(tmpView);
|
setView(tmpView);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const urlSearchParams = new URLSearchParams(window.location.search);
|
||||||
|
const params = Object.fromEntries(urlSearchParams.entries());
|
||||||
|
const foundTab = params["top"];
|
||||||
|
if (foundTab !== null && foundTab !== undefined) {
|
||||||
|
// Check if it's a number
|
||||||
|
if (isNaN(foundTab)) {
|
||||||
|
getAvailableWorkflows()
|
||||||
|
} else {
|
||||||
|
getAvailableWorkflows(foundTab)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
getAvailableWorkflows()
|
||||||
|
}
|
||||||
|
|
||||||
getApps()
|
getApps()
|
||||||
getAvailableWorkflows();
|
|
||||||
getFramework()
|
getFramework()
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
@@ -1637,20 +1676,24 @@ const Workflows = (props) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const copyWorkflow = (data) => {
|
const duplicateWorkflow = (data) => {
|
||||||
data = JSON.parse(JSON.stringify(data));
|
//data = JSON.parse(JSON.stringify(data));
|
||||||
toast("Copying workflow " + data.name);
|
toast("Copying workflow '" + data.name + "'. The new workflow will load in and be highlighted.");
|
||||||
data.id = "";
|
//data.id = "";
|
||||||
data.name = data.name + "_copy";
|
//data.name = data.name + "_copy";
|
||||||
data = deduplicateIds(data, true);
|
//data = deduplicateIds(data, true);
|
||||||
|
|
||||||
fetch(globalUrl + "/api/v1/workflows", {
|
const duplicateData = {
|
||||||
|
name: data.name + "_copy",
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch(`${globalUrl}/api/v1/workflows/${data.id}/duplicate`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
Accept: "application/json",
|
Accept: "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify(data),
|
body: JSON.stringify(duplicateData),
|
||||||
credentials: "include",
|
credentials: "include",
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
@@ -1660,7 +1703,20 @@ const Workflows = (props) => {
|
|||||||
}
|
}
|
||||||
return response.json();
|
return response.json();
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then((responseJson) => {
|
||||||
|
if (responseJson.success === false) {
|
||||||
|
if (responseJson.reason !== undefined) {
|
||||||
|
toast("Failed copying workflow: " + responseJson.reason)
|
||||||
|
} else {
|
||||||
|
toast("Failed copying workflow")
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (responseJson.id !== undefined) {
|
||||||
|
setHighlightIds([responseJson.id])
|
||||||
|
}
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
getAvailableWorkflows();
|
getAvailableWorkflows();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
@@ -1689,15 +1745,68 @@ const Workflows = (props) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const sideloadWorkflow = (id, openEdit) => {
|
const exportSingleWorkflow = (data, setOpen) => {
|
||||||
|
setExportModalOpen(true)
|
||||||
|
|
||||||
|
if (data.triggers !== null && data.triggers !== undefined) {
|
||||||
|
var newSubflows = [];
|
||||||
|
for (var key in data.triggers) {
|
||||||
|
const trigger = data.triggers[key];
|
||||||
|
|
||||||
|
if (
|
||||||
|
trigger.parameters !== null &&
|
||||||
|
trigger.parameters !== undefined
|
||||||
|
) {
|
||||||
|
for (var subkey in trigger.parameters) {
|
||||||
|
const param = trigger.parameters[subkey];
|
||||||
|
if (
|
||||||
|
param.name === "workflow" &&
|
||||||
|
param.value !== data.id &&
|
||||||
|
!newSubflows.includes(param.value)
|
||||||
|
) {
|
||||||
|
newSubflows.push(param.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var parsedworkflows = [];
|
||||||
|
for (var key in newSubflows) {
|
||||||
|
const foundWorkflow = workflows.find(
|
||||||
|
(workflow) => workflow.id === newSubflows[key]
|
||||||
|
);
|
||||||
|
if (foundWorkflow !== undefined && foundWorkflow !== null) {
|
||||||
|
parsedworkflows.push(foundWorkflow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parsedworkflows.length > 0) {
|
||||||
|
console.log(
|
||||||
|
"Appending subflows during export: ",
|
||||||
|
parsedworkflows.length
|
||||||
|
);
|
||||||
|
data.subflows = parsedworkflows;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setExportData(data)
|
||||||
|
setOpen(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const sideloadWorkflow = (id, action, setOpen) => {
|
||||||
|
|
||||||
const storagewf = localStorage.getItem("workflows")
|
const storagewf = localStorage.getItem("workflows")
|
||||||
const storageWorkflows = JSON.parse(storagewf)
|
const storageWorkflows = JSON.parse(storagewf)
|
||||||
if (storageWorkflows === null || storageWorkflows === undefined || storageWorkflows.length === 0) {
|
if (storageWorkflows === null || storageWorkflows === undefined || storageWorkflows.length === 0) {
|
||||||
} else {
|
} else {
|
||||||
for (var i = 0; i < storageWorkflows.length; i++) {
|
for (var i = 0; i < storageWorkflows.length; i++) {
|
||||||
if (storageWorkflows[i].id === id) {
|
if (storageWorkflows[i].id === id) {
|
||||||
if (storageWorkflows[i].image !== "") {
|
if (storageWorkflows[i].image !== "" && storageWorkflows[i].image !== undefined && storageWorkflows[i].image !== null) {
|
||||||
return
|
|
||||||
|
if (action === undefined || action === null || action === "") {
|
||||||
|
console.log("RETURNING")
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1718,8 +1827,16 @@ const Workflows = (props) => {
|
|||||||
return response.json()
|
return response.json()
|
||||||
})
|
})
|
||||||
.then((responseJson) => {
|
.then((responseJson) => {
|
||||||
if (openEdit) {
|
if (responseJson.success !== false && responseJson.id !== undefined) {
|
||||||
setEditing(responseJson)
|
if (action === "edit") {
|
||||||
|
setEditing(responseJson)
|
||||||
|
} else if (action === "publish") {
|
||||||
|
setPublishModalOpen(true)
|
||||||
|
setSelectedWorkflow(responseJson)
|
||||||
|
} else if (action === "export") {
|
||||||
|
exportSingleWorkflow(responseJson, setOpen)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < storageWorkflows.length; i++) {
|
for (var i = 0; i < storageWorkflows.length; i++) {
|
||||||
@@ -1730,15 +1847,16 @@ const Workflows = (props) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setWorkflows(storageWorkflows)
|
//setWorkflows(storageWorkflows)
|
||||||
//setFilteredWorkflows(storageWorkflows)
|
setFilteredWorkflows(storageWorkflows)
|
||||||
|
//setUpdate(Math.random())
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.log(error.toString())
|
console.log(error.toString())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const deleteWorkflow = (id) => {
|
const deleteWorkflow = (id, bulk) => {
|
||||||
fetch(globalUrl + "/api/v1/workflows/" + id, {
|
fetch(globalUrl + "/api/v1/workflows/" + id, {
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
headers: {
|
headers: {
|
||||||
@@ -1752,15 +1870,19 @@ const Workflows = (props) => {
|
|||||||
console.log("Status not 200 for setting workflows :O!");
|
console.log("Status not 200 for setting workflows :O!");
|
||||||
toast("Failed deleting workflow. Do you have access?");
|
toast("Failed deleting workflow. Do you have access?");
|
||||||
} else {
|
} else {
|
||||||
toast("Deleted workflow " + id);
|
if (bulk !== true) {
|
||||||
|
toast("Deleted workflow " + id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.json();
|
return response.json();
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
setTimeout(() => {
|
if (bulk !== true) {
|
||||||
getAvailableWorkflows();
|
setTimeout(() => {
|
||||||
}, 1000);
|
getAvailableWorkflows();
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
toast(error.toString());
|
toast(error.toString());
|
||||||
@@ -1878,7 +2000,9 @@ const Workflows = (props) => {
|
|||||||
const appGroup = getWorkflowAppgroup(data)
|
const appGroup = getWorkflowAppgroup(data)
|
||||||
const [triggers, subflows] = getWorkflowMeta(data)
|
const [triggers, subflows] = getWorkflowMeta(data)
|
||||||
|
|
||||||
const isDistributed = data.suborg_distribution !== undefined && data.suborg_distribution !== null && data.suborg_distribution.includes(userdata.active_org.id)
|
const hasSuborgs = data.suborg_distribution !== undefined && data.suborg_distribution !== null && data.suborg_distribution.length > 0
|
||||||
|
const isDistributed = (data.parentorg_workflow !== undefined && data.parentorg_workflow !== null && data.parentorg_workflow.length > 0) //|| (data.org_id !== userdata.active_org.id && data.org_id !== undefined && data.org_id !== null && data.org_id.length > 0)
|
||||||
|
|
||||||
const workflowMenuButtons = (
|
const workflowMenuButtons = (
|
||||||
<Menu
|
<Menu
|
||||||
id="long-menu"
|
id="long-menu"
|
||||||
@@ -1890,8 +2014,20 @@ const Workflows = (props) => {
|
|||||||
setAnchorEl(null);
|
setAnchorEl(null);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
{isDistributed ?
|
||||||
|
<MenuItem
|
||||||
|
style={{ backgroundColor: theme.palette.inputColor, color: "white" }}
|
||||||
|
onClick={() => {
|
||||||
|
navigate(`/workflows/${data.id}`)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<VisibilityIcon style={{ marginLeft: 0, marginRight: 8 }} />
|
||||||
|
Explore Workflow
|
||||||
|
</MenuItem>
|
||||||
|
: null}
|
||||||
<MenuItem
|
<MenuItem
|
||||||
style={{ backgroundColor: theme.palette.inputColor, color: "white" }}
|
style={{ backgroundColor: theme.palette.inputColor, color: "white" }}
|
||||||
|
disabled={isDistributed}
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
if (data.actions !== undefined && data.actions !== null && data.actions.length > 0 && data.image !== "") {
|
if (data.actions !== undefined && data.actions !== null && data.actions.length > 0 && data.image !== "") {
|
||||||
@@ -1899,7 +2035,9 @@ const Workflows = (props) => {
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
//toast("Need to side-load workflow to be edited properly")
|
//toast("Need to side-load workflow to be edited properly")
|
||||||
sideloadWorkflow(data.id, true)
|
sideloadWorkflow(data.id, "edit")
|
||||||
|
|
||||||
|
toast.info("Loading full workflow for editing. Please wait...")
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
key={"change"}
|
key={"change"}
|
||||||
@@ -1909,9 +2047,11 @@ const Workflows = (props) => {
|
|||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
style={{ backgroundColor: theme.palette.inputColor, color: "white" }}
|
style={{ backgroundColor: theme.palette.inputColor, color: "white" }}
|
||||||
|
disabled={isDistributed}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedWorkflow(data);
|
sideloadWorkflow(data.id, "publish")
|
||||||
setPublishModalOpen(true);
|
|
||||||
|
toast.info("Loading full workflow for publishing. Please wait...")
|
||||||
}}
|
}}
|
||||||
key={"publish"}
|
key={"publish"}
|
||||||
>
|
>
|
||||||
@@ -1920,9 +2060,10 @@ const Workflows = (props) => {
|
|||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
style={{ backgroundColor: theme.palette.inputColor, color: "white" }}
|
style={{ backgroundColor: theme.palette.inputColor, color: "white" }}
|
||||||
|
disabled={isDistributed}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
copyWorkflow(data);
|
duplicateWorkflow(data)
|
||||||
setOpen(false);
|
setOpen(false)
|
||||||
}}
|
}}
|
||||||
key={"duplicate"}
|
key={"duplicate"}
|
||||||
>
|
>
|
||||||
@@ -1931,52 +2072,11 @@ const Workflows = (props) => {
|
|||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
style={{ backgroundColor: theme.palette.inputColor, color: "white" }}
|
style={{ backgroundColor: theme.palette.inputColor, color: "white" }}
|
||||||
|
disabled={isDistributed}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setExportModalOpen(true);
|
sideloadWorkflow(data.id, "export", setOpen)
|
||||||
|
|
||||||
if (data.triggers !== null && data.triggers !== undefined) {
|
toast.info("Loading full workflow to be exported. Please wait...")
|
||||||
var newSubflows = [];
|
|
||||||
for (var key in data.triggers) {
|
|
||||||
const trigger = data.triggers[key];
|
|
||||||
|
|
||||||
if (
|
|
||||||
trigger.parameters !== null &&
|
|
||||||
trigger.parameters !== undefined
|
|
||||||
) {
|
|
||||||
for (var subkey in trigger.parameters) {
|
|
||||||
const param = trigger.parameters[subkey];
|
|
||||||
if (
|
|
||||||
param.name === "workflow" &&
|
|
||||||
param.value !== data.id &&
|
|
||||||
!newSubflows.includes(param.value)
|
|
||||||
) {
|
|
||||||
newSubflows.push(param.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var parsedworkflows = [];
|
|
||||||
for (var key in newSubflows) {
|
|
||||||
const foundWorkflow = workflows.find(
|
|
||||||
(workflow) => workflow.id === newSubflows[key]
|
|
||||||
);
|
|
||||||
if (foundWorkflow !== undefined && foundWorkflow !== null) {
|
|
||||||
parsedworkflows.push(foundWorkflow);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parsedworkflows.length > 0) {
|
|
||||||
console.log(
|
|
||||||
"Appending subflows during export: ",
|
|
||||||
parsedworkflows.length
|
|
||||||
);
|
|
||||||
data.subflows = parsedworkflows;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setExportData(data);
|
|
||||||
setOpen(false);
|
|
||||||
}}
|
}}
|
||||||
key={"export"}
|
key={"export"}
|
||||||
>
|
>
|
||||||
@@ -1985,6 +2085,7 @@ const Workflows = (props) => {
|
|||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
style={{ backgroundColor: theme.palette.inputColor, color: "white" }}
|
style={{ backgroundColor: theme.palette.inputColor, color: "white" }}
|
||||||
|
disabled={isDistributed}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setDeleteModalOpen(true);
|
setDeleteModalOpen(true);
|
||||||
setSelectedWorkflowId(data.id);
|
setSelectedWorkflowId(data.id);
|
||||||
@@ -2076,7 +2177,7 @@ const Workflows = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{width: "100%", minWidth: 321, position: "relative", border: isDistributed ? "2px solid #40E0D0" : "inherit", borderRadius: theme.palette.borderRadius, }}>
|
<div style={{width: "100%", minWidth: 320, position: "relative", border: highlightIds.includes(data.id) ? "2px solid #f85a3e" : isDistributed || hasSuborgs ? "1px solid #40E0D0" : "inherit", borderRadius: theme.palette.borderRadius, }}>
|
||||||
<Paper square style={paperAppStyle}>
|
<Paper square style={paperAppStyle}>
|
||||||
{selectedCategory !== "" ?
|
{selectedCategory !== "" ?
|
||||||
<Tooltip title={`Usecase Category: ${selectedCategory}`} placement="bottom">
|
<Tooltip title={`Usecase Category: ${selectedCategory}`} placement="bottom">
|
||||||
@@ -2129,8 +2230,17 @@ const Workflows = (props) => {
|
|||||||
<Typography>
|
<Typography>
|
||||||
Edit '{data.name}'
|
Edit '{data.name}'
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
{isDistributed || hasSuborgs ?
|
||||||
|
<Typography>
|
||||||
|
This is a parentorg-controlled workflow.
|
||||||
|
</Typography>
|
||||||
|
: null}
|
||||||
</div>
|
</div>
|
||||||
} placement="right">
|
} placement="right">
|
||||||
|
|
||||||
<Typography
|
<Typography
|
||||||
variant="body1"
|
variant="body1"
|
||||||
style={{
|
style={{
|
||||||
@@ -2462,6 +2572,8 @@ const Workflows = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
|
var workflowids = []
|
||||||
|
|
||||||
// Waits for the read
|
// Waits for the read
|
||||||
reader.addEventListener("load", (event) => {
|
reader.addEventListener("load", (event) => {
|
||||||
var data = reader.result;
|
var data = reader.result;
|
||||||
@@ -2498,6 +2610,7 @@ const Workflows = (props) => {
|
|||||||
data.org = []
|
data.org = []
|
||||||
data.execution_org = {}
|
data.execution_org = {}
|
||||||
|
|
||||||
|
workflowids.push(data.id)
|
||||||
|
|
||||||
// Actually create it
|
// Actually create it
|
||||||
setNewWorkflow(
|
setNewWorkflow(
|
||||||
@@ -2528,6 +2641,10 @@ const Workflows = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setLoadWorkflowsModalOpen(false);
|
setLoadWorkflowsModalOpen(false);
|
||||||
|
|
||||||
|
if (workflowids.length > 0) {
|
||||||
|
setHighlightIds(workflowids)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getWorkflowMeta = (data) => {
|
const getWorkflowMeta = (data) => {
|
||||||
@@ -2869,7 +2986,7 @@ const Workflows = (props) => {
|
|||||||
className={classes.datagrid}
|
className={classes.datagrid}
|
||||||
rows={rows}
|
rows={rows}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
pageSize={25}
|
pageSize={100}
|
||||||
checkboxSelection
|
checkboxSelection
|
||||||
autoHeight
|
autoHeight
|
||||||
density="standard"
|
density="standard"
|
||||||
@@ -3682,7 +3799,7 @@ const Workflows = (props) => {
|
|||||||
workflowDelay += 75
|
workflowDelay += 75
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<Grid key={index} item xs={isMobile ? 12 : 4} style={{ padding: "12px 10px 12px 10px" }}>
|
<Grid key={index} item xs={isMobile ? 12 : 4} style={{ padding: "12px 10px 12px 10px", }}>
|
||||||
<WorkflowPaper key={index} data={data} />
|
<WorkflowPaper key={index} data={data} />
|
||||||
</Grid>
|
</Grid>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,16 +4,16 @@ go 1.22.0
|
|||||||
|
|
||||||
toolchain go1.22.2
|
toolchain go1.22.2
|
||||||
|
|
||||||
replace github.com/shuffle/shuffle-shared => ../../../../shuffle-shared
|
//replace github.com/shuffle/shuffle-shared => ../../../../shuffle-shared
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/docker/docker v26.1.0+incompatible
|
github.com/docker/docker v26.1.0+incompatible
|
||||||
github.com/docker/go-connections v0.5.0
|
github.com/docker/go-connections v0.5.0
|
||||||
github.com/satori/go.uuid v1.2.0
|
github.com/satori/go.uuid v1.2.0
|
||||||
github.com/shuffle/shuffle-shared v0.6.37
|
github.com/shuffle/shuffle-shared v0.6.50
|
||||||
k8s.io/api v0.30.0
|
k8s.io/api v0.30.2
|
||||||
k8s.io/apimachinery v0.30.0
|
k8s.io/apimachinery v0.30.2
|
||||||
k8s.io/client-go v0.30.0
|
k8s.io/client-go v0.30.2
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -31,7 +31,7 @@ require (
|
|||||||
github.com/algolia/algoliasearch-client-go/v3 v3.18.1 // indirect
|
github.com/algolia/algoliasearch-client-go/v3 v3.18.1 // indirect
|
||||||
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect
|
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect
|
||||||
github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013 // indirect
|
github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013 // indirect
|
||||||
github.com/cloudflare/circl v1.3.3 // indirect
|
github.com/cloudflare/circl v1.3.7 // indirect
|
||||||
github.com/containerd/log v0.1.0 // indirect
|
github.com/containerd/log v0.1.0 // indirect
|
||||||
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
|
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
@@ -41,7 +41,7 @@ require (
|
|||||||
github.com/emirpasic/gods v1.18.1 // indirect
|
github.com/emirpasic/gods v1.18.1 // indirect
|
||||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
github.com/frikky/kin-openapi v0.41.0 // indirect
|
github.com/frikky/kin-openapi v0.41.0 // indirect
|
||||||
github.com/frikky/schemaless v0.0.11 // indirect
|
github.com/frikky/schemaless v0.0.13 // indirect
|
||||||
github.com/ghodss/yaml v1.0.0 // indirect
|
github.com/ghodss/yaml v1.0.0 // indirect
|
||||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||||
github.com/go-git/go-billy/v5 v5.5.0 // indirect
|
github.com/go-git/go-billy/v5 v5.5.0 // indirect
|
||||||
@@ -82,6 +82,8 @@ require (
|
|||||||
github.com/pjbgf/sha1cd v0.3.0 // indirect
|
github.com/pjbgf/sha1cd v0.3.0 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/sashabaranov/go-openai v1.19.2 // indirect
|
github.com/sashabaranov/go-openai v1.19.2 // indirect
|
||||||
|
github.com/sendgrid/rest v2.6.9+incompatible // indirect
|
||||||
|
github.com/sendgrid/sendgrid-go v3.14.0+incompatible // indirect
|
||||||
github.com/sergi/go-diff v1.1.0 // indirect
|
github.com/sergi/go-diff v1.1.0 // indirect
|
||||||
github.com/skeema/knownhosts v1.2.1 // indirect
|
github.com/skeema/knownhosts v1.2.1 // indirect
|
||||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
|
||||||
|
|||||||
@@ -97,6 +97,8 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn
|
|||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs=
|
github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs=
|
||||||
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
|
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
|
||||||
|
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
|
||||||
|
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
|
||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||||
github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa h1:jQCWAUqqlij9Pgj2i/PB79y4KOPYVyFYdROxgaCwdTQ=
|
github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa h1:jQCWAUqqlij9Pgj2i/PB79y4KOPYVyFYdROxgaCwdTQ=
|
||||||
@@ -139,6 +141,8 @@ github.com/frikky/schemaless v0.0.9 h1:RzNLPkJq5c4nlm5iLiTndFcbeQxdMGJIj266wSGt2
|
|||||||
github.com/frikky/schemaless v0.0.9/go.mod h1:mooDxY+D6weHjhKvjy3+IE9S7P4g4cpNnidkdRv/cHQ=
|
github.com/frikky/schemaless v0.0.9/go.mod h1:mooDxY+D6weHjhKvjy3+IE9S7P4g4cpNnidkdRv/cHQ=
|
||||||
github.com/frikky/schemaless v0.0.11 h1:c4r6CJX30XI+SoJdT9RlUd9qYSQlx6hvwGRtsypu+uM=
|
github.com/frikky/schemaless v0.0.11 h1:c4r6CJX30XI+SoJdT9RlUd9qYSQlx6hvwGRtsypu+uM=
|
||||||
github.com/frikky/schemaless v0.0.11/go.mod h1:mooDxY+D6weHjhKvjy3+IE9S7P4g4cpNnidkdRv/cHQ=
|
github.com/frikky/schemaless v0.0.11/go.mod h1:mooDxY+D6weHjhKvjy3+IE9S7P4g4cpNnidkdRv/cHQ=
|
||||||
|
github.com/frikky/schemaless v0.0.13 h1:ARiN9V7wr2VZXAr9JK5wvTbyPgpGrgeiL1VhR5MlgaQ=
|
||||||
|
github.com/frikky/schemaless v0.0.13/go.mod h1:mooDxY+D6weHjhKvjy3+IE9S7P4g4cpNnidkdRv/cHQ=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||||
@@ -385,6 +389,10 @@ github.com/sashabaranov/go-openai v1.19.2 h1:+dkuCADSnwXV02YVJkdphY8XD9AyHLUWwk6
|
|||||||
github.com/sashabaranov/go-openai v1.19.2/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg=
|
github.com/sashabaranov/go-openai v1.19.2/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg=
|
||||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||||
|
github.com/sendgrid/rest v2.6.9+incompatible h1:1EyIcsNdn9KIisLW50MKwmSRSK+ekueiEMJ7NEoxJo0=
|
||||||
|
github.com/sendgrid/rest v2.6.9+incompatible/go.mod h1:kXX7q3jZtJXK5c5qK83bSGMdV6tsOE70KbHoqJls4lE=
|
||||||
|
github.com/sendgrid/sendgrid-go v3.14.0+incompatible h1:KDSasSTktAqMJCYClHVE94Fcif2i7P7wzISv1sU6DUA=
|
||||||
|
github.com/sendgrid/sendgrid-go v3.14.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8=
|
||||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||||
github.com/shuffle/shuffle-shared v0.6.16 h1:dQBDRmb2Wgl3pEuewqjDvN6v6nUKr+1EvGSEja9zG6s=
|
github.com/shuffle/shuffle-shared v0.6.16 h1:dQBDRmb2Wgl3pEuewqjDvN6v6nUKr+1EvGSEja9zG6s=
|
||||||
@@ -395,6 +403,8 @@ github.com/shuffle/shuffle-shared v0.6.27 h1:q4qZD6bGZFIvZ5Y10unGr3N3rZ7OryWyvva
|
|||||||
github.com/shuffle/shuffle-shared v0.6.27/go.mod h1:rWkh1eWdIx7OqQzJ1+JzF3Hck1X/Ty1WkUtjLrp+CU4=
|
github.com/shuffle/shuffle-shared v0.6.27/go.mod h1:rWkh1eWdIx7OqQzJ1+JzF3Hck1X/Ty1WkUtjLrp+CU4=
|
||||||
github.com/shuffle/shuffle-shared v0.6.37 h1:IB8tJqubJmJwwpLYbXNMVWck5jgVO9SKcKo/BBu+wTc=
|
github.com/shuffle/shuffle-shared v0.6.37 h1:IB8tJqubJmJwwpLYbXNMVWck5jgVO9SKcKo/BBu+wTc=
|
||||||
github.com/shuffle/shuffle-shared v0.6.37/go.mod h1:rWkh1eWdIx7OqQzJ1+JzF3Hck1X/Ty1WkUtjLrp+CU4=
|
github.com/shuffle/shuffle-shared v0.6.37/go.mod h1:rWkh1eWdIx7OqQzJ1+JzF3Hck1X/Ty1WkUtjLrp+CU4=
|
||||||
|
github.com/shuffle/shuffle-shared v0.6.50 h1:MBeGAiBNkw9Eg+3YTJIlBOuskWntGvT0uefFUYOBhbY=
|
||||||
|
github.com/shuffle/shuffle-shared v0.6.50/go.mod h1:RAJiSFjmuKmijKTbbEf9A6Ojb+3/te7g71lED7JjPus=
|
||||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
@@ -908,10 +918,16 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9
|
|||||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||||
k8s.io/api v0.30.0 h1:siWhRq7cNjy2iHssOB9SCGNCl2spiF1dO3dABqZ8niA=
|
k8s.io/api v0.30.0 h1:siWhRq7cNjy2iHssOB9SCGNCl2spiF1dO3dABqZ8niA=
|
||||||
k8s.io/api v0.30.0/go.mod h1:OPlaYhoHs8EQ1ql0R/TsUgaRPhpKNxIMrKQfWUp8QSE=
|
k8s.io/api v0.30.0/go.mod h1:OPlaYhoHs8EQ1ql0R/TsUgaRPhpKNxIMrKQfWUp8QSE=
|
||||||
|
k8s.io/api v0.30.2 h1:+ZhRj+28QT4UOH+BKznu4CBgPWgkXO7XAvMcMl0qKvI=
|
||||||
|
k8s.io/api v0.30.2/go.mod h1:ULg5g9JvOev2dG0u2hig4Z7tQ2hHIuS+m8MNZ+X6EmI=
|
||||||
k8s.io/apimachinery v0.30.0 h1:qxVPsyDM5XS96NIh9Oj6LavoVFYff/Pon9cZeDIkHHA=
|
k8s.io/apimachinery v0.30.0 h1:qxVPsyDM5XS96NIh9Oj6LavoVFYff/Pon9cZeDIkHHA=
|
||||||
k8s.io/apimachinery v0.30.0/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc=
|
k8s.io/apimachinery v0.30.0/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc=
|
||||||
|
k8s.io/apimachinery v0.30.2 h1:fEMcnBj6qkzzPGSVsAZtQThU62SmQ4ZymlXRC5yFSCg=
|
||||||
|
k8s.io/apimachinery v0.30.2/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc=
|
||||||
k8s.io/client-go v0.30.0 h1:sB1AGGlhY/o7KCyCEQ0bPWzYDL0pwOZO4vAtTSh/gJQ=
|
k8s.io/client-go v0.30.0 h1:sB1AGGlhY/o7KCyCEQ0bPWzYDL0pwOZO4vAtTSh/gJQ=
|
||||||
k8s.io/client-go v0.30.0/go.mod h1:g7li5O5256qe6TYdAMyX/otJqMhIiGgTapdLchhmOaY=
|
k8s.io/client-go v0.30.0/go.mod h1:g7li5O5256qe6TYdAMyX/otJqMhIiGgTapdLchhmOaY=
|
||||||
|
k8s.io/client-go v0.30.2 h1:sBIVJdojUNPDU/jObC+18tXWcTJVcwyqS9diGdWHk50=
|
||||||
|
k8s.io/client-go v0.30.2/go.mod h1:JglKSWULm9xlJLx4KCkfLLQ7XwtlbflV6uFFSHTMgVs=
|
||||||
k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
|
k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
|
||||||
k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag=
|
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag=
|
||||||
|
|||||||
@@ -1452,7 +1452,7 @@ func main() {
|
|||||||
if isKubernetes == "true" {
|
if isKubernetes == "true" {
|
||||||
clientset, _, err := shuffle.GetKubernetesClient()
|
clientset, _, err := shuffle.GetKubernetesClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("[ERROR] Error getting kubernetes client:", err)
|
log.Printf("[ERROR] Error getting kubernetes client: %s", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1897,87 +1897,87 @@ func buildEnvVars(envMap map[string]string) []corev1.EnvVar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handleWorkflowQueue(resp http.ResponseWriter, request *http.Request) {
|
func handleWorkflowQueue(resp http.ResponseWriter, request *http.Request) {
|
||||||
if request.Body == nil {
|
if request.Body == nil {
|
||||||
log.Printf("[WARNING] (2) No body in request for workflowqueue")
|
log.Printf("[WARNING] (2) No body in request for workflowqueue")
|
||||||
resp.WriteHeader(http.StatusBadRequest)
|
resp.WriteHeader(http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
|
||||||
|
|
||||||
defer request.Body.Close()
|
|
||||||
body, err := ioutil.ReadAll(request.Body)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("[WARNING] (3) Failed reading body for workflowqueue")
|
|
||||||
resp.WriteHeader(401)
|
|
||||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var actionResult shuffle.ActionResult
|
|
||||||
err = json.Unmarshal(body, &actionResult)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("[ERROR] Failed shuffle.ActionResult unmarshaling (2): %s", err)
|
|
||||||
//resp.WriteHeader(401)
|
|
||||||
//resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err)))
|
|
||||||
//return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(actionResult.ExecutionId) == 0 {
|
|
||||||
log.Printf("[ERROR] No workflow execution id in action result. Data: %s", string(body))
|
|
||||||
resp.WriteHeader(400)
|
|
||||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "No workflow execution id in action result"}`)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1. Get the shuffle.WorkflowExecution(ExecutionId) from the database
|
|
||||||
// 2. if shuffle.ActionResult.Authentication != shuffle.WorkflowExecution.Authentication -> exit
|
|
||||||
// 3. Add to and update actionResult in workflowExecution
|
|
||||||
// 4. Push to db
|
|
||||||
// IF FAIL: Set executionstatus: abort or cancel
|
|
||||||
ctx := context.Background()
|
|
||||||
workflowExecution, err := shuffle.GetWorkflowExecution(ctx, actionResult.ExecutionId)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("[ERROR][%s] Failed getting execution (workflowqueue) %s: %s", actionResult.ExecutionId, actionResult.ExecutionId, err)
|
|
||||||
resp.WriteHeader(500)
|
|
||||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed getting execution ID %s because it doesn't exist locally."}`, actionResult.ExecutionId)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if workflowExecution.Authorization != actionResult.Authorization {
|
|
||||||
log.Printf("[ERROR][%s] Bad authorization key when updating node (workflowQueue). Want: %s, Have: %s", actionResult.ExecutionId, workflowExecution.Authorization, actionResult.Authorization)
|
|
||||||
resp.WriteHeader(403)
|
|
||||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Bad authorization key"}`)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if workflowExecution.Status == "FINISHED" {
|
|
||||||
log.Printf("[DEBUG][%s] Workflowexecution is already FINISHED. No further action can be taken", workflowExecution.ExecutionId)
|
|
||||||
resp.WriteHeader(200)
|
|
||||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Workflowexecution is already finished because it has status %s. Lastnode: %s"}`, workflowExecution.Status, workflowExecution.LastNode)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if workflowExecution.Status == "ABORTED" || workflowExecution.Status == "FAILURE" {
|
|
||||||
log.Printf("[WARNING][%s] Workflowexecution already has status %s. No further action can be taken", workflowExecution.ExecutionId, workflowExecution.Status)
|
|
||||||
resp.WriteHeader(200)
|
|
||||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Workflowexecution is aborted because of %s with result %s and status %s"}`, workflowExecution.LastNode, workflowExecution.Result, workflowExecution.Status)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
retries := 0
|
|
||||||
retry, retriesok := request.URL.Query()["retries"]
|
|
||||||
if retriesok && len(retry) > 0 {
|
|
||||||
val, err := strconv.Atoi(retry[0])
|
|
||||||
if err == nil {
|
|
||||||
retries = val
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("[DEBUG][%s] Action: Received, Label: '%s', Action: '%s', Status: %s, Run status: %s, Extra=Retry:%d", workflowExecution.ExecutionId, actionResult.Action.Label, actionResult.Action.AppName, actionResult.Status, workflowExecution.Status, retries)
|
defer request.Body.Close()
|
||||||
|
body, err := ioutil.ReadAll(request.Body)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[WARNING] (3) Failed reading body for workflowqueue")
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
//results = append(results, actionResult)
|
var actionResult shuffle.ActionResult
|
||||||
//log.Printf("[INFO][%s] Time to execute %s (%s) with app %s:%s, function %s, env %s with %d parameters.", workflowExecution.ExecutionId, action.ID, action.Label, action.AppName, action.AppVersion, action.Name, action.Environment, len(action.Parameters))
|
err = json.Unmarshal(body, &actionResult)
|
||||||
//log.Printf("[DEBUG][%s] In workflowQueue with transaction", workflowExecution.ExecutionId)
|
if err != nil {
|
||||||
runWorkflowExecutionTransaction(ctx, 0, workflowExecution.ExecutionId, actionResult, resp)
|
log.Printf("[ERROR] Failed shuffle.ActionResult unmarshaling (2): %s", err)
|
||||||
|
//resp.WriteHeader(401)
|
||||||
|
//resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err)))
|
||||||
|
//return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(actionResult.ExecutionId) == 0 {
|
||||||
|
log.Printf("[ERROR] No workflow execution id in action result. Data: %s", string(body))
|
||||||
|
resp.WriteHeader(400)
|
||||||
|
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "No workflow execution id in action result"}`)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. Get the shuffle.WorkflowExecution(ExecutionId) from the database
|
||||||
|
// 2. if shuffle.ActionResult.Authentication != shuffle.WorkflowExecution.Authentication -> exit
|
||||||
|
// 3. Add to and update actionResult in workflowExecution
|
||||||
|
// 4. Push to db
|
||||||
|
// IF FAIL: Set executionstatus: abort or cancel
|
||||||
|
ctx := context.Background()
|
||||||
|
workflowExecution, err := shuffle.GetWorkflowExecution(ctx, actionResult.ExecutionId)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[ERROR][%s] Failed getting execution (workflowqueue) %s: %s", actionResult.ExecutionId, actionResult.ExecutionId, err)
|
||||||
|
resp.WriteHeader(500)
|
||||||
|
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed getting execution ID %s because it doesn't exist locally."}`, actionResult.ExecutionId)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if workflowExecution.Authorization != actionResult.Authorization {
|
||||||
|
log.Printf("[ERROR][%s] Bad authorization key when updating node (workflowQueue). Want: %s, Have: %s", actionResult.ExecutionId, workflowExecution.Authorization, actionResult.Authorization)
|
||||||
|
resp.WriteHeader(403)
|
||||||
|
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Bad authorization key"}`)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if workflowExecution.Status == "FINISHED" {
|
||||||
|
log.Printf("[DEBUG][%s] Workflowexecution is already FINISHED. No further action can be taken", workflowExecution.ExecutionId)
|
||||||
|
resp.WriteHeader(200)
|
||||||
|
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Workflowexecution is already finished because it has status %s. Lastnode: %s"}`, workflowExecution.Status, workflowExecution.LastNode)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if workflowExecution.Status == "ABORTED" || workflowExecution.Status == "FAILURE" {
|
||||||
|
log.Printf("[WARNING][%s] Workflowexecution already has status %s. No further action can be taken", workflowExecution.ExecutionId, workflowExecution.Status)
|
||||||
|
resp.WriteHeader(200)
|
||||||
|
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Workflowexecution is aborted because of %s with result %s and status %s"}`, workflowExecution.LastNode, workflowExecution.Result, workflowExecution.Status)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
retries := 0
|
||||||
|
retry, retriesok := request.URL.Query()["retries"]
|
||||||
|
if retriesok && len(retry) > 0 {
|
||||||
|
val, err := strconv.Atoi(retry[0])
|
||||||
|
if err == nil {
|
||||||
|
retries = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG][%s] Action: Received, Label: '%s', Action: '%s', Status: %s, Run status: %s, Extra=Retry:%d", workflowExecution.ExecutionId, actionResult.Action.Label, actionResult.Action.AppName, actionResult.Status, workflowExecution.Status, retries)
|
||||||
|
|
||||||
|
//results = append(results, actionResult)
|
||||||
|
//log.Printf("[INFO][%s] Time to execute %s (%s) with app %s:%s, function %s, env %s with %d parameters.", workflowExecution.ExecutionId, action.ID, action.Label, action.AppName, action.AppVersion, action.Name, action.Environment, len(action.Parameters))
|
||||||
|
//log.Printf("[DEBUG][%s] In workflowQueue with transaction", workflowExecution.ExecutionId)
|
||||||
|
runWorkflowExecutionTransaction(ctx, 0, workflowExecution.ExecutionId, actionResult, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Will make sure transactions are always ran for an execution. This is recursive if it fails. Allowed to fail up to 5 times
|
// Will make sure transactions are always ran for an execution. This is recursive if it fails. Allowed to fail up to 5 times
|
||||||
|
|||||||
Reference in New Issue
Block a user