|
@@ -0,0 +1,225 @@
|
|
|
|
|
+# Exam Sprint Download URL Prefix Implementation Plan
|
|
|
|
|
+
|
|
|
|
|
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
|
|
|
+
|
|
|
|
|
+**Goal:** Rename the Azure report download URL configuration from `url-prefix` to `download-url-prefix` and make test downloads return direct Azure Blob URLs.
|
|
|
|
|
+
|
|
|
|
|
+**Architecture:** Keep the existing storage boundary unchanged: `DefaultExamSprintReportApplicationService` asks `ExamSprintReportStorage.generateDownloadUrl(...)`, and `AzureBlobExamSprintReportStorage` builds a public URL from a configured download prefix, container name, and storage object key. This change only clarifies the configuration name and default test URL; upload/download/delete continue to use the Azure SDK client and connection string.
|
|
|
|
|
+
|
|
|
|
|
+**Tech Stack:** Java 17, Spring Boot `@Value` configuration binding, Maven, JUnit 5, AssertJ, Mockito, Spring MockMvc tests.
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## File Structure
|
|
|
|
|
+
|
|
|
|
|
+- Modify `abilities/exam-sprint/application/src/main/java/cn/yunzhixue/ability/center/examsprint/application/report/ExamSprintReportProperties.java`
|
|
|
|
|
+ - Replace the `Storage.urlPrefix` property with `Storage.downloadUrlPrefix`.
|
|
|
|
|
+- Modify `abilities/exam-sprint/infrastructure/src/main/java/cn/yunzhixue/ability/center/examsprint/infrastructure/report/storage/AzureBlobExamSprintReportStorage.java`
|
|
|
|
|
+ - Inject `ability.exam-sprint.report.storage.download-url-prefix` instead of `url-prefix`.
|
|
|
|
|
+ - Rename internal field and validation method naming from `urlPrefix` to `downloadUrlPrefix`.
|
|
|
|
|
+- Modify `ability-center-runtime/src/main/resources/application-test.yml`
|
|
|
|
|
+ - Replace `url-prefix` with `download-url-prefix` and default to `https://dcjxbtest.blob.core.chinacloudapi.cn`.
|
|
|
|
|
+- Modify `ability-center-runtime/src/main/resources/application-prod.yml`
|
|
|
|
|
+ - Replace `url-prefix` with `download-url-prefix` and keep the existing production Blob account host default.
|
|
|
|
|
+- Modify `abilities/exam-sprint/infrastructure/src/test/java/cn/yunzhixue/ability/center/examsprint/infrastructure/report/storage/AzureBlobExamSprintReportStorageTest.java`
|
|
|
|
|
+ - Update test names, expected URLs, and validation messages.
|
|
|
|
|
+- Modify `ability-center-runtime/src/test/java/cn/yunzhixue/ability/center/examsprint/adapter/http/ExamSprintReportControllerWebMvcTest.java`
|
|
|
|
|
+ - Update expected `downloadUrl` values from CDN host to Blob host where tests assert exact values.
|
|
|
|
|
+
|
|
|
|
|
+### Task 1: Rename the Azure storage configuration property
|
|
|
|
|
+
|
|
|
|
|
+**Files:**
|
|
|
|
|
+- Modify: `abilities/exam-sprint/application/src/main/java/cn/yunzhixue/ability/center/examsprint/application/report/ExamSprintReportProperties.java`
|
|
|
|
|
+- Modify: `abilities/exam-sprint/infrastructure/src/main/java/cn/yunzhixue/ability/center/examsprint/infrastructure/report/storage/AzureBlobExamSprintReportStorage.java`
|
|
|
|
|
+- Modify: `ability-center-runtime/src/main/resources/application-test.yml`
|
|
|
|
|
+- Modify: `ability-center-runtime/src/main/resources/application-prod.yml`
|
|
|
|
|
+- Test: `abilities/exam-sprint/infrastructure/src/test/java/cn/yunzhixue/ability/center/examsprint/infrastructure/report/storage/AzureBlobExamSprintReportStorageTest.java`
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 1: Write the failing infrastructure expectations**
|
|
|
|
|
+
|
|
|
|
|
+In `AzureBlobExamSprintReportStorageTest`, update the constructor reflection test so it still expects seven parameters but treats the sixth `String` as `download-url-prefix` by name in the surrounding test naming. Rename `generateDownloadUrlReturnsConfiguredOssFileLink` to `generateDownloadUrlReturnsConfiguredBlobFileLink` and change the expected URL to:
|
|
|
|
|
+
|
|
|
|
|
+```java
|
|
|
|
|
+assertThat(downloadUrl).isEqualTo(URI.create(
|
|
|
|
|
+ "https://dcjxbtest.blob.core.chinacloudapi.cn/exam-assault-report/exam-sprint-outlook-report-report-123.pdf"));
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+Rename `constructorRejectsAllSlashUrlPrefix` to `constructorRejectsAllSlashDownloadUrlPrefix` and `constructorRejectsBlankAfterTrimUrlPrefix` to `constructorRejectsBlankAfterTrimDownloadUrlPrefix`. Change both expected messages to:
|
|
|
|
|
+
|
|
|
|
|
+```java
|
|
|
|
|
+.hasMessage("Azure storage download-url-prefix is incomplete");
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 2: Run the focused infrastructure test and verify it fails**
|
|
|
|
|
+
|
|
|
|
|
+Run:
|
|
|
|
|
+
|
|
|
|
|
+```bash
|
|
|
|
|
+mvn -pl abilities/exam-sprint/infrastructure -Dtest=AzureBlobExamSprintReportStorageTest test
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+Expected: FAIL because production code still emits the old CDN URL and old `url-prefix` error message.
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 3: Implement the Java property rename**
|
|
|
|
|
+
|
|
|
|
|
+In `ExamSprintReportProperties.Storage`, replace:
|
|
|
|
|
+
|
|
|
|
|
+```java
|
|
|
|
|
+private String urlPrefix;
|
|
|
|
|
+
|
|
|
|
|
+public String getUrlPrefix() {
|
|
|
|
|
+ return urlPrefix;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+public void setUrlPrefix(String urlPrefix) {
|
|
|
|
|
+ this.urlPrefix = urlPrefix;
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+with:
|
|
|
|
|
+
|
|
|
|
|
+```java
|
|
|
|
|
+private String downloadUrlPrefix;
|
|
|
|
|
+
|
|
|
|
|
+public String getDownloadUrlPrefix() {
|
|
|
|
|
+ return downloadUrlPrefix;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+public void setDownloadUrlPrefix(String downloadUrlPrefix) {
|
|
|
|
|
+ this.downloadUrlPrefix = downloadUrlPrefix;
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+In `AzureBlobExamSprintReportStorage`, replace the `urlPrefix` field and constructor parameter with `downloadUrlPrefix`, inject:
|
|
|
|
|
+
|
|
|
|
|
+```java
|
|
|
|
|
+@Value("${ability.exam-sprint.report.storage.download-url-prefix:}") String downloadUrlPrefix,
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+and change URL creation to:
|
|
|
|
|
+
|
|
|
|
|
+```java
|
|
|
|
|
+return URI.create(downloadUrlPrefix + "/" + containerName + "/" + normalizedStorageObjectKey);
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+Rename `normalizeUrlPrefix` to `normalizeDownloadUrlPrefix` and make both thrown messages say:
|
|
|
|
|
+
|
|
|
|
|
+```java
|
|
|
|
|
+throw new IllegalStateException("Azure storage download-url-prefix is incomplete");
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 4: Update runtime YAML configuration**
|
|
|
|
|
+
|
|
|
|
|
+In `application-test.yml`, replace:
|
|
|
|
|
+
|
|
|
|
|
+```yaml
|
|
|
|
|
+url-prefix: "${AZURE_BLOB_URL_PREFIX:https://dcjxb-cdntest.yunzhixue.cn}"
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+with:
|
|
|
|
|
+
|
|
|
|
|
+```yaml
|
|
|
|
|
+download-url-prefix: "${AZURE_BLOB_DOWNLOAD_URL_PREFIX:https://dcjxbtest.blob.core.chinacloudapi.cn}"
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+In `application-prod.yml`, replace:
|
|
|
|
|
+
|
|
|
|
|
+```yaml
|
|
|
|
|
+url-prefix: "${AZURE_BLOB_URL_PREFIX:https://dcjxb.blob.core.chinacloudapi.cn}"
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+with:
|
|
|
|
|
+
|
|
|
|
|
+```yaml
|
|
|
|
|
+download-url-prefix: "${AZURE_BLOB_DOWNLOAD_URL_PREFIX:https://dcjxb.blob.core.chinacloudapi.cn}"
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 5: Run the focused infrastructure test and verify it passes**
|
|
|
|
|
+
|
|
|
|
|
+Run:
|
|
|
|
|
+
|
|
|
|
|
+```bash
|
|
|
|
|
+mvn -pl abilities/exam-sprint/infrastructure -Dtest=AzureBlobExamSprintReportStorageTest test
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+Expected: PASS with `Tests run: 8, Failures: 0, Errors: 0, Skipped: 0`.
|
|
|
|
|
+
|
|
|
|
|
+### Task 2: Update HTTP-facing tests for the new download host
|
|
|
|
|
+
|
|
|
|
|
+**Files:**
|
|
|
|
|
+- Modify: `ability-center-runtime/src/test/java/cn/yunzhixue/ability/center/examsprint/adapter/http/ExamSprintReportControllerWebMvcTest.java`
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 1: Write the failing WebMvc expectations**
|
|
|
|
|
+
|
|
|
|
|
+Replace exact expected URLs using `https://dcjxb-cdntest.yunzhixue.cn/exam-assault-report/` with `https://dcjxbtest.blob.core.chinacloudapi.cn/exam-assault-report/`. For example:
|
|
|
|
|
+
|
|
|
|
|
+```java
|
|
|
|
|
+"https://dcjxbtest.blob.core.chinacloudapi.cn/exam-assault-report/exam-sprint-outlook-report-report-sync-001.pdf"
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+and:
|
|
|
|
|
+
|
|
|
|
|
+```java
|
|
|
|
|
+"https://dcjxbtest.blob.core.chinacloudapi.cn/exam-assault-report/exam-sprint-achievement-report-report-sync-002.pdf"
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 2: Run the focused runtime WebMvc test and verify it fails if code/config is incomplete**
|
|
|
|
|
+
|
|
|
|
|
+Run:
|
|
|
|
|
+
|
|
|
|
|
+```bash
|
|
|
|
|
+mvn -pl ability-center-runtime -Dtest=ExamSprintReportControllerWebMvcTest test
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+Expected before Task 1 implementation: FAIL because the old configuration emits CDN URLs. Expected after Task 1 implementation: PASS.
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 3: Re-run the focused runtime WebMvc test after Task 1 changes**
|
|
|
|
|
+
|
|
|
|
|
+Run:
|
|
|
|
|
+
|
|
|
|
|
+```bash
|
|
|
|
|
+mvn -pl ability-center-runtime -Dtest=ExamSprintReportControllerWebMvcTest test
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+Expected: PASS with all `ExamSprintReportControllerWebMvcTest` cases passing.
|
|
|
|
|
+
|
|
|
|
|
+### Task 3: Final verification
|
|
|
|
|
+
|
|
|
|
|
+**Files:**
|
|
|
|
|
+- Verify only; no additional files should change in this task.
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 1: Run all directly affected tests**
|
|
|
|
|
+
|
|
|
|
|
+Run:
|
|
|
|
|
+
|
|
|
|
|
+```bash
|
|
|
|
|
+mvn -pl abilities/exam-sprint/infrastructure -Dtest=AzureBlobExamSprintReportStorageTest test && mvn -pl ability-center-runtime -Dtest=ExamSprintReportControllerWebMvcTest test
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+Expected: both Maven commands finish with `BUILD SUCCESS`.
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 2: Inspect the diff for accidental compatibility code or secret changes**
|
|
|
|
|
+
|
|
|
|
|
+Run:
|
|
|
|
|
+
|
|
|
|
|
+```bash
|
|
|
|
|
+git diff -- abilities/exam-sprint/application/src/main/java/cn/yunzhixue/ability/center/examsprint/application/report/ExamSprintReportProperties.java abilities/exam-sprint/infrastructure/src/main/java/cn/yunzhixue/ability/center/examsprint/infrastructure/report/storage/AzureBlobExamSprintReportStorage.java ability-center-runtime/src/main/resources/application-test.yml ability-center-runtime/src/main/resources/application-prod.yml abilities/exam-sprint/infrastructure/src/test/java/cn/yunzhixue/ability/center/examsprint/infrastructure/report/storage/AzureBlobExamSprintReportStorageTest.java ability-center-runtime/src/test/java/cn/yunzhixue/ability/center/examsprint/adapter/http/ExamSprintReportControllerWebMvcTest.java
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+Expected: diff contains only the `download-url-prefix` rename, Blob host default for test, unchanged production Blob host default, and test expectation updates.
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 3: Report deployment configuration note**
|
|
|
|
|
+
|
|
|
|
|
+Tell the operator that test deployments should use:
|
|
|
|
|
+
|
|
|
|
|
+```bash
|
|
|
|
|
+AZURE_BLOB_DOWNLOAD_URL_PREFIX=https://dcjxbtest.blob.core.chinacloudapi.cn
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+and that `AZURE_BLOB_URL_PREFIX` is intentionally no longer read.
|
|
|
|
|
+
|
|
|
|
|
+## Self-Review
|
|
|
|
|
+
|
|
|
|
|
+- Spec coverage: The plan covers the approved rename to `download-url-prefix`, direct test Blob URL default, no backward compatibility for `url-prefix`, production config rename, and affected tests.
|
|
|
|
|
+- Placeholder scan: No placeholder tasks remain; each step lists exact files, code snippets, commands, and expected outcomes.
|
|
|
|
|
+- Type consistency: The property name is consistently `downloadUrlPrefix` in Java and `download-url-prefix` in YAML/Spring configuration.
|