diff --git a/.github/actions/build/action.yml b/.github/actions/build/action.yml deleted file mode 100644 index 1d928c6..0000000 --- a/.github/actions/build/action.yml +++ /dev/null @@ -1,44 +0,0 @@ -name: 'Build Plugin and Sample' -description: 'Builds emoji plugin, installs it, builds incident app, validates integration tests syntax' -inputs: - java-version: - description: 'Java version to use' - required: false - default: '21' - node-version: - description: 'Node.js version to use' - required: false - default: '22' - -runs: - using: 'composite' - steps: - - name: Set up Java - uses: actions/setup-java@v4 - with: - distribution: 'temurin' - java-version: ${{ inputs.java-version }} - cache: maven - - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: ${{ inputs.node-version }} - cache: 'npm' - - - name: Build and Install Emoji Plugin - shell: bash - run: | - mvn clean install -DskipTests=true -B -V - - - name: Build Incident App (with test compilation) - shell: bash - working-directory: tests/incident-app - run: | - mvn clean compile test-compile -B -V - - - name: Validate Integration Tests Syntax - shell: bash - working-directory: tests/integration-tests - run: | - mvn compile test-compile -DskipTests=true -B -V \ No newline at end of file diff --git a/.github/actions/scan-with-sonar/action.yml b/.github/actions/scan-with-sonar/action.yml index 9ec0a46..4bcba15 100644 --- a/.github/actions/scan-with-sonar/action.yml +++ b/.github/actions/scan-with-sonar/action.yml @@ -32,7 +32,7 @@ runs: maven-version: ${{inputs.maven-version}} - name: Set up Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version: '22' cache: 'npm' diff --git a/.github/actions/test/action.yml b/.github/actions/test/action.yml index 0297fd2..bf8c211 100644 --- a/.github/actions/test/action.yml +++ b/.github/actions/test/action.yml @@ -1,5 +1,5 @@ -name: 'Test Plugin and Sample' -description: 'Tests emoji plugin, bookshop, and integration tests' +name: 'Test Notifications Plugin and Sample' +description: 'Builds and tests the notifications plugin, then runs sample app tests' inputs: java-version: description: 'Java version to use' @@ -14,24 +14,24 @@ runs: using: 'composite' steps: - name: Set up Java - uses: actions/setup-java@v4 + uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4 with: distribution: 'temurin' java-version: ${{ inputs.java-version }} cache: maven - name: Set up Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version: ${{ inputs.node-version }} cache: 'npm' - - name: Build, Install and Test Emoji Plugin + - name: Build, Install and Test Notifications Plugin shell: bash run: | mvn clean install -B -V - - name: Test Bookshop App (Integration Tests) + - name: Test Sample App (Integration Tests) shell: bash working-directory: sample-app/srv run: | diff --git a/.github/dependabot.yml b/.github/dependabot.yml index f5b0f1d..04fc4f9 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -3,10 +3,11 @@ updates: - package-ecosystem: maven directories: - "/" - - "/tests/bookshop" schedule: - interval: daily + interval: weekly open-pull-requests-limit: 10 + cooldown: + default-days: 7 ignore: - dependency-name: com.sap.cds:* versions: @@ -15,12 +16,19 @@ updates: - package-ecosystem: npm directories: - "/" - - "/tests/bookshop" schedule: - interval: daily + interval: weekly open-pull-requests-limit: 10 + cooldown: + default-days: 7 - package-ecosystem: github-actions directory: "/" schedule: - interval: daily + interval: weekly + cooldown: + default-days: 7 + groups: + github-actions: + patterns: + - "*" diff --git a/.github/workflows/check-changelog.yml b/.github/workflows/check-changelog.yml index b46ddb1..34c2e0f 100644 --- a/.github/workflows/check-changelog.yml +++ b/.github/workflows/check-changelog.yml @@ -9,6 +9,6 @@ jobs: name: Check Changelog Action runs-on: ubuntu-latest steps: - - uses: tarides/changelog-check-action@v3 + - uses: tarides/changelog-check-action@0189fc7eedec3ef3e9648c713908f6f2a6e99057 # v3 with: changelog: CHANGELOG.md \ No newline at end of file diff --git a/.github/workflows/issue.yml b/.github/workflows/issue.yml index cb2f5a8..123c41f 100644 --- a/.github/workflows/issue.yml +++ b/.github/workflows/issue.yml @@ -1,5 +1,7 @@ name: Label issues +permissions: {} + on: issues: types: diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 1e991ab..5d7bc97 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -3,15 +3,15 @@ name: Linting on: [pull_request] jobs: - build: + lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - name: Set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4 with: java-version: '17' distribution: 'temurin' - name: Run Spotless - run: mvn spotless:check + run: mvn spotless:check -Dspotless.check.skip=false diff --git a/.github/workflows/prevent-issue-labeling.yml b/.github/workflows/prevent-issue-labeling.yml index ba2473b..b30cc2c 100644 --- a/.github/workflows/prevent-issue-labeling.yml +++ b/.github/workflows/prevent-issue-labeling.yml @@ -1,5 +1,7 @@ name: Prevent "New" Label on Issues +permissions: {} + on: issues: types: [labeled] diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0713dea..90e8517 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,7 +18,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - name: Test CAP Projects uses: ./.github/actions/test diff --git a/cds-feature-notifications/pom.xml b/cds-feature-notifications/pom.xml index 1856161..38a94bd 100644 --- a/cds-feature-notifications/pom.xml +++ b/cds-feature-notifications/pom.xml @@ -1,4 +1,5 @@ - + + 4.0.0 @@ -214,4 +215,4 @@ - \ No newline at end of file + diff --git a/cds-feature-notifications/src/main/java/com/sap/cds/notifications/assemblers/NotificationAssembler.java b/cds-feature-notifications/src/main/java/com/sap/cds/notifications/assemblers/NotificationAssembler.java index fd3a9a2..fcee284 100644 --- a/cds-feature-notifications/src/main/java/com/sap/cds/notifications/assemblers/NotificationAssembler.java +++ b/cds-feature-notifications/src/main/java/com/sap/cds/notifications/assemblers/NotificationAssembler.java @@ -110,7 +110,8 @@ public List buildNotifications(EventContext context) { "Batch notification emit for event '{}' with empty data list, skipping", eventName); return List.of(); } - logger.debug("Batch notification emit for event '{}': {} entries", eventName, dataList.size()); + logger.debug( + "Batch notification emit for event '{}': {} entries", eventName, dataList.size()); List results = new ArrayList<>(); for (Object item : dataList) { if (!(item instanceof CdsData cdsData)) { @@ -274,7 +275,8 @@ public CqnPredicate containment( * @param serviceCatalog service catalog to obtain the persistence service * @return query result containing a single row with column "result" */ - public static Result executeDummySelect(CqnValue resolvedExpression, ServiceCatalog serviceCatalog) { + public static Result executeDummySelect( + CqnValue resolvedExpression, ServiceCatalog serviceCatalog) { Value expr = ExpressionBuilder.create(resolvedExpression).value(); PersistenceService ps = serviceCatalog.getService(PersistenceService.class, PersistenceService.DEFAULT_NAME); diff --git a/cds-feature-notifications/src/main/java/com/sap/cds/notifications/assemblers/NotificationTemplateAssembler.java b/cds-feature-notifications/src/main/java/com/sap/cds/notifications/assemblers/NotificationTemplateAssembler.java index da2ac44..de2f69f 100644 --- a/cds-feature-notifications/src/main/java/com/sap/cds/notifications/assemblers/NotificationTemplateAssembler.java +++ b/cds-feature-notifications/src/main/java/com/sap/cds/notifications/assemblers/NotificationTemplateAssembler.java @@ -7,6 +7,7 @@ import cds.gen.notificationtemplateproviderservice.NotificationTemplates; import cds.gen.notificationtemplateproviderservice.Tags; import cds.gen.notificationtemplateproviderservice.Translations; +import com.fasterxml.jackson.databind.ObjectMapper; import com.sap.cds.Struct; import com.sap.cds.notifications.helpers.I18nHelper; import com.sap.cds.reflect.CdsBaseType; @@ -16,13 +17,12 @@ import com.sap.cds.reflect.CdsSimpleType; import com.sap.cds.reflect.CdsType; import com.sap.cds.services.runtime.CdsRuntime; -import com.fasterxml.jackson.databind.ObjectMapper; import java.util.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * Helper class to build standalone NotificationTemplate objects from CDS event annotations. + * Helper class to build standalone NotificationTemplate objects from CDS event annotations. * *

