├── .editorconfig ├── .github └── workflows │ ├── main.yml │ ├── pr.yml │ ├── release.yml │ └── tests.yml ├── .gitignore ├── .license └── license-header.txt ├── LICENSE ├── README.md ├── Screenshot_Frontpage.png ├── Screenshot_Group.png ├── Screenshot_History.png ├── db-scheduler-ui-frontend ├── .env.development ├── .env.production ├── .eslintrc.cjs ├── .gitignore ├── .prettierignore ├── .prettierrc ├── README.md ├── index.html ├── main.tsx ├── package-lock.json ├── package.json ├── public │ └── favicon.svg ├── src │ ├── App.tsx │ ├── assets │ │ └── icons │ │ │ ├── DoubleChevronIcon.tsx │ │ │ ├── IoEllipsisVerticalIcon.tsx │ │ │ ├── Logo.tsx │ │ │ ├── PlayIcon.tsx │ │ │ ├── RepeatIcon.tsx │ │ │ └── index.ts │ ├── components │ │ ├── common │ │ │ ├── HeaderBar.tsx │ │ │ ├── JsonViewer.tsx │ │ │ ├── NumberCircle.tsx │ │ │ ├── NumberCircleGroup.tsx │ │ │ ├── RefreshCircle.tsx │ │ │ ├── StatusBox.tsx │ │ │ ├── TitleRow.tsx │ │ │ └── TopBar.tsx │ │ ├── history │ │ │ ├── LogAccordionButton.tsx │ │ │ ├── LogAccordionItem.tsx │ │ │ ├── LogCard.tsx │ │ │ ├── LogDataRow.tsx │ │ │ ├── LogList.tsx │ │ │ └── LogStatus.tsx │ │ ├── input │ │ │ ├── DateTimeInput.tsx │ │ │ ├── DotButton.tsx │ │ │ ├── FilterBox.tsx │ │ │ ├── PaginationButtons.tsx │ │ │ ├── RefreshButton.tsx │ │ │ ├── ScheduleRunAlert.tsx │ │ │ ├── SortButton.tsx │ │ │ └── TaskRunButton.tsx │ │ └── scheduled │ │ │ ├── RunAllAlert.tsx │ │ │ ├── TaskAccordionButton.tsx │ │ │ ├── TaskAccordionItem.tsx │ │ │ ├── TaskCard.tsx │ │ │ ├── TaskDataRow.tsx │ │ │ ├── TaskGroupCard.tsx │ │ │ └── TaskList.tsx │ ├── hooks │ │ └── useInfiniteTaskScrolling.ts │ ├── models │ │ ├── Log.ts │ │ ├── PollResponse.ts │ │ ├── QueryParams.ts │ │ ├── Task.ts │ │ ├── TaskRequestParams.ts │ │ └── TasksResponse.ts │ ├── pages │ │ └── FrontPage.tsx │ ├── services │ │ ├── deleteTask.ts │ │ ├── getLogs.ts │ │ ├── getTask.ts │ │ ├── getTasks.ts │ │ ├── pollLogs.ts │ │ ├── pollTasks.ts │ │ ├── runTask.ts │ │ └── runTaskGroup.ts │ ├── styles │ │ ├── colors.ts │ │ ├── index.ts │ │ └── theme.ts │ ├── utils │ │ ├── config.ts │ │ ├── dateFormatText.ts │ │ ├── determineStatus.ts │ │ └── global.d.ts │ └── vite-env.d.ts ├── tsconfig.json ├── tsconfig.node.json └── vite.config.ts ├── db-scheduler-ui-starter ├── pom.xml └── src │ ├── main │ ├── java │ │ └── no │ │ │ └── bekk │ │ │ └── dbscheduler │ │ │ └── uistarter │ │ │ ├── autoconfigure │ │ │ └── UiApiAutoConfiguration.java │ │ │ └── config │ │ │ ├── DbSchedulerUiProperties.java │ │ │ ├── DbSchedulerUiUtil.java │ │ │ ├── DbSchedulerUiWebConfiguration.java │ │ │ └── PrefixedRequestMappingHandlerMapping.java │ └── resources │ │ └── META-INF │ │ ├── spring.factories │ │ └── spring │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ └── test │ └── java │ └── no │ └── bekk │ └── dbscheduler │ └── uistarter │ └── config │ └── DbSchedulerUiUtilTest.java ├── db-scheduler-ui ├── pom.xml └── src │ └── main │ ├── java │ └── no │ │ └── bekk │ │ └── dbscheduler │ │ └── ui │ │ ├── controller │ │ ├── ConfigController.java │ │ ├── IndexHtmlController.java │ │ ├── LogController.java │ │ ├── SpaFallbackMvc.java │ │ ├── TaskAdminController.java │ │ └── TaskController.java │ │ ├── model │ │ ├── ConfigResponse.java │ │ ├── GetLogsResponse.java │ │ ├── GetTasksResponse.java │ │ ├── LogModel.java │ │ ├── LogPollResponse.java │ │ ├── PollResponse.java │ │ ├── TaskDetailsRequestParams.java │ │ ├── TaskModel.java │ │ └── TaskRequestParams.java │ │ ├── service │ │ ├── LogLogic.java │ │ └── TaskLogic.java │ │ └── util │ │ ├── AndCondition.java │ │ ├── Caching.java │ │ ├── QueryBuilder.java │ │ ├── QueryUtils.java │ │ └── mapper │ │ └── TaskMapper.java │ └── resources │ └── META-INF │ └── additional-spring-configuration-metadata.json ├── example-app-webflux ├── Dockerfile ├── pom.xml └── src │ ├── main │ ├── java │ │ ├── com │ │ │ └── github │ │ │ │ └── bekk │ │ │ │ └── exampleapp │ │ │ │ ├── ExampleAppWebFlux.java │ │ │ │ ├── config │ │ │ │ ├── DatabaseConfig.java │ │ │ │ └── SchedulerConfig.java │ │ │ │ ├── model │ │ │ │ ├── TaskData.java │ │ │ │ └── TestObject.java │ │ │ │ ├── service │ │ │ │ └── TaskService.java │ │ │ │ └── tasks │ │ │ │ ├── ChainTask.java │ │ │ │ ├── FailingTask.java │ │ │ │ ├── LongRunningTask.java │ │ │ │ ├── OneTimeTaskExample.java │ │ │ │ ├── RecurringTaskExample.java │ │ │ │ └── SpawnerTask.java │ │ └── utils │ │ │ └── Utils.java │ └── resources │ │ ├── application.properties │ │ └── db │ │ └── migration │ │ ├── V1__create_db.sql │ │ └── V2__add_logging.sql │ └── test │ ├── java │ └── com │ │ └── github │ │ └── bekk │ │ └── exampleapp │ │ └── SmokeTest.java │ └── resources │ └── application.properties ├── example-app ├── Dockerfile ├── pom.xml └── src │ ├── main │ ├── java │ │ ├── com │ │ │ └── github │ │ │ │ └── bekk │ │ │ │ └── exampleapp │ │ │ │ ├── ExampleApp.java │ │ │ │ ├── config │ │ │ │ ├── DatabaseConfig.java │ │ │ │ └── SchedulerConfig.java │ │ │ │ ├── model │ │ │ │ ├── TaskData.java │ │ │ │ ├── TaskScheduleAndNoData.java │ │ │ │ └── TestObject.java │ │ │ │ ├── service │ │ │ │ └── TaskService.java │ │ │ │ └── tasks │ │ │ │ ├── ChainTask.java │ │ │ │ ├── DynamicRecurringTaskExample.java │ │ │ │ ├── FailingTask.java │ │ │ │ ├── LongRunningTask.java │ │ │ │ ├── OneTimeTaskExample.java │ │ │ │ ├── RecurringTaskExample.java │ │ │ │ └── SpawnerTask.java │ │ └── utils │ │ │ └── Utils.java │ └── resources │ │ ├── application.properties │ │ └── db │ │ └── migration │ │ ├── V1__create_db.sql │ │ └── V2__add_logging.sql │ └── test │ ├── java │ └── com │ │ └── github │ │ └── bekk │ │ └── exampleapp │ │ ├── CtxPathTest.java │ │ ├── DynamicRecurringSchedulingTest.java │ │ ├── SmokeReadOnlyTest.java │ │ ├── SmokeSpringSecurityTest.java │ │ └── SmokeTest.java │ └── resources │ ├── application-ctxpath.properties │ └── application.properties ├── heroku.yml ├── package-lock.json └── pom.xml /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - main 8 | 9 | jobs: 10 | build: 11 | name: Build frontend and all packages 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: Checkout repo 16 | uses: actions/checkout@v3 17 | 18 | - name: Ensure "bd-scheduler" is not in code changes 19 | run: | 20 | git fetch origin main 21 | git diff origin/main..HEAD -- . ':!.github/workflows' | grep -i "bd-scheduler" && echo "Found forbidden word: bd-scheduler" && exit 1 || echo "Check passed" 22 | 23 | - name: Setup Node 24 | uses: actions/setup-node@v3 25 | 26 | - name: Cache Node.js modules 27 | uses: actions/cache@v3 28 | with: 29 | path: ~/.npm 30 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} 31 | restore-keys: | 32 | ${{ runner.os }}-node- 33 | 34 | - name: Install Node.js dependencies 35 | run: | 36 | cd db-scheduler-ui-frontend 37 | npm install 38 | 39 | - name: Set up JDK 17 40 | uses: actions/setup-java@v3 41 | with: 42 | java-version: '17' 43 | distribution: 'temurin' 44 | 45 | - name: Build all packages with Maven 46 | run: mvn --batch-mode --update-snapshots package -DskipTests -------------------------------------------------------------------------------- /.github/workflows/pr.yml: -------------------------------------------------------------------------------- 1 | name: PR validation 2 | 3 | on: [pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - name: Checkout repo 11 | uses: actions/checkout@v3 12 | 13 | - name: Cache Maven dependencies 14 | uses: actions/cache@v3 15 | with: 16 | path: ~/.m2 17 | key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} 18 | restore-keys: | 19 | ${{ runner.os }}-maven- 20 | 21 | - name: Set up JDK 17 22 | uses: actions/setup-java@v3 23 | with: 24 | java-version: '17' 25 | distribution: 'temurin' 26 | 27 | - name: Run spotless check 28 | run: mvn -B spotless:check 29 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | release: 5 | types: 6 | - published 7 | 8 | jobs: 9 | release: 10 | name: Build and release packages 11 | runs-on: ubuntu-latest 12 | permissions: 13 | contents: read 14 | packages: write 15 | 16 | steps: 17 | - name: Checkout repo 18 | uses: actions/checkout@v3 19 | 20 | - name: Setup Node 21 | uses: actions/setup-node@v3 22 | 23 | - name: Cache Node.js modules 24 | uses: actions/cache@v3 25 | with: 26 | path: ~/.npm 27 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} 28 | restore-keys: | 29 | ${{ runner.os }}-node- 30 | 31 | - name: Install Node.js dependencies 32 | run: | 33 | cd db-scheduler-ui-frontend 34 | npm install 35 | 36 | - name: Set up JDK 17 37 | uses: actions/setup-java@v3 38 | with: 39 | java-version: '17' 40 | distribution: 'temurin' 41 | gpg-private-key: ${{ secrets.JRELEASER_GPG_SECRET_KEY }} 42 | gpg-passphrase: JRELEASER_GPG_PASSPHRASE 43 | 44 | - name: remove v in tag 45 | run: | 46 | TAG=${{ github.event.release.tag_name }} 47 | echo "VERSION=${TAG#v}" >> $GITHUB_ENV 48 | 49 | - name: Set release version 50 | run: | 51 | TAG=${{ github.event.release.tag_name }} 52 | echo "VERSION=${TAG#v}" >> $GITHUB_ENV 53 | mvn --no-transfer-progress --batch-mode versions:set -DnewVersion=$VERSION -DgenerateBackupPoms=false 54 | 55 | - name: Stage release 56 | run: mvn --no-transfer-progress --batch-mode -Ppublication clean deploy -DskipTests 57 | env: 58 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 59 | JRELEASER_GPG_PASSPHRASE: ${{ secrets.JRELEASER_GPG_PASSPHRASE }} 60 | 61 | - name: Publish to maven central 62 | run: mvn jreleaser:full-release 63 | env: 64 | JRELEASER_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 65 | JRELEASER_MAVENCENTRAL_SONATYPE_USERNAME: ${{ secrets.JRELEASER_NEXUS2_MAVEN_CENTRAL_USERNAME }} 66 | JRELEASER_MAVENCENTRAL_SONATYPE_PASSWORD: ${{ secrets.JRELEASER_NEXUS2_MAVEN_CENTRAL_PASSWORD }} 67 | JRELEASER_GPG_PASSPHRASE: ${{ secrets.JRELEASER_GPG_PASSPHRASE }} 68 | JRELEASER_GPG_PUBLIC_KEY: ${{ secrets.JRELEASER_GPG_PUBLIC_KEY }} 69 | JRELEASER_GPG_SECRET_KEY: ${{ secrets.JRELEASER_GPG_SECRET_KEY }} 70 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Run tests 2 | 3 | on: [push] 4 | 5 | jobs: 6 | tests: 7 | name: Run tests with spring boot version compatibility check 8 | runs-on: ubuntu-latest 9 | strategy: 10 | matrix: 11 | spring-boot: [ '3.3.13', '3.4.7', '3.5.3' ] 12 | 13 | steps: 14 | - name: Checkout repo 15 | uses: actions/checkout@v3 16 | 17 | - name: Setup Node 18 | uses: actions/setup-node@v3 19 | 20 | - name: Cache Node.js modules 21 | uses: actions/cache@v3 22 | with: 23 | path: ~/.npm 24 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} 25 | restore-keys: | 26 | ${{ runner.os }}-node- 27 | 28 | - name: Install Node.js dependencies 29 | run: | 30 | cd db-scheduler-ui-frontend 31 | npm install 32 | 33 | - name: Set up JDK 17 34 | uses: actions/setup-java@v3 35 | with: 36 | java-version: '17' 37 | distribution: 'temurin' 38 | 39 | - name: Run tests 40 | run: mvn -B -Dspring-boot.version=${{ matrix.spring-boot }} clean test --file pom.xml 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | **/target/ 3 | *.class 4 | 5 | # Compiled frontend files 6 | **/static/ 7 | **/dist 8 | 9 | # Log file 10 | *.log 11 | 12 | # Local database 13 | *.mv.db 14 | *.trace.db 15 | 16 | # Package Files # 17 | *.jar 18 | *.war 19 | *.nar 20 | *.ear 21 | *.zip 22 | *.tar.gz 23 | *.rar 24 | 25 | ### IntelliJ IDEA ### 26 | *.idea 27 | *.iws 28 | *.iml 29 | *.ipr 30 | 31 | ### VSCode ### 32 | .vscode 33 | 34 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 35 | hs_err_pid* 36 | replay_pid* 37 | 38 | *.DS_Store 39 | /db-scheduler-ui/src/main/resources/templates/index.html 40 | /db-scheduler-ui/src/main/resources/templates/db-scheduler-ui/index.html 41 | 42 | ### STS ### 43 | .apt_generated 44 | .classpath 45 | .factorypath 46 | .project 47 | .settings 48 | .springBeans 49 | .sts4-cache 50 | .dbeaver 51 | -------------------------------------------------------------------------------- /.license/license-header.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) Bekk 2 | 3 |

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 4 | except in compliance with the License. You may obtain a copy of the License at 5 | 6 |

http://www.apache.org/licenses/LICENSE-2.0 7 | 8 |

Unless required by applicable law or agreed to in writing, software distributed under the 9 | License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 10 | express or implied. See the License for the specific language governing permissions and 11 | limitations under the License. 12 | -------------------------------------------------------------------------------- /Screenshot_Frontpage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekk/db-scheduler-ui/d994f22d50de884349f5589adc8953a6ef35f1fa/Screenshot_Frontpage.png -------------------------------------------------------------------------------- /Screenshot_Group.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekk/db-scheduler-ui/d994f22d50de884349f5589adc8953a6ef35f1fa/Screenshot_Group.png -------------------------------------------------------------------------------- /Screenshot_History.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bekk/db-scheduler-ui/d994f22d50de884349f5589adc8953a6ef35f1fa/Screenshot_History.png -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/.env.development: -------------------------------------------------------------------------------- 1 | VITE_API_BASE_URL=http://localhost:8081/db-scheduler-api 2 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/.env.production: -------------------------------------------------------------------------------- 1 | VITE_UI_BASE_URL="db-scheduler-ui" -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2022: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:@typescript-eslint/recommended', 7 | 'plugin:react-hooks/recommended', 8 | 'eslint-config-prettier', 9 | ], 10 | settings: { 11 | 'import/resolver': 'vite', 12 | }, 13 | ignorePatterns: ['dist', '.eslintrc.cjs'], 14 | parser: '@typescript-eslint/parser', 15 | plugins: ['react-refresh', 'import'], 16 | rules: { 17 | 'react-refresh/only-export-components': [ 18 | 'warn', 19 | { allowConstantExport: true }, 20 | ], 21 | 'no-console': 'warn', 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "tabWidth": 2, 4 | "useTabs": false, 5 | "semi": true, 6 | "singleQuote": true, 7 | "jsxSingleQuote": false, 8 | "trailingComma": "all", 9 | "bracketSpacing": true, 10 | "jsxBracketSameLine": false, 11 | "arrowParens": "always", 12 | "requirePragma": false, 13 | "insertPragma": false, 14 | "proseWrap": "preserve" 15 | } 16 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/README.md: -------------------------------------------------------------------------------- 1 | # DB Scheduler UI frontend 2 | 3 | ## React + TypeScript + Vite 4 | 5 | ### Getting started: 6 | 7 | 1. Install dependencies: `npm install` 8 | 2. Run the app: `npm run dev` 9 | 3. Run the backend `DemoApplication.java` 10 | 4. Open the app: http://localhost:51373 -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | DB Scheduler UI 8 | 9 | 10 | 11 |

