├── .github
├── dco.yml
├── labels-manage.yml
├── settings.xml
└── workflows
│ ├── build-snapshot-worker.yml
│ ├── ci-e2e.yml
│ ├── ci-pr.yml
│ ├── ci.yml
│ ├── issue-handler.yml
│ ├── label-manage.yml
│ ├── milestone-worker.yml
│ ├── next-dev-version-worker.yml
│ └── release-worker.yml
├── .gitignore
├── .mvn
└── wrapper
│ ├── maven-wrapper.jar
│ └── maven-wrapper.properties
├── LICENSE
├── README.md
├── README_DEV.md
├── SECURITY.md
├── appveyor.yml
├── master-ui-assets
├── header-logo-mobile.psd
├── header-logo.psd
├── images
│ ├── Powered by Sauce Labs badges white.svg
│ ├── browserstack-logo-600x315.png
│ └── saucelabs-logo-600x315.png
└── svg
│ ├── favicon
│ ├── 16x16.png
│ ├── 24x24.png
│ ├── 32x32.png
│ ├── 48x48.png
│ ├── 64x64.png
│ └── favicon-152.png
│ ├── scdf-logo-teal-white.svg
│ ├── scdf-logo-teal.svg
│ ├── spring-logo-teal.svg
│ └── spring-logo-transparent.svg
├── mvnw
├── mvnw.cmd
├── pom.xml
├── run-maven-build.sh
├── run-npm-e2e-browserstack.sh
├── run-npm-e2e-local.sh
├── run-npm-e2e-saucelabs.sh
├── run-npm-test-browserstack.sh
├── run-npm-test-saucelabs.sh
├── src
└── main
│ └── resources
│ └── public
│ └── favicon.ico
└── ui
├── .browserslistrc
├── .editorconfig
├── .eslintrc.json
├── .prettierrc
├── .vscode
└── launch.json
├── README.md
├── angular.json
├── cypress-dc-local.json
├── cypress-dc-local.yml
├── cypress.json
├── cypress
├── fixtures
│ └── example.json
├── integration
│ ├── applications.spec.ts
│ ├── streams.spec.ts
│ └── tasks.spec.ts
├── plugins
│ └── index.ts
├── support
│ ├── commands.ts
│ ├── index.ts
│ └── navigation.ts
├── tsconfig.json
└── videos
│ └── applications.spec.ts.mp4
├── package-lock.json
├── package.json
├── proxy.conf.json
├── src
├── app
│ ├── about
│ │ ├── about.module.ts
│ │ ├── info
│ │ │ ├── info.component.html
│ │ │ ├── info.component.scss
│ │ │ ├── info.component.spec.ts
│ │ │ └── info.component.ts
│ │ ├── signpost
│ │ │ ├── signpost.component.html
│ │ │ ├── signpost.component.spec.ts
│ │ │ └── signpost.component.ts
│ │ └── user
│ │ │ ├── user.component.html
│ │ │ ├── user.component.spec.ts
│ │ │ └── user.component.ts
│ ├── app-routing.module.ts
│ ├── app.component.html
│ ├── app.component.spec.ts
│ ├── app.component.ts
│ ├── app.module.ts
│ ├── apps
│ │ ├── add
│ │ │ ├── add.compoment.spec.ts
│ │ │ ├── add.component.html
│ │ │ ├── add.component.scss
│ │ │ ├── add.component.ts
│ │ │ ├── add.validator.spec.ts
│ │ │ ├── add.validtor.ts
│ │ │ ├── props
│ │ │ │ ├── props.component.html
│ │ │ │ ├── props.component.spec.ts
│ │ │ │ └── props.component.ts
│ │ │ ├── register
│ │ │ │ ├── register.component.html
│ │ │ │ ├── register.component.spec.ts
│ │ │ │ └── register.component.ts
│ │ │ ├── uri
│ │ │ │ ├── uri.component.html
│ │ │ │ ├── uri.component.spec.ts
│ │ │ │ └── uri.component.ts
│ │ │ └── website-starters
│ │ │ │ ├── website-starters.component.html
│ │ │ │ ├── website-starters.component.spec.ts
│ │ │ │ └── website-starters.component.ts
│ │ ├── app
│ │ │ ├── app.component.html
│ │ │ ├── app.component.spec.ts
│ │ │ └── app.component.ts
│ │ ├── apps-routing.module.ts
│ │ ├── apps.component.html
│ │ ├── apps.component.spec.ts
│ │ ├── apps.component.ts
│ │ ├── apps.module.ts
│ │ ├── type.filter.spec.ts
│ │ ├── type.filter.ts
│ │ ├── unregister
│ │ │ ├── unregister.component.html
│ │ │ ├── unregister.component.spec.ts
│ │ │ └── unregister.component.ts
│ │ └── version
│ │ │ ├── version.component.html
│ │ │ ├── version.component.spec.ts
│ │ │ └── version.component.ts
│ ├── dev
│ │ ├── dashboard
│ │ │ ├── dashboard.component.html
│ │ │ ├── dashboard.component.scss
│ │ │ ├── dashboard.component.ts
│ │ │ ├── stream-create
│ │ │ │ ├── stream-create.component.html
│ │ │ │ └── stream-create.component.ts
│ │ │ └── task-create
│ │ │ │ ├── task-create.component.html
│ │ │ │ └── task-create.component.ts
│ │ ├── dev-routing.module.ts
│ │ └── dev.module.ts
│ ├── flo
│ │ ├── shared-flo.module.ts
│ │ ├── shared
│ │ │ ├── flo.scss
│ │ │ ├── graph-view
│ │ │ │ ├── graph-view.component.html
│ │ │ │ ├── graph-view.component.spec.ts
│ │ │ │ └── graph-view.component.ts
│ │ │ ├── properties-groups
│ │ │ │ ├── properties-groups-dialog.component.html
│ │ │ │ ├── properties-groups-dialog.component.scss
│ │ │ │ └── properties-groups-dialog.component.ts
│ │ │ ├── properties
│ │ │ │ ├── df.property.component.html
│ │ │ │ ├── df.property.component.ts
│ │ │ │ ├── properties-dialog.component.html
│ │ │ │ ├── properties-dialog.component.scss
│ │ │ │ ├── properties-dialog.component.ts
│ │ │ │ ├── properties.group.component.html
│ │ │ │ └── properties.group.component.ts
│ │ │ ├── service
│ │ │ │ ├── doc.service.ts
│ │ │ │ ├── parser.service.ts
│ │ │ │ ├── parser.spec.ts
│ │ │ │ ├── parser.ts
│ │ │ │ ├── tokenizer.spec.ts
│ │ │ │ └── tokenizer.ts
│ │ │ └── support
│ │ │ │ ├── app-metadata.ts
│ │ │ │ ├── app-ui-property.ts
│ │ │ │ ├── graph-node-properties-source.ts
│ │ │ │ ├── node-component.spec.ts
│ │ │ │ ├── node-component.ts
│ │ │ │ ├── properties-group-model.ts
│ │ │ │ ├── shape-component.ts
│ │ │ │ ├── shared-shapes.ts
│ │ │ │ ├── utils.ts
│ │ │ │ └── view-utils.ts
│ │ ├── stream-flo.module.ts
│ │ ├── stream
│ │ │ ├── component
│ │ │ │ ├── create-component.spec.ts
│ │ │ │ ├── create.component.ts
│ │ │ │ ├── runtime-view.component.spec.ts
│ │ │ │ ├── runtime-view.component.ts
│ │ │ │ ├── view.component.spec.ts
│ │ │ │ └── view.component.ts
│ │ │ ├── content-assist.service.ts
│ │ │ ├── dsl-sanitize.service.ts
│ │ │ ├── editor.service.spec.ts
│ │ │ ├── editor.service.ts
│ │ │ ├── graph-to-text.spec.ts
│ │ │ ├── graph-to-text.ts
│ │ │ ├── instance-dot
│ │ │ │ ├── instance-dot.component.html
│ │ │ │ ├── instance-dot.component.scss
│ │ │ │ ├── instance-dot.component.spec.ts
│ │ │ │ └── instance-dot.component.ts
│ │ │ ├── message-rate
│ │ │ │ ├── message-rate.component.html
│ │ │ │ ├── message-rate.component.scss
│ │ │ │ ├── message-rate.component.spec.ts
│ │ │ │ └── message-rate.component.ts
│ │ │ ├── metamodel.service.ts
│ │ │ ├── node-helper.service.ts
│ │ │ ├── node
│ │ │ │ ├── stream-node.component.html
│ │ │ │ ├── stream-node.component.scss
│ │ │ │ └── stream-node.component.ts
│ │ │ ├── properties-editor.service.ts
│ │ │ ├── properties
│ │ │ │ ├── stream-properties-dialog.component.ts
│ │ │ │ └── stream-properties-source.ts
│ │ │ ├── render.service.spec.ts
│ │ │ ├── render.service.ts
│ │ │ ├── support
│ │ │ │ ├── layout.ts
│ │ │ │ ├── shapes.ts
│ │ │ │ ├── utils.spec.ts
│ │ │ │ ├── utils.ts
│ │ │ │ └── view-helper.ts
│ │ │ ├── text-to-graph.spec.ts
│ │ │ └── text-to-graph.ts
│ │ ├── task-flo.module.ts
│ │ └── task
│ │ │ ├── component
│ │ │ ├── create.component.spec.ts
│ │ │ ├── create.component.ts
│ │ │ ├── view.component.spec.ts
│ │ │ └── view.component.ts
│ │ │ ├── content-assist.service.ts
│ │ │ ├── editor.service.spec.ts
│ │ │ ├── editor.service.ts
│ │ │ ├── metamodel.service.spec.ts
│ │ │ ├── metamodel.service.ts
│ │ │ ├── model
│ │ │ └── models.ts
│ │ │ ├── node
│ │ │ ├── task-node.component.html
│ │ │ ├── task-node.component.scss
│ │ │ └── task-node.component.ts
│ │ │ ├── properties
│ │ │ ├── task-properties-dialog-component.ts
│ │ │ └── task-properties-source.ts
│ │ │ ├── render.service.spec.ts
│ │ │ ├── render.service.ts
│ │ │ ├── support
│ │ │ ├── layout.spec.ts
│ │ │ ├── layout.ts
│ │ │ └── shapes.ts
│ │ │ ├── tools.service.spec.ts
│ │ │ └── tools.service.ts
│ ├── layout
│ │ ├── layout.module.ts
│ │ ├── logo
│ │ │ └── logo.component.ts
│ │ ├── nav
│ │ │ ├── nav.component.html
│ │ │ ├── nav.component.scss
│ │ │ └── nav.component.ts
│ │ └── theme
│ │ │ ├── dark-theme.ts
│ │ │ ├── default-theme.ts
│ │ │ ├── theme.service.ts
│ │ │ └── types.ts
│ ├── manage
│ │ ├── manage-routing.module.ts
│ │ ├── manage.module.ts
│ │ ├── records
│ │ │ ├── action.filter.spec.ts
│ │ │ ├── action.filter.ts
│ │ │ ├── operation.filter.spec.ts
│ │ │ ├── operation.filter.ts
│ │ │ ├── records.component.html
│ │ │ ├── records.component.spec.ts
│ │ │ └── records.component.ts
│ │ └── tools
│ │ │ ├── cleanup
│ │ │ ├── cleanup.component.html
│ │ │ ├── cleanup.component.spec.ts
│ │ │ └── cleanup.component.ts
│ │ │ ├── stream
│ │ │ ├── export.component.spec.ts
│ │ │ ├── export.component.ts
│ │ │ ├── import.component.spec.ts
│ │ │ └── import.component.ts
│ │ │ ├── task
│ │ │ ├── export.component.spec.ts
│ │ │ ├── export.component.ts
│ │ │ ├── import.component.spec.ts
│ │ │ └── import.component.ts
│ │ │ ├── tools.component.html
│ │ │ ├── tools.component.spec.ts
│ │ │ └── tools.component.ts
│ ├── reducers
│ │ └── reducer.ts
│ ├── security
│ │ ├── component
│ │ │ ├── authentication-required.component.spec.ts
│ │ │ ├── authentication-required.component.ts
│ │ │ ├── feature-disabled.component.spec.ts
│ │ │ ├── feature-disabled.component.ts
│ │ │ ├── roles-missing.component.spec.ts
│ │ │ └── roles-missing.component.ts
│ │ ├── directive
│ │ │ └── role.directive.ts
│ │ ├── security-routing.module.ts
│ │ ├── security.module.ts
│ │ ├── service
│ │ │ ├── security.service.spec.ts
│ │ │ └── security.service.ts
│ │ ├── store
│ │ │ ├── security.action.ts
│ │ │ ├── security.effect.spec.ts
│ │ │ ├── security.effect.ts
│ │ │ ├── security.reducer.spec.ts
│ │ │ └── security.reducer.ts
│ │ └── support
│ │ │ ├── security.guard.ts
│ │ │ └── security.interceptor.ts
│ ├── settings
│ │ ├── settings-routing.module.ts
│ │ ├── settings.module.ts
│ │ ├── settings.service.ts
│ │ ├── settings
│ │ │ ├── settings.component.html
│ │ │ ├── settings.component.spec.ts
│ │ │ └── settings.component.ts
│ │ └── store
│ │ │ ├── settings.action.ts
│ │ │ ├── settings.effect.spec.ts
│ │ │ ├── settings.effect.ts
│ │ │ ├── settings.reducer.spec.ts
│ │ │ └── settings.reducer.ts
│ ├── shared
│ │ ├── api
│ │ │ ├── about.service.spec.ts
│ │ │ ├── about.service.ts
│ │ │ ├── app.service.spec.ts
│ │ │ ├── app.service.ts
│ │ │ ├── job.service.spec.ts
│ │ │ ├── job.service.ts
│ │ │ ├── record.service.spec.ts
│ │ │ ├── record.service.ts
│ │ │ ├── runtime.service.spec.ts
│ │ │ ├── runtime.service.ts
│ │ │ ├── schedule.service.spec.ts
│ │ │ ├── schedule.service.ts
│ │ │ ├── stream.service.spec.ts
│ │ │ ├── stream.service.ts
│ │ │ ├── task.service.spec.ts
│ │ │ └── task.service.ts
│ │ ├── component
│ │ │ ├── card
│ │ │ │ ├── card.component.html
│ │ │ │ └── card.component.ts
│ │ │ ├── confirm
│ │ │ │ ├── confirm.component.html
│ │ │ │ └── confirm.component.ts
│ │ │ ├── datagrid
│ │ │ │ └── datagrid.component.ts
│ │ │ ├── key-value
│ │ │ │ ├── key-value.component.scss
│ │ │ │ ├── key-value.component.spec.ts
│ │ │ │ ├── key-value.component.ts
│ │ │ │ ├── key-value.interface.ts
│ │ │ │ ├── key-value.validator.spec.ts
│ │ │ │ └── key-value.validator.ts
│ │ │ ├── search
│ │ │ │ ├── search.component.html
│ │ │ │ ├── search.component.scss
│ │ │ │ ├── search.component.spec.ts
│ │ │ │ └── search.component.ts
│ │ │ ├── stream-dsl
│ │ │ │ ├── stream-dsl.component.spec.ts
│ │ │ │ └── stream-dsl.component.ts
│ │ │ └── toast
│ │ │ │ ├── toast.component.html
│ │ │ │ ├── toast.component.scss
│ │ │ │ └── toast.component.ts
│ │ ├── directive
│ │ │ ├── auto-resize.directive.ts
│ │ │ ├── focus.directive.ts
│ │ │ └── tippy.directive.ts
│ │ ├── filter
│ │ │ └── date
│ │ │ │ ├── date.filter.spec.ts
│ │ │ │ └── date.filter.ts
│ │ ├── grafana
│ │ │ ├── grafana.directive.spec.ts
│ │ │ ├── grafana.directive.ts
│ │ │ ├── grafana.service.spec.ts
│ │ │ └── grafana.service.ts
│ │ ├── model
│ │ │ ├── app.model.ts
│ │ │ ├── context.model.ts
│ │ │ ├── detailed-app.model.ts
│ │ │ ├── error.model.ts
│ │ │ ├── instance.model.ts
│ │ │ ├── job.model.ts
│ │ │ ├── metrics.model.ts
│ │ │ ├── page.model.ts
│ │ │ ├── platform.model.ts
│ │ │ ├── record.model.ts
│ │ │ ├── runtime.model.ts
│ │ │ ├── schedule.model.ts
│ │ │ ├── security.model.ts
│ │ │ ├── setting.model.ts
│ │ │ ├── stream.model.ts
│ │ │ ├── task-execution.model.ts
│ │ │ └── task.model.ts
│ │ ├── pipe
│ │ │ ├── capitalize.pipe.ts
│ │ │ ├── datagrid-column.pipe.ts
│ │ │ ├── datetime.pipe.ts
│ │ │ ├── duration.pipe.ts
│ │ │ └── order-by.pipe.ts
│ │ ├── service
│ │ │ ├── clipboard-copy.service.ts
│ │ │ ├── context.service.ts
│ │ │ ├── group.service.ts
│ │ │ ├── import-export.service.ts
│ │ │ ├── local-storage.service.ts
│ │ │ ├── logger.service.ts
│ │ │ ├── modal.service.ts
│ │ │ └── notification.service.ts
│ │ ├── shared.module.ts
│ │ ├── store
│ │ │ ├── about.action.ts
│ │ │ ├── about.reducer.ts
│ │ │ ├── about.support.ts
│ │ │ ├── context.action.ts
│ │ │ └── context.reducer.ts
│ │ ├── support
│ │ │ ├── encoder.utils.ts
│ │ │ ├── error.utils.ts
│ │ │ └── http.utils.ts
│ │ └── wavefront
│ │ │ ├── wavefront.directive.spec.ts
│ │ │ ├── wavefront.directive.ts
│ │ │ ├── wavefront.service.spec.ts
│ │ │ └── wavefront.service.ts
│ ├── streams
│ │ ├── runtime
│ │ │ ├── details
│ │ │ │ ├── details.component.html
│ │ │ │ ├── details.component.scss
│ │ │ │ ├── details.component.spec.ts
│ │ │ │ └── details.component.ts
│ │ │ ├── runtime.component.html
│ │ │ ├── runtime.component.spec.ts
│ │ │ └── runtime.component.ts
│ │ ├── streams-routing.module.ts
│ │ ├── streams.module.ts
│ │ └── streams
│ │ │ ├── clone
│ │ │ ├── clone.component.html
│ │ │ ├── clone.component.spec.ts
│ │ │ └── clone.component.ts
│ │ │ ├── create
│ │ │ ├── create.component.html
│ │ │ ├── create.component.spec.ts
│ │ │ └── create.component.ts
│ │ │ ├── deploy
│ │ │ ├── builder
│ │ │ │ ├── builder.component.html
│ │ │ │ ├── builder.component.scss
│ │ │ │ ├── builder.component.spec.ts
│ │ │ │ ├── builder.component.ts
│ │ │ │ └── errors
│ │ │ │ │ ├── errors.component.html
│ │ │ │ │ ├── errors.component.scss
│ │ │ │ │ ├── errors.component.spec.ts
│ │ │ │ │ └── errors.component.ts
│ │ │ ├── deploy.component.html
│ │ │ ├── deploy.component.ts
│ │ │ ├── free-text
│ │ │ │ ├── free-text.component.html
│ │ │ │ ├── free-text.component.scss
│ │ │ │ ├── free-text.component.spec.ts
│ │ │ │ └── free-text.component.ts
│ │ │ ├── stream-deploy.component.spec.ts
│ │ │ ├── stream-deploy.validator.spec.ts
│ │ │ └── stream-deploy.validator.ts
│ │ │ ├── destroy
│ │ │ ├── destroy.component.html
│ │ │ ├── destroy.component.spec.ts
│ │ │ └── destroy.component.ts
│ │ │ ├── multi-deploy
│ │ │ ├── multi-deploy.component.html
│ │ │ ├── multi-deploy.component.scss
│ │ │ ├── multi-deploy.component.spec.ts
│ │ │ └── multi-deploy.component.ts
│ │ │ ├── rollback
│ │ │ ├── rollback.component.html
│ │ │ ├── rollback.component.spec.ts
│ │ │ └── rollback.component.ts
│ │ │ ├── scale
│ │ │ ├── scale.component.html
│ │ │ ├── scale.component.scss
│ │ │ ├── scale.component.spec.ts
│ │ │ └── scale.component.ts
│ │ │ ├── status
│ │ │ ├── status.component.spec.ts
│ │ │ └── status.component.ts
│ │ │ ├── stream-deploy.service.spec.ts
│ │ │ ├── stream-deploy.service.ts
│ │ │ ├── stream
│ │ │ ├── stream.component.html
│ │ │ ├── stream.component.spec.ts
│ │ │ └── stream.component.ts
│ │ │ ├── streams.component.html
│ │ │ ├── streams.component.spec.ts
│ │ │ ├── streams.component.ts
│ │ │ └── undeploy
│ │ │ ├── undeploy.component.html
│ │ │ ├── undeploy.component.spec.ts
│ │ │ └── undeploy.component.ts
│ ├── tasks-jobs
│ │ ├── executions
│ │ │ ├── cleanup
│ │ │ │ ├── cleanup.component.html
│ │ │ │ ├── cleanup.component.spec.ts
│ │ │ │ └── cleanup.component.ts
│ │ │ ├── execution
│ │ │ │ ├── execution.component.html
│ │ │ │ ├── execution.component.spec.ts
│ │ │ │ ├── execution.component.ts
│ │ │ │ └── log
│ │ │ │ │ └── log.component.ts
│ │ │ ├── executions.component.html
│ │ │ ├── executions.component.spec.ts
│ │ │ ├── executions.component.ts
│ │ │ └── stop
│ │ │ │ ├── stop.component.html
│ │ │ │ ├── stop.component.spec.ts
│ │ │ │ └── stop.component.ts
│ │ ├── jobs
│ │ │ ├── execution
│ │ │ │ ├── execution.component.html
│ │ │ │ ├── execution.component.spec.ts
│ │ │ │ └── execution.component.ts
│ │ │ ├── jobs.component.html
│ │ │ ├── jobs.component.spec.ts
│ │ │ ├── jobs.component.ts
│ │ │ └── step
│ │ │ │ ├── step.component.html
│ │ │ │ ├── step.component.spec.ts
│ │ │ │ └── step.component.ts
│ │ ├── schedules
│ │ │ ├── create
│ │ │ │ ├── create.component.html
│ │ │ │ ├── create.component.spec.ts
│ │ │ │ ├── create.component.ts
│ │ │ │ ├── create.validator.spec.ts
│ │ │ │ └── create.validator.ts
│ │ │ ├── destroy
│ │ │ │ ├── destroy.component.html
│ │ │ │ ├── destroy.component.spec.ts
│ │ │ │ └── destroy.component.ts
│ │ │ ├── platform.filter.ts
│ │ │ ├── schedule
│ │ │ │ ├── schedule.component.html
│ │ │ │ ├── schedule.component.spec.ts
│ │ │ │ └── schedule.component.ts
│ │ │ ├── schedules.component.html
│ │ │ ├── schedules.component.spec.ts
│ │ │ └── schedules.component.ts
│ │ ├── tasks-jobs-routing.module.ts
│ │ ├── tasks-jobs.module.ts
│ │ └── tasks
│ │ │ ├── cleanup
│ │ │ ├── cleanup.component.html
│ │ │ ├── cleanup.component.spec.ts
│ │ │ └── cleanup.component.ts
│ │ │ ├── clone
│ │ │ ├── clone.component.html
│ │ │ ├── clone.component.spec.ts
│ │ │ └── clone.component.ts
│ │ │ ├── create
│ │ │ ├── create.component.html
│ │ │ ├── create.component.spec.ts
│ │ │ └── create.component.ts
│ │ │ ├── destroy
│ │ │ ├── destroy.component.html
│ │ │ ├── destroy.component.spec.ts
│ │ │ └── destroy.component.ts
│ │ │ ├── launch
│ │ │ ├── builder
│ │ │ │ ├── builder.component.html
│ │ │ │ ├── builder.component.scss
│ │ │ │ ├── builder.component.spec.ts
│ │ │ │ ├── builder.component.ts
│ │ │ │ └── errors
│ │ │ │ │ ├── errors.component.html
│ │ │ │ │ ├── errors.component.scss
│ │ │ │ │ ├── errors.component.spec.ts
│ │ │ │ │ └── errors.component.ts
│ │ │ ├── free-text
│ │ │ │ ├── free-text.component.html
│ │ │ │ ├── free-text.component.scss
│ │ │ │ ├── free-text.component.spec.ts
│ │ │ │ └── free-text.component.ts
│ │ │ ├── launch.component.html
│ │ │ ├── launch.component.spec.ts
│ │ │ ├── launch.component.ts
│ │ │ ├── task-launch.service.ts
│ │ │ └── task-launch.validator.ts
│ │ │ ├── task-prop.validator.spec.ts
│ │ │ ├── task-prop.validator.ts
│ │ │ ├── task
│ │ │ ├── task.component.html
│ │ │ ├── task.component.spec.ts
│ │ │ └── task.component.ts
│ │ │ ├── tasks.component.html
│ │ │ ├── tasks.component.spec.ts
│ │ │ └── tasks.component.ts
│ ├── tests
│ │ ├── api
│ │ │ ├── about.service.mock.ts
│ │ │ ├── app.service.mock.ts
│ │ │ ├── content-assist.service.spec.ts
│ │ │ ├── job.service.mock.ts
│ │ │ ├── record.service.mock.ts
│ │ │ ├── runtime.service.mock.spec.ts
│ │ │ ├── schedule.service.mock.ts
│ │ │ ├── security.service.mock.ts
│ │ │ ├── stream.service.mock.ts
│ │ │ └── task.service.mock.ts
│ │ ├── data
│ │ │ ├── about.ts
│ │ │ ├── app.ts
│ │ │ ├── job.ts
│ │ │ ├── record.ts
│ │ │ ├── runtime.ts
│ │ │ ├── schedule.ts
│ │ │ ├── security.ts
│ │ │ ├── stream-deploy.ts
│ │ │ ├── stream.ts
│ │ │ └── task.ts
│ │ └── service
│ │ │ ├── app.service.mock.ts
│ │ │ ├── context.service.mock.ts
│ │ │ ├── grafana.service.mock.ts
│ │ │ ├── group.service.mock.ts
│ │ │ ├── import-export.service.mock.ts
│ │ │ ├── notification.service.mock.ts
│ │ │ ├── settings.service.mock.ts
│ │ │ ├── stream-deploy.service.mock.ts
│ │ │ ├── stream.service.mock.ts
│ │ │ ├── task-launch.service.mock.ts
│ │ │ └── task-tools.service.mock.ts
│ └── url-utilities.service.ts
├── assets
│ ├── .gitkeep
│ ├── i18n
│ │ ├── de.json
│ │ ├── en.json
│ │ └── ru.json
│ └── img
│ │ ├── api.png
│ │ ├── app.svg
│ │ ├── chevron-down-white.svg
│ │ ├── chevron-down.svg
│ │ ├── chevron-left-white.svg
│ │ ├── chevron-left.svg
│ │ ├── cog.svg
│ │ ├── delete.svg
│ │ ├── documentation.png
│ │ ├── error.svg
│ │ ├── forum.png
│ │ ├── github.png
│ │ ├── processor.svg
│ │ ├── project-page.png
│ │ ├── shell.png
│ │ ├── sink.svg
│ │ ├── source.svg
│ │ ├── tap.svg
│ │ ├── tracker.png
│ │ └── unknown.svg
├── environments
│ ├── environment.prod.ts
│ └── environment.ts
├── favicon.ico
├── index.html
├── karma-browserstack.conf.js
├── karma-saucelabs.conf.js
├── karma.conf.js
├── logout-success-oauth.html
├── main.ts
├── polyfills.ts
├── scss
│ └── styles.scss
├── styles.scss
├── test.ts
├── tsconfig.app.json
└── tsconfig.spec.json
└── tsconfig.json
/.github/dco.yml:
--------------------------------------------------------------------------------
1 | require:
2 | members: false
3 |
--------------------------------------------------------------------------------
/.github/workflows/ci-e2e.yml:
--------------------------------------------------------------------------------
1 | name: CI E2E
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | jobs:
7 | e2e:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - uses: actions/checkout@v2
11 | - uses: actions/setup-node@v2
12 | with:
13 | node-version: '14'
14 | - name: Start scdf
15 | working-directory: ui
16 | run: |
17 | docker-compose -f cypress-dc-local.yml up -d
18 | - uses: cypress-io/github-action@v2
19 | with:
20 | working-directory: ui
21 | config-file: cypress-dc-local.json
22 | browser: chrome
23 | headless: true
24 | - uses: actions/upload-artifact@v4
25 | if: failure()
26 | with:
27 | name: cypress-artifacts
28 | path: |
29 | ui/cypress/screenshots/
30 | ui/cypress/videos/
31 |
--------------------------------------------------------------------------------
/.github/workflows/ci-pr.yml:
--------------------------------------------------------------------------------
1 | name: CI PRs
2 |
3 | on:
4 | pull_request:
5 |
6 | jobs:
7 | build:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - uses: actions/checkout@v2
11 | # cache maven repo
12 | - uses: actions/cache@v3
13 | with:
14 | path: ~/.m2/repository
15 | key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
16 | restore-keys: |
17 | ${{ runner.os }}-m2-
18 | # jdk8
19 | - uses: actions/setup-java@v1
20 | with:
21 | java-version: 1.8
22 | # build
23 | - name: Build
24 | run: |
25 | ./mvnw -U -B clean package
26 | # clean m2 cache
27 | - name: Clean cache
28 | run: |
29 | find ~/.m2/repository -type d -name '*SNAPSHOT' | xargs rm -fr
30 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | workflow_dispatch:
5 | push:
6 | paths-ignore:
7 | - '.github/**'
8 |
9 | jobs:
10 | build:
11 | if: github.repository_owner == 'spring-cloud'
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v2
15 | # cache maven repo
16 | - uses: actions/cache@v3
17 | with:
18 | path: ~/.m2/repository
19 | key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
20 | restore-keys: |
21 | ${{ runner.os }}-m2-
22 | # jdk8
23 | - uses: actions/setup-java@v1
24 | with:
25 | java-version: 1.8
26 | # jfrog cli
27 | - uses: jfrog/setup-jfrog-cli@v3
28 | env:
29 | JF_URL: 'https://repo.spring.io'
30 | JF_ENV_SPRING: ${{ secrets.JF_ARTIFACTORY_SPRING }}
31 | # setup frog cli
32 | - name: Configure JFrog Cli
33 | run: |
34 | jfrog mvnc --use-wrapper \
35 | --server-id-resolve=${{ vars.JF_SERVER_ID }} \
36 | --server-id-deploy=${{ vars.JF_SERVER_ID }} \
37 | --repo-resolve-releases=libs-milestone \
38 | --repo-resolve-snapshots=libs-snapshot \
39 | --repo-deploy-releases=libs-release-local \
40 | --repo-deploy-snapshots=libs-snapshot-local
41 | echo JFROG_CLI_BUILD_NAME=spring-cloud-dataflow-ui-main >> $GITHUB_ENV
42 | echo JFROG_CLI_BUILD_NUMBER=$GITHUB_RUN_NUMBER >> $GITHUB_ENV
43 | # build and publish
44 | - name: Build and Publish
45 | run: |
46 | jfrog mvn -U -B clean install
47 | jfrog rt build-publish
48 | # clean m2 cache
49 | - name: Clean cache
50 | run: |
51 | find ~/.m2/repository -type d -name '*SNAPSHOT' | xargs rm -fr
52 |
--------------------------------------------------------------------------------
/.github/workflows/label-manage.yml:
--------------------------------------------------------------------------------
1 | name: Labels Manage
2 |
3 | on:
4 | push:
5 | branches:
6 | - 'main'
7 | paths:
8 | - '.github/labels-manage.yml'
9 | - '.github/workflows/label-manage.yml'
10 | workflow_dispatch:
11 |
12 | jobs:
13 | labeler:
14 | runs-on: ubuntu-latest
15 | steps:
16 | - uses: actions/checkout@v2
17 | - name: Manage Labels
18 | uses: crazy-max/ghaction-github-labeler@v3
19 | with:
20 | github-token: ${{ secrets.GITHUB_TOKEN }}
21 | yaml-file: .github/labels-manage.yml
22 | dry-run: false
23 | skip-delete: true
24 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ui/.bower_cache
2 | ui/bower_components
3 | ui/node
4 | ui/node_modules
5 | ui/app/lib/
6 | ui/app/styles/main.css
7 | ui/app/styles/main.css.map
8 | ui/.tmp
9 | ui/documentation/
10 | ui/dist
11 | ui/tmp
12 | ui/out-tsc
13 | ui/.c9/
14 | ui/.angular/
15 |
16 | ui/.sass-cache
17 | ui/connect.lock
18 | ui/coverage
19 | ui/libpeerconnection.log
20 | ui/npm-debug.log
21 | ui/testem.log
22 | ui/local.log
23 | ui/typings
24 |
25 |
26 | src/main/resources/public/dashboard
27 | target/
28 | .classpath
29 | .project
30 | .settings/
31 | .idea
32 | *.iml
33 | *.ipr
34 | *.iws
35 | npm-debug.log
36 | *.launch
37 | *.sublime-workspace
38 | .DS_Store
39 | Thumbs.db
40 | .jfrog/
41 |
42 | # IDE - VSCode main
43 | .vscode/*
44 | !.vscode/settings.json
45 | !.vscode/tasks.json
46 | !.vscode/launch.json
47 | !.vscode/extensions.json
48 |
49 | # IDE - VSCode ui
50 | ui/.vscode/*
51 | !ui/.vscode/settings.json
52 | !ui/.vscode/tasks.json
53 | !ui/.vscode/launch.json
54 | !ui/.vscode/extensions.json
55 |
56 | # Cypress e2e tests
57 | ui/cypress/videos
58 | ui/cypress/downloads
59 | ui/cypress/screenshots
60 |
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spring-attic/spring-cloud-dataflow-ui/c31733bc9be66e9014690a5c0b9682b19518368e/.mvn/wrapper/maven-wrapper.jar
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
3 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 | ## Reporting a Vulnerability
3 |
4 | If you think you have found a security vulnerability, please **DO NOT** disclose it publicly until we’ve had a chance to fix it.
5 | Please don’t report security vulnerabilities using GitHub issues, instead head over to https://spring.io/security-policy and learn how to disclose them responsibly.
6 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | version: '{build}'
2 | skip_tags: true
3 | clone_depth: 10
4 | environment:
5 | matrix:
6 | - JAVA_HOME: C:\Program Files\Java\jdk1.8.0
7 | branches:
8 | only:
9 | - master
10 | except:
11 | - gh-pages
12 | os: Windows Server 2012
13 | install:
14 | - cmd: SET PATH=C:\maven\apache-maven-3.2.5\bin;%JAVA_HOME%\bin;%PATH:C:\Ruby193\bin;=%
15 | - cmd: SET MAVEN_OPTS=-XX:MaxPermSize=2g -Xmx4g
16 | - cmd: SET JAVA_OPTS=-XX:MaxPermSize=2g -Xmx4g
17 | - cmd: java -version
18 | build_script:
19 | - mvnw clean install --batch-mode -Dmaven.test.skip=false
20 | cache:
21 | - C:\Users\appveyor\.m2
22 |
--------------------------------------------------------------------------------
/master-ui-assets/header-logo-mobile.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spring-attic/spring-cloud-dataflow-ui/c31733bc9be66e9014690a5c0b9682b19518368e/master-ui-assets/header-logo-mobile.psd
--------------------------------------------------------------------------------
/master-ui-assets/header-logo.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spring-attic/spring-cloud-dataflow-ui/c31733bc9be66e9014690a5c0b9682b19518368e/master-ui-assets/header-logo.psd
--------------------------------------------------------------------------------
/master-ui-assets/images/browserstack-logo-600x315.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spring-attic/spring-cloud-dataflow-ui/c31733bc9be66e9014690a5c0b9682b19518368e/master-ui-assets/images/browserstack-logo-600x315.png
--------------------------------------------------------------------------------
/master-ui-assets/images/saucelabs-logo-600x315.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spring-attic/spring-cloud-dataflow-ui/c31733bc9be66e9014690a5c0b9682b19518368e/master-ui-assets/images/saucelabs-logo-600x315.png
--------------------------------------------------------------------------------
/master-ui-assets/svg/favicon/16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spring-attic/spring-cloud-dataflow-ui/c31733bc9be66e9014690a5c0b9682b19518368e/master-ui-assets/svg/favicon/16x16.png
--------------------------------------------------------------------------------
/master-ui-assets/svg/favicon/24x24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spring-attic/spring-cloud-dataflow-ui/c31733bc9be66e9014690a5c0b9682b19518368e/master-ui-assets/svg/favicon/24x24.png
--------------------------------------------------------------------------------
/master-ui-assets/svg/favicon/32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spring-attic/spring-cloud-dataflow-ui/c31733bc9be66e9014690a5c0b9682b19518368e/master-ui-assets/svg/favicon/32x32.png
--------------------------------------------------------------------------------
/master-ui-assets/svg/favicon/48x48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spring-attic/spring-cloud-dataflow-ui/c31733bc9be66e9014690a5c0b9682b19518368e/master-ui-assets/svg/favicon/48x48.png
--------------------------------------------------------------------------------
/master-ui-assets/svg/favicon/64x64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spring-attic/spring-cloud-dataflow-ui/c31733bc9be66e9014690a5c0b9682b19518368e/master-ui-assets/svg/favicon/64x64.png
--------------------------------------------------------------------------------
/master-ui-assets/svg/favicon/favicon-152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spring-attic/spring-cloud-dataflow-ui/c31733bc9be66e9014690a5c0b9682b19518368e/master-ui-assets/svg/favicon/favicon-152.png
--------------------------------------------------------------------------------
/master-ui-assets/svg/spring-logo-transparent.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/run-maven-build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -ev
3 | mvn clean package
4 |
--------------------------------------------------------------------------------
/run-npm-e2e-browserstack.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -ev
3 | cd ui
4 | mkdir .docker
5 | cd .docker
6 | curl -O https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/v$DATAFLOW_VERSION/spring-cloud-dataflow-server/docker-compose.yml
7 | docker-compose up --no-start
8 | cd ..
9 | npm install
10 | npm run e2e-browserstack-local
11 | cd ..
12 |
--------------------------------------------------------------------------------
/run-npm-e2e-local.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -ev
3 | cd ui
4 | mkdir .docker
5 | cd .docker
6 | curl -O https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/v$DATAFLOW_VERSION/spring-cloud-dataflow-server/docker-compose.yml
7 | docker-compose up --no-start
8 | cd ..
9 | npm install
10 | npm run e2e
11 | cd ..
12 |
--------------------------------------------------------------------------------
/run-npm-e2e-saucelabs.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -ev
3 | cd ui
4 | mkdir .docker
5 | cd .docker
6 | curl -O https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/v$DATAFLOW_VERSION/spring-cloud-dataflow-server/docker-compose.yml
7 | docker-compose up --no-start
8 | cd ..
9 | npm install
10 | npm run e2e-saucelabs-local
11 | cd ..
12 |
--------------------------------------------------------------------------------
/run-npm-test-browserstack.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -ev
3 | cd ui
4 | npm install
5 | npm run test-browserstack-local
6 | cd ..
--------------------------------------------------------------------------------
/run-npm-test-saucelabs.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -ev
3 | cd ui
4 | npm install
5 | npm run test-saucelabs-local
6 | cd ..
--------------------------------------------------------------------------------
/src/main/resources/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spring-attic/spring-cloud-dataflow-ui/c31733bc9be66e9014690a5c0b9682b19518368e/src/main/resources/public/favicon.ico
--------------------------------------------------------------------------------
/ui/.browserslistrc:
--------------------------------------------------------------------------------
1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
2 | # For additional information regarding the format and rule options, please see:
3 | # https://github.com/browserslist/browserslist#queries
4 |
5 | # You can see what browsers were selected by your queries by running:
6 | # npx browserslist
7 |
8 | > 0.5%
9 | last 2 versions
10 | Firefox ESR
11 | not dead
12 | not IE 9-11 # For IE 9-11 support, remove 'not'.
13 | not ios_saf 15.2-15.3
14 | not safari 15.2-15.3
--------------------------------------------------------------------------------
/ui/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see https://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 | quote_type = single
11 |
12 | [*.md]
13 | max_line_length = off
14 | trim_trailing_whitespace = false
15 |
--------------------------------------------------------------------------------
/ui/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "none",
4 | "arrowParens": "avoid",
5 | "printWidth": 120,
6 | "bracketSpacing": false,
7 | "overrides": [
8 | {
9 | "files": "*.{component.html}",
10 | "options": { "parser": "angular" }
11 | },
12 | {
13 | "files": "*.{html}",
14 | "options": { "parser": "html" }
15 | }
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/ui/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "type": "chrome",
6 | "request": "launch",
7 | "name": "Launch Chrome against localhost",
8 | "url": "http://localhost:4200",
9 | "webRoot": "${workspaceFolder}"
10 | }
11 | ]
12 | }
--------------------------------------------------------------------------------
/ui/cypress-dc-local.json:
--------------------------------------------------------------------------------
1 | {
2 | "integrationFolder": "cypress/integration",
3 | "supportFile": "cypress/support/index.ts",
4 | "videosFolder": "cypress/videos",
5 | "screenshotsFolder": "cypress/screenshots",
6 | "pluginsFile": "cypress/plugins/index.ts",
7 | "fixturesFolder": "cypress/fixtures",
8 | "baseUrl": "http://localhost:9393/dashboard/index.html",
9 | "chromeWebSecurity": false,
10 | "defaultCommandTimeout": 60000,
11 | "pageLoadTimeout": 60000,
12 | "viewportWidth": 1400,
13 | "viewportHeight": 800
14 | }
15 |
--------------------------------------------------------------------------------
/ui/cypress-dc-local.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 |
3 | services:
4 |
5 | rabbitmq-server:
6 | image: rabbitmq:management
7 | ports:
8 | - "5672:5672"
9 | - "15672:15672"
10 |
11 | skipper-server:
12 | image: springcloud/spring-cloud-skipper-server:2.9.0-SNAPSHOT
13 | ports:
14 | - "7577:7577"
15 | - "8888:8888"
16 | - "8889:8889"
17 | - "20000-20099:20000-20099"
18 | environment:
19 | - SPRING_CLOUD_SKIPPER_SERVER_PLATFORM_LOCAL_ACCOUNTS_DEFAULT_PORTRANGE_LOW=20000
20 | - SPRING_CLOUD_SKIPPER_SERVER_PLATFORM_LOCAL_ACCOUNTS_DEFAULT_PORTRANGE_HIGH=20100
21 |
22 | dataflow-server:
23 | image: springcloud/spring-cloud-dataflow-server:2.10.0-SNAPSHOT
24 | ports:
25 | - "9393:9393"
26 | - "20100-20199:20100-20199"
27 | environment:
28 | - MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE=*
29 | - SPRING_CLOUD_DATAFLOW_FEATURES_SCHEDULES_ENABLED=true
30 | - SPRING_CLOUD_SKIPPER_CLIENT_SERVER_URI=http://skipper-server:7577/api
31 | - SPRING_CLOUD_DATAFLOW_APPLICATIONPROPERTIES_STREAM_SPRING_RABBITMQ_ADDRESSES=rabbitmq-server:5672
32 |
--------------------------------------------------------------------------------
/ui/cypress.json:
--------------------------------------------------------------------------------
1 | {
2 | "integrationFolder": "cypress/integration",
3 | "supportFile": "cypress/support/index.ts",
4 | "videosFolder": "cypress/videos",
5 | "screenshotsFolder": "cypress/screenshots",
6 | "pluginsFile": "cypress/plugins/index.ts",
7 | "fixturesFolder": "cypress/fixtures",
8 | "baseUrl": "http://localhost:4200",
9 | "chromeWebSecurity": false,
10 | "defaultCommandTimeout": 60000,
11 | "pageLoadTimeout": 60000,
12 | "viewportWidth": 1400,
13 | "viewportHeight": 800
14 | }
15 |
--------------------------------------------------------------------------------
/ui/cypress/fixtures/example.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Using fixtures to represent data",
3 | "email": "hello@cypress.io",
4 | "body": "Fixtures are a great way to mock data for responses to routes"
5 | }
6 |
--------------------------------------------------------------------------------
/ui/cypress/plugins/index.ts:
--------------------------------------------------------------------------------
1 | // Plugins enable you to tap into, modify, or extend the internal behavior of Cypress
2 | // For more info, visit https://on.cypress.io/plugins-api
3 | module.exports = (on, config) => {};
4 |
--------------------------------------------------------------------------------
/ui/cypress/support/index.ts:
--------------------------------------------------------------------------------
1 | // Import commands.js
2 |
3 | import './navigation';
4 | import './commands';
5 |
--------------------------------------------------------------------------------
/ui/cypress/support/navigation.ts:
--------------------------------------------------------------------------------
1 | declare namespace Cypress {
2 | interface Chainable {
3 | apps(): void;
4 | streams(): void;
5 | tasks(): void;
6 | taskExecutions(): void;
7 | jobExecutions(): void;
8 | schedules(): void;
9 | auditRecords(): void;
10 | tools(): void;
11 | }
12 | }
13 |
14 | Cypress.Commands.add('apps', () => {
15 | cy.visit(Cypress.config('baseUrl'));
16 | cy.get('a[data-cy=navApplications]').click();
17 | cy.get('clr-spinner').should('not.exist');
18 | });
19 |
20 | Cypress.Commands.add('streams', () => {
21 | cy.visit(Cypress.config('baseUrl'));
22 | cy.get('a[data-cy=navStreams]').click();
23 | cy.get('clr-spinner').should('not.exist');
24 | });
25 |
26 | Cypress.Commands.add('tasks', () => {
27 | cy.visit(Cypress.config('baseUrl'));
28 | cy.get('a[data-cy=navTasks]').click();
29 | cy.get('clr-spinner').should('not.exist');
30 | });
31 |
32 | Cypress.Commands.add('taskExecutions', () => {
33 | cy.visit(Cypress.config('baseUrl'));
34 | cy.get('a[data-cy=navTaskExecutions]').click();
35 | cy.get('clr-spinner').should('not.exist');
36 | });
37 |
38 | Cypress.Commands.add('jobExecutions', () => {
39 | cy.visit(Cypress.config('baseUrl'));
40 | cy.get('a[data-cy=navJobExecutions]').click();
41 | cy.get('clr-spinner').should('not.exist');
42 | });
43 |
44 | Cypress.Commands.add('schedules', () => {
45 | cy.visit(Cypress.config('baseUrl'));
46 | cy.get('a[data-cy=navSchedules]').click();
47 | cy.get('clr-spinner').should('not.exist');
48 | });
49 |
50 | Cypress.Commands.add('auditRecords', () => {
51 | cy.visit(Cypress.config('baseUrl'));
52 | cy.get('a[data-cy=navAuditRecords]').click();
53 | cy.get('clr-spinner').should('not.exist');
54 | });
55 |
56 | Cypress.Commands.add('tools', () => {
57 | cy.visit(Cypress.config('baseUrl'));
58 | cy.get('a[data-cy=navTools]').click();
59 | cy.get('clr-spinner').should('not.exist');
60 | });
61 |
--------------------------------------------------------------------------------
/ui/cypress/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "include": ["**/*.ts"],
4 | "compilerOptions": {
5 | "sourceMap": false,
6 | "types": ["cypress"]
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/ui/cypress/videos/applications.spec.ts.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spring-attic/spring-cloud-dataflow-ui/c31733bc9be66e9014690a5c0b9682b19518368e/ui/cypress/videos/applications.spec.ts.mp4
--------------------------------------------------------------------------------
/ui/src/app/about/about.module.ts:
--------------------------------------------------------------------------------
1 | import {NgModule} from '@angular/core';
2 | import {CommonModule} from '@angular/common';
3 | import {InfoComponent} from './info/info.component';
4 | import {ClarityModule} from '@clr/angular';
5 | import {SignpostComponent} from './signpost/signpost.component';
6 | import {UserComponent} from './user/user.component';
7 | import {StoreModule} from '@ngrx/store';
8 | import * as fromAbout from '../shared/store/about.reducer';
9 | import {TranslateModule} from '@ngx-translate/core';
10 |
11 | @NgModule({
12 | declarations: [InfoComponent, SignpostComponent, UserComponent],
13 | imports: [
14 | CommonModule,
15 | ClarityModule,
16 | StoreModule.forFeature(fromAbout.aboutFeatureKey, fromAbout.reducer),
17 | TranslateModule
18 | ],
19 | exports: [SignpostComponent, UserComponent]
20 | })
21 | export class AboutModule {}
22 |
--------------------------------------------------------------------------------
/ui/src/app/about/info/info.component.scss:
--------------------------------------------------------------------------------
1 | .modal-about {
2 | clr-icon {
3 | color: var(--clr-global-font-color);
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/ui/src/app/about/info/info.component.ts:
--------------------------------------------------------------------------------
1 | import {Component, ElementRef, Input, OnInit, ViewChild} from '@angular/core';
2 | import {AboutState} from '../../shared/store/about.reducer';
3 | import {AboutService} from '../../shared/api/about.service';
4 | import {NotificationService} from '../../shared/service/notification.service';
5 | import {ClipboardCopyService} from '../../shared/service/clipboard-copy.service';
6 | import {JsonPipe} from '@angular/common';
7 | import {TranslateService} from '@ngx-translate/core';
8 |
9 | @Component({
10 | selector: 'app-about-info',
11 | templateUrl: './info.component.html',
12 | styleUrls: ['./info.component.scss']
13 | })
14 | export class InfoComponent implements OnInit {
15 | loading = true;
16 | about: AboutState;
17 | @Input() isOpen = false;
18 | @ViewChild('container', {read: ElementRef, static: true}) container: ElementRef;
19 |
20 | constructor(
21 | private aboutService: AboutService,
22 | private clipboardCopyService: ClipboardCopyService,
23 | private notificationService: NotificationService,
24 | private translate: TranslateService
25 | ) {}
26 |
27 | ngOnInit(): void {
28 | this.aboutService.getAbout().subscribe(
29 | (about: AboutState) => {
30 | this.about = about;
31 | this.loading = false;
32 | },
33 | error => {
34 | this.notificationService.error(this.translate.instant('commons.message.error'), error);
35 | this.loading = false;
36 | }
37 | );
38 | }
39 |
40 | copyToClipboard(): void {
41 | if (this.about) {
42 | this.clipboardCopyService.executeCopy(new JsonPipe().transform(this.about), this.container.nativeElement);
43 | this.notificationService.success(
44 | this.translate.instant('commons.copyToClipboard'),
45 | this.translate.instant('about.info.message.copyContent')
46 | );
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/ui/src/app/about/signpost/signpost.component.ts:
--------------------------------------------------------------------------------
1 | import {Component, OnInit, ViewChild} from '@angular/core';
2 | import {InfoComponent} from '../info/info.component';
3 | import {ClrSignpostContent} from '@clr/angular';
4 | import {AboutState} from '../../shared/store/about.reducer';
5 | import {AboutService} from '../../shared/api/about.service';
6 | import {TranslateService} from '@ngx-translate/core';
7 | import {NotificationService} from '../../shared/service/notification.service';
8 |
9 | @Component({
10 | selector: 'app-about-signpost',
11 | templateUrl: './signpost.component.html'
12 | })
13 | export class SignpostComponent implements OnInit {
14 | loading = true;
15 | about: AboutState;
16 | @ViewChild('infoModal', {static: true}) infoModal: InfoComponent;
17 | @ViewChild('signpost') signpost: ClrSignpostContent;
18 |
19 | constructor(
20 | private aboutService: AboutService,
21 | private notificationService: NotificationService,
22 | private translate: TranslateService
23 | ) {}
24 |
25 | ngOnInit(): void {
26 | this.aboutService.getAbout().subscribe(
27 | (about: AboutState) => {
28 | this.about = about;
29 | this.loading = false;
30 | },
31 | error => {
32 | this.notificationService.error(this.translate.instant('commons.message.error'), error);
33 | this.loading = false;
34 | }
35 | );
36 | }
37 |
38 | more(): void {
39 | this.infoModal.isOpen = true;
40 | this.signpost.close();
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/ui/src/app/about/user/user.component.html:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
17 |
18 |
--------------------------------------------------------------------------------
/ui/src/app/about/user/user.component.spec.ts:
--------------------------------------------------------------------------------
1 | import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
2 | import {FormsModule} from '@angular/forms';
3 | import {ClarityModule} from '@clr/angular';
4 | import {RouterTestingModule} from '@angular/router/testing';
5 | import {UserComponent} from './user.component';
6 | import {NotificationServiceMock} from '../../tests/service/notification.service.mock';
7 | import {SecurityServiceMock} from '../../../app/tests/api/security.service.mock';
8 | import {TranslateTestingModule} from 'ngx-translate-testing';
9 | import TRANSLATIONS from '../../../assets/i18n/en.json';
10 |
11 | describe('UserComponent', () => {
12 | let component: UserComponent;
13 | let fixture: ComponentFixture;
14 |
15 | beforeEach(waitForAsync(() => {
16 | TestBed.configureTestingModule({
17 | declarations: [UserComponent],
18 | imports: [
19 | FormsModule,
20 | ClarityModule,
21 | TranslateTestingModule.withTranslations('en', TRANSLATIONS),
22 | RouterTestingModule.withRoutes([])
23 | ],
24 | providers: [SecurityServiceMock.provider, NotificationServiceMock.provider]
25 | }).compileComponents();
26 | }));
27 |
28 | beforeEach(() => {
29 | NotificationServiceMock.mock.clearAll();
30 | fixture = TestBed.createComponent(UserComponent);
31 | component = fixture.componentInstance;
32 | fixture.detectChanges();
33 | });
34 |
35 | it('should create', () => {
36 | expect(component).toBeTruthy();
37 | });
38 | });
39 |
--------------------------------------------------------------------------------
/ui/src/app/about/user/user.component.ts:
--------------------------------------------------------------------------------
1 | import {Component} from '@angular/core';
2 | import {Router} from '@angular/router';
3 | import {SecurityService} from '../../security/service/security.service';
4 | import {UrlUtilities} from '../../url-utilities.service';
5 | import {NotificationService} from '../../shared/service/notification.service';
6 | import {TranslateService} from '@ngx-translate/core';
7 |
8 | @Component({
9 | selector: 'app-user',
10 | templateUrl: './user.component.html'
11 | })
12 | export class UserComponent {
13 | loggedinUser$ = this.securityService.loggedinUser();
14 | baseApiUrl = UrlUtilities.calculateBaseApiUrl();
15 |
16 | constructor(
17 | private securityService: SecurityService,
18 | private router: Router,
19 | private notificationService: NotificationService,
20 | private translate: TranslateService
21 | ) {}
22 |
23 | logout(): void {
24 | this.securityService.logout().subscribe(
25 | security => {
26 | this.router.navigate(['/']);
27 | },
28 | error => {
29 | this.notificationService.error(this.translate.instant('commons.message.error'), error);
30 | }
31 | );
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/ui/src/app/app-routing.module.ts:
--------------------------------------------------------------------------------
1 | import {NgModule} from '@angular/core';
2 | import {Routes, RouterModule} from '@angular/router';
3 |
4 | const routes: Routes = [
5 | {
6 | path: '',
7 | pathMatch: 'full',
8 | redirectTo: 'apps'
9 | }
10 | ];
11 | @NgModule({
12 | imports: [RouterModule.forRoot(routes, {useHash: true})],
13 | exports: [RouterModule]
14 | })
15 | export class AppRoutingModule {}
16 |
--------------------------------------------------------------------------------
/ui/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
34 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/ui/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import {Component, ElementRef} from '@angular/core';
2 | import {SecurityService} from './security/service/security.service';
3 | import {ModalService} from './shared/service/modal.service';
4 | import {SettingsComponent} from './settings/settings/settings.component';
5 | import {UrlUtilities} from './url-utilities.service';
6 |
7 | @Component({
8 | selector: 'app-root',
9 | templateUrl: './app.component.html'
10 | })
11 | export class AppComponent {
12 | shouldProtect = this.securityService.shouldProtect();
13 | securityEnabled = this.securityService.securityEnabled();
14 | baseApiUrl = UrlUtilities.calculateBaseApiUrl();
15 |
16 | constructor(
17 | private securityService: SecurityService,
18 | private modalService: ModalService,
19 | public elementRef: ElementRef
20 | ) {}
21 |
22 | openSettings(): void {
23 | this.modalService.show(SettingsComponent);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/ui/src/app/apps/add/add.component.html:
--------------------------------------------------------------------------------
1 | {{ 'applications.main.addApplications' | translate }}
2 |
3 |
4 | {{ 'applications.add.registerOneOrMore' | translate }}
5 |
6 |
7 |
8 |
9 |
10 | {{ 'applications.add.importFromHttp' | translate }}
11 |
12 |
13 |
14 |
15 |
16 | {{
17 | 'applications.add.importFromFile' | translate
18 | }}
19 |
20 |
21 |
22 |
23 |
24 | {{ 'applications.add.importFromSpringDataFlow' | translate }}.
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/ui/src/app/apps/add/add.component.scss:
--------------------------------------------------------------------------------
1 | :host {
2 | p {
3 | padding: .6rem 0;
4 | margin: 0;
5 | }
6 | .clr-error .clr-select-wrapper:after, .clr-success .clr-select-wrapper:after {
7 | right: 0;
8 | }
9 | h1.title {
10 | margin-bottom: 1rem;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/ui/src/app/apps/add/add.component.ts:
--------------------------------------------------------------------------------
1 | import {Component} from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-add',
5 | templateUrl: './add.component.html',
6 | styleUrls: ['./add.component.scss']
7 | })
8 | export class AddComponent {
9 | constructor() {}
10 | }
11 |
--------------------------------------------------------------------------------
/ui/src/app/apps/add/uri/uri.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
32 |
33 | {{ 'applications.add.uri.import' | translate }}
34 |
35 |
{{ 'applications.add.uri.importing' | translate }}
36 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/ui/src/app/apps/apps-routing.module.ts:
--------------------------------------------------------------------------------
1 | import {NgModule} from '@angular/core';
2 | import {Routes, RouterModule} from '@angular/router';
3 | import {AppsComponent} from './apps.component';
4 | import {AppComponent} from './app/app.component';
5 | import {AddComponent} from './add/add.component';
6 | import {SecurityGuard} from '../security/support/security.guard';
7 |
8 | const routes: Routes = [
9 | {
10 | path: 'apps',
11 | component: AppsComponent,
12 | canActivate: [SecurityGuard],
13 | data: {
14 | authenticate: true,
15 | roles: ['ROLE_VIEW']
16 | }
17 | },
18 | {
19 | path: 'apps/:appType/:appName',
20 | component: AppComponent,
21 | canActivate: [SecurityGuard],
22 | data: {
23 | authenticate: true,
24 | roles: ['ROLE_VIEW']
25 | }
26 | },
27 | {
28 | path: 'apps/add',
29 | component: AddComponent,
30 | data: {
31 | authenticate: true,
32 | roles: ['ROLE_CREATE']
33 | }
34 | }
35 | ];
36 |
37 | @NgModule({
38 | imports: [RouterModule.forChild(routes)],
39 | exports: [RouterModule]
40 | })
41 | export class AppsRoutingModule {}
42 |
--------------------------------------------------------------------------------
/ui/src/app/apps/apps.module.ts:
--------------------------------------------------------------------------------
1 | import {NgModule} from '@angular/core';
2 | import {CommonModule} from '@angular/common';
3 |
4 | import {ClarityModule} from '@clr/angular';
5 | import {FormsModule, ReactiveFormsModule} from '@angular/forms';
6 | import {SharedModule} from '../shared/shared.module';
7 | import {SecurityModule} from '../security/security.module';
8 | import {AppsRoutingModule} from './apps-routing.module';
9 | import {AddComponent} from './add/add.component';
10 | import {AppComponent} from './app/app.component';
11 | import {AppsComponent} from './apps.component';
12 | import {UnregisterComponent} from './unregister/unregister.component';
13 | import {VersionComponent} from './version/version.component';
14 | import {TypeFilterComponent} from './type.filter';
15 | import {PropsComponent} from './add/props/props.component';
16 | import {RegisterComponent} from './add/register/register.component';
17 | import {UriComponent} from './add/uri/uri.component';
18 | import {WebsiteStartersComponent} from './add/website-starters/website-starters.component';
19 | import {TranslateModule} from '@ngx-translate/core';
20 |
21 | @NgModule({
22 | declarations: [
23 | AddComponent,
24 | AppComponent,
25 | AppsComponent,
26 | UnregisterComponent,
27 | VersionComponent,
28 | TypeFilterComponent,
29 | PropsComponent,
30 | RegisterComponent,
31 | UriComponent,
32 | WebsiteStartersComponent
33 | ],
34 | imports: [
35 | CommonModule,
36 | FormsModule,
37 | ReactiveFormsModule,
38 | SharedModule,
39 | ClarityModule,
40 | SecurityModule,
41 | AppsRoutingModule,
42 | TranslateModule
43 | ],
44 | providers: []
45 | })
46 | export class AppsModule {}
47 |
--------------------------------------------------------------------------------
/ui/src/app/dev/dashboard/dashboard.component.scss:
--------------------------------------------------------------------------------
1 |
2 | .wizard .clr-textarea {
3 | width: 100%;
4 | }
5 |
--------------------------------------------------------------------------------
/ui/src/app/dev/dev-routing.module.ts:
--------------------------------------------------------------------------------
1 | import {RouterModule, Routes} from '@angular/router';
2 | import {NgModule} from '@angular/core';
3 | import {DashboardComponent} from './dashboard/dashboard.component';
4 |
5 | const routes: Routes = [
6 | {
7 | path: 'dev',
8 | redirectTo: 'dev/dashboard'
9 | },
10 | {
11 | path: 'dev/dashboard',
12 | component: DashboardComponent
13 | }
14 | ];
15 |
16 | @NgModule({
17 | imports: [RouterModule.forChild(routes)],
18 | exports: [RouterModule]
19 | })
20 | export class DevRoutingModule {}
21 |
--------------------------------------------------------------------------------
/ui/src/app/dev/dev.module.ts:
--------------------------------------------------------------------------------
1 | import {NgModule} from '@angular/core';
2 | import {CommonModule} from '@angular/common';
3 | import {ClarityModule} from '@clr/angular';
4 | import {FormsModule, ReactiveFormsModule} from '@angular/forms';
5 | import {SharedModule} from '../shared/shared.module';
6 | import {DashboardComponent} from './dashboard/dashboard.component';
7 | import {DevRoutingModule} from './dev-routing.module';
8 | import {StreamCreateComponent} from './dashboard/stream-create/stream-create.component';
9 | import {TaskCreateComponent} from './dashboard/task-create/task-create.component';
10 |
11 | @NgModule({
12 | declarations: [DashboardComponent, StreamCreateComponent, TaskCreateComponent],
13 | imports: [CommonModule, ClarityModule, FormsModule, ReactiveFormsModule, SharedModule, DevRoutingModule]
14 | })
15 | export class DevModule {}
16 |
--------------------------------------------------------------------------------
/ui/src/app/flo/shared-flo.module.ts:
--------------------------------------------------------------------------------
1 | import {NgModule} from '@angular/core';
2 | import {FloModule} from 'spring-flo';
3 | import {FormsModule, ReactiveFormsModule} from '@angular/forms';
4 | import {CommonModule} from '@angular/common';
5 | import {HttpClientModule} from '@angular/common/http';
6 | import {ClarityModule} from '@clr/angular';
7 | import {ClrDynamicFormPropertyComponent} from './shared/properties/df.property.component';
8 | import {ClrPropertiesGroupComponent} from './shared/properties/properties.group.component';
9 | import {PropertiesDialogComponent} from './shared/properties/properties-dialog.component';
10 | import {PropertiesGroupsDialogComponent} from './shared/properties-groups/properties-groups-dialog.component';
11 | import {TranslateModule} from '@ngx-translate/core';
12 |
13 | @NgModule({
14 | declarations: [
15 | ClrDynamicFormPropertyComponent,
16 | ClrPropertiesGroupComponent,
17 | PropertiesDialogComponent,
18 | PropertiesGroupsDialogComponent
19 | ],
20 | imports: [
21 | CommonModule,
22 | HttpClientModule,
23 | FormsModule,
24 | ReactiveFormsModule,
25 | ClarityModule,
26 | TranslateModule,
27 | FloModule
28 | ],
29 | providers: [],
30 | exports: [
31 | ClrDynamicFormPropertyComponent,
32 | ClrPropertiesGroupComponent,
33 | PropertiesDialogComponent,
34 | PropertiesGroupsDialogComponent
35 | ]
36 | })
37 | export class SharedFloModule {}
38 |
--------------------------------------------------------------------------------
/ui/src/app/flo/shared/graph-view/graph-view.component.html:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ui/src/app/flo/shared/graph-view/graph-view.component.ts:
--------------------------------------------------------------------------------
1 | import {Component, ViewEncapsulation, Input, Output, EventEmitter} from '@angular/core';
2 | import {Flo} from 'spring-flo';
3 |
4 | @Component({
5 | selector: 'app-graph-view',
6 | templateUrl: './graph-view.component.html',
7 | styleUrls: ['../flo.scss'],
8 | encapsulation: ViewEncapsulation.None
9 | })
10 | export class GraphViewComponent {
11 | @Input()
12 | dsl: string;
13 |
14 | @Input()
15 | paperPadding = 5;
16 |
17 | @Input()
18 | metamodel: Flo.Metamodel;
19 |
20 | @Input()
21 | renderer: Flo.Renderer;
22 |
23 | @Output()
24 | floApi = new EventEmitter();
25 |
26 | private editorContext: Flo.EditorContext;
27 |
28 | constructor() {}
29 |
30 | setEditorContext(editorContext: Flo.EditorContext): void {
31 | this.editorContext = editorContext;
32 | this.editorContext.noPalette = true;
33 | this.editorContext.readOnlyCanvas = true;
34 | this.floApi.emit(this.editorContext);
35 | }
36 |
37 | get flo(): Flo.EditorContext {
38 | return this.editorContext;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/ui/src/app/flo/shared/properties/df.property.component.ts:
--------------------------------------------------------------------------------
1 | import {Component, Input, OnInit, ViewEncapsulation} from '@angular/core';
2 | import {UntypedFormGroup, AbstractControl, FormControl} from '@angular/forms';
3 | import {DynamicFormPropertyComponent, Properties} from 'spring-flo';
4 | import PropertyFilter = Properties.PropertyFilter;
5 |
6 | @Component({
7 | selector: 'clr-df-property',
8 | templateUrl: './df.property.component.html',
9 | encapsulation: ViewEncapsulation.None
10 | })
11 | export class ClrDynamicFormPropertyComponent {
12 | @Input()
13 | model: Properties.ControlModel;
14 |
15 | @Input() form: UntypedFormGroup;
16 |
17 | constructor() {}
18 |
19 | get types(): any {
20 | return Properties.InputType;
21 | }
22 |
23 | get control(): AbstractControl {
24 | return this.form.controls[this.model.id];
25 | }
26 |
27 | get errorData(): any {
28 | return (this.model.validation && this.model.validation.errorData ? this.model.validation.errorData : []).filter(
29 | e => this.control.errors && this.control.errors[e.id]
30 | );
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/ui/src/app/flo/shared/properties/properties.group.component.html:
--------------------------------------------------------------------------------
1 |
6 |
0 && controlModelsToDisplay.length === 0;
9 | else propertiesList
10 | "
11 | class="no-matches-label"
12 | >
13 | {{ 'commons.noResultFound' | translate }}
14 |
15 |
16 |
17 |
18 |
24 |
25 |
--------------------------------------------------------------------------------
/ui/src/app/flo/shared/properties/properties.group.component.ts:
--------------------------------------------------------------------------------
1 | import {Component, Input, OnInit, ViewEncapsulation} from '@angular/core';
2 | import {UntypedFormGroup, UntypedFormControl} from '@angular/forms';
3 | import {Properties} from 'spring-flo';
4 | import PropertyFilter = Properties.PropertyFilter;
5 |
6 | @Component({
7 | selector: 'clr-properties-group',
8 | templateUrl: './properties.group.component.html',
9 | encapsulation: ViewEncapsulation.None
10 | })
11 | export class ClrPropertiesGroupComponent implements OnInit {
12 | @Input()
13 | propertiesGroupModel: Properties.PropertiesGroupModel;
14 |
15 | @Input()
16 | form: UntypedFormGroup;
17 |
18 | @Input()
19 | filter: PropertyFilter;
20 |
21 | ngOnInit(): void {
22 | if (this.propertiesGroupModel.isLoading) {
23 | const subscription = this.propertiesGroupModel.loadedSubject.subscribe(loaded => {
24 | if (loaded) {
25 | subscription.unsubscribe();
26 | this.createGroupControls();
27 | }
28 | });
29 | } else {
30 | this.createGroupControls();
31 | }
32 | }
33 |
34 | createGroupControls(): void {
35 | this.propertiesGroupModel.getControlsModels().forEach(c => {
36 | if (c.validation) {
37 | this.form.addControl(
38 | c.id,
39 | new UntypedFormControl(c.value || '', c.validation.validator, c.validation.asyncValidator)
40 | );
41 | } else {
42 | this.form.addControl(c.id, new UntypedFormControl(c.value || ''));
43 | }
44 | });
45 | }
46 |
47 | get controlModelsToDisplay(): any {
48 | return this.propertiesGroupModel.getControlsModels().filter(c => !this.filter || this.filter.accept(c.property));
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/ui/src/app/flo/shared/service/doc.service.ts:
--------------------------------------------------------------------------------
1 | import {Inject, Injectable} from '@angular/core';
2 | import {DOCUMENT} from '@angular/common';
3 |
4 | @Injectable()
5 | export class DocService {
6 | private mouseDown = false;
7 |
8 | constructor(@Inject(DOCUMENT) private document: Document) {
9 | document.body.addEventListener('mouseup', () => (this.mouseDown = false));
10 | document.body.addEventListener('mousedown', () => (this.mouseDown = true));
11 | }
12 |
13 | body(): HTMLElement {
14 | return this.document.body;
15 | }
16 |
17 | isMouseDown(): boolean {
18 | return this.mouseDown;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/ui/src/app/flo/shared/service/parser.service.ts:
--------------------------------------------------------------------------------
1 | import {Injectable} from '@angular/core';
2 | import {Parser} from './parser';
3 |
4 | @Injectable()
5 | export class ParserService {
6 | constructor() {}
7 |
8 | parseDsl(text: string, mode?: string): any {
9 | return Parser.parse(text, mode);
10 | }
11 |
12 | simplifyDsl(dsl: string): string {
13 | return Parser.simplify(dsl);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/ui/src/app/flo/shared/support/app-ui-property.ts:
--------------------------------------------------------------------------------
1 | import {Properties} from 'spring-flo';
2 |
3 | /**
4 | * App property consumable by properties dialog
5 | *
6 | * @author Alex Boyko
7 | */
8 | export interface AppUiProperty extends Properties.Property {
9 | attr: string;
10 | isSemantic: boolean;
11 | groups: string[];
12 | code?: CodeProperty;
13 | }
14 |
15 | export interface CodeProperty {
16 | langPropertyName: string;
17 | language: string;
18 | }
19 |
--------------------------------------------------------------------------------
/ui/src/app/flo/shared/support/shape-component.ts:
--------------------------------------------------------------------------------
1 | import {dia} from 'jointjs';
2 |
3 | export class ShapeComponent {
4 | public data: any;
5 | }
6 |
7 | export class ElementComponent {
8 | public view: dia.ElementView;
9 | }
10 |
--------------------------------------------------------------------------------
/ui/src/app/flo/stream/content-assist.service.ts:
--------------------------------------------------------------------------------
1 | import {Injectable} from '@angular/core';
2 | import {HttpClient, HttpParams} from '@angular/common/http';
3 | import {Observable} from 'rxjs';
4 | import {map} from 'rxjs/operators';
5 | import {HttpUtils} from '../../shared/support/http.utils';
6 | import {UrlUtilities} from '../../url-utilities.service';
7 |
8 | @Injectable()
9 | export class ContentAssistService {
10 | constructor(private httpClient: HttpClient) {}
11 |
12 | getProposals(prefix: string): Observable> {
13 | const headers = HttpUtils.getDefaultHttpHeaders();
14 | const params = new HttpParams().append('start', prefix).append('defaultLevel', '1');
15 | return this.httpClient
16 | .get(UrlUtilities.calculateBaseApiUrl() + 'completions/stream', {headers, params})
17 | .pipe(map(jsonResponse => jsonResponse.proposals));
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/ui/src/app/flo/stream/dsl-sanitize.service.ts:
--------------------------------------------------------------------------------
1 | import {Injectable} from '@angular/core';
2 | import {TextToGraphConverter} from './text-to-graph';
3 | import {Parser} from '../shared/service/parser';
4 |
5 | /**
6 | * Helper to break down stream DSL text into stream definitions
7 | *
8 | * @author Alex Boyko
9 | */
10 | @Injectable()
11 | export class SanitizeDsl {
12 | convert(dsl: string, parsedStreams: Parser.ParseResult): any {
13 | return TextToGraphConverter.convertParseResponseToJsonGraph(dsl, parsedStreams);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/ui/src/app/flo/stream/instance-dot/instance-dot.component.html:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
27 |
28 | {{ 'flo.commons.instanceDown' | translate }}
29 |
30 |
--------------------------------------------------------------------------------
/ui/src/app/flo/stream/instance-dot/instance-dot.component.scss:
--------------------------------------------------------------------------------
1 | @import '../../shared/flo';
2 |
3 | .instance-dot:hover circle {
4 | stroke: $navy-6;
5 | }
6 |
7 | .joint-type-dataflow-instancedot.joint-element {
8 | .deployed {
9 | // stroke: $spring-green;
10 | stroke: #80b600;
11 | }
12 | .failed {
13 | // stroke: red;
14 | stroke: #ff5400;
15 | }
16 | stroke: gray;
17 | fill: transparent;
18 | cursor: initial;
19 | }
20 |
--------------------------------------------------------------------------------
/ui/src/app/flo/stream/instance-dot/instance-dot.component.ts:
--------------------------------------------------------------------------------
1 | import {Component, ViewEncapsulation} from '@angular/core';
2 | import {ElementComponent} from '../../shared/support/shape-component';
3 | import {InstanceStatus} from '../../../shared/model/metrics.model';
4 |
5 | /**
6 | * Component for displaying "dot" for instance streamStatuses data under the module
7 | *
8 | * @author Alex Boyko
9 | * @author Andy Clement
10 | */
11 | @Component({
12 | selector: 'app-flo-instance-dot',
13 | templateUrl: 'instance-dot.component.html',
14 | styleUrls: ['instance-dot.component.scss'],
15 | encapsulation: ViewEncapsulation.None
16 | })
17 | export class InstanceDotComponent extends ElementComponent {
18 | get instance(): InstanceStatus {
19 | return this.view ? this.view.model.attr('instance') : undefined;
20 | }
21 |
22 | get guid(): string {
23 | return this.instance ? this.instance.guid : '';
24 | }
25 |
26 | get state(): string {
27 | return this.instance ? this.instance.state : '';
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/ui/src/app/flo/stream/message-rate/message-rate.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{ rateLabel }}
5 |
6 |
7 |
8 |
12 |
13 | {{ rateTooltip }}
14 |
--------------------------------------------------------------------------------
/ui/src/app/flo/stream/message-rate/message-rate.component.scss:
--------------------------------------------------------------------------------
1 | .joint-link {
2 | .label.dataflow-outgoing-rate {
3 | text {
4 | fill: black;
5 | stroke: none;
6 | font-size: 8px;
7 | }
8 | rect {
9 | stroke: black;
10 | stroke-width: 1px;
11 | fill: #CFE2F3;
12 | }
13 | }
14 | .label.dataflow-incoming-rate {
15 | text {
16 | fill: white;
17 | stroke: none;
18 | font-size: 8px;
19 | }
20 | rect {
21 | stroke: #3498DB;
22 | fill: #3498DB;
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/ui/src/app/flo/stream/node/stream-node.component.scss:
--------------------------------------------------------------------------------
1 | .app-tooltip .tooltip-inner {
2 | max-width: 400px;
3 | }
4 |
5 | .tooltip-property-value {
6 | word-break: break-all;
7 | max-width: 390px;
8 | white-space: pre;
9 | text-overflow: ellipsis;
10 | overflow: hidden;
11 | text-align: left;
12 | }
13 |
14 | .tooltip-property-key {
15 | text-align: left;
16 | vertical-align: top;
17 | max-width: 200px;
18 | vertical-align: top;
19 | text-overflow: ellipsis;
20 | overflow: hidden;
21 | }
22 |
23 | .tooltip-property-value-code {
24 | font-family: monospace;
25 | font-size: 85%;
26 | }
27 |
--------------------------------------------------------------------------------
/ui/src/app/flo/stream/node/stream-node.component.ts:
--------------------------------------------------------------------------------
1 | import {Component, ElementRef, ViewChild, ViewEncapsulation} from '@angular/core';
2 | import {dia} from 'jointjs';
3 | import {NodeComponent} from '../../shared/support/node-component';
4 | import {DocService} from '../../shared/service/doc.service';
5 | import {PropertiesEditor} from '../properties-editor.service';
6 | import {UrlUtilities} from '../../../url-utilities.service';
7 |
8 | /**
9 | * Component for displaying application properties and capturing their values.
10 | *
11 | * @author Alex Boyko
12 | * @author Andy Clement
13 | */
14 | @Component({
15 | selector: 'app-flo-node',
16 | templateUrl: './stream-node.component.html',
17 | styleUrls: ['./stream-node.component.scss'],
18 | encapsulation: ViewEncapsulation.None
19 | })
20 | export class StreamNodeComponent extends NodeComponent {
21 | @ViewChild('markerTooltip')
22 | markerTooltipElement: ElementRef;
23 |
24 | @ViewChild('canvasNodeTooltip')
25 | canvasTooltipElement: ElementRef;
26 |
27 | @ViewChild('paletteNodeTooltip')
28 | paletteTooltipElement: ElementRef;
29 |
30 | assetUrl = UrlUtilities.calculateAssetUrl();
31 |
32 | constructor(
33 | docService: DocService,
34 | private propertiesEditor: PropertiesEditor
35 | ) {
36 | super(docService);
37 | }
38 |
39 | showOptions(): void {
40 | this.propertiesEditor.showForNode(this.view.model, this.paper.model);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/ui/src/app/flo/stream/support/layout.ts:
--------------------------------------------------------------------------------
1 | import {dia} from 'jointjs';
2 | import * as dagre from 'dagre';
3 | import {Flo} from 'spring-flo';
4 |
5 | const NODE_SEPARATION = 60.0;
6 | const RANK_SEPARATION = 60.0;
7 | const EDGE_SEPARATION = 30.0;
8 |
9 | export function layout(paper: dia.Paper): void {
10 | const graph = paper.model;
11 |
12 | const g = new dagre.graphlib.Graph();
13 | g.setGraph({});
14 | g.setDefaultEdgeLabel(() => ({}));
15 |
16 | graph
17 | .getElements()
18 | .filter(e => !e.get('parent'))
19 | .forEach(node => g.setNode(node.id, node.get('size')));
20 |
21 | graph
22 | .getLinks()
23 | .filter(link => link.get('source').id && link.get('target').id)
24 | .forEach(link => {
25 | g.setEdge(link.get('source').id, link.get('target').id, {weight: link.get('source').port === 'output' ? 200 : 1});
26 | link.set('vertices', []);
27 | });
28 |
29 | const gridSize = paper.options.gridSize;
30 | g.graph().rankdir = 'LR';
31 | g.graph().nodesep = gridSize <= 1 ? NODE_SEPARATION : Math.round(NODE_SEPARATION / gridSize) * gridSize;
32 | g.graph().ranksep = gridSize <= 1 ? RANK_SEPARATION : Math.round(RANK_SEPARATION / gridSize) * gridSize;
33 | g.graph().edgesep = gridSize <= 1 ? EDGE_SEPARATION : Math.round(EDGE_SEPARATION / gridSize) * gridSize;
34 |
35 | dagre.layout(g);
36 | g.nodes().forEach(v => {
37 | const node = graph.getCell(v) as dia.Element;
38 | if (node) {
39 | const bbox = node.getBBox();
40 | node.translate(g.node(v).x - g.node(v).width / 2 - bbox.x, g.node(v).y - g.node(v).height / 2 - bbox.y);
41 | }
42 | });
43 |
44 | g.edges().forEach(o => g.edge(o));
45 | }
46 |
47 | export function arrangeAll(editorContext: Flo.EditorContext): Promise {
48 | return editorContext.performLayout().then(() => {
49 | editorContext.fitToPage();
50 | const currentScale = editorContext.getPaper().scale();
51 | if (currentScale.sx > 1 || currentScale.sy > 1) {
52 | editorContext.getPaper().scale(1);
53 | }
54 | });
55 | }
56 |
--------------------------------------------------------------------------------
/ui/src/app/flo/task-flo.module.ts:
--------------------------------------------------------------------------------
1 | import {NgModule} from '@angular/core';
2 | import {CommonModule} from '@angular/common';
3 | import {HttpClientModule} from '@angular/common/http';
4 | import {FormsModule, ReactiveFormsModule} from '@angular/forms';
5 | import {ClarityModule} from '@clr/angular';
6 | import {FloModule} from 'spring-flo';
7 | import {DocService} from './shared/service/doc.service';
8 | import {EditorService} from './task/editor.service';
9 | import {MetamodelService} from './task/metamodel.service';
10 | import {RenderService} from './task/render.service';
11 | import {ParserService} from './shared/service/parser.service';
12 | import {ContentAssistService} from './task/content-assist.service';
13 | import {TaskNodeComponent} from './task/node/task-node.component';
14 | import {TaskPropertiesDialogComponent} from './task/properties/task-properties-dialog-component';
15 | import {ViewComponent} from './task/component/view.component';
16 | import {TaskFloCreateComponent} from './task/component/create.component';
17 | import {ToolsService} from './task/tools.service';
18 | import {SharedFloModule} from './shared-flo.module';
19 | import {SharedModule} from '../shared/shared.module';
20 | import {TranslateModule} from '@ngx-translate/core';
21 |
22 | @NgModule({
23 | declarations: [TaskNodeComponent, TaskPropertiesDialogComponent, ViewComponent, TaskFloCreateComponent],
24 | imports: [
25 | CommonModule,
26 | HttpClientModule,
27 | FormsModule,
28 | ReactiveFormsModule,
29 | ClarityModule,
30 | FloModule,
31 | SharedFloModule,
32 | TranslateModule,
33 | SharedModule
34 | ],
35 | providers: [
36 | DocService,
37 | EditorService,
38 | MetamodelService,
39 | RenderService,
40 | ParserService,
41 | ContentAssistService,
42 | ToolsService
43 | ],
44 | exports: [ViewComponent, TaskFloCreateComponent, TaskPropertiesDialogComponent]
45 | })
46 | export class TaskFloModule {}
47 |
--------------------------------------------------------------------------------
/ui/src/app/flo/task/component/view.component.ts:
--------------------------------------------------------------------------------
1 | import {Component, Input, OnInit} from '@angular/core';
2 | import {Flo} from 'spring-flo';
3 | import {MetamodelService} from '../metamodel.service';
4 | import {RenderService} from '../render.service';
5 |
6 | @Component({
7 | selector: 'app-task-flo-view',
8 | template: `
9 |
10 |
17 |
18 |
19 | `,
20 | styleUrls: ['../../shared/flo.scss']
21 | })
22 | export class ViewComponent {
23 | @Input()
24 | dsl: string;
25 |
26 | @Input()
27 | paperPadding = 5;
28 |
29 | private editorContext: Flo.EditorContext;
30 |
31 | constructor(
32 | public metamodelService: MetamodelService,
33 | public renderService: RenderService
34 | ) {}
35 |
36 | setEditorContext(editorContext: Flo.EditorContext): void {
37 | this.editorContext = editorContext;
38 | this.editorContext.noPalette = true;
39 | this.editorContext.readOnlyCanvas = true;
40 | }
41 |
42 | get flo(): Flo.EditorContext {
43 | return this.editorContext;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/ui/src/app/flo/task/content-assist.service.ts:
--------------------------------------------------------------------------------
1 | import {Injectable} from '@angular/core';
2 | import {HttpClient, HttpParams} from '@angular/common/http';
3 | import {Observable} from 'rxjs';
4 | import {map} from 'rxjs/operators';
5 | import {HttpUtils} from '../../shared/support/http.utils';
6 | import {UrlUtilities} from '../../url-utilities.service';
7 |
8 | @Injectable()
9 | export class ContentAssistService {
10 | constructor(private httpClient: HttpClient) {}
11 |
12 | getProposals(prefix: string): Observable> {
13 | const headers = HttpUtils.getDefaultHttpHeaders();
14 | const params = new HttpParams().append('start', prefix).append('defaultLevel', '1');
15 | return this.httpClient
16 | .get(UrlUtilities.calculateBaseApiUrl() + 'completions/task', {headers, params})
17 | .pipe(map(jsonResponse => jsonResponse.proposals));
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/ui/src/app/flo/task/model/models.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Generic response type returned from both tools conversions.
3 | */
4 | export class TaskConversion {
5 | public graph: Graph;
6 | public dsl: string;
7 | public errors: Array = [];
8 |
9 | constructor(dsl: string, errors?: Array