Annotation mapping to standalone template fields: * @@ -55,7 +55,8 @@ public List buildAllNotificationTemplates() { List templates = new ArrayList<>(); CdsModel model = runtime.getCdsModel(); - model.events() + model + .events() .filter(event -> event.findAnnotation("notification.template.title").isPresent()) .forEach(event -> extractTemplateFromEvent(event).ifPresent(templates::add)); @@ -121,7 +122,8 @@ private Translations createTranslation( translation.setDisplayName(eventName); // Title (required) - from @notification.template.title - String title = i18nHelper.resolveAnnotationValue(event, "notification.template.title", i18nTexts); + String title = + i18nHelper.resolveAnnotationValue(event, "notification.template.title", i18nTexts); if (title == null || title.trim().isEmpty()) { throw new IllegalStateException( String.format( @@ -132,19 +134,22 @@ private Translations createTranslation( translation.setTitle(title); // Preview - from @notification.template.publicTitle - String preview = i18nHelper.resolveAnnotationValue(event, "notification.template.publicTitle", i18nTexts); + String preview = + i18nHelper.resolveAnnotationValue(event, "notification.template.publicTitle", i18nTexts); if (preview != null && !preview.trim().isEmpty()) { translation.setPreview(preview); } // Body - from @notification.template.subtitle - String body = i18nHelper.resolveAnnotationValue(event, "notification.template.subtitle", i18nTexts); + String body = + i18nHelper.resolveAnnotationValue(event, "notification.template.subtitle", i18nTexts); if (body != null && !body.trim().isEmpty()) { translation.setBody(body); } // Description - from @notification.template.description or @description - String description = i18nHelper.resolveAnnotationValue(event, "notification.template.description", i18nTexts); + String description = + i18nHelper.resolveAnnotationValue(event, "notification.template.description", i18nTexts); if (description == null) { description = i18nHelper.resolveAnnotationValue(event, "description", i18nTexts); } @@ -198,15 +203,14 @@ private Email buildEmail(CdsEvent event, Map i18nTexts) { } private String extractVisibility(CdsEvent event) { - return Boolean.TRUE.equals( - event.getAnnotationValue("notification.customizable", Boolean.FALSE)) + return Boolean.TRUE.equals(event.getAnnotationValue("notification.customizable", Boolean.FALSE)) ? "PUBLIC" : null; } /** - * Extract source (service name) from the event's qualified name. E.g. "CatalogService.BookOrdered" - * → "CatalogService". + * Extract source (service name) from the event's qualified name. E.g. + * "CatalogService.BookOrdered" → "CatalogService". */ private String extractSource(String qualifiedName) { int lastDot = qualifiedName.lastIndexOf('.'); @@ -289,5 +293,4 @@ private String cdsTypeToJsonSchemaType(CdsElementDefinition element) { return "string"; } } - } diff --git a/cds-feature-notifications/src/main/java/com/sap/cds/notifications/assemblers/NotificationTypeAssembler.java b/cds-feature-notifications/src/main/java/com/sap/cds/notifications/assemblers/NotificationTypeAssembler.java index e867ab5..ce4e19a 100644 --- a/cds-feature-notifications/src/main/java/com/sap/cds/notifications/assemblers/NotificationTypeAssembler.java +++ b/cds-feature-notifications/src/main/java/com/sap/cds/notifications/assemblers/NotificationTypeAssembler.java @@ -29,9 +29,11 @@ public List buildAllNotificationTypes() { List notificationTypes = new ArrayList<>(); CdsModel model = runtime.getCdsModel(); - model.events() + model + .events() .filter(event -> event.findAnnotation("notification.template.title").isPresent()) - .forEach(event -> extractNotificationTypeFromEvent(event).ifPresent(notificationTypes::add)); + .forEach( + event -> extractNotificationTypeFromEvent(event).ifPresent(notificationTypes::add)); return notificationTypes; } diff --git a/cds-feature-notifications/src/main/java/com/sap/cds/notifications/handlers/EntityNotificationHandler.java b/cds-feature-notifications/src/main/java/com/sap/cds/notifications/handlers/EntityNotificationHandler.java index 1035844..b57d123 100644 --- a/cds-feature-notifications/src/main/java/com/sap/cds/notifications/handlers/EntityNotificationHandler.java +++ b/cds-feature-notifications/src/main/java/com/sap/cds/notifications/handlers/EntityNotificationHandler.java @@ -406,7 +406,8 @@ public CqnPredicate containment( } }); - Result result = NotificationAssembler.executeDummySelect(resolved, context.getServiceCatalog()); + Result result = + NotificationAssembler.executeDummySelect(resolved, context.getServiceCatalog()); boolean conditionMet = result diff --git a/cds-feature-notifications/src/main/java/com/sap/cds/notifications/handlers/LocalHandler.java b/cds-feature-notifications/src/main/java/com/sap/cds/notifications/handlers/LocalHandler.java index f0bd228..c673335 100644 --- a/cds-feature-notifications/src/main/java/com/sap/cds/notifications/handlers/LocalHandler.java +++ b/cds-feature-notifications/src/main/java/com/sap/cds/notifications/handlers/LocalHandler.java @@ -6,7 +6,6 @@ import cds.gen.notificationproviderservice.NotificationProperties; import cds.gen.notificationproviderservice.Notifications; import cds.gen.notificationproviderservice.Recipients; - import com.sap.cds.notifications.assemblers.NotificationAssembler; import com.sap.cds.services.EventContext; import com.sap.cds.services.cds.ApplicationService; diff --git a/cds-feature-notifications/src/main/java/com/sap/cds/notifications/handlers/LocalNotificationTemplateAutoProvisionerHandler.java b/cds-feature-notifications/src/main/java/com/sap/cds/notifications/handlers/LocalNotificationTemplateAutoProvisionerHandler.java index 58f656e..fefeec7 100644 --- a/cds-feature-notifications/src/main/java/com/sap/cds/notifications/handlers/LocalNotificationTemplateAutoProvisionerHandler.java +++ b/cds-feature-notifications/src/main/java/com/sap/cds/notifications/handlers/LocalNotificationTemplateAutoProvisionerHandler.java @@ -5,7 +5,6 @@ import cds.gen.notificationtemplateproviderservice.NotificationTemplates; import cds.gen.notificationtemplateproviderservice.Translations; - import com.sap.cds.notifications.assemblers.NotificationTemplateAssembler; import com.sap.cds.services.application.ApplicationLifecycleService; import com.sap.cds.services.handler.EventHandler; @@ -99,7 +98,8 @@ private void logTemplate(NotificationTemplates template) { ? translation .getEmail() .getBodyHtml() - .substring(0, Math.min(100, translation.getEmail().getBodyHtml().length())) + .substring( + 0, Math.min(100, translation.getEmail().getBodyHtml().length())) + "..." : "null", translation.getEmail().getBodyText()); @@ -108,6 +108,7 @@ private void logTemplate(NotificationTemplates template) { } logger.info("==============================================================="); - logger.info("Standalone template '{}' logged (LOCAL MODE - not sent to ANS)", template.getKey()); + logger.info( + "Standalone template '{}' logged (LOCAL MODE - not sent to ANS)", template.getKey()); } } diff --git a/cds-feature-notifications/src/main/java/com/sap/cds/notifications/handlers/LocalNotificationTypeAutoProvisionerHandler.java b/cds-feature-notifications/src/main/java/com/sap/cds/notifications/handlers/LocalNotificationTypeAutoProvisionerHandler.java index 3ab91f8..cf67bce 100644 --- a/cds-feature-notifications/src/main/java/com/sap/cds/notifications/handlers/LocalNotificationTypeAutoProvisionerHandler.java +++ b/cds-feature-notifications/src/main/java/com/sap/cds/notifications/handlers/LocalNotificationTypeAutoProvisionerHandler.java @@ -6,7 +6,6 @@ import cds.gen.notificationtypeproviderservice.DeliveryChannels; import cds.gen.notificationtypeproviderservice.NotificationTypes; import cds.gen.notificationtypeproviderservice.Templates; - import com.sap.cds.notifications.assemblers.NotificationTypeAssembler; import com.sap.cds.services.application.ApplicationLifecycleService; import com.sap.cds.services.handler.EventHandler; diff --git a/cds-feature-notifications/src/main/java/com/sap/cds/notifications/handlers/NotificationTemplateAutoProvisionerHandler.java b/cds-feature-notifications/src/main/java/com/sap/cds/notifications/handlers/NotificationTemplateAutoProvisionerHandler.java index 4ddc142..9e1ff12 100644 --- a/cds-feature-notifications/src/main/java/com/sap/cds/notifications/handlers/NotificationTemplateAutoProvisionerHandler.java +++ b/cds-feature-notifications/src/main/java/com/sap/cds/notifications/handlers/NotificationTemplateAutoProvisionerHandler.java @@ -6,7 +6,6 @@ import cds.gen.notificationtemplateproviderservice.NotificationTemplateProviderService; import cds.gen.notificationtemplateproviderservice.NotificationTemplates; import cds.gen.notificationtemplateproviderservice.NotificationTemplates_; - import com.sap.cds.notifications.assemblers.NotificationTemplateAssembler; import com.sap.cds.ql.Insert; import com.sap.cds.ql.Select; @@ -36,8 +35,7 @@ public class NotificationTemplateAutoProvisionerHandler implements EventHandler private final NotificationTemplateProviderService notificationTemplateProviderService; public NotificationTemplateAutoProvisionerHandler( - CdsRuntime runtime, - NotificationTemplateProviderService notificationTemplateProviderService) { + CdsRuntime runtime, NotificationTemplateProviderService notificationTemplateProviderService) { this.notificationTemplateBuilder = new NotificationTemplateAssembler(runtime); this.notificationTemplateProviderService = notificationTemplateProviderService; } @@ -91,8 +89,7 @@ private Set fetchExistingTemplateKeys() { .filter(Objects::nonNull) .collect(Collectors.toSet()); } catch (Exception e) { - logger.warn( - "Could not fetch existing standalone templates from ANS: {}", e.getMessage()); + logger.warn("Could not fetch existing standalone templates from ANS: {}", e.getMessage()); return Collections.emptySet(); } } @@ -130,8 +127,7 @@ private void createTemplate(NotificationTemplates template) { e); } - logger.error( - "Failed to create standalone template '{}' in ANS", template.getKey(), e); + logger.error("Failed to create standalone template '{}' in ANS", template.getKey(), e); throw e; } } @@ -160,8 +156,7 @@ private void updateTemplate(NotificationTemplates template) { e); } - logger.error( - "Failed to update standalone template '{}' in ANS", template.getKey(), e); + logger.error("Failed to update standalone template '{}' in ANS", template.getKey(), e); throw e; } } diff --git a/cds-feature-notifications/src/main/java/com/sap/cds/notifications/handlers/NotificationTypeAutoProvisionerHandler.java b/cds-feature-notifications/src/main/java/com/sap/cds/notifications/handlers/NotificationTypeAutoProvisionerHandler.java index 84c82ed..52bffab 100644 --- a/cds-feature-notifications/src/main/java/com/sap/cds/notifications/handlers/NotificationTypeAutoProvisionerHandler.java +++ b/cds-feature-notifications/src/main/java/com/sap/cds/notifications/handlers/NotificationTypeAutoProvisionerHandler.java @@ -6,7 +6,6 @@ import cds.gen.notificationtypeproviderservice.NotificationTypeProviderService; import cds.gen.notificationtypeproviderservice.NotificationTypes; import cds.gen.notificationtypeproviderservice.NotificationTypes_; - import com.sap.cds.notifications.assemblers.NotificationTypeAssembler; import com.sap.cds.ql.Insert; import com.sap.cds.ql.Select; diff --git a/cds-feature-notifications/src/main/java/com/sap/cds/notifications/handlers/ProductionHandler.java b/cds-feature-notifications/src/main/java/com/sap/cds/notifications/handlers/ProductionHandler.java index 10519bb..97c638c 100644 --- a/cds-feature-notifications/src/main/java/com/sap/cds/notifications/handlers/ProductionHandler.java +++ b/cds-feature-notifications/src/main/java/com/sap/cds/notifications/handlers/ProductionHandler.java @@ -6,7 +6,6 @@ import cds.gen.notificationproviderservice.NotificationProviderService; import cds.gen.notificationproviderservice.Notifications; import cds.gen.notificationproviderservice.Notifications_; - import com.sap.cds.notifications.assemblers.NotificationAssembler; import com.sap.cds.ql.Insert; import com.sap.cds.services.EventContext; diff --git a/cds-feature-notifications/src/test/java/com/sap/cds/notifications/builders/NotificationBuilderTest.java b/cds-feature-notifications/src/test/java/com/sap/cds/notifications/builders/NotificationBuilderTest.java index 6faa89d..32dc04f 100644 --- a/cds-feature-notifications/src/test/java/com/sap/cds/notifications/builders/NotificationBuilderTest.java +++ b/cds-feature-notifications/src/test/java/com/sap/cds/notifications/builders/NotificationBuilderTest.java @@ -6,7 +6,6 @@ import static org.junit.jupiter.api.Assertions.*; import cds.gen.notificationproviderservice.Recipients; - import com.sap.cds.notifications.assemblers.NotificationAssembler; import com.sap.cds.ql.CQL; import com.sap.cds.ql.cqn.CqnComparisonPredicate; diff --git a/sample-app/srv/src/test/java/customer/sample_app/handlers/mock/NotificationTemplateProviderServiceMockHandler.java b/sample-app/srv/src/test/java/customer/sample_app/handlers/mock/NotificationTemplateProviderServiceMockHandler.java index 15e640c..6e3ba8c 100644 --- a/sample-app/srv/src/test/java/customer/sample_app/handlers/mock/NotificationTemplateProviderServiceMockHandler.java +++ b/sample-app/srv/src/test/java/customer/sample_app/handlers/mock/NotificationTemplateProviderServiceMockHandler.java @@ -32,9 +32,8 @@ public class NotificationTemplateProviderServiceMockHandler implements EventHand private static final Logger logger = LoggerFactory.getLogger(NotificationTemplateProviderServiceMockHandler.class); - // In-memory storage for notification templates - private static final Map templateStore = - new ConcurrentHashMap<>(); + // In-memory storage for notification templates + private static final Map templateStore = new ConcurrentHashMap<>(); // Tracks how many times each template key has been updated private static final Map updateCountByKey = new ConcurrentHashMap<>(); @@ -108,9 +107,7 @@ public void interceptUpdate(CdsUpdateEventContext context) { String key = updated.getKey(); if (key != null && templateStore.containsKey(key)) { templateStore.put(key, updated); - updateCountByKey - .computeIfAbsent(key, k -> new AtomicInteger(0)) - .incrementAndGet(); + updateCountByKey.computeIfAbsent(key, k -> new AtomicInteger(0)).incrementAndGet(); logger.debug("MockHandler updated notification template: Key={}", key); } else { logger.warn("MockHandler UPDATE: no existing template with Key={}", key); diff --git a/sample-app/srv/src/test/java/customer/sample_app/integration/NotificationTemplateProvisioningTest.java b/sample-app/srv/src/test/java/customer/sample_app/integration/NotificationTemplateProvisioningTest.java index e806030..26d2ced 100644 --- a/sample-app/srv/src/test/java/customer/sample_app/integration/NotificationTemplateProvisioningTest.java +++ b/sample-app/srv/src/test/java/customer/sample_app/integration/NotificationTemplateProvisioningTest.java @@ -85,14 +85,21 @@ void testCertificateExpirationTemplateStructure() { assertEquals("CertificateExpiration", template.getKey()); // Visibility - @notification.customizable: true → PUBLIC - assertEquals("PUBLIC", template.getVisibility(), "Template with @notification.customizable: true should be PUBLIC"); + assertEquals( + "PUBLIC", + template.getVisibility(), + "Template with @notification.customizable: true should be PUBLIC"); // PropertiesSchema - should include all event elements EXCEPT recipients String schema = template.getPropertiesSchema(); assertNotNull(schema, "PropertiesSchema should be set"); - assertFalse(schema.contains("recipients"), "Schema should NOT contain 'recipients' (it's not a template variable)"); - assertTrue(schema.contains("certificateName"), "Schema should contain 'certificateName' property"); - assertTrue(schema.contains("expirationDate"), "Schema should contain 'expirationDate' property"); + assertFalse( + schema.contains("recipients"), + "Schema should NOT contain 'recipients' (it's not a template variable)"); + assertTrue( + schema.contains("certificateName"), "Schema should contain 'certificateName' property"); + assertTrue( + schema.contains("expirationDate"), "Schema should contain 'expirationDate' property"); assertTrue(schema.contains("name"), "Schema should contain 'name' property"); // Translations @@ -100,8 +107,11 @@ void testCertificateExpirationTemplateStructure() { assertNotNull(translations, "Translations should not be null"); assertFalse(translations.isEmpty(), "Translations should not be empty"); - LOG.debug("CertificateExpiration template: Key={}, Visibility={}, Translations={}", - template.getKey(), template.getVisibility(), translations.size()); + LOG.debug( + "CertificateExpiration template: Key={}, Visibility={}, Translations={}", + template.getKey(), + template.getVisibility(), + translations.size()); } // ────────────────────────────────────────────────────────────── @@ -111,17 +121,21 @@ void testCertificateExpirationTemplateStructure() { @Test void testVisibilityDefaultsToPrivate() { LOG.debug("=========================================="); - LOG.debug("Test: Templates without @notification.customizable should have null visibility (PRIVATE)"); + LOG.debug( + "Test: Templates without @notification.customizable should have null visibility (PRIVATE)"); LOG.debug("=========================================="); - // SystemMaintenance has no @notification.customizable → visibility should be null (ANS defaults PRIVATE) + // SystemMaintenance has no @notification.customizable → visibility should be null (ANS defaults + // PRIVATE) NotificationTemplates template = NotificationTemplateProviderServiceMockHandler.getTemplateByKey("SystemMaintenance"); assertNotNull(template, "SystemMaintenance template should be provisioned"); - assertNull(template.getVisibility(), + assertNull( + template.getVisibility(), "Template without @notification.customizable should have null visibility (ANS defaults to PRIVATE)"); - LOG.debug("SystemMaintenance visibility: {} (null = ANS default PRIVATE)", template.getVisibility()); + LOG.debug( + "SystemMaintenance visibility: {} (null = ANS default PRIVATE)", template.getVisibility()); } // ────────────────────────────────────────────────────────────── @@ -159,7 +173,8 @@ void testI18nResolvedInTranslations() { assertNoUnresolvedI18n(t.getEmail().getBodyHtml(), "Email.BodyHtml", lang); } - LOG.debug("[{}] Title={}, Body={}, Preview={}", lang, t.getTitle(), t.getBody(), t.getPreview()); + LOG.debug( + "[{}] Title={}, Body={}, Preview={}", lang, t.getTitle(), t.getBody(), t.getPreview()); } } @@ -180,13 +195,15 @@ void testMultiLanguageTranslations() { List translations = template.getTranslations(); // The sample-app has i18n files for: en, de, tr, es - assertTrue(translations.size() >= 4, + assertTrue( + translations.size() >= 4, "Expected translations for at least 4 languages, got: " + translations.size()); // Verify English Translations en = findTranslation(translations, "en"); assertNotNull(en, "English translation should exist"); - assertTrue(en.getTitle().contains("{{certificateName}}"), + assertTrue( + en.getTitle().contains("{{certificateName}}"), "English title should contain Mustache variable {{certificateName}}"); // Verify German @@ -214,7 +231,8 @@ void testMultiLanguageTranslations() { @Test void testStaticTemplateTranslation() { LOG.debug("=========================================="); - LOG.debug("Test: Templates with static strings should have same value across all locale translations"); + LOG.debug( + "Test: Templates with static strings should have same value across all locale translations"); LOG.debug("=========================================="); // SystemMaintenance uses static strings (no {i18n>...} placeholders). @@ -231,18 +249,23 @@ void testStaticTemplateTranslation() { // Verify title contains Mustache variable Translations first = translations.get(0); assertNotNull(first.getTitle()); - assertTrue(first.getTitle().contains("{{systemName}}"), + assertTrue( + first.getTitle().contains("{{systemName}}"), "Title should contain Mustache variable: " + first.getTitle()); // All translations should have the same title (static string, no i18n differentiation) String expectedTitle = first.getTitle(); for (Translations t : translations) { - assertEquals(expectedTitle, t.getTitle(), + assertEquals( + expectedTitle, + t.getTitle(), "Static template should have identical title across all locales, but lang '" - + t.getLanguage() + "' differs"); + + t.getLanguage() + + "' differs"); } - LOG.debug("Static template: {} translations, all with title: {}", translations.size(), expectedTitle); + LOG.debug( + "Static template: {} translations, all with title: {}", translations.size(), expectedTitle); } // ────────────────────────────────────────────────────────────── @@ -319,9 +342,11 @@ void testTranslationMetadata() { assertEquals("CertificateExpiration", en.getEvent(), "Event should be the event name"); // DisplayName = event name - assertEquals("CertificateExpiration", en.getDisplayName(), "DisplayName should be the event name"); + assertEquals( + "CertificateExpiration", en.getDisplayName(), "DisplayName should be the event name"); - LOG.debug("Source={}, Event={}, DisplayName={}", en.getSource(), en.getEvent(), en.getDisplayName()); + LOG.debug( + "Source={}, Event={}, DisplayName={}", en.getSource(), en.getEvent(), en.getDisplayName()); } // ────────────────────────────────────────────────────────────── @@ -342,7 +367,8 @@ void testPropertiesSchemaReflectsEventElements() { assertNotNull(schema, "PropertiesSchema should be set"); // Should be valid JSON-like structure - assertTrue(schema.contains("properties") || schema.contains("type"), + assertTrue( + schema.contains("properties") || schema.contains("type"), "Schema should contain JSON Schema keywords: " + schema); LOG.debug("PropertiesSchema: {}", schema); @@ -365,10 +391,12 @@ void testI18nPlaceholdersResolvedForAllLanguages() { List translations = template.getTranslations(); // Verify Title (maps to @notification.template.title) - assertEquals("Certificate: {{certificateName}}", findTranslation(translations, "en").getTitle()); + assertEquals( + "Certificate: {{certificateName}}", findTranslation(translations, "en").getTitle()); assertEquals("Zertifikat: {{certificateName}}", findTranslation(translations, "de").getTitle()); assertEquals("Sertifika: {{certificateName}}", findTranslation(translations, "tr").getTitle()); - assertEquals("Certificado: {{certificateName}}", findTranslation(translations, "es").getTitle()); + assertEquals( + "Certificado: {{certificateName}}", findTranslation(translations, "es").getTitle()); // Verify Body (maps to @notification.template.subtitle) assertEquals("Certificate Expiration", findTranslation(translations, "en").getBody()); @@ -383,16 +411,26 @@ void testI18nPlaceholdersResolvedForAllLanguages() { assertEquals("Expiración de Certificado", findTranslation(translations, "es").getPreview()); // Verify Email Subject - assertEquals("Certificate Expiration Alert", findTranslation(translations, "en").getEmail().getSubject()); - assertEquals("Zertifikatablauf-Warnung", findTranslation(translations, "de").getEmail().getSubject()); - assertEquals("Sertifika Sona Erme Uyarısı", findTranslation(translations, "tr").getEmail().getSubject()); - assertEquals("Alerta de Expiración de Certificado", findTranslation(translations, "es").getEmail().getSubject()); + assertEquals( + "Certificate Expiration Alert", + findTranslation(translations, "en").getEmail().getSubject()); + assertEquals( + "Zertifikatablauf-Warnung", findTranslation(translations, "de").getEmail().getSubject()); + assertEquals( + "Sertifika Sona Erme Uyarısı", findTranslation(translations, "tr").getEmail().getSubject()); + assertEquals( + "Alerta de Expiración de Certificado", + findTranslation(translations, "es").getEmail().getSubject()); // Verify Description - assertEquals("Certificate Expiration Alert", findTranslation(translations, "en").getDescription()); + assertEquals( + "Certificate Expiration Alert", findTranslation(translations, "en").getDescription()); assertEquals("Zertifikatablauf-Warnung", findTranslation(translations, "de").getDescription()); - assertEquals("Sertifika Sona Erme Uyarısı", findTranslation(translations, "tr").getDescription()); - assertEquals("Alerta de Expiración de Certificado", findTranslation(translations, "es").getDescription()); + assertEquals( + "Sertifika Sona Erme Uyarısı", findTranslation(translations, "tr").getDescription()); + assertEquals( + "Alerta de Expiración de Certificado", + findTranslation(translations, "es").getDescription()); LOG.debug("All 4 languages verified with exact i18n values"); } @@ -499,10 +537,7 @@ void testEmailHtmlI18nResolvedPerLanguage() { // ────────────────────────────────────────────────────────────── private Translations findTranslation(List translations, String lang) { - return translations.stream() - .filter(t -> lang.equals(t.getLanguage())) - .findFirst() - .orElse(null); + return translations.stream().filter(t -> lang.equals(t.getLanguage())).findFirst().orElse(null); } private void assertNoUnresolvedI18n(String value, String fieldName, String lang) { diff --git a/sample-app/srv/src/test/java/customer/sample_app/testdata/ContractDeadlineTestData.java b/sample-app/srv/src/test/java/customer/sample_app/testdata/ContractDeadlineTestData.java index 57ace13..d790011 100644 --- a/sample-app/srv/src/test/java/customer/sample_app/testdata/ContractDeadlineTestData.java +++ b/sample-app/srv/src/test/java/customer/sample_app/testdata/ContractDeadlineTestData.java @@ -34,8 +34,8 @@ public static ContractDeadline createWithFarDeadline() { } /** - * Creates a ContractDeadline with deadline exactly 30 days away (boundary condition). - * {@code days_between($now, deadlineDate) = 30}, and {@code 30 < 30 = false} → LOW priority. + * Creates a ContractDeadline with deadline exactly 30 days away (boundary condition). {@code + * days_between($now, deadlineDate) = 30}, and {@code 30 < 30 = false} → LOW priority. */ public static ContractDeadline createWithBoundaryDeadline() { ContractDeadline data = ContractDeadline.create(); diff --git a/sample-app/srv/src/test/java/customer/sample_app/testdata/SystemMaintenanceTestData.java b/sample-app/srv/src/test/java/customer/sample_app/testdata/SystemMaintenanceTestData.java index 7036c76..4871d7d 100644 --- a/sample-app/srv/src/test/java/customer/sample_app/testdata/SystemMaintenanceTestData.java +++ b/sample-app/srv/src/test/java/customer/sample_app/testdata/SystemMaintenanceTestData.java @@ -66,8 +66,8 @@ public static SystemMaintenance createWithoutRecipients() { } /** - * Creates a SystemMaintenance WITHOUT the impact field. The priority expression - * {@code contains(impact, 'critical')} will fail to resolve, triggering the NEUTRAL fallback. + * Creates a SystemMaintenance WITHOUT the impact field. The priority expression {@code + * contains(impact, 'critical')} will fail to resolve, triggering the NEUTRAL fallback. */ public static SystemMaintenance createWithoutImpact() { SystemMaintenance data = SystemMaintenance.create();