Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d00c230140
|
||
|
|
11f1fbff3d |
No files matched your search
+4
-2
@@ -5,7 +5,8 @@ All notable changes to this project are documented here. The format follows Keep
|
||||
## [0.1.0] - Unreleased
|
||||
|
||||
### Added
|
||||
- Every table explains itself: a help bundle (`help/qbit-object-storage.yaml`, QQQ help-bundle format) says what each `object_storage_*` table is for and how its rows get created; it shows on the table screen and when the table is empty. Applied by `ObjectStorageHelp` (STAND-IN for QRun-IO/qqq#1198); a host's own help is kept.
|
||||
- Expanded the host-neutral in-app help bundle to cover storage fields, actions, widgets and possible values, with live references and a `HelpBundleAssertions` coverage gate. Registered it for QQQ startup sync and HOST overrides; retained inline table/app help for hosts without help-content tables.
|
||||
- Every table explains itself: a help bundle (`help/qbit-object-storage.yaml`, QQQ help-bundle format) says what each `object_storage_*` table is for and how its rows get created; it shows on the table screen and when the table is empty. Applied inline by `ObjectStorageHelp` for hosts without help-content tables; a host's own help is kept.
|
||||
- A Setup card on the Storage home (connections, buckets, header rules, protection rules) with the state and next step of each, resolved the way the runtime resolves them.
|
||||
- A starter changelog `db/liquibase/qbit-object-storage/object-storage-defaults.changelog.sql`: a disabled `default-mutable` header rule. Insert-only, run through real Liquibase in `DefaultsChangelogTest`.
|
||||
- `ObjectStorageTablesGuardrailTest` (each table is used at runtime, seeded or explained, and has help).
|
||||
@@ -24,9 +25,10 @@ All notable changes to this project are documented here. The format follows Keep
|
||||
- Conformance suite against MinIO and Garage (Testcontainers), service, index and action tests, and the index and actions on PostgreSQL.
|
||||
|
||||
### Changed
|
||||
- Raised the QQQ BOM baseline to released `4.1.0-RC.7` for the core help-bundle registry and coverage assertions.
|
||||
- Raised the QQQ BOM baseline to released `4.1.0-RC.5` and delegated S3 client construction to core `S3ClientFactory.buildClient`. Removed local HTTP client and trust-manager implementations; existing endpoint, region, addressing, checksum, TLS/CA, credentials and timeout settings remain supported.
|
||||
- Clients remain owned by each connection runtime and are rebuilt on reset/reinstall. Retry behavior now follows core/SDK defaults instead of forcing `ADAPTIVE_V2`; hosts can select the SDK retry mode through `AWS_RETRY_MODE` or `aws.retryMode`.
|
||||
- Retained local presigning (including a separate endpoint/key). A request-credentials bridge preserves static session tokens because RC.5 core metadata/factory has no session-token field or credentials-provider hook. This bridge and the existing `${file.PATH}` resolver are marked for removal by RC.7 under issue #10.
|
||||
- Retained local presigning (including a separate endpoint/key). A request-credentials bridge preserves static session tokens because RC.5 core metadata/factory has no session-token field or credentials-provider hook. This bridge and the existing `${file.PATH}` resolver remain necessary in released RC.7 (no session-token/provider hook or file-reference interpreter); their review deadline is now RC.8 under issue #10.
|
||||
- Added the filesystem module with its unused SFTP/SSH stack excluded. Resolved dependencies otherwise only change QQQ RC.4 to RC.5 and jsoup 1.23.1 to 1.23.2; see [dependency details](docs/S3-FACTORY-MIGRATION.md).
|
||||
|
||||
### Notes
|
||||
|
||||
@@ -15,7 +15,7 @@ S3-compatible storage as a capability for any QQQ app, and an admin app to run i
|
||||
## Requirements
|
||||
|
||||
- Java 21.
|
||||
- QQQ `4.1.0-RC.5` (released BOM), including `qqq-backend-module-filesystem`. `ConnectionRuntime` maps connection settings to core's `S3BackendMetaData` and uses `S3ClientFactory.buildClient`; core owns HTTP, TLS and checksum setup. The unused SFTP/SSH dependency is excluded. See the [dependency diff](docs/S3-FACTORY-MIGRATION.md).
|
||||
- QQQ `4.1.0-RC.7` (released BOM), including `qqq-backend-module-filesystem`. `ConnectionRuntime` maps connection settings to core's `S3BackendMetaData` and uses `S3ClientFactory.buildClient`; core owns HTTP, TLS and checksum setup. The unused SFTP/SSH dependency is excluded. See the [dependency diff](docs/S3-FACTORY-MIGRATION.md).
|
||||
- A QQQ backend for the qbit's tables (PostgreSQL is the intended store). `ObjectStorageSchema.postgresScript(config)` generates the DDL from the same metadata the app runs on, for your Liquibase or Flyway migration.
|
||||
|
||||
## Install
|
||||
@@ -47,6 +47,20 @@ Connections and buckets come from this code only. Secrets are **references** (`$
|
||||
|
||||
Client settings take effect when the qbit configuration is reinstalled or the runtime is reset. Presigning keeps its separate public endpoint and credentials. Retry behavior now follows core/SDK defaults; hosts requiring adaptive retries can set `AWS_RETRY_MODE=adaptive` or `aws.retryMode=adaptive`. Static session tokens remain supported through a small local bridge pending core support.
|
||||
|
||||
## In-app help
|
||||
|
||||
The producer registers [help/qbit-object-storage.yaml](src/main/resources/help/qbit-object-storage.yaml)
|
||||
as a `QBIT` bundle. QQQ syncs it when the host has the help-content tables installed;
|
||||
registration is safe without them, and inline table/app help still explains empty lists.
|
||||
The bundle version is the literal POM version; update both when releasing.
|
||||
|
||||
Hosts can register their own `layer: HOST` bundle with `HelpBundles.register(qInstance, "help/my-host.yaml")`.
|
||||
Use the same entry key and role to override qbit text, keeping live `{{...}}` references where useful.
|
||||
Hosts that rename the optional Storage app should provide an app entry for their configured name.
|
||||
`ObjectStorageHelpBundleTest` checks all owned tables, fields, processes, apps, widgets and
|
||||
possible-value sources using `HelpBundleAssertions`, including valid keys, live references and
|
||||
plain summaries of at most 120 characters. It runs with `./mvnw verify`.
|
||||
|
||||
## Use
|
||||
|
||||
```java
|
||||
|
||||
@@ -255,14 +255,14 @@
|
||||
<profiles>
|
||||
<!-- Built against qqq 4.1 (QBitMetaDataProducer). Until qbit-build-parent -->
|
||||
<!-- imports the released 4.1 BOM, this profile is active by default and -->
|
||||
<!-- imports qqq-bom-pom 4.1.0-RC.5 ahead of the parent's 4.0.0 BOM. -->
|
||||
<!-- imports qqq-bom-pom 4.1.0-RC.7 ahead of the parent's 4.0.0 BOM. -->
|
||||
<profile>
|
||||
<id>qqq-snapshot</id>
|
||||
<activation>
|
||||
<activeByDefault>true</activeByDefault>
|
||||
</activation>
|
||||
<properties>
|
||||
<qqq.snapshot.version>4.1.0-RC.6</qqq.snapshot.version>
|
||||
<qqq.snapshot.version>4.1.0-RC.7</qqq.snapshot.version>
|
||||
</properties>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
|
||||
@@ -34,6 +34,7 @@ import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducerMultiOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.help.bundles.HelpBundles;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
|
||||
@@ -194,6 +195,8 @@ public class ObjectStorageQBitProducer implements QBitMetaDataProducer<ObjectSto
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
qInstance.getApp(qBitConfig.getParentAppName()).withChild(qInstance.getApp(qBitConfig.getAppName()));
|
||||
}
|
||||
// Registration is metadata-only; core skips startup sync without a helpContent table.
|
||||
HelpBundles.register(qInstance, "help/qbit-object-storage.yaml");
|
||||
ObjectStorageHelp.apply(qInstance, qBitConfig);
|
||||
return (new MetaDataProducerMultiOutput());
|
||||
}
|
||||
|
||||
@@ -291,8 +291,9 @@ public class ConnectionRuntime
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** STAND-IN(core=QRun-IO/qqq#1165, removeBy=4.1.0-RC.7)
|
||||
** RC.5 metadata/factory lacks a session token or credentials-provider hook.
|
||||
** STAND-IN(core=QRun-IO/qqq#1165, removeBy=4.1.0-RC.8)
|
||||
** RC.7 S3BackendMetaData and S3ClientFactory still lack a session token or
|
||||
** credentials-provider hook (verified against the released JAR).
|
||||
** Override request credentials only; core still owns the client and transport.
|
||||
*******************************************************************************/
|
||||
private static class SessionCredentialsClient extends DelegatingS3Client
|
||||
|
||||
@@ -36,7 +36,8 @@ import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
** the allow-list. Resolved values live only in memory, never in a record, a log
|
||||
** line or a process value.
|
||||
**
|
||||
** STAND-IN(core=QRun-IO/qqq#1165, removeBy=4.1.0-RC.7)
|
||||
** STAND-IN(core=QRun-IO/qqq#1165, removeBy=4.1.0-RC.8)
|
||||
** RC.7 QMetaDataVariableInterpreter still supports env/prop, not file references.
|
||||
** ${file.PATH} moves to QMetaDataVariableInterpreter in QQQ core
|
||||
** (spec section 8.2); then this class delegates entirely.
|
||||
*******************************************************************************/
|
||||
|
||||
@@ -40,9 +40,8 @@ import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
** Applies help/qbit-object-storage.yaml (the QQQ help-bundle format) to the
|
||||
** Storage app and its object_storage_* tables: what each table is for and how
|
||||
** its rows get created, shown on the table screen and when it is empty.
|
||||
** STAND-IN(core=QRun-IO/qqq#1198, removeBy=4.1.0-RC.8): replace with
|
||||
** HelpBundles.register once the pinned QQQ release includes it; the bundle file
|
||||
** stays the same.
|
||||
** Inline defaults preserve empty-state help for hosts without helpContent.
|
||||
** The producer also registers the bundle for core sync and HOST overrides.
|
||||
*******************************************************************************/
|
||||
public final class ObjectStorageHelp
|
||||
{
|
||||
@@ -81,7 +80,7 @@ public final class ObjectStorageHelp
|
||||
{
|
||||
String key = entry.path("key").asText();
|
||||
String summary = entry.path("summary").asText();
|
||||
if(key.isBlank() || summary.isBlank() || summary.length() > 120 || entry.path("content").asText().isBlank() || entries.put(key, entry) != null)
|
||||
if(key.isBlank() || summary.isBlank() || summary.length() > 120 || entries.put(key, entry) != null)
|
||||
{
|
||||
throw (new IOException("invalid or duplicate entry: " + key));
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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.objectstorage;
|
||||
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import com.kingsrook.qbits.objectstorage.testsupport.StorageTestSupport;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.help.bundles.HelpBundles;
|
||||
import com.kingsrook.qqq.backend.core.testing.HelpBundleAssertions;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Coverage of every object owned by the storage qbit, including its optional app.
|
||||
*******************************************************************************/
|
||||
class ObjectStorageHelpBundleTest
|
||||
{
|
||||
/*******************************************************************************
|
||||
** Release the installed runtime after each test.
|
||||
*******************************************************************************/
|
||||
@AfterEach
|
||||
void tearDown()
|
||||
{
|
||||
StorageTestSupport.tearDown();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Register safely without help tables and cover all owned metadata.
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void bundleIsCompleteWithoutHelpTables() throws Exception
|
||||
{
|
||||
QInstance instance = StorageTestSupport.setUp(new ObjectStorageQBitConfig()
|
||||
.withBackendName(StorageTestSupport.MEMORY).withBucketScoping(false).withOpenWhenAnonymous(true).withAdminApp(true));
|
||||
|
||||
assertThat(instance.getTable("helpContent")).isNull();
|
||||
assertThat(HelpBundles.of(instance).getBundles()).singleElement()
|
||||
.satisfies(bundle -> assertThat(bundle.getId()).isEqualTo("qbit-object-storage"));
|
||||
HelpBundles.syncOnStartup(instance);
|
||||
Set<String> keys = HelpBundles.of(instance).getBundles().get(0).getEntries().stream()
|
||||
.map(entry -> entry.getKey()).collect(Collectors.toSet());
|
||||
// RC.7 validates these entries but only requires app/table/process/field coverage.
|
||||
instance.getWidgets().keySet().stream().filter(name -> name.startsWith("object_storage_") || name.startsWith("objectStorage"))
|
||||
.forEach(name -> assertThat(keys).contains("widget:" + name + ";slot:label"));
|
||||
instance.getPossibleValueSources().keySet().stream().filter(name -> name.startsWith("object_storage_"))
|
||||
.forEach(name -> assertThat(keys).contains("possibleValueSource:" + name + ";slot:help"));
|
||||
HelpBundleAssertions.forInstance(instance)
|
||||
.bundles("help/qbit-object-storage.yaml")
|
||||
.owning(target -> target.name().startsWith("object_storage_") || target.name().startsWith("objectStorage"))
|
||||
.assertComplete();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user