Compare commits
25
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a20d36c108
|
||
|
|
a992133053
|
||
|
|
4b3ca697e5
|
||
|
|
1a800da73d
|
||
|
|
17a094d427
|
||
|
|
009e940ee6
|
||
|
|
b6c73cc3a5
|
||
|
|
5d4b212b56
|
||
|
|
1828c7f9d8
|
||
|
|
055e33c99b
|
||
|
|
912f1dc3a9
|
||
|
|
cd995254fd
|
||
|
|
da332ce0fb | ||
|
|
22a40ad295 | ||
|
|
c92df228b9 | ||
|
|
5af79ae05d | ||
|
|
469e727884 | ||
|
|
6434453666 | ||
|
|
f2584be8c5 | ||
|
|
43d65e2e58 | ||
|
|
6dbbdacd0e
|
||
|
|
913bd497ed | ||
|
|
8cb487343a
|
||
|
|
b8bc1a7719 | ||
|
|
dd06743824
|
No files matched your search
+11
-57
@@ -1,60 +1,14 @@
|
||||
version: 2.1
|
||||
|
||||
setup: true
|
||||
orbs:
|
||||
qqq-orb: kingsrook/qqq-orb@0.6.9
|
||||
|
||||
commands:
|
||||
configure-package-install:
|
||||
steps:
|
||||
- run:
|
||||
name: Keep package installation unattended
|
||||
command: |
|
||||
# These build machines are discarded after the job.
|
||||
sudo install -d /etc/needrestart/conf.d
|
||||
printf '%s\n' '$nrconf{restart} = "l";' | sudo tee /etc/needrestart/conf.d/99-qqq-ci.conf >/dev/null
|
||||
|
||||
qqq-orb: kingsrook/qqq-orb@0.8
|
||||
workflows:
|
||||
test_only:
|
||||
jobs:
|
||||
- qqq-orb/mvn_test_only:
|
||||
pre-steps:
|
||||
- configure-package-install
|
||||
context: [qqq-maven-registry-credentials]
|
||||
filters:
|
||||
branches:
|
||||
ignore: /(develop|release\/.*)/
|
||||
|
||||
publish_snapshot:
|
||||
jobs:
|
||||
- qqq-orb/mvn_publish:
|
||||
pre-steps:
|
||||
- configure-package-install
|
||||
context: [qqq-maven-registry-credentials]
|
||||
branch_type: snapshot
|
||||
filters:
|
||||
branches:
|
||||
only: [develop]
|
||||
|
||||
publish_release_candidate:
|
||||
jobs:
|
||||
- qqq-orb/mvn_publish:
|
||||
pre-steps:
|
||||
- configure-package-install
|
||||
context: [qqq-maven-registry-credentials]
|
||||
branch_type: release_candidate
|
||||
filters:
|
||||
branches:
|
||||
only: [/release\/.*/]
|
||||
|
||||
publish_release:
|
||||
jobs:
|
||||
- qqq-orb/mvn_publish:
|
||||
pre-steps:
|
||||
- configure-package-install
|
||||
context: [qqq-maven-registry-credentials]
|
||||
branch_type: release
|
||||
filters:
|
||||
branches:
|
||||
ignore: /.*/
|
||||
tags:
|
||||
only: [/^v\d+\.\d+\.\d+$/]
|
||||
setup:
|
||||
jobs:
|
||||
- qqq-orb/generate_pipeline:
|
||||
orb_version: '0.8'
|
||||
filters:
|
||||
branches:
|
||||
ignore: automation/qbit-rc-pending
|
||||
tags:
|
||||
only: /^v\d+\.\d+\.\d+$/
|
||||
@@ -0,0 +1,7 @@
|
||||
name: build
|
||||
on:
|
||||
push:
|
||||
jobs:
|
||||
maven:
|
||||
uses: self:KofTwentyTwo/ci-workflows/.gitea/workflows/maven.yaml@v1
|
||||
secrets: inherit
|
||||
@@ -0,0 +1,23 @@
|
||||
name: CodeQL
|
||||
'on':
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- develop
|
||||
- release/**
|
||||
- hotfix/**
|
||||
pull_request: {}
|
||||
schedule:
|
||||
- cron: 41 16 * * 0
|
||||
workflow_dispatch: {}
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
security-events: write
|
||||
packages: read
|
||||
concurrency:
|
||||
group: codeql-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
jobs:
|
||||
analyze:
|
||||
uses: QRun-IO/qqq-orb/.github/workflows/qbit-codeql.yml@c288a8864fe9054f68410e1d78f5863aea4c4d22
|
||||
@@ -1,25 +1,74 @@
|
||||
# Cut the next release candidate (QRun-IO/qqq docs/QRUN-QBIT-REPO-STANDARD.md, C6):
|
||||
# run this workflow by hand, or merge a develop PR labelled `release:rc`. It merges develop into
|
||||
# the release line and the release/* push runs CircleCI publish_release_candidate.
|
||||
name: Release candidate
|
||||
|
||||
on:
|
||||
'on':
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
dry_run:
|
||||
description: Print the plan without merging or creating branches
|
||||
description: Plan a cut without changing release state
|
||||
type: boolean
|
||||
default: false
|
||||
finalize:
|
||||
description: Retry finalization of an existing cut; do not cut another RC
|
||||
type: boolean
|
||||
default: false
|
||||
recover:
|
||||
description: Recover the same published RC after a deployment timeout; never
|
||||
reupload
|
||||
type: boolean
|
||||
default: false
|
||||
circle_build:
|
||||
description: Original Circle publisher build number (recovery only)
|
||||
type: string
|
||||
default: ''
|
||||
merge_sha:
|
||||
description: Existing cut merge_sha (finalize retry only)
|
||||
type: string
|
||||
default: ''
|
||||
bump_sha:
|
||||
description: Existing cut bump_sha (finalize retry only)
|
||||
type: string
|
||||
default: ''
|
||||
branch:
|
||||
description: Existing cut branch (finalize retry only)
|
||||
type: string
|
||||
default: ''
|
||||
version:
|
||||
description: Existing cut version (finalize retry only)
|
||||
type: string
|
||||
default: ''
|
||||
pull_request:
|
||||
types: [closed]
|
||||
branches: [develop]
|
||||
|
||||
types:
|
||||
- closed
|
||||
branches:
|
||||
- develop
|
||||
repository_dispatch:
|
||||
types:
|
||||
- qbit-rc-published
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
actions: read
|
||||
checks: read
|
||||
jobs:
|
||||
release-rc:
|
||||
if: github.event_name == 'workflow_dispatch' || (github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'release:rc'))
|
||||
uses: QRun-IO/.github/.github/workflows/qbit-release-rc.yml@main
|
||||
if: github.event_name == 'workflow_dispatch' || github.event_name == 'repository_dispatch'
|
||||
|| (github.event_name == 'pull_request' && github.event.pull_request.merged
|
||||
== true && github.event.pull_request.base.ref == 'develop' && contains(github.event.pull_request.labels.*.name,
|
||||
'release:rc'))
|
||||
uses: QRun-IO/qqq-orb/.github/workflows/qbit-release-rc.yml@cc30f599e63c8e41629c99d23395bac1adb8bc44
|
||||
with:
|
||||
dry_run: ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run }}
|
||||
dry_run: ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run || false
|
||||
}}
|
||||
finalize: ${{ github.event_name == 'repository_dispatch' || (github.event_name
|
||||
== 'workflow_dispatch' && inputs.finalize) }}
|
||||
recover: ${{ github.event_name == 'workflow_dispatch' && inputs.recover || false
|
||||
}}
|
||||
circle_build: ${{ github.event_name == 'workflow_dispatch' && inputs.circle_build
|
||||
|| '' }}
|
||||
merge_sha: ${{ github.event_name == 'repository_dispatch' && github.event.client_payload.merge_sha
|
||||
|| inputs.merge_sha || '' }}
|
||||
bump_sha: ${{ github.event_name == 'repository_dispatch' && github.event.client_payload.bump_sha
|
||||
|| inputs.bump_sha || '' }}
|
||||
branch: ${{ github.event_name == 'repository_dispatch' && github.event.client_payload.branch
|
||||
|| inputs.branch || '' }}
|
||||
version: ${{ github.event_name == 'repository_dispatch' && github.event.client_payload.version
|
||||
|| inputs.version || '' }}
|
||||
secrets: inherit
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
profile: qbit
|
||||
orb_version: '0.8'
|
||||
default_branch: develop
|
||||
java_version: '21'
|
||||
browsers: true
|
||||
api_checks: false
|
||||
docs_only: false
|
||||
release_on_main: false
|
||||
release_tag_policy: stable
|
||||
test_branch_policy: non-release
|
||||
publish:
|
||||
snapshot: true
|
||||
release_candidate: true
|
||||
release: true
|
||||
asciidoc: false
|
||||
release_completion: true
|
||||
@@ -5,6 +5,12 @@ and [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [0.1.0] - Unreleased
|
||||
|
||||
### Security
|
||||
|
||||
- The Jobs app and its five non-admin home widgets now require `<appName>.hasAccess`
|
||||
(default `jobs.hasAccess`) and are hidden otherwise. Behaviour change: hosts must grant
|
||||
this permission to the users who should see the Jobs app. Workers still needs `jobs_admin`.
|
||||
|
||||
### Changed
|
||||
|
||||
- Built on QQQ 4.1.0-RC.7; stand-ins removed.
|
||||
|
||||
@@ -42,6 +42,9 @@ The tables are core's, prefixed `jobs_`. This qbit adds no tables.
|
||||
|
||||
## Permissions
|
||||
- Users see their own jobs: core's record lock on `jobs_job.userId` through the `jobs_user` key.
|
||||
- The Jobs app and its home widgets require `<appName>.hasAccess` (default `jobs.hasAccess`, from
|
||||
`JobsQBitConfig.getHasAccessPermissionName()`); without it they are hidden. Hosts must grant it to the
|
||||
users who should see the Jobs app. The header Jobs tray and record locks are unaffected.
|
||||
- `jobs_admin` sees and controls every job, and sees the Workers widget.
|
||||
- Apply the keys in your auth customizer with `DurableJobsSecurity.applyToSession(session)`.
|
||||
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<properties>
|
||||
<revision>0.1.0-RC.2</revision>
|
||||
<qqq.version>4.1.0-RC.26</qqq.version> <!-- feeds qbit-build-parent's qqq-bom-pom import -->
|
||||
<revision>0.1.0-SNAPSHOT</revision>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
<jacoco.version>0.8.15</jacoco.version>
|
||||
@@ -27,7 +28,7 @@
|
||||
<parent>
|
||||
<groupId>com.kingsrook</groupId>
|
||||
<artifactId>qbit-build-parent</artifactId>
|
||||
<version>2.0.0</version>
|
||||
<version>2.2.1</version>
|
||||
</parent>
|
||||
|
||||
<groupId>com.kingsrook.qbits</groupId>
|
||||
@@ -179,7 +180,7 @@
|
||||
<activeByDefault>true</activeByDefault>
|
||||
</activation>
|
||||
<properties>
|
||||
<qqq.snapshot.version>4.1.0-RC.8</qqq.snapshot.version>
|
||||
<qqq.snapshot.version>4.1.0-RC.26</qqq.snapshot.version>
|
||||
</properties>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
@@ -192,18 +193,6 @@
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>sonatype-snapshots</id>
|
||||
<url>https://central.sonatype.com/repository/maven-snapshots/</url>
|
||||
<releases>
|
||||
<enabled>false</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
</repositories>
|
||||
</profile>
|
||||
|
||||
<!-- Local demo server and screenshots: builds src/demo/java against released QQQ RC.7. -->
|
||||
@@ -211,7 +200,7 @@
|
||||
<profile>
|
||||
<id>demo</id>
|
||||
<properties>
|
||||
<qqq.snapshot.version>4.1.0-RC.7</qqq.snapshot.version>
|
||||
<qqq.snapshot.version>4.1.0-RC.26</qqq.snapshot.version>
|
||||
</properties>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
@@ -238,7 +227,7 @@
|
||||
<dependency>
|
||||
<groupId>com.kingsrook.qqq</groupId>
|
||||
<artifactId>qqq-frontend-next</artifactId>
|
||||
<version>1.0.0-RC.19</version>
|
||||
<version>1.0.0-RC.38</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
||||
@@ -82,6 +82,15 @@ public class JobsQBitConfig implements QBitConfig
|
||||
assertCondition(schedulerName == null || !schedulerName.isBlank(), "schedulerName must be null or nonblank", errors);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
** The permission a user needs to see the Jobs app and its home widgets,
|
||||
** "<appName>.hasAccess" (default "jobs.hasAccess"). Hosts must grant it.
|
||||
*******************************************************************************/
|
||||
public String getHasAccessPermissionName()
|
||||
{
|
||||
return (appName + ".hasAccess");
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
** Get appName.
|
||||
*******************************************************************************/
|
||||
|
||||
@@ -45,6 +45,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.DenyBehavior;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.PermissionLevel;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.QPermissionRules;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData;
|
||||
@@ -167,8 +168,16 @@ public class JobsQBitProducer implements QBitMetaDataProducer<JobsQBitConfig>
|
||||
QWidgetMetaData workers = registerWidget(instance, "jobsHomeWorkers", "Workers", WidgetType.TABLE, qBitConfig.getHomeRefreshSeconds());
|
||||
workers.withPermissionRules(adminRules());
|
||||
registerWidget(instance, "jobsHomeHistory", "Job history", WidgetType.STACKED_BAR_CHART, qBitConfig.getHomeRefreshSeconds());
|
||||
for(String name : HOME_WIDGETS)
|
||||
{
|
||||
if(!"jobsHomeWorkers".equals(name))
|
||||
{
|
||||
instance.getWidget(name).setPermissionRules(appAccessRules());
|
||||
}
|
||||
}
|
||||
QAppMetaData app = new QAppMetaData().withName(qBitConfig.getAppName()).withLabel("Jobs").withIcon(new QIcon("pending_actions"))
|
||||
.withChild(instance.getTable(JOBS_JOB)).withWidgets(new ArrayList<>(HOME_WIDGETS));
|
||||
.withChild(instance.getTable(JOBS_JOB)).withWidgets(new ArrayList<>(HOME_WIDGETS))
|
||||
.withPermissionRules(appAccessRules());
|
||||
app.setParentAppName(qBitConfig.getParentAppName());
|
||||
instance.addApp(app);
|
||||
if(qBitConfig.getParentAppName() != null)
|
||||
@@ -235,6 +244,16 @@ public class JobsQBitProducer implements QBitMetaDataProducer<JobsQBitConfig>
|
||||
.withTier(Tier.T2).withWidgetName(widgetName).withGridColumns(12));
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
** The Jobs app and its home widgets require "<appName>.hasAccess"; users
|
||||
** without it never see the app or its widgets (hidden, not locked).
|
||||
*******************************************************************************/
|
||||
private QPermissionRules appAccessRules()
|
||||
{
|
||||
return (new QPermissionRules().withLevel(PermissionLevel.HAS_ACCESS_PERMISSION)
|
||||
.withPermissionBaseName(qBitConfig.getAppName()).withDenyBehavior(DenyBehavior.HIDDEN));
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
** Match the exact core permission, without a .hasAccess suffix.
|
||||
*******************************************************************************/
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2026. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
package com.kingsrook.qbits.jobs;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.actions.metadata.MetaDataAction;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QPermissionDeniedException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.DenyBehavior;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.PermissionLevel;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** The Jobs app and its home widgets are hidden from users without
|
||||
** "jobs.hasAccess"; Workers additionally needs jobs_admin.
|
||||
*******************************************************************************/
|
||||
class JobsAppPermissionTest extends BaseJobsTest
|
||||
{
|
||||
private static final List<String> NON_ADMIN_WIDGETS = JobsQBitProducer.HOME_WIDGETS.stream().filter(w -> !"jobsHomeWorkers".equals(w)).toList();
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** The permission name is public and follows the configured app name.
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void permissionNameAndRules()
|
||||
{
|
||||
assertThat(config.getHasAccessPermissionName()).isEqualTo("jobs.hasAccess");
|
||||
assertThat(new JobsQBitConfig().withAppName("bg").getHasAccessPermissionName()).isEqualTo("bg.hasAccess");
|
||||
assertThat(instance.getApp("jobs").getPermissionRules().getLevel()).isEqualTo(PermissionLevel.HAS_ACCESS_PERMISSION);
|
||||
assertThat(instance.getApp("jobs").getPermissionRules().getPermissionBaseName()).isEqualTo("jobs");
|
||||
assertThat(instance.getApp("jobs").getPermissionRules().getDenyBehavior()).isEqualTo(DenyBehavior.HIDDEN);
|
||||
assertThat(instance.getWidget("jobsJobProgress").getPermissionRules().getLevel()).isNotEqualTo(PermissionLevel.HAS_ACCESS_PERMISSION);
|
||||
for(String name : NON_ADMIN_WIDGETS)
|
||||
{
|
||||
assertThat(instance.getWidget(name).getPermissionRules().getPermissionBaseName()).isEqualTo("jobs");
|
||||
assertThat(instance.getWidget(name).getPermissionRules().getDenyBehavior()).isEqualTo(DenyBehavior.HIDDEN);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** No permissions: no app, no home widgets, every render denied.
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void noPermissionsSeesNothing() throws Exception
|
||||
{
|
||||
instance.setEnforcePermissionsInCoreActions(true);
|
||||
login("nobody", false);
|
||||
MetaDataOutput output = new MetaDataAction().execute(new MetaDataInput());
|
||||
assertThat(output.getApps()).doesNotContainKey("jobs");
|
||||
assertThat(output.getWidgets()).doesNotContainKeys(JobsQBitProducer.HOME_WIDGETS.toArray(String[]::new));
|
||||
for(String name : JobsQBitProducer.HOME_WIDGETS)
|
||||
{
|
||||
assertThatThrownBy(() -> render(name, null)).isInstanceOf(QPermissionDeniedException.class);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** jobs.hasAccess: app and the five widgets, Workers only with jobs_admin.
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void hasAccessSeesAppAndWidgets() throws Exception
|
||||
{
|
||||
instance.setEnforcePermissionsInCoreActions(true);
|
||||
login("user", false).withPermission(config.getHasAccessPermissionName());
|
||||
MetaDataOutput output = new MetaDataAction().execute(new MetaDataInput());
|
||||
assertThat(output.getApps()).containsKey("jobs");
|
||||
assertThat(output.getWidgets()).containsKeys(NON_ADMIN_WIDGETS.toArray(String[]::new));
|
||||
assertThat(output.getWidgets()).doesNotContainKey("jobsHomeWorkers");
|
||||
for(String name : NON_ADMIN_WIDGETS)
|
||||
{
|
||||
assertThat(render(name, null)).isNotNull();
|
||||
}
|
||||
assertThatThrownBy(() -> render("jobsHomeWorkers", null)).isInstanceOf(QPermissionDeniedException.class);
|
||||
|
||||
login("admin", true).withPermission(config.getHasAccessPermissionName());
|
||||
assertThat(new MetaDataAction().execute(new MetaDataInput()).getWidgets()).containsKey("jobsHomeWorkers");
|
||||
assertThat(render("jobsHomeWorkers", null)).isNotNull();
|
||||
}
|
||||
}
|
||||
@@ -139,7 +139,7 @@ class JobsProgressTest extends BaseJobsTest
|
||||
.withValue(PROGRESS_MESSAGE, "Uploading manifest").withValue(BYTES_DONE, 1536L).withValue(BYTES_TOTAL, 1048576L)
|
||||
.withValue(ETA_SECONDS, 90L).withValue(LOG_DROPPED, 12)
|
||||
.withValue(PROGRESS_JSON, "{\"phases\":[{\"name\":\"upload\",\"label\":\"Upload backup\"}]}"));
|
||||
login(USER_A, false);
|
||||
login(USER_A, false).withPermission(config.getHasAccessPermissionName());
|
||||
TableData home = (TableData) render("jobsHomeActive", null);
|
||||
assertThat(home.getRows()).singleElement().satisfies(row -> assertThat(row)
|
||||
.containsEntry("Progress", "73%").containsEntry("Phase", "Upload backup")
|
||||
@@ -148,7 +148,7 @@ class JobsProgressTest extends BaseJobsTest
|
||||
MultiStatisticsData record = (MultiStatisticsData) render("jobsJobProgress", job.getValueString(ID));
|
||||
assertThat(record.getStatisticsGroupData()).extracting(MultiStatisticsData.StatisticsGroupData::getSubheader)
|
||||
.contains("Upload backup | Uploading manifest | 1.5 KiB / 1.0 MiB", "73% done", "ETA in 1m", "12 earlier lines not kept");
|
||||
login(USER_B, false);
|
||||
login(USER_B, false).withPermission(config.getHasAccessPermissionName());
|
||||
assertThat(((TableData) render("jobsHomeActive", null)).getRows()).isEmpty();
|
||||
assertThat(((MultiStatisticsData) render("jobsJobProgress", job.getValueString(ID))).getStatisticsGroupData()).isEmpty();
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user