12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/main.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | import ReactDOM from 'react-dom/client'; 15 | import { BrowserRouter } from 'react-router-dom'; 16 | import App from './src/App'; 17 | 18 | const basename = (window.CONTEXT_PATH || '') + import.meta.env.BASE_URL; 19 | 20 | ReactDOM.createRoot(document.getElementById('root')!).render( 21 | 22 | 23 | , 24 | ); 25 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "db-scheduler-ui", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "@chakra-ui/icons": "^2.1.0", 14 | "@chakra-ui/react": "^2.8.0", 15 | "@emotion/react": "^11.11.1", 16 | "@emotion/styled": "^11.11.0", 17 | "@tanstack/react-query": "^4.33.0", 18 | "date-fns": "^2.30.0", 19 | "framer-motion": "^10.15.2", 20 | "react": "^18.2.0", 21 | "react-datepicker": "^4.17.0", 22 | "react-dom": "^18.2.0", 23 | "react-router-dom": "^6.15.0" 24 | }, 25 | "devDependencies": { 26 | "@tanstack/react-query-devtools": "^4.33.0", 27 | "@types/node": "^20.5.0", 28 | "@types/react": "^18.2.15", 29 | "@types/react-datepicker": "^4.15.0", 30 | "@types/react-dom": "^18.2.7", 31 | "@typescript-eslint/eslint-plugin": "^6.0.0", 32 | "@typescript-eslint/parser": "^6.0.0", 33 | "@vitejs/plugin-react": "^4.0.3", 34 | "eslint": "^8.47.0", 35 | "eslint-config-prettier": "^9.0.0", 36 | "eslint-import-resolver-vite": "^1.3.2", 37 | "eslint-plugin-import": "^2.28.1", 38 | "eslint-plugin-react-hooks": "^4.6.0", 39 | "eslint-plugin-react-refresh": "^0.4.3", 40 | "prettier": "^3.0.2", 41 | "typescript": "^5.0.2", 42 | "vite": "^4.4.5", 43 | "vite-plugin-eslint": "^1.8.1" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/public/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/App.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | import { ChakraProvider } from '@chakra-ui/react'; 15 | import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; 16 | import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; 17 | import { theme } from 'src/styles/theme'; 18 | import { FrontPage } from './pages/FrontPage'; 19 | 20 | const queryClient = new QueryClient({ 21 | defaultOptions: { queries: { refetchInterval: 2000 } }, 22 | }); 23 | 24 | function App() { 25 | return ( 26 | 27 | 28 | 29 | 30 | 31 | 32 | ); 33 | } 34 | 35 | export default App; 36 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/assets/icons/DoubleChevronIcon.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | import { createIcon } from '@chakra-ui/icons'; 15 | import React from 'react'; 16 | 17 | export const DoubleChevronIcon = createIcon({ 18 | displayName: 'DoubleChevronIcon', 19 | viewBox: '0 0 24 24', 20 | path: ( 21 | 26 | ), 27 | }); 28 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/assets/icons/IoEllipsisVerticalIcon.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | import { createIcon } from '@chakra-ui/icons'; 15 | import React from 'react'; 16 | 17 | export const IoEllipsisVerticalIcon = createIcon({ 18 | displayName: 'IoEllipsisVertical', 19 | viewBox: '0 0 24 24', 20 | path: ( 21 | 28 | 32 | 36 | 40 | 41 | ), 42 | }); 43 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/assets/icons/Logo.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | import { createIcon } from '@chakra-ui/icons'; 15 | 16 | export const LogoIcon = createIcon({ 17 | displayName: 'LogoIcon', 18 | viewBox: '0 0 32 32', 19 | path: ( 20 | 27 | 28 | 29 | 30 | 31 | ), 32 | }); 33 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/assets/icons/PlayIcon.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | import { createIcon } from '@chakra-ui/icons'; 15 | import React from 'react'; 16 | 17 | export const PlayIcon = createIcon({ 18 | displayName: 'Play', 19 | viewBox: '0 0 512 512', 20 | path: ( 21 | 27 | ), 28 | }); 29 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/assets/icons/RepeatIcon.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | import { createIcon } from '@chakra-ui/icons'; 15 | import React from 'react'; 16 | 17 | export const RepeatIcon = createIcon({ 18 | displayName: 'Repeat', 19 | viewBox: '0 0 24 24', 20 | path: ( 21 | 22 | 30 | 38 | 46 | 47 | ), 48 | }); 49 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/assets/icons/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | export { PlayIcon } from './PlayIcon'; 15 | export { DoubleChevronIcon } from './DoubleChevronIcon'; 16 | export { IoEllipsisVerticalIcon } from './IoEllipsisVerticalIcon'; 17 | export { RepeatIcon } from './RepeatIcon'; -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/components/common/JsonViewer.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | import React from 'react'; 15 | import colors from 'src/styles/colors'; 16 | 17 | type JsonViewerProps = { 18 | data: object | null; 19 | }; 20 | 21 | const JsonViewer: React.FC = ({ data }) => { 22 | const renderJson = (data: object | null, indent: number = 0) => { 23 | if (typeof data === 'object' && data !== null) { 24 | return ( 25 |

26 | {Array.isArray(data) 27 | ? data.map((item, index) => ( 28 |
{renderJson(item, indent + 1)}
29 | )) 30 | : Object.entries(data).map(([key, value]) => ( 31 |
32 | {key}:{' '} 33 | {typeof value === 'object' ? ( 34 | renderJson(value, indent + 1) 35 | ) : ( 36 | 37 | {JSON.stringify(value)} 38 | 39 | )} 40 |
41 | ))} 42 |
43 | ); 44 | } else { 45 | return {JSON.stringify(data)}; 46 | } 47 | }; 48 | 49 | return
{renderJson(data)}
; 50 | }; 51 | 52 | export default JsonViewer; 53 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/components/common/NumberCircle.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | import { Box, ResponsiveValue } from '@chakra-ui/react'; 15 | import { Property } from 'csstype'; 16 | import colors from 'src/styles/colors'; 17 | 18 | interface NumberCircleProps { 19 | number: number | string; 20 | bgColor?: string; 21 | textColor?: string; 22 | position?: ResponsiveValue; 23 | transform?: ResponsiveValue; 24 | style?: React.CSSProperties; 25 | top?: string | number; 26 | } 27 | 28 | export const NumberCircle: React.FC = ({ 29 | number, 30 | bgColor, 31 | textColor, 32 | position = 'absolute', 33 | transform, 34 | style, 35 | top, 36 | }) => { 37 | const powerOfTen = (number + '').length - 1; 38 | const isExpanded = 1 <= powerOfTen; 39 | 40 | const baseSize: number = 22; 41 | 42 | const width = isExpanded ? baseSize + 7 * powerOfTen : baseSize; 43 | 44 | const height: number = 22; 45 | 46 | const borderRadius = isExpanded 47 | ? `${baseSize / 2}px ${baseSize / 2}px ${baseSize / 2}px ${baseSize / 2}px` 48 | : '50%'; 49 | const leftOffset = isExpanded ? (width - baseSize) / 2 : 0; 50 | 51 | return ( 52 | 68 | {number} 69 | 70 | ); 71 | }; 72 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/components/common/NumberCircleGroup.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | import React from 'react'; 15 | import { Task } from 'src/models/Task'; 16 | import { NumberCircle } from './NumberCircle'; 17 | import { Box } from '@chakra-ui/react'; 18 | import colors from 'src/styles/colors'; 19 | 20 | export const NumberCircleGroup: React.FC = ({ 21 | pickedBy, 22 | consecutiveFailures, 23 | taskInstance, 24 | }) => { 25 | const runningCount = pickedBy?.reduce( 26 | (acc, entry) => (entry !== null ? acc + 1 : acc), 27 | 0, 28 | ); 29 | const failureCount = consecutiveFailures?.reduce( 30 | (acc, entry) => (entry > 0 ? acc + 1 : acc), 31 | 0, 32 | ); 33 | const scheduledCount = taskInstance.length - runningCount - failureCount; 34 | 35 | return ( 36 | 37 | {failureCount > 0 && ( 38 | 44 | )} 45 | {runningCount > 0 && ( 46 | 52 | )} 53 | {scheduledCount > 0 && ( 54 | 61 | )} 62 | 63 | ); 64 | }; 65 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/components/common/RefreshCircle.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | import { Box } from '@chakra-ui/react'; 15 | import { useState } from 'react'; 16 | import { NumberCircle } from './NumberCircle'; 17 | 18 | type RefreshCircleProps = { 19 | number: number; 20 | color: string; 21 | textColor?: string; 22 | visible?: boolean; 23 | hoverText: string; 24 | }; 25 | 26 | export const RefreshCircle: React.FC = ({ 27 | number, 28 | color, 29 | textColor, 30 | visible, 31 | hoverText, 32 | }) => { 33 | const [hovered, setHovered] = useState(false); 34 | 35 | const text = hovered ? hoverText : ''; 36 | 37 | const powerOfTen = (number + hoverText).length - 1; 38 | const isExpanded = 1 <= powerOfTen; 39 | const baseSize: number = 22; 40 | const width = isExpanded ? baseSize + 7 * powerOfTen : baseSize; 41 | 42 | const marginLeft = isExpanded ? -width : 0; 43 | 44 | return ( 45 | 54 | setHovered(true)} 56 | onMouseLeave={() => setHovered(false)} 57 | > 58 | 66 | 67 | 68 | ); 69 | }; 70 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/components/common/StatusBox.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | import { Box } from '@chakra-ui/react'; 15 | import { NumberCircle } from 'src/components/common/NumberCircle'; 16 | import colors from 'src/styles/colors'; 17 | 18 | interface StatusBoxProps { 19 | status: string; 20 | consecutiveFailures: number; 21 | } 22 | 23 | const statusColors: Record< 24 | string, 25 | { borderColor: string; backgroundColor: string; color: string } 26 | > = { 27 | Failed: { 28 | borderColor: colors.failed['200'], 29 | backgroundColor: colors.failed['100'], 30 | color: colors.failed['200'], 31 | }, 32 | Running: { 33 | borderColor: colors.running['100'], 34 | backgroundColor: colors.running['100'], 35 | color: colors.primary['900'], 36 | }, 37 | Scheduled: { 38 | borderColor: colors.primary['300'], 39 | backgroundColor: colors.primary['200'], 40 | color: colors.primary['900'], 41 | }, 42 | Group: { 43 | borderColor: colors.primary['900'], 44 | backgroundColor: colors.primary['200'], 45 | color: colors.primary['900'], 46 | }, 47 | }; 48 | 49 | export const StatusBox: React.FC = ({ 50 | status, 51 | consecutiveFailures, 52 | }) => { 53 | const statusInfo = 54 | status === 'Group' 55 | ? statusColors['Group'] 56 | : statusColors[status] || statusColors['Scheduled']; 57 | const { borderColor, backgroundColor, color } = statusInfo; 58 | 59 | return ( 60 | 74 | {consecutiveFailures > 0 && status !== 'Group' ? ( 75 | 80 | ) : ( 81 | <> 82 | )} 83 | 84 | {status} 85 | 86 | ); 87 | }; 88 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/components/common/TitleRow.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | import React from 'react'; 15 | 16 | import { Box, HStack } from '@chakra-ui/react'; 17 | 18 | import { SortButton } from 'src/components/input/SortButton'; 19 | import colors from 'src/styles/colors'; 20 | import { SortBy } from 'src/models/QueryParams'; 21 | 22 | interface TitleRowProps { 23 | currentSort: SortBy; 24 | setCurrentSort: React.Dispatch>; 25 | sortAsc: boolean; 26 | setSortAsc: React.Dispatch>; 27 | isDetailsView?: boolean; 28 | } 29 | 30 | const TitleRow: React.FC = ({ 31 | currentSort, 32 | setCurrentSort, 33 | setSortAsc, 34 | sortAsc, 35 | isDetailsView, 36 | }) => ( 37 | 43 | 49 | Status 50 | 51 | {!isDetailsView && ( 52 | 60 | )} 61 | 67 | Task-ID 68 | 69 | 77 | 78 | ); 79 | 80 | export default TitleRow; 81 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/components/history/LogAccordionButton.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | import { AccordionButton, AccordionIcon, Box, HStack } from '@chakra-ui/react'; 15 | import { LogStatus } from 'src/components/history/LogStatus'; 16 | import { dateFormatText } from 'src/utils/dateFormatText'; 17 | import React from 'react'; 18 | import { useParams } from 'react-router-dom'; 19 | import { AttachmentIcon } from '@chakra-ui/icons'; 20 | 21 | interface LogAccordionButtonProps { 22 | succeeded: boolean; 23 | id: number; 24 | taskName: string; 25 | taskData: object | null; 26 | taskInstance: string; 27 | exceptionClass: string | null; 28 | exceptionMessage: string | null; 29 | timeFinished: Date; 30 | } 31 | 32 | export const LogAccordionButton: React.FC = ( 33 | props, 34 | ) => { 35 | const { taskName } = useParams(); 36 | return ( 37 |

38 | 44 | 45 | 46 | 47 | {props.taskData != null && ( 48 | 52 | )} 53 | 54 | 57 | 58 | {props.taskInstance} 59 | 60 | 61 | {dateFormatText(new Date(props.timeFinished))} 62 | 63 | 64 | {props.exceptionMessage} 65 | 66 | 67 | 68 | 69 |

70 | ); 71 | }; 72 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/components/history/LogAccordionItem.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | import { AccordionPanel, Box, Divider, Text, VStack } from '@chakra-ui/react'; 15 | import colors from 'src/styles/colors'; 16 | import { LogDataRow } from 'src/components/history/LogDataRow'; 17 | interface LogAccordionItemProps { 18 | taskData: object | null; 19 | stackTrace: string | null; 20 | } 21 | 22 | export const LogAccordionItem: React.FC = ({ 23 | stackTrace, 24 | taskData, 25 | }) => { 26 | return ( 27 | 28 | 29 | 37 | {stackTrace && ( 38 | 43 | Stacktrace 44 | 45 | )} 46 | 47 |

{stackTrace}
48 | {stackTrace && taskData && } 49 | {taskData && ( 50 | 55 | Taskdata 56 | 57 | )} 58 | 59 | 60 | 61 | 62 | ); 63 | }; 64 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/components/history/LogCard.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | import { Log } from 'src/models/Log'; 15 | import { AccordionItem, Divider } from '@chakra-ui/react'; 16 | import { LogAccordionButton } from 'src/components/history/LogAccordionButton'; 17 | import { LogAccordionItem } from 'src/components/history/LogAccordionItem'; 18 | import colors from 'src/styles/colors'; 19 | 20 | interface LogCardProps { 21 | log: Log; 22 | } 23 | 24 | export const LogCard: React.FC = ({ log }) => ( 25 | 32 | 42 | 43 | 47 | 48 | ); 49 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/components/history/LogDataRow.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | import { Box } from '@chakra-ui/react'; 15 | import React from 'react'; 16 | 17 | import JsonViewer from 'src/components/common/JsonViewer'; 18 | 19 | export const LogDataRow: React.FC<{ taskData: object | null }> = ({ 20 | taskData, 21 | }) => { 22 | return ( 23 | 24 | {taskData !== null && ( 25 | 26 | 27 | 28 | )} 29 | 30 | ); 31 | }; 32 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/components/history/LogStatus.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | import { Box } from '@chakra-ui/react'; 15 | import colors from 'src/styles/colors'; 16 | interface LogStatusProps { 17 | succeeded: boolean; 18 | } 19 | 20 | export const LogStatus: React.FC = ({ succeeded }) => { 21 | const borderColor = succeeded ? colors.success[200] : colors.failed[200]; 22 | const backgroundColor = succeeded ? colors.success[100] : colors.failed[100]; 23 | const color = succeeded ? colors.success[200] : colors.failed[200]; 24 | 25 | return ( 26 | 39 | {succeeded ? 'Succeeded' : 'Failed'} 40 | 41 | ); 42 | }; 43 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/components/input/DateTimeInput.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | import DatePicker from 'react-datepicker'; 15 | import { Box } from '@chakra-ui/react'; 16 | import 'react-datepicker/dist/react-datepicker.css'; 17 | import colors from 'src/styles/colors'; 18 | 19 | interface DateTimeInputProps { 20 | selectedDate: Date | null; 21 | onChange: (date: Date | null) => void; 22 | forceFutureTime?: boolean; 23 | } 24 | 25 | export const DateTimeInput: React.FC = ({ 26 | selectedDate, 27 | onChange, 28 | forceFutureTime, 29 | }) => { 30 | return ( 31 | 39 | 57 | 58 | ); 59 | }; 60 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/components/input/FilterBox.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | import { 15 | Box, 16 | Button, 17 | Menu, 18 | MenuButton, 19 | MenuItem, 20 | MenuList, 21 | } from '@chakra-ui/react'; 22 | import React from 'react'; 23 | import { ChevronDownIcon } from '@chakra-ui/icons'; 24 | import colors from 'src/styles/colors'; 25 | import { FilterBy } from 'src/models/QueryParams'; 26 | 27 | export const FilterBox: React.FC<{ 28 | currentFilter: FilterBy; 29 | setCurrentFilter: (filter: FilterBy) => void; 30 | history?: boolean; 31 | }> = ({ currentFilter, setCurrentFilter, history }) => { 32 | const filters = history 33 | ? [FilterBy.All, FilterBy.Failed, FilterBy.Succeeded] 34 | : [FilterBy.All, FilterBy.Failed, FilterBy.Running, FilterBy.Scheduled]; 35 | 36 | return ( 37 | 45 |

46 | } 49 | backgroundColor={colors.primary['100']} 50 | borderColor={colors.primary['300']} 51 | borderWidth={1} 52 | width={200} 53 | textAlign={'left'} 54 | fontWeight={'normal'} 55 | ml={14} 56 | > 57 | Status: {currentFilter} 58 | 59 | 60 | {filters.map((filterValue) => ( 61 | setCurrentFilter(filterValue)} 64 | fontWeight={currentFilter === filterValue ? 'bold' : 'normal'} 65 | > 66 | {filterValue} 67 | 68 | ))} 69 | 70 | 71 | 72 | ); 73 | }; 74 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/components/input/PaginationButtons.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | import { Box, Button } from '@chakra-ui/react'; 15 | import { ChevronLeftIcon, ChevronRightIcon } from '@chakra-ui/icons'; 16 | import { DoubleChevronIcon } from 'src/assets/icons/DoubleChevronIcon'; 17 | import React from 'react'; 18 | 19 | interface PaginationButtonsProps { 20 | page: number; 21 | limit: number; 22 | setPage: (value: number) => void; 23 | numberOfPages: number | undefined; 24 | } 25 | 26 | const PaginationButtons: React.FC = ({ 27 | page, 28 | setPage, 29 | numberOfPages, 30 | }) => ( 31 | 32 | {page > 2 && ( 33 | 47 | )} 48 | {page > 1 && ( 49 | 52 | )} 53 | {'page ' + page + ' of ' + (numberOfPages || 1)} 54 | 63 | 72 | 73 | ); 74 | 75 | export default PaginationButtons; 76 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/components/input/SortButton.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | import { Text } from '@chakra-ui/react'; 15 | import React from 'react'; 16 | import { ChevronDownIcon } from '@chakra-ui/icons'; 17 | import colors from 'src/styles/colors'; 18 | import { SortBy } from 'src/models/QueryParams'; 19 | 20 | export const SortButton: React.FC<{ 21 | title: string; 22 | name: SortBy; 23 | currentSort: SortBy; 24 | setCurrentSort: (sorting: SortBy) => void; 25 | sortAsc: boolean; 26 | setSortAsc: (sortAsc: boolean) => void; 27 | }> = ({ title, currentSort, setCurrentSort, name, sortAsc, setSortAsc }) => ( 28 | 35 | currentSort === name ? setSortAsc(!sortAsc) : setCurrentSort(name) 36 | } 37 | > 38 | {title} 39 | 49 | 50 | ); 51 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/components/scheduled/TaskAccordionItem.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | import { AccordionPanel, Box, VStack } from '@chakra-ui/react'; 15 | import { TaskDataRow } from 'src/components/scheduled/TaskDataRow'; 16 | import React from 'react'; 17 | import { dateFormatText } from 'src/utils/dateFormatText'; 18 | import colors from 'src/styles/colors'; 19 | 20 | interface TaskAccordionItemProps { 21 | lastSuccess: Date | null; 22 | lastFailure: Date | null; 23 | taskData: (object | null)[]; 24 | } 25 | 26 | export const TaskAccordionItem: React.FC = ({ 27 | lastFailure, 28 | lastSuccess, 29 | taskData, 30 | }) => ( 31 | 32 | 33 | 41 | {(lastSuccess || lastFailure) && ( 42 | 43 | Last Execution Time: 44 | {lastSuccess 45 | ? dateFormatText(new Date(lastSuccess)) 46 | : lastFailure && dateFormatText(new Date(lastFailure))} 47 | 48 | )} 49 | 50 | 51 | 52 | 53 | ); 54 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/components/scheduled/TaskCard.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | import React from 'react'; 15 | 16 | import { AccordionItem, AccordionItemProps, Divider } from '@chakra-ui/react'; 17 | import { Task } from 'src/models/Task'; 18 | 19 | import { TaskAccordionButton } from 'src/components/scheduled/TaskAccordionButton'; 20 | import { TaskAccordionItem } from 'src/components/scheduled/TaskAccordionItem'; 21 | import { isStatus } from 'src/utils/determineStatus'; 22 | import colors from 'src/styles/colors'; 23 | 24 | interface TaskCardProps extends Task { 25 | refetch: () => void; 26 | accordionProps?: AccordionItemProps; 27 | } 28 | 29 | const TaskCard: React.FC = (props) => { 30 | const { accordionProps, lastSuccess, lastFailure, taskData } = props; 31 | 32 | return ( 33 | 42 | 43 | {!isStatus('Group', props) && ( 44 | <> 45 | 46 | 51 | 52 | )} 53 | 54 | ); 55 | }; 56 | export default TaskCard; 57 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/components/scheduled/TaskDataRow.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | import { Box } from '@chakra-ui/react'; 15 | import React from 'react'; 16 | import JsonViewer from 'src/components/common/JsonViewer'; 17 | 18 | export const TaskDataRow: React.FC<{ taskData: (object | null)[] }> = ({ 19 | taskData, 20 | }) => { 21 | return ( 22 | <> 23 | {taskData[0] !== null && ( 24 | 25 | 26 | 27 | )} 28 | 29 | ); 30 | }; 31 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/components/scheduled/TaskGroupCard.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | import React, { useRef, useState, useEffect } from 'react'; 15 | import { Task } from 'src/models/Task'; 16 | import TaskCard from './TaskCard'; 17 | import { Box } from '@chakra-ui/react'; 18 | 19 | interface TaskCardProps extends Task { 20 | refetch: () => void; 21 | } 22 | 23 | const TaskGroupCard: React.FC = (taskProps) => { 24 | const [marginBottom, setMarginBottom] = useState(0); 25 | const ref = useRef(null); 26 | 27 | const updateRef = () => { 28 | if (ref.current) { 29 | const el = ref.current; 30 | setMarginBottom(-(el.clientHeight * 1.8)); 31 | } 32 | }; 33 | 34 | useEffect(() => { 35 | updateRef(); 36 | window.addEventListener('resize', updateRef); // updateRef on resize to avoid overlapping 37 | return () => { 38 | window.removeEventListener('resize', updateRef); 39 | }; 40 | }, []); 41 | 42 | return ( 43 | 44 | 45 | 51 | 52 | 59 | 66 | 67 | 75 | 82 | 83 | 84 | ); 85 | }; 86 | 87 | export default TaskGroupCard; 88 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/models/Log.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | export type Log = { 15 | id: number; 16 | taskName: string; 17 | taskInstance: string; 18 | taskData: object | null; 19 | pickedBy: string | null; 20 | timeStarted: Date; 21 | timeFinished: Date; 22 | succeeded: boolean; 23 | durationMs: number; 24 | exceptionClass: string | null; 25 | exceptionMessage: string | null; 26 | exceptionStackTrace: string | null; 27 | }; 28 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/models/PollResponse.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | export interface PollResponse { 15 | newFailures: number; 16 | newRunning: number; 17 | newTasks: number; 18 | newSucceeded?: number; 19 | stoppedFailing: number; 20 | finishedRunning: number; 21 | } -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/models/QueryParams.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | export enum FilterBy { 15 | All = 'All', 16 | Failed = 'Failed', 17 | Running = 'Running', 18 | Scheduled = 'Scheduled', 19 | Succeeded = 'Succeeded', 20 | } 21 | 22 | export interface PaginationParams { 23 | pageNumber: number; 24 | limit: number; 25 | } 26 | 27 | export enum SortBy { 28 | Default = 'Default', 29 | Name = 'Name', 30 | } -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/models/Task.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | export type Task = { 15 | taskName: string; 16 | taskInstance: string[]; 17 | taskData: (object|null)[]; 18 | executionTime: Date[]; 19 | picked: boolean; 20 | pickedBy: (string|null)[]; 21 | lastSuccess: Date[] | null; 22 | lastFailure: Date | null; 23 | consecutiveFailures: number[]; 24 | lastHeartbeat: Date | null; 25 | version: number; 26 | }; 27 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/models/TaskRequestParams.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | import { FilterBy, SortBy } from './QueryParams'; 15 | 16 | export interface TaskRequestParams { 17 | filter: FilterBy; 18 | pageNumber?: number; 19 | limit?: number; 20 | size?: number; 21 | sorting?: SortBy; 22 | asc?: boolean; 23 | searchTermTaskName?: string; 24 | searchTermTaskInstance?: string; 25 | startTime?: Date; 26 | endTime?: Date; 27 | refresh?: boolean; 28 | taskNameExactMatch?: boolean; 29 | taskInstanceExactMatch?: boolean; 30 | } 31 | 32 | export interface TaskDetailsRequestParams extends TaskRequestParams { 33 | taskName?: string; 34 | taskId?: string; 35 | } 36 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/models/TasksResponse.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | import { Task } from "src/models/Task"; 15 | import { Log } from "./Log"; 16 | 17 | 18 | export interface InfiniteScrollResponse { 19 | items: ItemType[]; 20 | numberOfItems: number; 21 | numberOfPages: number; 22 | } 23 | 24 | export type TasksResponse = InfiniteScrollResponse; 25 | 26 | export type LogResponse = InfiniteScrollResponse; 27 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/pages/FrontPage.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | import { Box } from '@chakra-ui/react'; 15 | import { Route, Routes } from 'react-router-dom'; 16 | import { TopBar } from 'src/components/common/TopBar'; 17 | import { LogList } from 'src/components/history/LogList'; 18 | import TaskList from 'src/components/scheduled/TaskList'; 19 | import { getShowHistory } from 'src/utils/config'; 20 | 21 | export const FrontPage: React.FC = () => { 22 | const showHistory = getShowHistory(); 23 | return ( 24 | <> 25 | 26 | 27 | 28 | }> 29 | }> 30 | }> 31 | } 34 | > 35 | {showHistory && ( 36 | }> 37 | )} 38 | 39 | 40 | 41 | ); 42 | }; 43 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/services/deleteTask.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | const API_BASE_URL: string = 15 | (import.meta.env.VITE_API_BASE_URL as string) ?? 16 | window.location.origin + (window.CONTEXT_PATH || '') + '/db-scheduler-api'; 17 | 18 | const deleteTask = async (id: string, name: string) => { 19 | const response = await fetch( 20 | `${API_BASE_URL}/tasks/delete?id=${id}&name=${name}`, 21 | { 22 | method: 'POST', 23 | }, 24 | ); 25 | 26 | if (response.status == 401) { 27 | document.location.href = '/db-scheduler'; 28 | } else if (!response.ok) { 29 | throw new Error(`Error executing task. Status: ${response.statusText}`); 30 | } 31 | }; 32 | 33 | export default deleteTask; 34 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/services/getLogs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | import { TaskDetailsRequestParams } from 'src/models/TaskRequestParams'; 16 | import { LogResponse } from 'src/models/TasksResponse'; 17 | 18 | const API_BASE_URL: string = 19 | (import.meta.env.VITE_API_BASE_URL as string) ?? 20 | window.location.origin + (window.CONTEXT_PATH || '') + '/db-scheduler-api'; 21 | 22 | export const ALL_LOG_QUERY_KEY = `logs/all`; 23 | 24 | export const getLogs = async ( 25 | params: TaskDetailsRequestParams, 26 | ): Promise => { 27 | const queryParams = new URLSearchParams(); 28 | 29 | params.filter && queryParams.append('filter', params.filter.toUpperCase()); 30 | params.pageNumber && 31 | queryParams.append('pageNumber', params.pageNumber.toString()); 32 | params.limit && queryParams.append('size', params.limit.toString()); 33 | params.sorting && queryParams.append('sorting', params.sorting.toUpperCase()); 34 | params.asc!==undefined && queryParams.append('asc', params.asc.toString()); 35 | params.startTime && queryParams.append('startTime', params.startTime.toISOString()); 36 | params.endTime && queryParams.append('endTime', params.endTime.toISOString()); 37 | params.refresh!==undefined && queryParams.append('refresh', params.refresh.toString()); 38 | params.searchTermTaskName && 39 | queryParams.append('searchTermTaskName', params.searchTermTaskName.trim()); 40 | params.searchTermTaskInstance && 41 | queryParams.append( 42 | 'searchTermTaskInstance', 43 | params.searchTermTaskInstance.trim(), 44 | ); 45 | params.taskName && queryParams.append('taskName', params.taskName); 46 | params.taskId && queryParams.append('taskId', params.taskId); 47 | params.taskNameExactMatch !== undefined && 48 | queryParams.append( 49 | 'taskNameExactMatch', 50 | params.taskNameExactMatch.toString(), 51 | ); 52 | params.taskInstanceExactMatch !== undefined && 53 | queryParams.append( 54 | 'taskInstanceExactMatch', 55 | params.taskInstanceExactMatch.toString(), 56 | ); 57 | 58 | const response = await fetch(`${API_BASE_URL}/logs/all?${queryParams}`, { 59 | method: 'GET', 60 | headers: { 61 | 'Content-Type': 'application/json', 62 | }, 63 | }); 64 | 65 | if (response.status == 401) { 66 | document.location.href = '/db-scheduler'; 67 | } else if (!response.ok) { 68 | throw new Error(`Error fetching logs. Status: ${response.statusText}`); 69 | } 70 | 71 | return await response.json(); 72 | }; 73 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/services/getTask.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | import { TasksResponse } from 'src/models/TasksResponse'; 15 | import { TaskDetailsRequestParams } from 'src/models/TaskRequestParams'; 16 | 17 | const API_BASE_URL: string = 18 | (import.meta.env.VITE_API_BASE_URL as string) ?? 19 | window.location.origin + (window.CONTEXT_PATH || '') + '/db-scheduler-api'; 20 | 21 | export const TASK_DETAILS_QUERY_KEY = `tasks/details`; 22 | 23 | export const getTask = async ( 24 | params: TaskDetailsRequestParams, 25 | ): Promise => { 26 | const queryParams = new URLSearchParams(); 27 | 28 | params.filter && queryParams.append('filter', params.filter.toUpperCase()); 29 | params.pageNumber && 30 | queryParams.append('pageNumber', params.pageNumber.toString()); 31 | params.limit && queryParams.append('size', params.limit.toString()); 32 | params.sorting && queryParams.append('sorting', params.sorting.toUpperCase()); 33 | params.asc !== undefined && queryParams.append('asc', params.asc.toString()); 34 | params.refresh !== undefined && 35 | queryParams.append('refresh', params.refresh.toString()); 36 | params.searchTermTaskName && 37 | queryParams.append('searchTermTaskName', params.searchTermTaskName.trim()); 38 | params.searchTermTaskInstance && 39 | queryParams.append( 40 | 'searchTermTaskInstance', 41 | params.searchTermTaskInstance.trim(), 42 | ); 43 | params.taskName && queryParams.append('taskName', params.taskName); 44 | params.taskId && queryParams.append('taskId', params.taskId); 45 | params.taskNameExactMatch !== undefined && 46 | queryParams.append( 47 | 'taskNameExactMatch', 48 | params.taskNameExactMatch.toString(), 49 | ); 50 | params.taskInstanceExactMatch !== undefined && 51 | queryParams.append( 52 | 'taskInstanceExactMatch', 53 | params.taskInstanceExactMatch.toString(), 54 | ); 55 | 56 | const response = await fetch(`${API_BASE_URL}/tasks/details?${queryParams}`, { 57 | method: 'GET', 58 | headers: { 59 | 'Content-Type': 'application/json', 60 | }, 61 | }); 62 | 63 | if (response.status == 401) { 64 | document.location.href = '/db-scheduler'; 65 | } else if (!response.ok) { 66 | throw new Error(`Error fetching tasks. Status: ${response.statusText}`); 67 | } 68 | 69 | return await response.json(); 70 | }; 71 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/services/getTasks.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | import { TasksResponse } from 'src/models/TasksResponse'; 15 | import { TaskRequestParams } from 'src/models/TaskRequestParams'; 16 | 17 | const API_BASE_URL: string = 18 | (import.meta.env.VITE_API_BASE_URL as string) ?? 19 | window.location.origin + (window.CONTEXT_PATH || '') + '/db-scheduler-api'; 20 | 21 | export const TASK_QUERY_KEY = `tasks`; 22 | 23 | export const getTasks = async ( 24 | params: TaskRequestParams, 25 | ): Promise => { 26 | const queryParams = new URLSearchParams(); 27 | 28 | params.filter && queryParams.append('filter', params.filter.toUpperCase()); 29 | params.pageNumber && 30 | queryParams.append('pageNumber', params.pageNumber.toString()); 31 | params.limit && queryParams.append('size', params.limit.toString()); 32 | params.sorting && queryParams.append('sorting', params.sorting.toUpperCase()); 33 | params.asc !== undefined && queryParams.append('asc', params.asc.toString()); 34 | params.refresh !== undefined && 35 | queryParams.append('refresh', params.refresh.toString()); 36 | params.searchTermTaskName && 37 | queryParams.append('searchTermTaskName', params.searchTermTaskName.trim()); 38 | params.searchTermTaskInstance && 39 | queryParams.append( 40 | 'searchTermTaskInstance', 41 | params.searchTermTaskInstance.trim(), 42 | ); 43 | params.taskNameExactMatch !== undefined && 44 | queryParams.append( 45 | 'taskNameExactMatch', 46 | params.taskNameExactMatch.toString(), 47 | ); 48 | params.taskInstanceExactMatch !== undefined && 49 | queryParams.append( 50 | 'taskInstanceExactMatch', 51 | params.taskInstanceExactMatch.toString(), 52 | ); 53 | 54 | const response = await fetch(`${API_BASE_URL}/tasks/all?${queryParams}`, { 55 | method: 'GET', 56 | headers: { 57 | 'Content-Type': 'application/json', 58 | }, 59 | }); 60 | 61 | if (response.status == 401) { 62 | document.location.href = '/db-scheduler'; 63 | } else if (!response.ok) { 64 | throw new Error(`Error fetching tasks. Status: ${response.statusText}`); 65 | } 66 | 67 | return await response.json(); 68 | }; 69 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/services/pollLogs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | import { PollResponse } from 'src/models/PollResponse'; 15 | import { TaskDetailsRequestParams } from 'src/models/TaskRequestParams'; 16 | 17 | const API_BASE_URL: string = 18 | (import.meta.env.VITE_API_BASE_URL as string) ?? 19 | window.location.origin + (window.CONTEXT_PATH || '') + '/db-scheduler-api'; 20 | 21 | export const POLL_LOGS_QUERY_KEY = `logs/poll`; 22 | 23 | export const pollLogs = async ( 24 | params: TaskDetailsRequestParams, 25 | ): Promise => { 26 | const queryParams = new URLSearchParams(); 27 | 28 | queryParams.append('filter', params.filter.toUpperCase()); 29 | params.pageNumber && 30 | queryParams.append('pageNumber', params.pageNumber.toString()); 31 | params.size && queryParams.append('size', params.size.toString()); 32 | params.sorting && queryParams.append('sorting', params.sorting.toUpperCase()); 33 | params.asc!==undefined && queryParams.append('asc', params.asc.toString()); 34 | params.searchTermTaskName && 35 | queryParams.append('searchTermTaskName', params.searchTermTaskName.trim()); 36 | params.searchTermTaskInstance && 37 | queryParams.append( 38 | 'searchTermTaskInstance', 39 | params.searchTermTaskInstance.trim(), 40 | ); 41 | params.startTime && 42 | queryParams.append('startTime', params.startTime.toISOString()); 43 | params.endTime && queryParams.append('endTime', params.endTime.toISOString()); 44 | params.refresh!==undefined && queryParams.append('refresh', params.refresh.toString()); 45 | params.taskNameExactMatch !== undefined && 46 | queryParams.append( 47 | 'taskNameExactMatch', 48 | params.taskNameExactMatch.toString(), 49 | ); 50 | params.taskInstanceExactMatch !== undefined && 51 | queryParams.append( 52 | 'taskInstanceExactMatch', 53 | params.taskInstanceExactMatch.toString(), 54 | ); 55 | 56 | const response = await fetch(`${API_BASE_URL}/logs/poll?${queryParams}`, { 57 | method: 'GET', 58 | headers: { 59 | 'Content-Type': 'application/json', 60 | }, 61 | }); 62 | 63 | if (response.status == 401) { 64 | document.location.href = '/db-scheduler'; 65 | } else if (!response.ok) { 66 | throw new Error(`Error polling tasks. Status: ${response.statusText}`); 67 | } 68 | 69 | return await response.json(); 70 | }; 71 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/services/pollTasks.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | import { PollResponse } from 'src/models/PollResponse'; 15 | import { TaskDetailsRequestParams } from 'src/models/TaskRequestParams'; 16 | 17 | const API_BASE_URL: string = 18 | (import.meta.env.VITE_API_BASE_URL as string) ?? 19 | window.location.origin + (window.CONTEXT_PATH || '') + '/db-scheduler-api'; 20 | 21 | export const POLL_TASKS_QUERY_KEY = `tasks/poll`; 22 | 23 | export const pollTasks = async ( 24 | params: TaskDetailsRequestParams, 25 | ): Promise => { 26 | const queryParams = new URLSearchParams(); 27 | 28 | queryParams.append('filter', params.filter.toUpperCase()); 29 | params.pageNumber && 30 | queryParams.append('pageNumber', params.pageNumber.toString()); 31 | params.size && queryParams.append('size', params.size.toString()); 32 | params.sorting && queryParams.append('sorting', params.sorting.toUpperCase()); 33 | params.asc !== undefined && queryParams.append('asc', params.asc.toString()); 34 | params.searchTermTaskName && 35 | queryParams.append('searchTermTaskName', params.searchTermTaskName.trim()); 36 | params.searchTermTaskInstance && 37 | queryParams.append( 38 | 'searchTermTaskInstance', 39 | params.searchTermTaskInstance.trim(), 40 | ); 41 | params.startTime && 42 | queryParams.append('startTime', params.startTime.toISOString()); 43 | params.endTime && queryParams.append('endTime', params.endTime.toISOString()); 44 | params.refresh !== undefined && 45 | queryParams.append('refresh', params.refresh.toString()); 46 | params.taskNameExactMatch !== undefined && 47 | queryParams.append( 48 | 'taskNameExactMatch', 49 | params.taskNameExactMatch.toString(), 50 | ); 51 | params.taskInstanceExactMatch !== undefined && 52 | queryParams.append( 53 | 'taskInstanceExactMatch', 54 | params.taskInstanceExactMatch.toString(), 55 | ); 56 | 57 | const response = await fetch(`${API_BASE_URL}/tasks/poll?${queryParams}`, { 58 | method: 'GET', 59 | headers: { 60 | 'Content-Type': 'application/json', 61 | }, 62 | }); 63 | if (response.status == 401) { 64 | document.location.href = '/db-scheduler'; 65 | } else if (!response.ok) { 66 | throw new Error(`Error polling tasks. Status: ${response.statusText}`); 67 | } 68 | 69 | return await response.json(); 70 | }; 71 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/services/runTask.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | const API_BASE_URL: string = 15 | (import.meta.env.VITE_API_BASE_URL as string) ?? 16 | window.location.origin + (window.CONTEXT_PATH || '') + '/db-scheduler-api'; 17 | 18 | const runTask = async (id: string, name: string, scheduleTime?:Date) => { 19 | 20 | const queryParams = new URLSearchParams(); 21 | 22 | queryParams.append('id', id); 23 | queryParams.append('name', name); 24 | if (scheduleTime) { 25 | queryParams.append('scheduleTime', scheduleTime.toISOString()); 26 | } else { 27 | queryParams.append('scheduleTime', new Date().toISOString()); 28 | } 29 | 30 | const response = await fetch( 31 | `${API_BASE_URL}/tasks/rerun?${queryParams}`, 32 | { 33 | method: 'POST', 34 | }, 35 | ); 36 | 37 | if (response.status == 401) { 38 | document.location.href = '/db-scheduler'; 39 | } else if (!response.ok) { 40 | throw new Error(`Error executing task. Status: ${response.statusText}`); 41 | } 42 | }; 43 | 44 | export default runTask; 45 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/services/runTaskGroup.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | const API_BASE_URL: string = 15 | (import.meta.env.VITE_API_BASE_URL as string) ?? 16 | window.location.origin + (window.CONTEXT_PATH || '') + '/db-scheduler-api'; 17 | 18 | const runTaskGroup = async (name: string, onlyFailed: boolean) => { 19 | const response = await fetch( 20 | `${API_BASE_URL}/tasks/rerunGroup?name=${name}&onlyFailed=${onlyFailed}`, 21 | { 22 | method: 'POST', 23 | }, 24 | ); 25 | 26 | if (response.status == 401) { 27 | document.location.href = '/db-scheduler'; 28 | } else if (!response.ok) { 29 | throw new Error(`Error executing task. Status: ${response.statusText}`); 30 | } 31 | }; 32 | 33 | export default runTaskGroup; 34 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/styles/colors.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | const colors = { 15 | primary: { 16 | 100: '#ffffff', 17 | 200: '#efefef', 18 | 300: '#d2d2d2', 19 | 400: '#787878', 20 | 500: '#4f4f4f', 21 | 600: '#3b3b3b', 22 | 900: '#1a1a1a', 23 | }, 24 | running: { 25 | 100: '#DAE2F6', 26 | 200: '#cfd5ff', 27 | 300: '#5068F6', 28 | 400: '#455ad2', 29 | 500: '#3948a6', 30 | }, 31 | failed: { 32 | 100: '#EFC2C2', 33 | 200: '#BB0101', 34 | }, 35 | warning: '#FFB400', 36 | 37 | dbBlue: '#002FA7', 38 | 39 | success: { 40 | 100: '#68D391', 41 | 200: '#08662C', 42 | }, 43 | }; 44 | 45 | export default colors; 46 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/styles/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | import colors from './colors'; 15 | 16 | const theme = { 17 | colors, 18 | }; 19 | 20 | export default theme; 21 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/styles/theme.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | import { extendTheme } from '@chakra-ui/react'; 15 | import colors from 'src/styles/colors'; 16 | 17 | export const theme = extendTheme({ 18 | styles: { 19 | global: { 20 | body: { 21 | bg: colors.primary['200'], 22 | }, 23 | }, 24 | }, 25 | }); 26 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/utils/config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | const API_BASE_URL: string = 16 | (import.meta.env.VITE_API_BASE_URL as string) ?? 17 | window.location.origin + (window.CONTEXT_PATH || '') + '/db-scheduler-api'; 18 | 19 | const config = await fetch(`${API_BASE_URL}/config`).then((res) => 20 | res.json(), 21 | ); 22 | 23 | const showHistory = 24 | 'showHistory' in config ? Boolean(config.showHistory) : false; 25 | 26 | const readOnly = 'readOnly' in config ? Boolean(config.readOnly) : false; 27 | 28 | export const getShowHistory = (): boolean => showHistory; 29 | export const getReadonly = (): boolean => readOnly; 30 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/utils/dateFormatText.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | import { format } from 'date-fns'; 15 | export function dateFormatText(date: Date) { 16 | return format(date, 'dd. MMM yy, H:mm:ss'); 17 | } 18 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/utils/determineStatus.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | import { Task } from "src/models/Task"; 15 | 16 | export const status = ['Failed', 'Running', 'Scheduled', 'Group'] as const; 17 | type StatusType = typeof status[number]; 18 | 19 | export function determineStatus(task: Task): StatusType; 20 | export function determineStatus( 21 | taskInstance: Task["taskInstance"], 22 | pickedBy: Task["pickedBy"], 23 | consecutiveFailures: Task["consecutiveFailures"] 24 | ): StatusType; 25 | 26 | export function determineStatus( 27 | taskOrTaskInstance: Task | Task["taskInstance"], 28 | pickedBy?: Task["pickedBy"], 29 | consecutiveFailures?: Task["consecutiveFailures"] 30 | ): StatusType { 31 | if (typeof taskOrTaskInstance === "object" && 'taskName' in taskOrTaskInstance) { 32 | const task = taskOrTaskInstance; 33 | 34 | if (task.taskInstance.length > 1) return status[3]; 35 | if (task.pickedBy[0]) return status[1]; 36 | if (task.consecutiveFailures[0] > 0) return status[0]; 37 | 38 | return status[2]; 39 | } else { 40 | if (taskOrTaskInstance.length > 1) return status[3]; 41 | if (pickedBy![0]) return status[1]; 42 | if (consecutiveFailures![0] > 0) return status[0]; 43 | 44 | return status[2]; 45 | } 46 | } 47 | 48 | export function isStatus(givenStatus: StatusType, task: Task): boolean; 49 | export function isStatus( 50 | givenStatus: StatusType, 51 | taskInstance: Task["taskInstance"], 52 | pickedBy: Task["pickedBy"], 53 | consecutiveFailures: Task["consecutiveFailures"] 54 | ): boolean; 55 | 56 | export function isStatus( 57 | givenStatus: StatusType, 58 | taskInstanceOrTask: Task["taskInstance"] | Task, 59 | pickedBy?: Task["pickedBy"], 60 | consecutiveFailures?: Task["consecutiveFailures"] 61 | ): boolean { 62 | if (typeof taskInstanceOrTask === "object" && 'taskName' in taskInstanceOrTask) { 63 | return givenStatus === determineStatus(taskInstanceOrTask); 64 | } else { 65 | return givenStatus === determineStatus(taskInstanceOrTask, pickedBy!, consecutiveFailures!); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/utils/global.d.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | export {}; 15 | 16 | declare global { 17 | interface Window { 18 | /** Path to prepended to API calls and the router basename (optional). */ 19 | CONTEXT_PATH: string; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | /// 15 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "lib": ["dom", "dom.iterable", "esnext", "scripthost"], 6 | "allowJs": false, 7 | "allowSyntheticDefaultImports": true, 8 | "esModuleInterop": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "isolatedModules": true, 11 | "jsx": "react-jsx", 12 | "moduleResolution": "Node", 13 | "noEmit": true, 14 | "noFallthroughCasesInSwitch": true, 15 | "resolveJsonModule": true, 16 | "skipLibCheck": true, 17 | "strict": true, 18 | "sourceMap": true, 19 | "types": ["vite/client", "node"], 20 | "baseUrl": ".", 21 | "paths": { 22 | "src/*": ["./src/*"] 23 | } 24 | }, 25 | "include": ["src", "main.tsx"], 26 | "references": [ 27 | { 28 | "path": "./tsconfig.node.json" 29 | } 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": [ 10 | "vite.config.ts" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /db-scheduler-ui-frontend/vite.config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | import react from '@vitejs/plugin-react'; 15 | import { defineConfig } from 'vite'; 16 | import eslintPlugin from 'vite-plugin-eslint'; 17 | 18 | const BASE_URL: string = 19 | process.env.NODE_ENV === 'production' ? '/db-scheduler' : '/'; 20 | 21 | export default defineConfig({ 22 | base: BASE_URL, 23 | server: { 24 | port: 51373, 25 | proxy: { 26 | '/db-scheduler-api': { 27 | target: 'http://localhost:8081', 28 | changeOrigin: true, 29 | }, 30 | }, 31 | }, 32 | build: { 33 | target: 'es2022', 34 | }, 35 | plugins: [react(), eslintPlugin()], 36 | resolve: { 37 | alias: { 38 | src: '/src', 39 | }, 40 | }, 41 | }); 42 | -------------------------------------------------------------------------------- /db-scheduler-ui-starter/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | no.bekk.db-scheduler-ui 7 | db-scheduler-ui-parent 8 | main-SNAPSHOT 9 | 10 | 11 | db-scheduler-ui-starter 12 | db-scheduler-ui-starter 13 | Spring boot starter for db-scheduler-ui 14 | https://github.com/bekk/db-scheduler-ui 15 | 16 | 17 | 18 | org.springframework.boot 19 | spring-boot-configuration-processor 20 | true 21 | 22 | 23 | com.github.kagkarlsson 24 | db-scheduler-spring-boot-starter 25 | 26 | 27 | no.bekk.db-scheduler-ui 28 | db-scheduler-ui 29 | ${project.version} 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-starter-test 34 | test 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /db-scheduler-ui-starter/src/main/java/no/bekk/dbscheduler/uistarter/config/DbSchedulerUiProperties.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package no.bekk.dbscheduler.uistarter.config; 15 | 16 | import lombok.Getter; 17 | import lombok.Setter; 18 | import org.springframework.boot.context.properties.ConfigurationProperties; 19 | 20 | @Setter 21 | @Getter 22 | @ConfigurationProperties("db-scheduler-ui") 23 | public class DbSchedulerUiProperties { 24 | 25 | private boolean enabled = true; 26 | private boolean readOnly = false; 27 | private boolean taskData = true; 28 | private boolean history = false; 29 | private int logLimit = 0; 30 | } 31 | -------------------------------------------------------------------------------- /db-scheduler-ui-starter/src/main/java/no/bekk/dbscheduler/uistarter/config/DbSchedulerUiUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package no.bekk.dbscheduler.uistarter.config; 15 | 16 | public class DbSchedulerUiUtil { 17 | 18 | private DbSchedulerUiUtil() {} 19 | 20 | public static String normalizePath(String path) { 21 | if (path == null || path.trim().isEmpty()) { 22 | return ""; 23 | } 24 | 25 | String normalized = path.trim(); 26 | 27 | if (!normalized.startsWith("/")) { 28 | normalized = "/" + normalized; 29 | } 30 | 31 | if (normalized.length() > 1 && normalized.endsWith("/")) { 32 | normalized = normalized.substring(0, normalized.length() - 1); 33 | } 34 | return normalized; 35 | } 36 | 37 | public static String normalizePaths(String... paths) { 38 | String normalized = ""; 39 | for (String path : paths) { 40 | String normalizedPath = normalizePath(path); 41 | if (!normalizedPath.trim().isEmpty()) { 42 | normalized = normalizePath(normalized + normalizedPath); 43 | } 44 | } 45 | return normalized; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /db-scheduler-ui-starter/src/main/java/no/bekk/dbscheduler/uistarter/config/DbSchedulerUiWebConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package no.bekk.dbscheduler.uistarter.config; 15 | 16 | import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations; 17 | import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; 18 | 19 | public class DbSchedulerUiWebConfiguration implements WebMvcRegistrations { 20 | 21 | private final String apiPrefix; 22 | 23 | public DbSchedulerUiWebConfiguration(String apiPrefix) { 24 | this.apiPrefix = apiPrefix; 25 | } 26 | 27 | @Override 28 | public RequestMappingHandlerMapping getRequestMappingHandlerMapping() { 29 | return new PrefixedRequestMappingHandlerMapping(apiPrefix); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /db-scheduler-ui-starter/src/main/java/no/bekk/dbscheduler/uistarter/config/PrefixedRequestMappingHandlerMapping.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package no.bekk.dbscheduler.uistarter.config; 16 | 17 | import java.lang.reflect.Method; 18 | import no.bekk.dbscheduler.ui.controller.ConfigController; 19 | import org.springframework.web.servlet.mvc.method.RequestMappingInfo; 20 | import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; 21 | import org.springframework.web.util.pattern.PathPatternParser; 22 | 23 | public class PrefixedRequestMappingHandlerMapping extends RequestMappingHandlerMapping { 24 | 25 | private final String prefix; 26 | 27 | public PrefixedRequestMappingHandlerMapping(String prefix) { 28 | this.prefix = prefix; 29 | setPatternParser(new PathPatternParser()); 30 | } 31 | 32 | @Override 33 | protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) { 34 | final Package aPackage = method.getDeclaringClass().getPackage(); 35 | RequestMappingInfo finalMapping = mapping; 36 | 37 | if (ConfigController.class.getPackage().equals(aPackage)) { 38 | finalMapping = RequestMappingInfo.paths(prefix).build().combine(mapping); 39 | } 40 | super.registerHandlerMethod(handler, method, finalMapping); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /db-scheduler-ui-starter/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 2 | no.bekk.dbscheduler.uistarter.autoconfigure.UiApiAutoConfiguration 3 | -------------------------------------------------------------------------------- /db-scheduler-ui-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | no.bekk.dbscheduler.uistarter.autoconfigure.UiApiAutoConfiguration 2 | -------------------------------------------------------------------------------- /db-scheduler-ui-starter/src/test/java/no/bekk/dbscheduler/uistarter/config/DbSchedulerUiUtilTest.java: -------------------------------------------------------------------------------- 1 | package no.bekk.dbscheduler.uistarter.config; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | class DbSchedulerUiUtilTest { 8 | 9 | @Test 10 | void testNormalizeNoPaths() { 11 | String path = DbSchedulerUiUtil.normalizePaths("", ""); 12 | assertThat(path).isEqualTo(""); 13 | } 14 | 15 | @Test 16 | void testNormalizeNullPaths() { 17 | String path = DbSchedulerUiUtil.normalizePaths(null, null); 18 | assertThat(path).isEqualTo(""); 19 | } 20 | 21 | @Test 22 | void testNormalizeSinglePath() { 23 | String path = DbSchedulerUiUtil.normalizePaths(null, "/db-scheduler-ui"); 24 | assertThat(path).isEqualTo("/db-scheduler-ui"); 25 | } 26 | 27 | @Test 28 | void testNormalizeNoSlash() { 29 | String path = DbSchedulerUiUtil.normalizePaths(null, "db-scheduler-ui"); 30 | assertThat(path).isEqualTo("/db-scheduler-ui"); 31 | } 32 | 33 | @Test 34 | void testNormalizePath() { 35 | String path = DbSchedulerUiUtil.normalizePath("db-scheduler-ui"); 36 | assertThat(path).isEqualTo("/db-scheduler-ui"); 37 | } 38 | 39 | @Test 40 | void testNormalizePaths() { 41 | String path = DbSchedulerUiUtil.normalizePaths("/api", "db-scheduler-ui"); 42 | assertThat(path).isEqualTo("/api/db-scheduler-ui"); 43 | } 44 | 45 | @Test 46 | void testNormalizePathsExtraSlashes() { 47 | String path = DbSchedulerUiUtil.normalizePaths("/api/", "/db-scheduler-ui"); 48 | assertThat(path).isEqualTo("/api/db-scheduler-ui"); 49 | } 50 | 51 | @Test 52 | void testNormalize3Paths() { 53 | String path = DbSchedulerUiUtil.normalizePaths("/api/", "/v1/", "/db-scheduler-ui"); 54 | assertThat(path).isEqualTo("/api/v1/db-scheduler-ui"); 55 | } 56 | 57 | @Test 58 | void testNormalize3PathsNulls() { 59 | String path = DbSchedulerUiUtil.normalizePaths(null, "/v1/", "/db-scheduler-ui"); 60 | assertThat(path).isEqualTo("/v1/db-scheduler-ui"); 61 | 62 | path = DbSchedulerUiUtil.normalizePaths("/v2/", "", "/db-scheduler-ui"); 63 | assertThat(path).isEqualTo("/v2/db-scheduler-ui"); 64 | 65 | path = DbSchedulerUiUtil.normalizePaths("/v3/", null, "/db-scheduler-ui"); 66 | assertThat(path).isEqualTo("/v3/db-scheduler-ui"); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /db-scheduler-ui/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | no.bekk.db-scheduler-ui 6 | db-scheduler-ui-parent 7 | main-SNAPSHOT 8 | 9 | 10 | db-scheduler-ui 11 | db-scheduler-ui 12 | Provides a UI for db-scheduler 13 | https://github.com/bekk/db-scheduler-ui 14 | 15 | 16 | 17 | com.fasterxml.jackson.core 18 | jackson-databind 19 | 20 | 21 | com.github.kagkarlsson 22 | db-scheduler 23 | 24 | 25 | org.springframework 26 | spring-web 27 | 28 | 29 | org.springframework 30 | spring-webflux 31 | 32 | 33 | org.springframework 34 | spring-webmvc 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-jdbc 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /db-scheduler-ui/src/main/java/no/bekk/dbscheduler/ui/controller/ConfigController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package no.bekk.dbscheduler.ui.controller; 15 | 16 | import java.util.function.Supplier; 17 | import no.bekk.dbscheduler.ui.model.ConfigResponse; 18 | import org.springframework.web.bind.annotation.CrossOrigin; 19 | import org.springframework.web.bind.annotation.GetMapping; 20 | import org.springframework.web.bind.annotation.RequestMapping; 21 | import org.springframework.web.bind.annotation.RestController; 22 | 23 | @RestController 24 | @CrossOrigin 25 | @RequestMapping("/db-scheduler-api/config") 26 | public class ConfigController { 27 | 28 | private final boolean showHistory; 29 | private final Supplier readOnly; 30 | 31 | public ConfigController(boolean showHistory, Supplier readOnly) { 32 | this.showHistory = showHistory; 33 | this.readOnly = readOnly; 34 | } 35 | 36 | @GetMapping 37 | public ConfigResponse getConfig() { 38 | return new ConfigResponse(showHistory, readOnly.get()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /db-scheduler-ui/src/main/java/no/bekk/dbscheduler/ui/controller/IndexHtmlController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package no.bekk.dbscheduler.ui.controller; 15 | 16 | import org.springframework.beans.factory.annotation.Qualifier; 17 | import org.springframework.http.MediaType; 18 | import org.springframework.web.bind.annotation.GetMapping; 19 | import org.springframework.web.bind.annotation.RestController; 20 | 21 | @RestController 22 | public class IndexHtmlController { 23 | 24 | private final String patchedIndexHtml; 25 | private final String contextPath; 26 | 27 | public IndexHtmlController( 28 | @Qualifier("indexHtml") String indexHtml, @Qualifier("contextPath") String contextPath) { 29 | this.patchedIndexHtml = indexHtml; 30 | this.contextPath = contextPath; 31 | } 32 | 33 | @GetMapping( 34 | path = { 35 | "/db-scheduler/index.html", 36 | "/db-scheduler", 37 | "/db-scheduler/history/all/index.html", 38 | "/db-scheduler/history/all" 39 | }, 40 | produces = MediaType.TEXT_HTML_VALUE) 41 | public String indexHtml() { 42 | return patchedIndexHtml; 43 | } 44 | 45 | @GetMapping( 46 | path = {"/db-scheduler/js/context-path.js"}, 47 | produces = "text/javascript") 48 | public String contextPath() { 49 | return "window.CONTEXT_PATH='" + contextPath + "';"; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /db-scheduler-ui/src/main/java/no/bekk/dbscheduler/ui/controller/LogController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package no.bekk.dbscheduler.ui.controller; 15 | 16 | import no.bekk.dbscheduler.ui.model.*; 17 | import no.bekk.dbscheduler.ui.service.LogLogic; 18 | import org.springframework.beans.factory.annotation.Autowired; 19 | import org.springframework.web.bind.annotation.*; 20 | 21 | @RestController 22 | @CrossOrigin 23 | @RequestMapping("/db-scheduler-api/logs") 24 | public class LogController { 25 | 26 | private final LogLogic logLogic; 27 | 28 | @Autowired 29 | public LogController(LogLogic logLogic) { 30 | this.logLogic = logLogic; 31 | } 32 | 33 | @GetMapping("/all") 34 | public GetLogsResponse getAllLogs(TaskDetailsRequestParams params) { 35 | return logLogic.getLogs(params); 36 | } 37 | 38 | @GetMapping("/poll") 39 | public LogPollResponse pollLogs(TaskDetailsRequestParams params) { 40 | return logLogic.pollLogs(params); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /db-scheduler-ui/src/main/java/no/bekk/dbscheduler/ui/controller/SpaFallbackMvc.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package no.bekk.dbscheduler.ui.controller; 15 | 16 | import java.io.IOException; 17 | import java.nio.charset.StandardCharsets; 18 | import lombok.NonNull; 19 | import org.springframework.beans.factory.annotation.Value; 20 | import org.springframework.core.io.ByteArrayResource; 21 | import org.springframework.core.io.Resource; 22 | import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; 23 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 24 | import org.springframework.web.servlet.resource.PathResourceResolver; 25 | 26 | public class SpaFallbackMvc implements WebMvcConfigurer { 27 | 28 | public static final String DEFAULT_STARTING_PAGE = "static/db-scheduler/index.html"; 29 | 30 | private final String prefix; 31 | 32 | private final String indexHtml; 33 | 34 | public SpaFallbackMvc(@Value("${db-scheduler-ui.context-path}") String prefix, String indexHtml) { 35 | this.prefix = prefix; 36 | this.indexHtml = indexHtml; 37 | } 38 | 39 | @Override 40 | public void addResourceHandlers(ResourceHandlerRegistry registry) { 41 | registry 42 | .addResourceHandler(prefix + "/db-scheduler", prefix + "/db-scheduler/**") 43 | .addResourceLocations("classpath:/static/db-scheduler/") 44 | .resourceChain(true) 45 | .addResolver(new SpaFallbackResolver(indexHtml)); 46 | } 47 | 48 | static class SpaFallbackResolver extends PathResourceResolver { 49 | 50 | private final String indexHtml; 51 | 52 | public SpaFallbackResolver(String indexHtml) { 53 | this.indexHtml = indexHtml; 54 | } 55 | 56 | @Override 57 | protected Resource getResource(@NonNull String resourcePath, Resource location) 58 | throws IOException { 59 | var requestedResource = location.createRelative(resourcePath); 60 | 61 | if (requestedResource.exists() && requestedResource.isReadable()) { 62 | return requestedResource; 63 | } 64 | 65 | return new ByteArrayResource(indexHtml.getBytes(StandardCharsets.UTF_8)); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /db-scheduler-ui/src/main/java/no/bekk/dbscheduler/ui/controller/TaskAdminController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package no.bekk.dbscheduler.ui.controller; 15 | 16 | import java.time.Instant; 17 | import no.bekk.dbscheduler.ui.service.TaskLogic; 18 | import org.springframework.beans.factory.annotation.Autowired; 19 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 20 | import org.springframework.web.bind.annotation.CrossOrigin; 21 | import org.springframework.web.bind.annotation.PostMapping; 22 | import org.springframework.web.bind.annotation.RequestMapping; 23 | import org.springframework.web.bind.annotation.RequestParam; 24 | import org.springframework.web.bind.annotation.RestController; 25 | 26 | @RestController 27 | @CrossOrigin 28 | @RequestMapping("/db-scheduler-api/tasks") 29 | @ConditionalOnProperty( 30 | prefix = "db-scheduler-ui", 31 | name = "read-only", 32 | havingValue = "false", 33 | matchIfMissing = true) 34 | public class TaskAdminController { 35 | 36 | private final TaskLogic taskLogic; 37 | 38 | @Autowired 39 | public TaskAdminController(TaskLogic taskLogic) { 40 | this.taskLogic = taskLogic; 41 | } 42 | 43 | @PostMapping("/rerun") 44 | public void runNow( 45 | @RequestParam String id, @RequestParam String name, @RequestParam Instant scheduleTime) { 46 | taskLogic.runTaskNow(id, name, scheduleTime); 47 | } 48 | 49 | @PostMapping("/rerunGroup") 50 | public void runAllNow(@RequestParam String name, @RequestParam boolean onlyFailed) { 51 | taskLogic.runTaskGroupNow(name, onlyFailed); 52 | } 53 | 54 | @PostMapping("/delete") 55 | public void deleteTaskNow(@RequestParam String id, @RequestParam String name) { 56 | taskLogic.deleteTask(id, name); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /db-scheduler-ui/src/main/java/no/bekk/dbscheduler/ui/controller/TaskController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package no.bekk.dbscheduler.ui.controller; 15 | 16 | import no.bekk.dbscheduler.ui.model.GetTasksResponse; 17 | import no.bekk.dbscheduler.ui.model.PollResponse; 18 | import no.bekk.dbscheduler.ui.model.TaskDetailsRequestParams; 19 | import no.bekk.dbscheduler.ui.model.TaskRequestParams; 20 | import no.bekk.dbscheduler.ui.service.TaskLogic; 21 | import org.springframework.beans.factory.annotation.Autowired; 22 | import org.springframework.web.bind.annotation.CrossOrigin; 23 | import org.springframework.web.bind.annotation.GetMapping; 24 | import org.springframework.web.bind.annotation.RequestMapping; 25 | import org.springframework.web.bind.annotation.RestController; 26 | 27 | @RestController 28 | @CrossOrigin 29 | @RequestMapping("/db-scheduler-api/tasks") 30 | public class TaskController { 31 | private final TaskLogic taskLogic; 32 | 33 | @Autowired 34 | public TaskController(TaskLogic taskLogic) { 35 | this.taskLogic = taskLogic; 36 | } 37 | 38 | @GetMapping("/all") 39 | public GetTasksResponse getTasks(TaskRequestParams params) { 40 | return taskLogic.getAllTasks(params); 41 | } 42 | 43 | @GetMapping("/details") 44 | public GetTasksResponse getTaskDetails(TaskDetailsRequestParams params) { 45 | return taskLogic.getTask(params); 46 | } 47 | 48 | @GetMapping("/poll") 49 | public PollResponse pollForUpdates(TaskDetailsRequestParams params) { 50 | return taskLogic.pollTasks(params); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /db-scheduler-ui/src/main/java/no/bekk/dbscheduler/ui/model/ConfigResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package no.bekk.dbscheduler.ui.model; 15 | 16 | import lombok.AllArgsConstructor; 17 | import lombok.Getter; 18 | import lombok.Setter; 19 | 20 | @Getter 21 | @Setter 22 | @AllArgsConstructor 23 | public class ConfigResponse { 24 | private boolean showHistory; 25 | private boolean readOnly; 26 | } 27 | -------------------------------------------------------------------------------- /db-scheduler-ui/src/main/java/no/bekk/dbscheduler/ui/model/GetLogsResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package no.bekk.dbscheduler.ui.model; 15 | 16 | import com.fasterxml.jackson.annotation.JsonCreator; 17 | import com.fasterxml.jackson.annotation.JsonProperty; 18 | import java.util.List; 19 | import lombok.Getter; 20 | 21 | @Getter 22 | public class GetLogsResponse { 23 | 24 | private final int numberOfItems; 25 | private final int numberOfPages; 26 | private final List items; 27 | 28 | @JsonCreator 29 | public GetLogsResponse( 30 | @JsonProperty("numberOfItems") int totalLogs, 31 | @JsonProperty("items") List pagedLogs, 32 | @JsonProperty("pageSize") int pageSize) { 33 | this.numberOfItems = totalLogs; 34 | this.numberOfPages = totalLogs == 0 ? 0 : (int) Math.ceil((double) totalLogs / pageSize); 35 | this.items = pagedLogs; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /db-scheduler-ui/src/main/java/no/bekk/dbscheduler/ui/model/GetTasksResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package no.bekk.dbscheduler.ui.model; 15 | 16 | import com.fasterxml.jackson.annotation.JsonCreator; 17 | import com.fasterxml.jackson.annotation.JsonProperty; 18 | import java.util.List; 19 | import lombok.Getter; 20 | 21 | @Getter 22 | public class GetTasksResponse { 23 | 24 | private final int numberOfItems; 25 | private final int numberOfPages; 26 | private final List items; 27 | 28 | @JsonCreator 29 | public GetTasksResponse( 30 | @JsonProperty("numberOfItems") int totalTasks, 31 | @JsonProperty("items") List pagedTasks, 32 | @JsonProperty("pageSize") int pageSize) { 33 | this.numberOfItems = totalTasks; 34 | this.numberOfPages = totalTasks == 0 ? 0 : (int) Math.ceil((double) totalTasks / pageSize); 35 | this.items = pagedTasks; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /db-scheduler-ui/src/main/java/no/bekk/dbscheduler/ui/model/LogModel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package no.bekk.dbscheduler.ui.model; 15 | 16 | import java.time.Instant; 17 | import lombok.Getter; 18 | import lombok.RequiredArgsConstructor; 19 | 20 | @RequiredArgsConstructor 21 | @Getter 22 | public class LogModel { 23 | private final Long id; 24 | private final String taskName; 25 | private final String taskInstance; 26 | private final Object taskData; 27 | private final Instant timeStarted; 28 | private final Instant timeFinished; 29 | private final boolean succeeded; 30 | private final Long durationMs; 31 | private final String exceptionClass; 32 | private final String exceptionMessage; 33 | private final String exceptionStackTrace; 34 | } 35 | -------------------------------------------------------------------------------- /db-scheduler-ui/src/main/java/no/bekk/dbscheduler/ui/model/LogPollResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package no.bekk.dbscheduler.ui.model; 15 | 16 | import lombok.Getter; 17 | 18 | @Getter 19 | public class LogPollResponse { 20 | 21 | private final int newFailures; 22 | private final int newSucceeded; 23 | 24 | public LogPollResponse(int newFailures, int newSucceeded) { 25 | this.newFailures = newFailures; 26 | this.newSucceeded = newSucceeded; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /db-scheduler-ui/src/main/java/no/bekk/dbscheduler/ui/model/PollResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package no.bekk.dbscheduler.ui.model; 15 | 16 | import lombok.Getter; 17 | 18 | @Getter 19 | public class PollResponse { 20 | 21 | private final int newFailures; 22 | private final int newRunning; 23 | private final int newTasks; 24 | private final int stoppedFailing; 25 | private final int finishedRunning; 26 | 27 | public PollResponse( 28 | int newFailures, int newRunning, int newTasks, int stoppedFailing, int finishedRunning) { 29 | this.newFailures = newFailures; 30 | this.newRunning = newRunning; 31 | this.newTasks = newTasks; 32 | this.stoppedFailing = stoppedFailing; 33 | this.finishedRunning = finishedRunning; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /db-scheduler-ui/src/main/java/no/bekk/dbscheduler/ui/model/TaskDetailsRequestParams.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package no.bekk.dbscheduler.ui.model; 15 | 16 | import java.time.Instant; 17 | import lombok.Getter; 18 | 19 | @Getter 20 | public class TaskDetailsRequestParams extends TaskRequestParams { 21 | 22 | private final String taskId; 23 | private final String taskName; 24 | 25 | public TaskDetailsRequestParams( 26 | TaskFilter filter, 27 | Integer pageNumber, 28 | Integer size, 29 | TaskSort sorting, 30 | Boolean asc, 31 | String searchTermTaskName, 32 | String searchTermTaskInstance, 33 | Boolean taskNameExactMatch, 34 | Boolean taskInstanceExactMatch, 35 | Instant startTime, 36 | Instant endTime, 37 | String taskName, 38 | String taskId, 39 | Boolean refresh) { 40 | super( 41 | filter, 42 | pageNumber, 43 | size, 44 | sorting, 45 | asc, 46 | searchTermTaskName, 47 | searchTermTaskInstance, 48 | taskNameExactMatch, 49 | taskInstanceExactMatch, 50 | startTime, 51 | endTime, 52 | refresh); 53 | this.taskId = taskId; 54 | this.taskName = taskName; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /db-scheduler-ui/src/main/java/no/bekk/dbscheduler/ui/model/TaskModel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package no.bekk.dbscheduler.ui.model; 15 | 16 | import java.time.Instant; 17 | import java.util.List; 18 | import lombok.AllArgsConstructor; 19 | import lombok.Getter; 20 | import lombok.NoArgsConstructor; 21 | import lombok.Setter; 22 | 23 | @Getter 24 | @Setter 25 | @NoArgsConstructor 26 | @AllArgsConstructor 27 | public class TaskModel { 28 | 29 | private String taskName; 30 | private List taskInstance; 31 | private List taskData; 32 | private List executionTime; 33 | private List picked; 34 | private List pickedBy; 35 | private List lastSuccess; 36 | private Instant lastFailure; 37 | private List consecutiveFailures; 38 | private Instant lastHeartbeat; 39 | private int version; 40 | } 41 | -------------------------------------------------------------------------------- /db-scheduler-ui/src/main/java/no/bekk/dbscheduler/ui/model/TaskRequestParams.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package no.bekk.dbscheduler.ui.model; 15 | 16 | import java.time.Instant; 17 | import lombok.Getter; 18 | 19 | @Getter 20 | public class TaskRequestParams { 21 | 22 | private final TaskFilter filter; 23 | private final int pageNumber; 24 | private final int size; 25 | private final TaskSort sorting; 26 | private final boolean asc; 27 | private final String searchTermTaskName; 28 | private final String searchTermTaskInstance; 29 | private final boolean taskNameExactMatch; 30 | private final boolean taskInstanceExactMatch; 31 | private final Instant startTime; 32 | private final Instant endTime; 33 | private final boolean refresh; 34 | 35 | public TaskRequestParams( 36 | TaskFilter filter, 37 | Integer pageNumber, 38 | Integer size, 39 | TaskSort sorting, 40 | Boolean asc, 41 | String searchTermTaskName, 42 | String searchTermTaskInstance, 43 | Boolean taskNameExactMatch, 44 | Boolean taskInstanceExactMatch, 45 | Instant startTime, 46 | Instant endTime, 47 | Boolean refresh) { 48 | this.filter = filter != null ? filter : TaskFilter.ALL; 49 | this.pageNumber = pageNumber != null ? pageNumber : 0; 50 | this.size = size != null ? size : 10; 51 | this.sorting = sorting != null ? sorting : TaskSort.DEFAULT; 52 | this.asc = asc != null ? asc : true; 53 | this.searchTermTaskName = searchTermTaskName; 54 | this.searchTermTaskInstance = searchTermTaskInstance; 55 | this.taskNameExactMatch = taskNameExactMatch != null ? taskNameExactMatch : false; 56 | this.taskInstanceExactMatch = taskInstanceExactMatch != null ? taskInstanceExactMatch : false; 57 | this.startTime = startTime; 58 | this.endTime = endTime; 59 | this.refresh = refresh != null ? refresh : true; 60 | } 61 | 62 | public enum TaskFilter { 63 | ALL, 64 | FAILED, 65 | RUNNING, 66 | SCHEDULED, 67 | SUCCEEDED 68 | } 69 | 70 | public enum TaskSort { 71 | DEFAULT, 72 | NAME 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /db-scheduler-ui/src/main/java/no/bekk/dbscheduler/ui/util/AndCondition.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package no.bekk.dbscheduler.ui.util; 15 | 16 | import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; 17 | 18 | public interface AndCondition { 19 | String getQueryPart(); 20 | 21 | void setParameters(MapSqlParameterSource parameterSource); 22 | } 23 | -------------------------------------------------------------------------------- /db-scheduler-ui/src/main/java/no/bekk/dbscheduler/ui/util/QueryBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package no.bekk.dbscheduler.ui.util; 15 | 16 | import static java.util.Optional.empty; 17 | import static java.util.stream.Collectors.joining; 18 | 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | import java.util.Optional; 22 | import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; 23 | 24 | public class QueryBuilder { 25 | 26 | private final String tableName; 27 | private final String databaseProductName; 28 | 29 | private final List andConditions = new ArrayList<>(); 30 | 31 | private Optional orderBy = empty(); 32 | private int limit; 33 | 34 | QueryBuilder(String tableName, String databaseProductName) { 35 | this.tableName = tableName; 36 | this.databaseProductName = databaseProductName; 37 | } 38 | 39 | public static QueryBuilder selectFromTable(String tableName, String databaseProductName) { 40 | return new QueryBuilder(tableName, databaseProductName); 41 | } 42 | 43 | public QueryBuilder andCondition(AndCondition andCondition) { 44 | andConditions.add(andCondition); 45 | return this; 46 | } 47 | 48 | public QueryBuilder orderBy(String orderBy) { 49 | this.orderBy = Optional.of(orderBy); 50 | return this; 51 | } 52 | 53 | public String getQuery() { 54 | StringBuilder s = new StringBuilder(); 55 | s.append("SELECT"); 56 | 57 | if (limit > 0 && databaseProductName.equals("Microsoft SQL Server")) { 58 | s.append(" TOP ").append(limit); 59 | } 60 | 61 | s.append(" * FROM ").append(tableName); 62 | 63 | if (!andConditions.isEmpty()) { 64 | s.append(" WHERE "); 65 | s.append(andConditions.stream().map(AndCondition::getQueryPart).collect(joining(" AND "))); 66 | } 67 | 68 | orderBy.ifPresent(o -> s.append(" ORDER BY ").append(o)); 69 | 70 | if (limit > 0) { 71 | if (databaseProductName.equals("Oracle")) { 72 | s.append(" FETCH NEXT ").append(limit).append(" ROWS ONLY"); 73 | } else if (!databaseProductName.equals("Microsoft SQL Server")) { 74 | s.append(" LIMIT ").append(limit); 75 | } 76 | } 77 | 78 | return s.toString(); 79 | } 80 | 81 | public MapSqlParameterSource getParameters() { 82 | MapSqlParameterSource parameterSource = new MapSqlParameterSource(); 83 | andConditions.forEach(c -> c.setParameters(parameterSource)); 84 | return parameterSource; 85 | } 86 | 87 | public void limit(int logLimit) { 88 | this.limit = logLimit; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /db-scheduler-ui/src/main/resources/META-INF/additional-spring-configuration-metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": [ 3 | { 4 | "name": "db-scheduler-ui.context-path", 5 | "type": "java.lang.String", 6 | "description": "Add a prefix for the UI context path." 7 | } 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /example-app-webflux/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use the official OpenJDK base image 2 | FROM openjdk:17-jdk-slim 3 | 4 | # Set the working directory inside the container 5 | WORKDIR /app 6 | 7 | COPY ./*.jar /app/app.jar 8 | 9 | EXPOSE 8081 10 | 11 | # Command to run the application 12 | CMD ["java", "-jar", "/app/app.jar"] -------------------------------------------------------------------------------- /example-app-webflux/src/main/java/com/github/bekk/exampleapp/ExampleAppWebFlux.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package com.github.bekk.exampleapp; 15 | 16 | import com.github.bekk.exampleapp.service.TaskService; 17 | import com.github.kagkarlsson.scheduler.Scheduler; 18 | import org.springframework.boot.CommandLineRunner; 19 | import org.springframework.boot.SpringApplication; 20 | import org.springframework.boot.autoconfigure.SpringBootApplication; 21 | import org.springframework.context.annotation.Bean; 22 | 23 | @SpringBootApplication 24 | public class ExampleAppWebFlux { 25 | public static void main(String[] args) { 26 | SpringApplication.run(ExampleAppWebFlux.class, args); 27 | } 28 | 29 | @Bean 30 | public CommandLineRunner runAllManuallyTriggeredTasks(Scheduler scheduler) { 31 | return args -> { 32 | System.out.println("Running all manually triggered tasks"); 33 | TaskService taskService = new TaskService(scheduler); 34 | taskService.runManuallyTriggeredTasks(); 35 | }; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /example-app-webflux/src/main/java/com/github/bekk/exampleapp/config/DatabaseConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package com.github.bekk.exampleapp.config; 15 | 16 | import javax.sql.DataSource; 17 | import org.springframework.context.annotation.Bean; 18 | import org.springframework.context.annotation.Configuration; 19 | import org.springframework.jdbc.core.JdbcTemplate; 20 | import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; 21 | import org.springframework.jdbc.core.simple.SimpleJdbcInsert; 22 | 23 | @Configuration 24 | public class DatabaseConfig { 25 | private final DataSource dataSource; 26 | 27 | public DatabaseConfig(DataSource dataSource) { 28 | this.dataSource = dataSource; 29 | } 30 | 31 | @Bean 32 | public JdbcTemplate jdbcTemplate() { 33 | return new JdbcTemplate(dataSource); 34 | } 35 | 36 | @Bean 37 | public SimpleJdbcInsert simpleJdbcInsert() { 38 | return new SimpleJdbcInsert(dataSource) 39 | .withTableName("scheduled_tasks") 40 | .usingGeneratedKeyColumns("id"); 41 | } 42 | 43 | @Bean 44 | public NamedParameterJdbcTemplate namedParameterJdbcTemplate() { 45 | return new NamedParameterJdbcTemplate(dataSource); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /example-app-webflux/src/main/java/com/github/bekk/exampleapp/config/SchedulerConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package com.github.bekk.exampleapp.config; 15 | 16 | import com.github.kagkarlsson.scheduler.serializer.JavaSerializer; 17 | import io.rocketbase.extension.jdbc.JdbcLogRepository; 18 | import io.rocketbase.extension.jdbc.Snowflake; 19 | import javax.sql.DataSource; 20 | import org.springframework.beans.factory.annotation.Autowired; 21 | import org.springframework.context.annotation.Bean; 22 | import org.springframework.context.annotation.Configuration; 23 | 24 | @Configuration 25 | public class SchedulerConfig { 26 | private final DataSource dataSource; 27 | 28 | @Autowired 29 | public SchedulerConfig(DataSource dataSource) { 30 | this.dataSource = dataSource; 31 | } 32 | 33 | @Bean 34 | public JdbcLogRepository jdbcLogRepository() { 35 | return new JdbcLogRepository( 36 | dataSource, new JavaSerializer(), JdbcLogRepository.DEFAULT_TABLE_NAME, new Snowflake()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /example-app-webflux/src/main/java/com/github/bekk/exampleapp/model/TaskData.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package com.github.bekk.exampleapp.model; 15 | 16 | import java.io.Serializable; 17 | import java.time.Instant; 18 | import lombok.AllArgsConstructor; 19 | import lombok.Getter; 20 | import lombok.Setter; 21 | 22 | @Setter 23 | @Getter 24 | @AllArgsConstructor 25 | public class TaskData implements Serializable { 26 | 27 | private long id; 28 | private String data; 29 | private Instant time; 30 | } 31 | -------------------------------------------------------------------------------- /example-app-webflux/src/main/java/com/github/bekk/exampleapp/model/TestObject.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package com.github.bekk.exampleapp.model; 15 | 16 | import java.io.Serializable; 17 | 18 | public class TestObject implements Serializable { 19 | private String name; 20 | private int id; 21 | private String email; 22 | 23 | public String getName() { 24 | return name; 25 | } 26 | 27 | public TestObject(String initialName, int id, String email) { 28 | this.id = id; 29 | this.name = initialName; 30 | this.email = email; 31 | } 32 | 33 | public void setName(String name) { 34 | this.name = name; 35 | } 36 | 37 | public int getId() { 38 | return id; 39 | } 40 | 41 | public void setId(int id) { 42 | this.id = id; 43 | } 44 | 45 | public String getEmail() { 46 | return email; 47 | } 48 | 49 | public void setEmail(String email) { 50 | this.email = email; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /example-app-webflux/src/main/java/com/github/bekk/exampleapp/service/TaskService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package com.github.bekk.exampleapp.service; 15 | 16 | import static com.github.bekk.exampleapp.tasks.ChainTask.CHAINED_STEP_1_TASK; 17 | import static com.github.bekk.exampleapp.tasks.FailingTask.FAILING_ONETIME_TASK; 18 | import static com.github.bekk.exampleapp.tasks.LongRunningTask.LONG_RUNNING_ONETIME_TASK; 19 | import static com.github.bekk.exampleapp.tasks.OneTimeTaskExample.ONE_TIME_TASK; 20 | 21 | import com.github.bekk.exampleapp.model.TaskData; 22 | import com.github.bekk.exampleapp.model.TestObject; 23 | import com.github.kagkarlsson.scheduler.Scheduler; 24 | import java.time.Instant; 25 | import org.springframework.stereotype.Service; 26 | 27 | @Service 28 | public class TaskService { 29 | 30 | private final Scheduler scheduler; 31 | 32 | public TaskService(Scheduler scheduler) { 33 | this.scheduler = scheduler; 34 | } 35 | 36 | public void runManuallyTriggeredTasks() { 37 | scheduler.schedule( 38 | ONE_TIME_TASK.instance("1").data(new TaskData(1, "test data", Instant.now())).build(), 39 | Instant.now()); 40 | 41 | scheduler.schedule( 42 | CHAINED_STEP_1_TASK 43 | .instance("3") 44 | .data(new TestObject("Ole Nordman", 1, "ole.nordman@mail.com")) 45 | .build(), 46 | Instant.now()); 47 | 48 | scheduler.schedule( 49 | LONG_RUNNING_ONETIME_TASK.instance("5").build(), Instant.now().plusSeconds(2)); 50 | scheduler.schedule(FAILING_ONETIME_TASK.instance("6").build(), Instant.now().plusSeconds(2)); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /example-app-webflux/src/main/java/com/github/bekk/exampleapp/tasks/ChainTask.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package com.github.bekk.exampleapp.tasks; 15 | 16 | import static utils.Utils.sleep; 17 | 18 | import com.github.bekk.exampleapp.model.TestObject; 19 | import com.github.kagkarlsson.scheduler.SchedulerClient; 20 | import com.github.kagkarlsson.scheduler.task.Task; 21 | import com.github.kagkarlsson.scheduler.task.TaskDescriptor; 22 | import com.github.kagkarlsson.scheduler.task.helper.Tasks; 23 | import java.time.Instant; 24 | import org.springframework.context.annotation.Bean; 25 | import org.springframework.context.annotation.Configuration; 26 | 27 | @Configuration 28 | public class ChainTask { 29 | 30 | public static final TaskDescriptor CHAINED_STEP_1_TASK = 31 | TaskDescriptor.of("chained-step-1", TestObject.class); 32 | 33 | public static final TaskDescriptor CHAINED_STEP_2_TASK = 34 | TaskDescriptor.of("chained-step-2", TestObject.class); 35 | 36 | @Bean 37 | public Task chainTaskStepOne() { 38 | return Tasks.oneTime(CHAINED_STEP_1_TASK) 39 | .execute( 40 | (inst, ctx) -> { 41 | sleep(5000); 42 | final SchedulerClient client = ctx.getSchedulerClient(); 43 | System.out.println( 44 | "Executed chained onetime task step 1: " 45 | + inst.getTaskName() 46 | + " " 47 | + inst.getId()); 48 | TestObject data = inst.getData(); 49 | data.setId(data.getId() + 1); 50 | client.scheduleIfNotExists( 51 | CHAINED_STEP_2_TASK.instance(inst.getId()).data(data).build(), 52 | Instant.now().plusSeconds(10)); 53 | }); 54 | } 55 | 56 | @Bean 57 | public Task chainTaskStepTwo() { 58 | return Tasks.oneTime(CHAINED_STEP_2_TASK) 59 | .execute( 60 | (inst, ctx) -> { 61 | sleep(5000); 62 | final SchedulerClient client = ctx.getSchedulerClient(); 63 | System.out.println( 64 | "Executed chained onetime task step 2: " 65 | + inst.getTaskName() 66 | + " " 67 | + inst.getId()); 68 | TestObject data = inst.getData(); 69 | data.setId(data.getId() + 1); 70 | client.scheduleIfNotExists( 71 | CHAINED_STEP_1_TASK.instance(inst.getId()).data(data).build(), 72 | Instant.now().plusSeconds(20)); 73 | }); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /example-app-webflux/src/main/java/com/github/bekk/exampleapp/tasks/FailingTask.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package com.github.bekk.exampleapp.tasks; 15 | 16 | import static java.time.Duration.ofSeconds; 17 | 18 | import com.github.kagkarlsson.scheduler.task.FailureHandler; 19 | import com.github.kagkarlsson.scheduler.task.Task; 20 | import com.github.kagkarlsson.scheduler.task.TaskDescriptor; 21 | import com.github.kagkarlsson.scheduler.task.helper.Tasks; 22 | import com.github.kagkarlsson.scheduler.task.schedule.FixedDelay; 23 | import org.springframework.context.annotation.Bean; 24 | import org.springframework.context.annotation.Configuration; 25 | 26 | @Configuration 27 | public class FailingTask { 28 | 29 | public static final TaskDescriptor FAILING_ONETIME_TASK = 30 | TaskDescriptor.of("failing-one-time-task"); 31 | 32 | public static final TaskDescriptor FAILING_RECURRING_TASK = 33 | TaskDescriptor.of("failing-recurring-task"); 34 | 35 | public static final TaskDescriptor FAILING_ONETIME_TASK_BACKOFF = 36 | TaskDescriptor.of("failing-one-time-with-backoff-task"); 37 | 38 | @Bean 39 | public Task runOneTimeFailing() { 40 | return Tasks.oneTime(FAILING_ONETIME_TASK) 41 | .execute( 42 | (inst, ctx) -> { 43 | throw new RuntimeException("Simulated task failure"); 44 | }); 45 | } 46 | 47 | @Bean 48 | public Task runRecurringFailing() { 49 | return Tasks.recurring(FAILING_RECURRING_TASK, FixedDelay.ofSeconds(6)) 50 | .execute( 51 | (inst, ctx) -> { 52 | throw new RuntimeException("Simulated task failure"); 53 | }); 54 | } 55 | 56 | @Bean 57 | public Task runOneTimeFailingWithBackoff() { 58 | return Tasks.oneTime(FAILING_ONETIME_TASK_BACKOFF) 59 | .onFailure(new FailureHandler.ExponentialBackoffFailureHandler<>(ofSeconds(1))) 60 | .execute( 61 | (inst, ctx) -> { 62 | throw new RuntimeException("Simulated task failure"); 63 | }); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /example-app-webflux/src/main/java/com/github/bekk/exampleapp/tasks/LongRunningTask.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package com.github.bekk.exampleapp.tasks; 15 | 16 | import static utils.Utils.sleep; 17 | 18 | import com.github.kagkarlsson.scheduler.task.Task; 19 | import com.github.kagkarlsson.scheduler.task.TaskDescriptor; 20 | import com.github.kagkarlsson.scheduler.task.helper.Tasks; 21 | import com.github.kagkarlsson.scheduler.task.schedule.FixedDelay; 22 | import org.springframework.context.annotation.Bean; 23 | import org.springframework.context.annotation.Configuration; 24 | 25 | @Configuration 26 | public class LongRunningTask { 27 | 28 | public static final TaskDescriptor LONG_RUNNING_ONETIME_TASK = 29 | TaskDescriptor.of("long-running-task"); 30 | 31 | public static final TaskDescriptor LONG_RUNNING_RECURRING_TASK = 32 | TaskDescriptor.of("long-running-recurring-task"); 33 | 34 | @Bean 35 | public Task runLongRunningTask() { 36 | return Tasks.oneTime(LONG_RUNNING_ONETIME_TASK) 37 | .execute( 38 | (inst, ctx) -> { 39 | System.out.println("Executing long running task: " + inst.getTaskName()); 40 | sleep(10000); 41 | }); 42 | } 43 | 44 | @Bean 45 | public Task runLongRunningRecurringTask() { 46 | return Tasks.recurring(LONG_RUNNING_RECURRING_TASK, FixedDelay.ofSeconds(20)) 47 | .execute( 48 | (inst, ctx) -> { 49 | System.out.println("Executing long running recurring task: " + inst.getTaskName()); 50 | sleep(10000); 51 | }); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /example-app-webflux/src/main/java/com/github/bekk/exampleapp/tasks/OneTimeTaskExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package com.github.bekk.exampleapp.tasks; 15 | 16 | import com.github.bekk.exampleapp.model.TaskData; 17 | import com.github.kagkarlsson.scheduler.task.Task; 18 | import com.github.kagkarlsson.scheduler.task.TaskDescriptor; 19 | import com.github.kagkarlsson.scheduler.task.helper.Tasks; 20 | import org.springframework.context.annotation.Bean; 21 | import org.springframework.context.annotation.Configuration; 22 | 23 | @Configuration 24 | public class OneTimeTaskExample { 25 | 26 | public static final TaskDescriptor ONE_TIME_TASK = 27 | TaskDescriptor.of("onetime-task", TaskData.class); 28 | 29 | @Bean 30 | public Task exampleOneTimeTask() { 31 | return Tasks.oneTime(ONE_TIME_TASK) 32 | .execute( 33 | (inst, ctx) -> { 34 | System.out.println("Executed onetime task: " + inst.getTaskName()); 35 | System.out.println( 36 | "With data id: " + inst.getData().getId() + " data: " + inst.getData().getData()); 37 | }); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /example-app-webflux/src/main/java/com/github/bekk/exampleapp/tasks/RecurringTaskExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package com.github.bekk.exampleapp.tasks; 15 | 16 | import static utils.Utils.sleep; 17 | 18 | import com.github.kagkarlsson.scheduler.task.TaskDescriptor; 19 | import com.github.kagkarlsson.scheduler.task.helper.RecurringTask; 20 | import com.github.kagkarlsson.scheduler.task.helper.Tasks; 21 | import com.github.kagkarlsson.scheduler.task.schedule.FixedDelay; 22 | import java.util.Random; 23 | import org.springframework.context.annotation.Bean; 24 | import org.springframework.context.annotation.Configuration; 25 | 26 | @Configuration 27 | public class RecurringTaskExample { 28 | 29 | public static final TaskDescriptor RECURRING_TASK = TaskDescriptor.of("recurring-task"); 30 | 31 | @Bean 32 | public RecurringTask getExample() { 33 | return Tasks.recurring(RECURRING_TASK, FixedDelay.ofSeconds(6)) 34 | .execute( 35 | (inst, ctx) -> { 36 | sleep(5000); 37 | if (new Random().nextInt(100) < 30) { 38 | throw new RuntimeException("Simulated failure in example recurring task"); 39 | } 40 | 41 | System.out.println("Executed recurring task: " + inst.getTaskName()); 42 | }); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /example-app-webflux/src/main/java/com/github/bekk/exampleapp/tasks/SpawnerTask.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package com.github.bekk.exampleapp.tasks; 15 | 16 | import static utils.Utils.sleep; 17 | 18 | import com.github.bekk.exampleapp.model.TaskData; 19 | import com.github.kagkarlsson.scheduler.SchedulerClient; 20 | import com.github.kagkarlsson.scheduler.task.Task; 21 | import com.github.kagkarlsson.scheduler.task.TaskDescriptor; 22 | import com.github.kagkarlsson.scheduler.task.helper.Tasks; 23 | import com.github.kagkarlsson.scheduler.task.schedule.FixedDelay; 24 | import java.time.Instant; 25 | import java.util.Random; 26 | import java.util.UUID; 27 | import org.springframework.context.annotation.Bean; 28 | import org.springframework.context.annotation.Configuration; 29 | 30 | @Configuration 31 | public class SpawnerTask { 32 | 33 | public static final TaskDescriptor RECURRING_SPAWNER_TASK = 34 | TaskDescriptor.of("recurring-spawner-task"); 35 | 36 | public static final TaskDescriptor ONE_TIME_SPAWNER_TASK = 37 | TaskDescriptor.of("onetime-spawned-task", TaskData.class); 38 | 39 | @Bean 40 | public Task runSpawner() { 41 | return Tasks.recurring(RECURRING_SPAWNER_TASK, FixedDelay.ofSeconds(180)) 42 | .execute( 43 | (inst, ctx) -> { 44 | final SchedulerClient client = ctx.getSchedulerClient(); 45 | final long randomUUID = UUID.randomUUID().getMostSignificantBits() & Long.MAX_VALUE; 46 | 47 | for (int i = 0; i < 100; i++) { 48 | client.scheduleIfNotExists( 49 | ONE_TIME_SPAWNER_TASK 50 | .instance("spawned " + randomUUID + " loopnr: " + i) 51 | .data(new TaskData(123, "{data: MASSIVEDATA}", Instant.now())) 52 | .build(), 53 | Instant.now().plusSeconds(60)); 54 | } 55 | }); 56 | } 57 | 58 | @Bean 59 | public Task runOneTimeSpawned() { 60 | return Tasks.oneTime(ONE_TIME_SPAWNER_TASK) 61 | .execute( 62 | (inst, ctx) -> { 63 | sleep(10000); 64 | if (new Random().nextInt(100) < 20) { 65 | throw new RuntimeException("Simulated failure"); 66 | } 67 | }); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /example-app-webflux/src/main/java/utils/Utils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package utils; 15 | 16 | public class Utils { 17 | public static void sleep(int millis) { 18 | try { 19 | Thread.sleep(millis); 20 | } catch (InterruptedException e) { 21 | throw new RuntimeException(e); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /example-app-webflux/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=${PORT:8081} 2 | spring.datasource.url=jdbc:h2:file:./db-scheduler-application/test-db;MODE=PostgreSQL;DATABASE_TO_LOWER=TRUE;DEFAULT_NULL_ORDERING=HIGH 3 | spring.datasource.driver-class-name=org.h2.Driver 4 | db-scheduler-log.enabled=true 5 | db-scheduler-log.table-name=scheduled_execution_logs 6 | db-scheduler-ui.history=true 7 | db-scheduler-ui.task-data=true 8 | #logging.level.org.springframework=DEBUG -------------------------------------------------------------------------------- /example-app-webflux/src/main/resources/db/migration/V1__create_db.sql: -------------------------------------------------------------------------------- 1 | create table if not exists scheduled_tasks ( 2 | task_name varchar(255), 3 | task_instance varchar(255), 4 | task_data BYTEA, 5 | execution_time TIMESTAMP WITH TIME ZONE, 6 | picked BIT, 7 | picked_by varchar(50), 8 | last_success TIMESTAMP WITH TIME ZONE, 9 | last_failure TIMESTAMP WITH TIME ZONE, 10 | consecutive_failures INT, 11 | last_heartbeat TIMESTAMP WITH TIME ZONE, 12 | version BIGINT, 13 | PRIMARY KEY (task_name, task_instance) 14 | ); 15 | -------------------------------------------------------------------------------- /example-app-webflux/src/main/resources/db/migration/V2__add_logging.sql: -------------------------------------------------------------------------------- 1 | create table scheduled_execution_logs 2 | ( 3 | id BIGINT not null primary key, 4 | task_name text not null, 5 | task_instance text not null, 6 | task_data bytea, 7 | picked_by text, 8 | time_started timestamp with time zone not null, 9 | time_finished timestamp with time zone not null, 10 | succeeded BOOLEAN not null, 11 | duration_ms BIGINT not null, 12 | exception_class text, 13 | exception_message text, 14 | exception_stacktrace text 15 | ); 16 | 17 | CREATE INDEX stl_started_idx ON scheduled_execution_logs (time_started); 18 | CREATE INDEX stl_task_name_idx ON scheduled_execution_logs (task_name); 19 | CREATE INDEX stl_exception_class_idx ON scheduled_execution_logs (exception_class); -------------------------------------------------------------------------------- /example-app-webflux/src/test/java/com/github/bekk/exampleapp/SmokeTest.java: -------------------------------------------------------------------------------- 1 | package com.github.bekk.exampleapp; 2 | 3 | import static com.github.bekk.exampleapp.tasks.OneTimeTaskExample.ONE_TIME_TASK; 4 | import static org.assertj.core.api.Assertions.assertThat; 5 | 6 | import no.bekk.dbscheduler.ui.controller.TaskAdminController; 7 | import no.bekk.dbscheduler.ui.model.GetTasksResponse; 8 | import org.junit.jupiter.api.Assertions; 9 | import org.junit.jupiter.api.BeforeEach; 10 | import org.junit.jupiter.api.Test; 11 | import org.junit.jupiter.api.extension.ExtendWith; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.boot.test.context.SpringBootTest; 14 | import org.springframework.boot.test.web.client.TestRestTemplate; 15 | import org.springframework.boot.test.web.server.LocalServerPort; 16 | import org.springframework.http.HttpStatus; 17 | import org.springframework.http.ResponseEntity; 18 | import org.springframework.test.context.junit.jupiter.SpringExtension; 19 | 20 | @ExtendWith(SpringExtension.class) 21 | @SpringBootTest( 22 | classes = {ExampleAppWebFlux.class}, 23 | webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) 24 | public class SmokeTest { 25 | 26 | @Autowired TaskAdminController controller; 27 | @LocalServerPort private Integer serverPort; 28 | private String baseUrl; 29 | @Autowired private TestRestTemplate restTemplate; 30 | 31 | @Test 32 | public void testContextLoads() throws Exception { 33 | assertThat(controller).isNotNull(); 34 | } 35 | 36 | @Test 37 | public void testGetTasksReturnsStatusOK() throws Exception { 38 | ResponseEntity result = 39 | this.restTemplate.getForEntity( 40 | baseUrl 41 | + "/db-scheduler-api/tasks/all?filter=ALL&pageNumber=0&size=10&sorting=DEFAULT&asc=true&searchTerm=" 42 | + ONE_TIME_TASK.getTaskName(), 43 | GetTasksResponse.class); 44 | Assertions.assertEquals(result.getStatusCode(), HttpStatus.OK); 45 | assertThat(result.getBody().getItems()).hasSizeGreaterThan(0); 46 | } 47 | 48 | @Test 49 | public void testGetTasksReturnsExampleOneTimeTask() throws Exception { 50 | ResponseEntity result = 51 | this.restTemplate.getForEntity( 52 | baseUrl 53 | + "/db-scheduler-api/tasks/all?filter=ALL&pageNumber=0&size=10&sorting=DEFAULT&asc=true&searchTerm=" 54 | + ONE_TIME_TASK.getTaskName(), 55 | GetTasksResponse.class); 56 | Assertions.assertEquals(result.getStatusCode(), HttpStatus.OK); 57 | result.getBody().getItems().forEach(t -> System.out.println(t.getTaskName())); 58 | assertThat(result.getBody().getItems()) 59 | .anyMatch(taskModel -> taskModel.getTaskName().equals(ONE_TIME_TASK.getTaskName())); 60 | } 61 | 62 | @BeforeEach 63 | public void setUp() { 64 | baseUrl = "http://localhost:" + serverPort; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /example-app-webflux/src/test/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.datasource.url=jdbc:h2:mem:testdb 2 | -------------------------------------------------------------------------------- /example-app/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use the official OpenJDK base image 2 | FROM openjdk:17-jdk-slim 3 | 4 | # Set the working directory inside the container 5 | WORKDIR /app 6 | 7 | COPY ./*.jar /app/app.jar 8 | 9 | EXPOSE 8081 10 | 11 | # Command to run the application 12 | CMD ["java", "-jar", "/app/app.jar"] -------------------------------------------------------------------------------- /example-app/src/main/java/com/github/bekk/exampleapp/ExampleApp.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package com.github.bekk.exampleapp; 15 | 16 | import com.github.bekk.exampleapp.service.TaskService; 17 | import com.github.kagkarlsson.scheduler.Scheduler; 18 | import org.springframework.boot.CommandLineRunner; 19 | import org.springframework.boot.SpringApplication; 20 | import org.springframework.boot.autoconfigure.SpringBootApplication; 21 | import org.springframework.context.annotation.Bean; 22 | 23 | @SpringBootApplication 24 | public class ExampleApp { 25 | public static void main(String[] args) { 26 | SpringApplication.run(ExampleApp.class, args); 27 | } 28 | 29 | @Bean 30 | public CommandLineRunner runAllManuallyTriggeredTasks(Scheduler scheduler) { 31 | return args -> { 32 | System.out.println("Running all manually triggered tasks"); 33 | TaskService taskService = new TaskService(scheduler); 34 | taskService.runManuallyTriggeredTasks(); 35 | }; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /example-app/src/main/java/com/github/bekk/exampleapp/config/DatabaseConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package com.github.bekk.exampleapp.config; 15 | 16 | import javax.sql.DataSource; 17 | import org.springframework.context.annotation.Bean; 18 | import org.springframework.context.annotation.Configuration; 19 | import org.springframework.jdbc.core.JdbcTemplate; 20 | import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; 21 | import org.springframework.jdbc.core.simple.SimpleJdbcInsert; 22 | 23 | @Configuration 24 | public class DatabaseConfig { 25 | private final DataSource dataSource; 26 | 27 | public DatabaseConfig(DataSource dataSource) { 28 | this.dataSource = dataSource; 29 | } 30 | 31 | @Bean 32 | public JdbcTemplate jdbcTemplate() { 33 | return new JdbcTemplate(dataSource); 34 | } 35 | 36 | @Bean 37 | public SimpleJdbcInsert simpleJdbcInsert() { 38 | return new SimpleJdbcInsert(dataSource) 39 | .withTableName("scheduled_tasks") 40 | .usingGeneratedKeyColumns("id"); 41 | } 42 | 43 | @Bean 44 | public NamedParameterJdbcTemplate namedParameterJdbcTemplate() { 45 | return new NamedParameterJdbcTemplate(dataSource); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /example-app/src/main/java/com/github/bekk/exampleapp/config/SchedulerConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package com.github.bekk.exampleapp.config; 15 | 16 | import com.github.kagkarlsson.scheduler.serializer.JavaSerializer; 17 | import io.rocketbase.extension.jdbc.JdbcLogRepository; 18 | import io.rocketbase.extension.jdbc.Snowflake; 19 | import javax.sql.DataSource; 20 | import org.springframework.beans.factory.annotation.Autowired; 21 | import org.springframework.context.annotation.Bean; 22 | import org.springframework.context.annotation.Configuration; 23 | 24 | @Configuration 25 | public class SchedulerConfig { 26 | private final DataSource dataSource; 27 | 28 | @Autowired 29 | public SchedulerConfig(DataSource dataSource) { 30 | this.dataSource = dataSource; 31 | } 32 | 33 | @Bean 34 | public JdbcLogRepository jdbcLogRepository() { 35 | return new JdbcLogRepository( 36 | dataSource, new JavaSerializer(), JdbcLogRepository.DEFAULT_TABLE_NAME, new Snowflake()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /example-app/src/main/java/com/github/bekk/exampleapp/model/TaskData.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package com.github.bekk.exampleapp.model; 15 | 16 | import java.io.Serializable; 17 | import java.time.Instant; 18 | import lombok.AllArgsConstructor; 19 | import lombok.Getter; 20 | import lombok.Setter; 21 | 22 | @Setter 23 | @Getter 24 | @AllArgsConstructor 25 | public class TaskData implements Serializable { 26 | 27 | private long id; 28 | private String data; 29 | private Instant time; 30 | } 31 | -------------------------------------------------------------------------------- /example-app/src/main/java/com/github/bekk/exampleapp/model/TaskScheduleAndNoData.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package com.github.bekk.exampleapp.model; 15 | 16 | import com.github.kagkarlsson.scheduler.task.helper.ScheduleAndData; 17 | import com.github.kagkarlsson.scheduler.task.schedule.CronSchedule; 18 | import java.io.Serializable; 19 | 20 | public class TaskScheduleAndNoData implements ScheduleAndData, Serializable { 21 | 22 | private static final long serialVersionUID = 1L; // recommended when using Java serialization 23 | 24 | private final CronSchedule schedule; 25 | 26 | private TaskScheduleAndNoData() { 27 | this(null); 28 | } 29 | 30 | public TaskScheduleAndNoData(CronSchedule schedule) { 31 | this.schedule = schedule; 32 | } 33 | 34 | @Override 35 | public CronSchedule getSchedule() { 36 | return this.schedule; 37 | } 38 | 39 | @Override 40 | public Object getData() { 41 | return null; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /example-app/src/main/java/com/github/bekk/exampleapp/model/TestObject.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package com.github.bekk.exampleapp.model; 15 | 16 | import java.io.Serializable; 17 | 18 | public class TestObject implements Serializable { 19 | private String name; 20 | private int id; 21 | private String email; 22 | 23 | public String getName() { 24 | return name; 25 | } 26 | 27 | public TestObject(String initialName, int id, String email) { 28 | this.id = id; 29 | this.name = initialName; 30 | this.email = email; 31 | } 32 | 33 | public void setName(String name) { 34 | this.name = name; 35 | } 36 | 37 | public int getId() { 38 | return id; 39 | } 40 | 41 | public void setId(int id) { 42 | this.id = id; 43 | } 44 | 45 | public String getEmail() { 46 | return email; 47 | } 48 | 49 | public void setEmail(String email) { 50 | this.email = email; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /example-app/src/main/java/com/github/bekk/exampleapp/service/TaskService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package com.github.bekk.exampleapp.service; 15 | 16 | import static com.github.bekk.exampleapp.tasks.ChainTask.CHAINED_STEP_1_TASK; 17 | import static com.github.bekk.exampleapp.tasks.FailingTask.FAILING_ONETIME_TASK; 18 | import static com.github.bekk.exampleapp.tasks.LongRunningTask.LONG_RUNNING_ONETIME_TASK; 19 | import static com.github.bekk.exampleapp.tasks.OneTimeTaskExample.ONE_TIME_TASK; 20 | 21 | import com.github.bekk.exampleapp.model.TaskData; 22 | import com.github.bekk.exampleapp.model.TestObject; 23 | import com.github.kagkarlsson.scheduler.Scheduler; 24 | import java.time.Instant; 25 | import org.springframework.stereotype.Service; 26 | 27 | @Service 28 | public class TaskService { 29 | 30 | private final Scheduler scheduler; 31 | 32 | public TaskService(Scheduler scheduler) { 33 | this.scheduler = scheduler; 34 | } 35 | 36 | public void runManuallyTriggeredTasks() { 37 | scheduler.schedule( 38 | ONE_TIME_TASK.instance("1").data(new TaskData(1, "test data", Instant.now())).build(), 39 | Instant.now()); 40 | 41 | scheduler.schedule( 42 | CHAINED_STEP_1_TASK 43 | .instance("3") 44 | .data(new TestObject("Ole Nordman", 1, "ole.nordman@mail.com")) 45 | .build(), 46 | Instant.now()); 47 | 48 | scheduler.schedule( 49 | LONG_RUNNING_ONETIME_TASK.instance("5").build(), Instant.now().plusSeconds(2)); 50 | scheduler.schedule(FAILING_ONETIME_TASK.instance("6").build(), Instant.now().plusSeconds(2)); 51 | 52 | scheduler.schedule( 53 | ONE_TIME_TASK 54 | .instance("delete-1") 55 | .data(new TaskData(-1, "task 1 to delete", Instant.now())) 56 | .build(), 57 | Instant.now()); 58 | 59 | scheduler.schedule( 60 | ONE_TIME_TASK 61 | .instance("delete-2") 62 | .data(new TaskData(-2, "task 2 to delete", Instant.now())) 63 | .build(), 64 | Instant.now()); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /example-app/src/main/java/com/github/bekk/exampleapp/tasks/ChainTask.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package com.github.bekk.exampleapp.tasks; 15 | 16 | import static utils.Utils.sleep; 17 | 18 | import com.github.bekk.exampleapp.model.TestObject; 19 | import com.github.kagkarlsson.scheduler.SchedulerClient; 20 | import com.github.kagkarlsson.scheduler.task.Task; 21 | import com.github.kagkarlsson.scheduler.task.TaskDescriptor; 22 | import com.github.kagkarlsson.scheduler.task.helper.Tasks; 23 | import java.time.Instant; 24 | import org.springframework.context.annotation.Bean; 25 | import org.springframework.context.annotation.Configuration; 26 | 27 | @Configuration 28 | public class ChainTask { 29 | 30 | public static final TaskDescriptor CHAINED_STEP_1_TASK = 31 | TaskDescriptor.of("chained-step-1", TestObject.class); 32 | 33 | public static final TaskDescriptor CHAINED_STEP_2_TASK = 34 | TaskDescriptor.of("chained-step-2", TestObject.class); 35 | 36 | @Bean 37 | public Task chainTaskStepOne() { 38 | return Tasks.oneTime(CHAINED_STEP_1_TASK) 39 | .execute( 40 | (inst, ctx) -> { 41 | sleep(5000); 42 | final SchedulerClient client = ctx.getSchedulerClient(); 43 | System.out.println( 44 | "Executed chained onetime task step 1: " 45 | + inst.getTaskName() 46 | + " " 47 | + inst.getId()); 48 | TestObject data = inst.getData(); 49 | data.setId(data.getId() + 1); 50 | client.scheduleIfNotExists( 51 | CHAINED_STEP_2_TASK.instance(inst.getId()).data(data).build(), 52 | Instant.now().plusSeconds(10)); 53 | }); 54 | } 55 | 56 | @Bean 57 | public Task chainTaskStepTwo() { 58 | return Tasks.oneTime(CHAINED_STEP_2_TASK) 59 | .execute( 60 | (inst, ctx) -> { 61 | sleep(5000); 62 | final SchedulerClient client = ctx.getSchedulerClient(); 63 | System.out.println( 64 | "Executed chained onetime task step 2: " 65 | + inst.getTaskName() 66 | + " " 67 | + inst.getId()); 68 | TestObject data = inst.getData(); 69 | data.setId(data.getId() + 1); 70 | client.scheduleIfNotExists( 71 | CHAINED_STEP_1_TASK.instance(inst.getId()).data(data).build(), 72 | Instant.now().plusSeconds(20)); 73 | }); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /example-app/src/main/java/com/github/bekk/exampleapp/tasks/DynamicRecurringTaskExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package com.github.bekk.exampleapp.tasks; 15 | 16 | import com.github.bekk.exampleapp.model.TaskScheduleAndNoData; 17 | import com.github.kagkarlsson.scheduler.task.TaskDescriptor; 18 | import com.github.kagkarlsson.scheduler.task.helper.RecurringTaskWithPersistentSchedule; 19 | import com.github.kagkarlsson.scheduler.task.helper.Tasks; 20 | import org.springframework.context.annotation.Bean; 21 | import org.springframework.context.annotation.Configuration; 22 | import utils.Utils; 23 | 24 | @Configuration 25 | public class DynamicRecurringTaskExample { 26 | 27 | public static final TaskDescriptor DYNAMIC_RECURRING_TASK = 28 | TaskDescriptor.of("dynamic-recurring-task", TaskScheduleAndNoData.class); 29 | 30 | @Bean 31 | public RecurringTaskWithPersistentSchedule runDynamicRecurringTask() { 32 | return Tasks.recurringWithPersistentSchedule(DYNAMIC_RECURRING_TASK) 33 | .execute( 34 | (inst, ctx) -> { 35 | Utils.sleep(500); 36 | System.out.println("Executed dynamic recurring task: " + inst.getTaskName()); 37 | }); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /example-app/src/main/java/com/github/bekk/exampleapp/tasks/FailingTask.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package com.github.bekk.exampleapp.tasks; 15 | 16 | import static java.time.Duration.ofSeconds; 17 | 18 | import com.github.kagkarlsson.scheduler.task.FailureHandler; 19 | import com.github.kagkarlsson.scheduler.task.Task; 20 | import com.github.kagkarlsson.scheduler.task.TaskDescriptor; 21 | import com.github.kagkarlsson.scheduler.task.helper.Tasks; 22 | import com.github.kagkarlsson.scheduler.task.schedule.FixedDelay; 23 | import org.springframework.context.annotation.Bean; 24 | import org.springframework.context.annotation.Configuration; 25 | 26 | @Configuration 27 | public class FailingTask { 28 | 29 | public static final TaskDescriptor FAILING_ONETIME_TASK = 30 | TaskDescriptor.of("failing-one-time-task"); 31 | 32 | public static final TaskDescriptor FAILING_RECURRING_TASK = 33 | TaskDescriptor.of("failing-recurring-task"); 34 | 35 | public static final TaskDescriptor FAILING_ONETIME_TASK_BACKOFF = 36 | TaskDescriptor.of("failing-one-time-with-backoff-task"); 37 | 38 | @Bean 39 | public Task runOneTimeFailing() { 40 | return Tasks.oneTime(FAILING_ONETIME_TASK) 41 | .execute( 42 | (inst, ctx) -> { 43 | throw new RuntimeException("Simulated task failure"); 44 | }); 45 | } 46 | 47 | @Bean 48 | public Task runRecurringFailing() { 49 | return Tasks.recurring(FAILING_RECURRING_TASK, FixedDelay.ofSeconds(6)) 50 | .execute( 51 | (inst, ctx) -> { 52 | throw new RuntimeException("Simulated task failure"); 53 | }); 54 | } 55 | 56 | @Bean 57 | public Task runOneTimeFailingWithBackoff() { 58 | return Tasks.oneTime(FAILING_ONETIME_TASK_BACKOFF) 59 | .onFailure(new FailureHandler.ExponentialBackoffFailureHandler<>(ofSeconds(1))) 60 | .execute( 61 | (inst, ctx) -> { 62 | throw new RuntimeException("Simulated task failure"); 63 | }); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /example-app/src/main/java/com/github/bekk/exampleapp/tasks/LongRunningTask.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package com.github.bekk.exampleapp.tasks; 15 | 16 | import static utils.Utils.sleep; 17 | 18 | import com.github.kagkarlsson.scheduler.task.Task; 19 | import com.github.kagkarlsson.scheduler.task.TaskDescriptor; 20 | import com.github.kagkarlsson.scheduler.task.helper.Tasks; 21 | import com.github.kagkarlsson.scheduler.task.schedule.FixedDelay; 22 | import org.springframework.context.annotation.Bean; 23 | import org.springframework.context.annotation.Configuration; 24 | 25 | @Configuration 26 | public class LongRunningTask { 27 | 28 | public static final TaskDescriptor LONG_RUNNING_ONETIME_TASK = 29 | TaskDescriptor.of("long-running-task"); 30 | 31 | public static final TaskDescriptor LONG_RUNNING_RECURRING_TASK = 32 | TaskDescriptor.of("long-running-recurring-task"); 33 | 34 | @Bean 35 | public Task runLongRunningTask() { 36 | return Tasks.oneTime(LONG_RUNNING_ONETIME_TASK) 37 | .execute( 38 | (inst, ctx) -> { 39 | System.out.println("Executing long running task: " + inst.getTaskName()); 40 | sleep(10000); 41 | }); 42 | } 43 | 44 | @Bean 45 | public Task runLongRunningRecurringTask() { 46 | return Tasks.recurring(LONG_RUNNING_RECURRING_TASK, FixedDelay.ofSeconds(20)) 47 | .execute( 48 | (inst, ctx) -> { 49 | System.out.println("Executing long running recurring task: " + inst.getTaskName()); 50 | sleep(10000); 51 | }); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /example-app/src/main/java/com/github/bekk/exampleapp/tasks/OneTimeTaskExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package com.github.bekk.exampleapp.tasks; 15 | 16 | import com.github.bekk.exampleapp.model.TaskData; 17 | import com.github.kagkarlsson.scheduler.task.Task; 18 | import com.github.kagkarlsson.scheduler.task.TaskDescriptor; 19 | import com.github.kagkarlsson.scheduler.task.helper.Tasks; 20 | import org.springframework.context.annotation.Bean; 21 | import org.springframework.context.annotation.Configuration; 22 | 23 | @Configuration 24 | public class OneTimeTaskExample { 25 | 26 | public static final TaskDescriptor ONE_TIME_TASK = 27 | TaskDescriptor.of("onetime-task", TaskData.class); 28 | 29 | @Bean 30 | public Task exampleOneTimeTask() { 31 | return Tasks.oneTime(ONE_TIME_TASK) 32 | .execute( 33 | (inst, ctx) -> { 34 | System.out.println("Executed onetime task: " + inst.getTaskName()); 35 | System.out.println( 36 | "With data id: " + inst.getData().getId() + " data: " + inst.getData().getData()); 37 | }); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /example-app/src/main/java/com/github/bekk/exampleapp/tasks/RecurringTaskExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package com.github.bekk.exampleapp.tasks; 15 | 16 | import static utils.Utils.sleep; 17 | 18 | import com.github.kagkarlsson.scheduler.task.TaskDescriptor; 19 | import com.github.kagkarlsson.scheduler.task.helper.RecurringTask; 20 | import com.github.kagkarlsson.scheduler.task.helper.Tasks; 21 | import com.github.kagkarlsson.scheduler.task.schedule.FixedDelay; 22 | import java.util.Random; 23 | import org.springframework.context.annotation.Bean; 24 | import org.springframework.context.annotation.Configuration; 25 | 26 | @Configuration 27 | public class RecurringTaskExample { 28 | 29 | public static final TaskDescriptor RECURRING_TASK = TaskDescriptor.of("recurring-task"); 30 | 31 | @Bean 32 | public RecurringTask getExample() { 33 | return Tasks.recurring(RECURRING_TASK, FixedDelay.ofSeconds(6)) 34 | .execute( 35 | (inst, ctx) -> { 36 | sleep(5000); 37 | if (new Random().nextInt(100) < 30) { 38 | throw new RuntimeException("Simulated failure in example recurring task"); 39 | } 40 | 41 | System.out.println("Executed recurring task: " + inst.getTaskName()); 42 | }); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /example-app/src/main/java/com/github/bekk/exampleapp/tasks/SpawnerTask.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package com.github.bekk.exampleapp.tasks; 15 | 16 | import static utils.Utils.sleep; 17 | 18 | import com.github.bekk.exampleapp.model.TaskData; 19 | import com.github.kagkarlsson.scheduler.SchedulerClient; 20 | import com.github.kagkarlsson.scheduler.task.Task; 21 | import com.github.kagkarlsson.scheduler.task.TaskDescriptor; 22 | import com.github.kagkarlsson.scheduler.task.helper.Tasks; 23 | import com.github.kagkarlsson.scheduler.task.schedule.FixedDelay; 24 | import java.time.Instant; 25 | import java.util.Random; 26 | import java.util.UUID; 27 | import org.springframework.context.annotation.Bean; 28 | import org.springframework.context.annotation.Configuration; 29 | 30 | @Configuration 31 | public class SpawnerTask { 32 | 33 | public static final TaskDescriptor RECURRING_SPAWNER_TASK = 34 | TaskDescriptor.of("recurring-spawner-task"); 35 | 36 | public static final TaskDescriptor ONE_TIME_SPAWNER_TASK = 37 | TaskDescriptor.of("onetime-spawned-task", TaskData.class); 38 | 39 | @Bean 40 | public Task runSpawner() { 41 | return Tasks.recurring(RECURRING_SPAWNER_TASK, FixedDelay.ofSeconds(180)) 42 | .execute( 43 | (inst, ctx) -> { 44 | final SchedulerClient client = ctx.getSchedulerClient(); 45 | final long randomUUID = UUID.randomUUID().getMostSignificantBits() & Long.MAX_VALUE; 46 | 47 | for (int i = 0; i < 100; i++) { 48 | client.scheduleIfNotExists( 49 | ONE_TIME_SPAWNER_TASK 50 | .instance("spawned " + randomUUID + " loopnr: " + i) 51 | .data(new TaskData(123, "{data: MASSIVEDATA}", Instant.now())) 52 | .build(), 53 | Instant.now().plusSeconds(60)); 54 | } 55 | }); 56 | } 57 | 58 | @Bean 59 | public Task runOneTimeSpawned() { 60 | return Tasks.oneTime(ONE_TIME_SPAWNER_TASK) 61 | .execute( 62 | (inst, ctx) -> { 63 | sleep(10000); 64 | if (new Random().nextInt(100) < 20) { 65 | throw new RuntimeException("Simulated failure"); 66 | } 67 | }); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /example-app/src/main/java/utils/Utils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Bekk 3 | * 4 | *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | * except in compliance with the License. You may obtain a copy of the License at 6 | * 7 | *

http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | *

Unless required by applicable law or agreed to in writing, software distributed under the 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 | * express or implied. See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | package utils; 15 | 16 | public class Utils { 17 | public static void sleep(int millis) { 18 | try { 19 | Thread.sleep(millis); 20 | } catch (InterruptedException e) { 21 | throw new RuntimeException(e); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /example-app/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=${PORT:8081} 2 | spring.datasource.url=jdbc:h2:file:./db-scheduler-application/test-db;MODE=PostgreSQL;DATABASE_TO_LOWER=TRUE;DEFAULT_NULL_ORDERING=HIGH 3 | spring.datasource.driver-class-name=org.h2.Driver 4 | db-scheduler-log.enabled=true 5 | db-scheduler-log.table-name=scheduled_execution_logs 6 | db-scheduler-ui.history=true 7 | db-scheduler-ui.task-data=true 8 | -------------------------------------------------------------------------------- /example-app/src/main/resources/db/migration/V1__create_db.sql: -------------------------------------------------------------------------------- 1 | create table if not exists scheduled_tasks ( 2 | task_name varchar(255), 3 | task_instance varchar(255), 4 | task_data BYTEA, 5 | execution_time TIMESTAMP WITH TIME ZONE, 6 | picked BIT, 7 | picked_by varchar(50), 8 | last_success TIMESTAMP WITH TIME ZONE, 9 | last_failure TIMESTAMP WITH TIME ZONE, 10 | consecutive_failures INT, 11 | last_heartbeat TIMESTAMP WITH TIME ZONE, 12 | version BIGINT, 13 | PRIMARY KEY (task_name, task_instance) 14 | ); 15 | -------------------------------------------------------------------------------- /example-app/src/main/resources/db/migration/V2__add_logging.sql: -------------------------------------------------------------------------------- 1 | create table scheduled_execution_logs 2 | ( 3 | id BIGINT not null primary key, 4 | task_name text not null, 5 | task_instance text not null, 6 | task_data bytea, 7 | picked_by text, 8 | time_started timestamp with time zone not null, 9 | time_finished timestamp with time zone not null, 10 | succeeded BOOLEAN not null, 11 | duration_ms BIGINT not null, 12 | exception_class text, 13 | exception_message text, 14 | exception_stacktrace text 15 | ); 16 | 17 | CREATE INDEX stl_started_idx ON scheduled_execution_logs (time_started); 18 | CREATE INDEX stl_task_name_idx ON scheduled_execution_logs (task_name); 19 | CREATE INDEX stl_exception_class_idx ON scheduled_execution_logs (exception_class); -------------------------------------------------------------------------------- /example-app/src/test/java/com/github/bekk/exampleapp/DynamicRecurringSchedulingTest.java: -------------------------------------------------------------------------------- 1 | package com.github.bekk.exampleapp; 2 | 3 | import static com.github.bekk.exampleapp.tasks.DynamicRecurringTaskExample.DYNAMIC_RECURRING_TASK; 4 | import static org.assertj.core.api.Assertions.assertThat; 5 | 6 | import com.github.bekk.exampleapp.model.TaskScheduleAndNoData; 7 | import com.github.kagkarlsson.scheduler.SchedulerClient; 8 | import com.github.kagkarlsson.scheduler.task.schedule.CronSchedule; 9 | import com.github.kagkarlsson.scheduler.task.schedule.Schedules; 10 | import java.time.Instant; 11 | import no.bekk.dbscheduler.ui.model.GetTasksResponse; 12 | import org.junit.jupiter.api.Assertions; 13 | import org.junit.jupiter.api.BeforeEach; 14 | import org.junit.jupiter.api.Test; 15 | import org.springframework.beans.factory.annotation.Autowired; 16 | import org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration; 17 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 18 | import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; 19 | import org.springframework.boot.test.context.SpringBootTest; 20 | import org.springframework.boot.test.web.client.TestRestTemplate; 21 | import org.springframework.boot.test.web.server.LocalServerPort; 22 | import org.springframework.http.HttpStatus; 23 | import org.springframework.http.ResponseEntity; 24 | 25 | @SpringBootTest( 26 | classes = {ExampleApp.class}, 27 | webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) 28 | @EnableAutoConfiguration( 29 | exclude = {SecurityAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class}) 30 | class DynamicRecurringSchedulingTest { 31 | 32 | @LocalServerPort private Integer serverPort; 33 | private String baseUrl; 34 | @Autowired private TestRestTemplate restTemplate; 35 | 36 | @Autowired private SchedulerClient schedulerClient; 37 | 38 | @Test 39 | void testGetTasksReturnsDynamicRecurringTask() { 40 | // Given 41 | CronSchedule cron = Schedules.cron("0 0/1 * * * *"); // every minute 42 | TaskScheduleAndNoData data = new TaskScheduleAndNoData(cron); 43 | 44 | schedulerClient.scheduleIfNotExists( 45 | DYNAMIC_RECURRING_TASK.instance("single_instance").data(data).build(), 46 | cron.getInitialExecutionTime(Instant.now())); 47 | 48 | // When 49 | ResponseEntity result = 50 | this.restTemplate.getForEntity( 51 | baseUrl 52 | + "/db-scheduler-api/tasks/all?filter=ALL&pageNumber=0&size=10&sorting=DEFAULT&asc=true&searchTerm=" 53 | + DYNAMIC_RECURRING_TASK.getTaskName(), 54 | GetTasksResponse.class); 55 | 56 | // Then 57 | Assertions.assertEquals(HttpStatus.OK, result.getStatusCode()); 58 | result.getBody().getItems().forEach(t -> System.out.println(t.getTaskName())); 59 | assertThat(result.getBody().getItems()) 60 | .anyMatch( 61 | taskModel -> taskModel.getTaskName().equals(DYNAMIC_RECURRING_TASK.getTaskName())); 62 | } 63 | 64 | @BeforeEach 65 | public void setUp() { 66 | baseUrl = "http://localhost:" + serverPort; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /example-app/src/test/java/com/github/bekk/exampleapp/SmokeReadOnlyTest.java: -------------------------------------------------------------------------------- 1 | package com.github.bekk.exampleapp; 2 | 3 | import static com.github.bekk.exampleapp.tasks.FailingTask.FAILING_ONETIME_TASK; 4 | import static org.assertj.core.api.Assertions.assertThat; 5 | 6 | import no.bekk.dbscheduler.ui.model.GetTasksResponse; 7 | import org.junit.jupiter.api.BeforeEach; 8 | import org.junit.jupiter.api.Test; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration; 11 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 12 | import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; 13 | import org.springframework.boot.test.context.SpringBootTest; 14 | import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; 15 | import org.springframework.boot.test.web.client.TestRestTemplate; 16 | import org.springframework.boot.test.web.server.LocalServerPort; 17 | import org.springframework.context.ApplicationContext; 18 | import org.springframework.http.HttpStatus; 19 | import org.springframework.http.ResponseEntity; 20 | 21 | @SpringBootTest( 22 | properties = "db-scheduler-ui.read-only=true", 23 | classes = ExampleApp.class, 24 | webEnvironment = WebEnvironment.RANDOM_PORT) 25 | @EnableAutoConfiguration( 26 | exclude = {SecurityAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class}) 27 | class SmokeReadOnlyTest { 28 | 29 | @LocalServerPort private int serverPort; 30 | 31 | private String baseUrl; 32 | 33 | @Autowired private ApplicationContext context; 34 | 35 | @Autowired private TestRestTemplate restTemplate; 36 | 37 | @BeforeEach 38 | void setUp() { 39 | baseUrl = "http://localhost:" + serverPort; 40 | } 41 | 42 | @Test 43 | void testContextLoads() { 44 | assertThat(context.containsBean("taskAdminController")).isFalse(); 45 | assertThat(context.containsBean("taskController")).isTrue(); 46 | } 47 | 48 | @Test 49 | void readingTasksWorks() { 50 | ResponseEntity result = 51 | restTemplate.getForEntity(baseUrl + "/db-scheduler-api/tasks/all", GetTasksResponse.class); 52 | assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); 53 | assertThat(result.getBody()).isNotNull(); 54 | assertThat(result.getBody().getItems()).isNotEmpty(); 55 | } 56 | 57 | @Test 58 | void deletingTasksIsNotAvailableInReadOnlyMode() { 59 | ResponseEntity result = 60 | restTemplate.postForEntity( 61 | baseUrl 62 | + "/db-scheduler-api/tasks/delete?id=%d&name=%s" 63 | .formatted(6, FAILING_ONETIME_TASK.getTaskName()), 64 | null, 65 | Void.class); 66 | 67 | assertThat(result.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /example-app/src/test/resources/application-ctxpath.properties: -------------------------------------------------------------------------------- 1 | spring.config.activate.on-profile=ctxpath 2 | server.servlet.context-path=/api 3 | spring.mvc.servlet.path=/test -------------------------------------------------------------------------------- /example-app/src/test/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.datasource.url=jdbc:h2:mem:testdb 2 | -------------------------------------------------------------------------------- /heroku.yml: -------------------------------------------------------------------------------- 1 | build: 2 | docker: 3 | web: dockerfile -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "db-scheduler-ui", 3 | "lockfileVersion": 3, 4 | "requires": true, 5 | "packages": {} 6 | } 7 | --------------------------------------------------------------------------------