Add deployed tab functionality 48/143548/1
authorFiete Ostkamp <fiete.ostkamp@telekom.de>
Mon, 9 Mar 2026 09:17:49 +0000 (10:17 +0100)
committerFiete Ostkamp <fiete.ostkamp@telekom.de>
Mon, 9 Mar 2026 09:17:49 +0000 (10:17 +0100)
- enhance /api/v1/blueprint-model/paged endpoint to accept
  ?published=true|false filter param
- use this endpoint in the Deployed tab of the packages page

Issue-ID: CCSDK-4182
Change-Id: I9f1be6c49f7d90fae26642ea21e9f613a79e273d
Signed-off-by: Fiete Ostkamp <fiete.ostkamp@telekom.de>
17 files changed:
cds-ui/designer-client/src/app/modules/feature-modules/packages/model/packages-dashboard.state.ts
cds-ui/designer-client/src/app/modules/feature-modules/packages/packages-api.service.ts
cds-ui/designer-client/src/app/modules/feature-modules/packages/packages-dashboard/packages-dashboard.component.html
cds-ui/designer-client/src/app/modules/feature-modules/packages/packages-dashboard/packages-dashboard.component.ts
cds-ui/designer-client/src/app/modules/feature-modules/packages/packages-dashboard/sort-packages/sort-packages.component.html
cds-ui/designer-client/src/app/modules/feature-modules/packages/packages.store.ts
cds-ui/designer-client/src/styles.css
cds-ui/e2e-playwright/mock-processor/fixtures/blueprints.json
cds-ui/e2e-playwright/mock-processor/server.js
cds-ui/e2e-playwright/tests/packages.spec.ts
cds-ui/server/src/controllers/blueprint-rest.controller.ts
cds-ui/server/src/datasources/blueprint.datasource-template.ts
cds-ui/server/src/services/blueprint.service.ts
ms/blueprintsprocessor/modules/commons/db-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/db/primary/repository/BlueprintModelSearchRepository.kt
ms/blueprintsprocessor/modules/inbounds/designer-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/designer/api/BlueprintModelController.kt
ms/blueprintsprocessor/modules/inbounds/designer-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/designer/api/handler/BluePrintModelHandler.kt
ms/blueprintsprocessor/modules/inbounds/designer-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/designer/api/BlueprintModelControllerTest.kt

index 8275f8c..9e25842 100644 (file)
@@ -36,14 +36,18 @@ export class PackagesApiService {
     constructor(private api: ApiService<BluePrintPage>) {
     }
 
-    getPagedPackages(pageNumber: number, pageSize: number, sortBy: string): Observable<BluePrintPage[]> {
+    getPagedPackages(pageNumber: number, pageSize: number, sortBy: string, published: boolean = null): Observable<BluePrintPage[]> {
         const sortType = sortBy.includes('DATE') ? 'DESC' : 'ASC';
-        return this.api.get(BlueprintURLs.getPagedBlueprints, {
+        const params: any = {
             offset: pageNumber,
             limit: pageSize,
             sort: sortBy,
-            sortType
-        });
+            sortType,
+        };
+        if (published !== null) {
+            params.published = published;
+        }
+        return this.api.get(BlueprintURLs.getPagedBlueprints, params);
     }
 
     async checkBluePrintIfItExists(name: string, version: string): Promise<BluePrintPage[]> {
index 25604cd..3cf16f2 100644 (file)
                 <!--Nav Tabs-->
                 <div class="col pr-0">
                     <div class="nav nav-tabs " id="nav-tab" role="tablist">
-                        <a class="nav-item nav-link active" id="nav-home-tab" data-toggle="tab" href="#nav-home"
-                            role="tab" aria-controls="nav-home" aria-selected="true">All</a>
-                        <a class="nav-item nav-link" id="nav-profile-tab" data-toggle="tab" href="#nav-profile"
-                            role="tab" aria-controls="nav-profile" aria-selected="false">Deployed</a>
-                        <a tourAnchor="test3" class="nav-item nav-link" id="nav-contact-tab" data-toggle="tab"
-                            href="#nav-contact" role="tab" aria-controls="nav-contact" aria-selected="false">Under
+                        <a class="nav-item nav-link" [class.active]="activeTab === 'All'"
+                            (click)="selectTab('All')" role="tab" aria-selected="true">All</a>
+                        <a class="nav-item nav-link" [class.active]="activeTab === 'Deployed'"
+                            (click)="selectTab('Deployed')" role="tab" aria-selected="false">Deployed</a>
+                        <a tourAnchor="test3" class="nav-item nav-link" [class.active]="activeTab === 'Under Construction'"
+                            (click)="selectTab('Under Construction')" role="tab" aria-selected="false">Under
                             Construction</a>
-                        <a class="nav-item nav-link" id="nav-contact1-tab" data-toggle="tab" href="#nav-contact1"
-                            role="tab" aria-controls="nav-contact1" aria-selected="false">Archived</a>
+                        <a class="nav-item nav-link" [class.active]="activeTab === 'Archived'"
+                            (click)="selectTab('Archived')" role="tab" aria-selected="false">Archived</a>
                     </div>
                 </div>
                 <!--Nav Search & Filter-->
@@ -61,4 +61,4 @@
         </div>
     </div>
     <!-- <div class="copyright text-center w-100 m-auto">Copyright &#64;2020 CDS Designer UI - v1.0.0</div> -->
-</div>
\ No newline at end of file
+</div>
index 9862608..d49f8f7 100644 (file)
@@ -32,8 +32,10 @@ declare var $: any;
 export class PackagesDashboardComponent implements OnInit, OnDestroy {
 
     startTour = false;
+    activeTab = 'All';
     constructor(
         private tourService: TourService,
+        private packagesStore: PackagesStore,
     ) { }
 
     ngOnInit() {
@@ -90,6 +92,12 @@ export class PackagesDashboardComponent implements OnInit, OnDestroy {
         localStorage.setItem('tour-guide', 'false');
     }
 
+    selectTab(tab: string) {
+        this.activeTab = tab;
+        const publishedMap = { All: null, Deployed: true, 'Under Construction': false, Archived: null };
+        this.packagesStore.filterByPublished(publishedMap[tab]);
+    }
+
     ngOnDestroy(): void {
         this.tourService.pause();
     }
index ff937fa..83645e5 100644 (file)
@@ -1,7 +1,7 @@
 <div class="row mt-4">
     <div class="col">
-        <div class="tab-content" id="nav-tabContent">
-            <div class="tab-pane fade show active" id="nav-home" role="tabpanel" aria-labelledby="nav-home-tab">
+        <div>
+            <div>
                 <div class="row">
                     <!--Package Sort-->
                     <div class="col sort-packages">
                     <app-package-pagination></app-package-pagination>
                 </div>
                 <app-packages-list></app-packages-list>
-
             </div>
-            <div class="tab-pane fade text-center" id="nav-profile" role="tabpanel" aria-labelledby="nav-profile-tab">No Deployed Package is available</div>
-            <div class="tab-pane fade text-center" id="nav-contact" role="tabpanel" aria-labelledby="nav-contact-tab">...</div>
-            <div class="tab-pane fade text-center" id="nav-contact1" role="tabpanel" aria-labelledby="nav-contact1-tab">No Archived Package is available</div>
         </div>
     </div>
 </div>
-
index 1e52e31..bc4741c 100644 (file)
@@ -47,6 +47,15 @@ export class PackagesStore extends Store<PackagesDashboardState> {
         this.getPagedPackages(0, this.pageSize);
     }
 
+    public filterByPublished(published: boolean) {
+        this.setState({
+            ...this.state,
+            publishedFilter: published,
+            currentPage: 0
+        });
+        this.getPagedPackages(0, this.pageSize);
+    }
+
     public search(command: string) {
         if (command) {
             this.searchPagedPackages(command, 0, this.pageSize);
@@ -82,7 +91,7 @@ export class PackagesStore extends Store<PackagesDashboardState> {
 
     protected getPagedPackages(pageNumber: number, pageSize: number, sortBy: string = this.state.sortBy) {
 
-        this.packagesServiceList.getPagedPackages(pageNumber, pageSize, sortBy)
+        this.packagesServiceList.getPagedPackages(pageNumber, pageSize, sortBy, this.state.publishedFilter)
             .subscribe((pages: BluePrintPage[]) => {
                 this.setState({
                     ...this.state,
index 4044bad..9766ef9 100644 (file)
@@ -732,6 +732,7 @@ height: 40px;
   font-weight: bold;
   color: #6B7D93 !important;
   border: none !important;
+  cursor: pointer;
 }
 .nav-tabs .nav-link:focus,
 .nav-tabs .nav-link:hover{
index a991562..2b33f04 100644 (file)
@@ -47,7 +47,7 @@
     "internalVersion": null,
     "createdDate": "2026-03-04T14:55:25.696Z",
     "artifactName": "vLB_CDS",
-    "published": "Y",
+    "published": "N",
     "updatedBy": "Seaudi, Abdelmuhaimen <abdelmuhaimen.seaudi@orange.com>",
     "tags": "vLB_CDS"
   },
@@ -86,7 +86,7 @@
     "internalVersion": null,
     "createdDate": "2026-03-04T14:55:38.385Z",
     "artifactName": "pnf_netconf",
-    "published": "Y",
+    "published": "N",
     "updatedBy": "Aarna Services",
     "tags": "pnf_netconf"
   },
     "internalVersion": null,
     "createdDate": "2026-03-04T14:56:10.388Z",
     "artifactName": "ubuntu20",
-    "published": "Y",
+    "published": "N",
     "updatedBy": "RG, ONES <romain.garel@orange.com>",
     "tags": "ubuntu20"
   }
-]
+]
\ No newline at end of file
index 24357b5..efa9c9a 100644 (file)
@@ -132,7 +132,12 @@ const server = http.createServer(async (req, res) => {
 
   // GET /api/v1/blueprint-model/paged
   if (method === 'GET' && pathname === `${BASE}/blueprint-model/paged`) {
-    return json(res, pagedResponse(blueprints, query));
+    let items = blueprints;
+    if (query.published) {
+      const pub = query.published === 'true' ? 'Y' : 'N';
+      items = items.filter(b => b.published === pub);
+    }
+    return json(res, pagedResponse(items, query));
   }
 
   // GET /api/v1/blueprint-model/search/:tags
index 97be42f..ab710a0 100644 (file)
@@ -9,18 +9,20 @@
  *      the LoopBack BFF which in turn calls the mock-processor.  Now that the
  *      mock is running the integration tests assert exact HTTP 200 responses
  *      and fixture data rendered in the DOM.
+ *   5. Tab filtering – Deployed / Under Construction tabs filter by published
+ *      status via the `published` query parameter.
  *
  * Fixture data (from mock-processor/fixtures/blueprints.json – real data from
  * cds-ui-oom-sm-master.tnaplab.telekom.de):
- *   RT-resource-resolution  1.0.0  tags: test, regression
- *   vLB_CDS_KOTLIN          1.0.0  tags: test, vDNS-CDS, SCALE-OUT, MARCO
- *   vLB_CDS_RESTCONF        1.0.0  tags: vLB-CDS
- *   vLB_CDS                 1.0.0  tags: vLB_CDS
- *   5G_Core                 2.0.0  tags: Thamlur Raju, Malinconico Aniello Paolo,Vamshi, 5G_Core
- *   vFW-CDS                 1.0.0  tags: vFW-CDS
- *   pnf_netconf             1.0.0  tags: pnf_netconf
- *   APACHE                  1.0.0  tags: Lukasz Rajewski, CNF
- *   ubuntu20                1.0.0  tags: ubuntu20
+ *   RT-resource-resolution  1.0.0  published=Y  tags: test, regression
+ *   vLB_CDS_KOTLIN          1.0.0  published=Y  tags: test, vDNS-CDS, SCALE-OUT, MARCO
+ *   vLB_CDS_RESTCONF        1.0.0  published=Y  tags: vLB-CDS
+ *   vLB_CDS                 1.0.0  published=N  tags: vLB_CDS
+ *   5G_Core                 2.0.0  published=Y  tags: Thamlur Raju, Malinconico Aniello Paolo,Vamshi, 5G_Core
+ *   vFW-CDS                 1.0.0  published=Y  tags: vFW-CDS
+ *   pnf_netconf             1.0.0  published=N  tags: pnf_netconf
+ *   APACHE                  1.0.0  published=Y  tags: Lukasz Rajewski, CNF
+ *   ubuntu20                1.0.0  published=N  tags: ubuntu20
  */
 
 import { test, expect } from '@playwright/test';
@@ -43,6 +45,22 @@ const FIXTURE_NAMES = [
 ] as const;
 const FIXTURE_COUNT = FIXTURE_NAMES.length;
 
+// Blueprints with published="Y" (deployed)
+const DEPLOYED_NAMES = [
+  'RT-resource-resolution', 'vLB_CDS_KOTLIN', 'vLB_CDS_RESTCONF',
+  '5G_Core', 'vFW-CDS', 'APACHE',
+] as const;
+const DEPLOYED_COUNT = DEPLOYED_NAMES.length;
+
+// Blueprints with published="N" (under construction)
+const UNDER_CONSTRUCTION_NAMES = ['vLB_CDS', 'pnf_netconf', 'ubuntu20'] as const;
+const UNDER_CONSTRUCTION_COUNT = UNDER_CONSTRUCTION_NAMES.length;
+
+/** Selector for a tab link by its visible text. */
+function tabLink(text: string) {
+  return `#nav-tab .nav-link`;
+}
+
 test.describe('Packages Dashboard', () => {
   test.beforeEach(async ({ page }) => {
     // Navigate directly to the packages route
@@ -69,31 +87,28 @@ test.describe('Packages Dashboard', () => {
   // -------------------------------------------------------------------------
 
   test('shows the "All" tab', async ({ page }) => {
-    const allTab = page.locator('#nav-home-tab');
+    const allTab = page.locator('#nav-tab .nav-link', { hasText: 'All' });
     await expect(allTab).toBeVisible({ timeout: 10_000 });
-    await expect(allTab).toHaveText('All');
   });
 
   test('shows the "Deployed" tab', async ({ page }) => {
-    const deployedTab = page.locator('#nav-profile-tab');
+    const deployedTab = page.locator('#nav-tab .nav-link', { hasText: 'Deployed' });
     await expect(deployedTab).toBeVisible({ timeout: 10_000 });
-    await expect(deployedTab).toHaveText('Deployed');
   });
 
   test('shows the "Under Construction" tab', async ({ page }) => {
-    const underConstructionTab = page.locator('#nav-contact-tab');
+    const underConstructionTab = page.locator('#nav-tab .nav-link', { hasText: 'Under' });
     await expect(underConstructionTab).toBeVisible({ timeout: 10_000 });
     await expect(underConstructionTab).toContainText('Under');
   });
 
   test('shows the "Archived" tab', async ({ page }) => {
-    const archivedTab = page.locator('#nav-contact1-tab');
+    const archivedTab = page.locator('#nav-tab .nav-link', { hasText: 'Archived' });
     await expect(archivedTab).toBeVisible({ timeout: 10_000 });
-    await expect(archivedTab).toHaveText('Archived');
   });
 
   test('"All" tab is active by default', async ({ page }) => {
-    const allTab = page.locator('#nav-home-tab');
+    const allTab = page.locator('#nav-tab .nav-link', { hasText: 'All' });
     await expect(allTab).toHaveClass(/active/, { timeout: 10_000 });
   });
 
@@ -121,22 +136,20 @@ test.describe('Packages Dashboard', () => {
   // -------------------------------------------------------------------------
 
   test('clicking "Deployed" tab makes it active', async ({ page }) => {
-    const deployedTab = page.locator('#nav-profile-tab');
-    // The ngx-ui-loader overlay from app-sort-packages persists while the app
-    // waits for the upstream CDS processor (which is absent in the e2e env).
-    // Bootstrap tabs are jQuery-driven; trigger the show via Bootstrap's API
-    // to reliably switch the active tab regardless of overlay state.
-    await page.evaluate(() => {
-      (window as any).$('#nav-profile-tab').tab('show');
-    });
+    const deployedTab = page.locator('#nav-tab .nav-link', { hasText: 'Deployed' });
+    await deployedTab.dispatchEvent('click');
     await expect(deployedTab).toHaveClass(/active/);
   });
 
+  test('clicking "Under Construction" tab makes it active', async ({ page }) => {
+    const ucTab = page.locator('#nav-tab .nav-link', { hasText: /Under/ });
+    await ucTab.dispatchEvent('click');
+    await expect(ucTab).toHaveClass(/active/);
+  });
+
   test('clicking "Archived" tab makes it active', async ({ page }) => {
-    const archivedTab = page.locator('#nav-contact1-tab');
-    await page.evaluate(() => {
-      (window as any).$('#nav-contact1-tab').tab('show');
-    });
+    const archivedTab = page.locator('#nav-tab .nav-link', { hasText: 'Archived' });
+    await archivedTab.dispatchEvent('click');
     await expect(archivedTab).toHaveClass(/active/);
   });
 
@@ -192,8 +205,8 @@ test.describe('Packages Dashboard – fixture data loaded from mock', () => {
     await expect(page.locator('.package-version', { hasText: 'v1.0.0' }).first()).toBeVisible();
     await expect(page.locator('.package-version', { hasText: 'v2.0.0' })).toBeVisible();
 
-    // Deployed icon – all 9 blueprints have published: "Y"
-    await expect(page.locator('img.icon-deployed')).toHaveCount(FIXTURE_COUNT);
+    // Deployed icon – only 6 blueprints have published: "Y"
+    await expect(page.locator('img.icon-deployed')).toHaveCount(DEPLOYED_COUNT);
 
     // Description and tags – one element per card, at least one non-empty desc
     await expect(page.locator('.package-desc')).toHaveCount(FIXTURE_COUNT);
@@ -331,3 +344,108 @@ test.describe('Client-side navigation', () => {
     await expect(dashboard).toBeAttached({ timeout: 10_000 });
   });
 });
+
+// ---------------------------------------------------------------------------
+// Tab filtering – Deployed / Under Construction tabs filter by published status
+// ---------------------------------------------------------------------------
+
+test.describe('Packages Dashboard – tab filtering', () => {
+  test.beforeEach(async ({ page }) => {
+    await page.goto('/#/packages');
+    await waitForPackageCards(page);
+    await page.waitForLoadState('networkidle');
+  });
+
+  test('clicking "Deployed" tab sends published=true and shows only deployed packages', async ({ page }) => {
+    const deployedTab = page.locator('#nav-tab .nav-link', { hasText: 'Deployed' });
+
+    const responsePromise = page.waitForResponse(
+      resp => resp.url().includes('controllerblueprint/paged') && resp.url().includes('published=true'),
+      { timeout: 15_000 },
+    );
+    await page.evaluate(() => {
+      (document.querySelector('#nav-tab .nav-link:nth-child(2)') as HTMLElement).click();
+    });
+    const apiResponse = await responsePromise;
+
+    expect(apiResponse.status()).toBe(200);
+
+    // Wait for cards to update
+    await page.waitForLoadState('networkidle');
+    await expect(page.locator('.packageName')).toHaveCount(DEPLOYED_COUNT, { timeout: 20_000 });
+
+    // Only deployed packages should be visible
+    for (const name of DEPLOYED_NAMES) {
+      await expect(
+        page.locator('.packageName').filter({ hasText: new RegExp(`^\\s*${name}\\s*$`) })
+      ).toBeVisible();
+    }
+
+    // Under-construction packages should NOT be visible
+    for (const name of UNDER_CONSTRUCTION_NAMES) {
+      await expect(
+        page.locator('.packageName').filter({ hasText: new RegExp(`^\\s*${name}\\s*$`) })
+      ).toHaveCount(0);
+    }
+
+    // All visible cards should have the deployed icon
+    await expect(page.locator('img.icon-deployed')).toHaveCount(DEPLOYED_COUNT);
+  });
+
+  test('clicking "Under Construction" tab sends published=false and shows only non-deployed packages', async ({ page }) => {
+    const ucTab = page.locator('#nav-tab .nav-link', { hasText: /Under/ });
+
+    const [apiResponse] = await Promise.all([
+      page.waitForResponse(
+        resp => resp.url().includes('controllerblueprint/paged') && resp.url().includes('published=false'),
+        { timeout: 15_000 },
+      ),
+      ucTab.dispatchEvent('click'),
+    ]);
+
+    expect(apiResponse.status()).toBe(200);
+
+    await page.waitForLoadState('networkidle');
+    await expect(page.locator('.packageName')).toHaveCount(UNDER_CONSTRUCTION_COUNT, { timeout: 20_000 });
+
+    // Only under-construction packages should be visible
+    for (const name of UNDER_CONSTRUCTION_NAMES) {
+      await expect(
+        page.locator('.packageName').filter({ hasText: new RegExp(`^\\s*${name}\\s*$`) })
+      ).toBeVisible();
+    }
+
+    // None of the deployed packages should be visible
+    for (const name of DEPLOYED_NAMES) {
+      await expect(
+        page.locator('.packageName').filter({ hasText: new RegExp(`^\\s*${name}\\s*$`) })
+      ).toHaveCount(0);
+    }
+
+    // Under-construction packages should NOT have the deployed icon
+    await expect(page.locator('img.icon-deployed')).toHaveCount(0);
+  });
+
+  test('switching back to "All" tab clears the published filter and shows all packages', async ({ page }) => {
+    // First switch to Deployed
+    const deployedTab = page.locator('#nav-tab .nav-link', { hasText: 'Deployed' });
+    await deployedTab.dispatchEvent('click');
+    await page.waitForLoadState('networkidle');
+    await expect(page.locator('.packageName')).toHaveCount(DEPLOYED_COUNT, { timeout: 20_000 });
+
+    // Now switch back to All
+    const allTab = page.locator('#nav-tab .nav-link', { hasText: 'All' });
+
+    const [apiResponse] = await Promise.all([
+      page.waitForResponse(
+        resp => resp.url().includes('controllerblueprint/paged') && resp.status() === 200,
+        { timeout: 15_000 },
+      ),
+      allTab.dispatchEvent('click'),
+    ]);
+
+    expect(apiResponse.status()).toBe(200);
+    await page.waitForLoadState('networkidle');
+    await expect(page.locator('.packageName')).toHaveCount(FIXTURE_COUNT, { timeout: 20_000 });
+  });
+});
index 8530a23..15c2e4e 100644 (file)
@@ -87,8 +87,9 @@ export class BlueprintRestController {
     @param.query.number('limit') limit: number,
     @param.query.number('offset') offset: number,
     @param.query.string('sort') sort: string,
-    @param.query.string('sortType') sortType: string) {
-    return await this.bpservice.getPagedBlueprints(limit, offset, sort, sortType);
+    @param.query.string('sortType') sortType: string,
+    @param.query.boolean('published') published: boolean) {
+    return await this.bpservice.getPagedBlueprints(limit, offset, sort, sortType, published);
   }
 
   @get('/controllerblueprint/metadata/paged/{keyword}', {
index 2dec3f1..50c2ab3 100644 (file)
@@ -89,7 +89,7 @@ export default {
     {
         "template": {
             "method": "GET",
-            "url": processorApiConfig.http.url + "/blueprint-model/paged?limit={limit}&offset={offset}&sort={sort}&sortType={sortType}",
+            "url": processorApiConfig.http.url + "/blueprint-model/paged?limit={limit}&offset={offset}&sort={sort}&sortType={sortType}&published={published}",
             "headers": {
                 "accepts": "application/json",
                 "content-type": "application/json",
@@ -98,7 +98,7 @@ export default {
             "responsePath": "$",
         },
         "functions": {
-            "getPagedBlueprints": ["limit", "offset", "sort", "sortType"],
+            "getPagedBlueprints": ["limit", "offset", "sort", "sortType", "published"],
         }
     },
     {
index a017e6e..85fe285 100644 (file)
@@ -8,7 +8,7 @@ export interface BlueprintService {
   getAllblueprints(): Promise<any>;
   getBlueprintsByKeyword(keyword: string): Promise<any>;
   getByTags(tags: string): Promise<JSON>;
-  getPagedBlueprints(limit: number, offset: number, sort: string, sortType: String): Promise<any>;
+  getPagedBlueprints(limit: number, offset: number, sort: string, sortType: String, published: boolean): Promise<any>;
   getMetaDataPagedBlueprints(limit: number, offset: number, sort: string, keyword: string, sortType: String): Promise<any>;
   getBlueprintByNameAndVersion(name: string, version: string): Promise<any>;
 
index b3b67ce..81f7304 100644 (file)
@@ -136,13 +136,18 @@ open class BlueprintModelController(private val bluePrintModelHandler: BluePrint
         @ApiParam(value = "Maximum number of returned blueprint models") @RequestParam(defaultValue = "20") limit: Int,
         @ApiParam(value = "Offset") @RequestParam(defaultValue = "0") offset: Int,
         @ApiParam(value = "Order of returned blueprint models") @RequestParam(defaultValue = "DATE") sort: BlueprintSortByOption,
-        @ApiParam(value = "Ascend or descend ordering") @RequestParam(defaultValue = "ASC") sortType: String
+        @ApiParam(value = "Ascend or descend ordering") @RequestParam(defaultValue = "ASC") sortType: String,
+        @ApiParam(value = "Filter by published status (true or false)") @RequestParam(required = false) published: Boolean?
     ): Page<BlueprintModelSearch> {
         val pageRequest = PageRequest.of(
             offset, limit,
             Sort.Direction.fromString(sortType), sort.columnName
         )
-        return this.bluePrintModelHandler.allBlueprintModel(pageRequest)
+        return if (published != null) {
+            this.bluePrintModelHandler.allBlueprintModelByPublished(published, pageRequest)
+        } else {
+            this.bluePrintModelHandler.allBlueprintModel(pageRequest)
+        }
     }
 
     @GetMapping("meta-data/{keyword}", produces = [MediaType.APPLICATION_JSON_VALUE])
index 97fa736..da6311d 100644 (file)
@@ -256,6 +256,11 @@ open class BluePrintModelHandler(
         return blueprintModelSearchRepository.findAll(pageRequest)
     }
 
+    open fun allBlueprintModelByPublished(published: Boolean, pageRequest: Pageable): Page<BlueprintModelSearch> {
+        val dbPublished = if (published) "Y" else "N"
+        return blueprintModelSearchRepository.findByPublished(dbPublished, pageRequest)
+    }
+
     /**
      * This is a saveBlueprintModel method
      *
index 7e37d4f..6d998d0 100644 (file)
@@ -216,6 +216,63 @@ class BlueprintModelControllerTest {
         }
     }
 
+    @Test
+    @Throws(JSONException::class)
+    fun test07a_getPagedBlueprintModels() {
+        webTestClient.get()
+            .uri("/api/v1/blueprint-model/paged?offset=0&limit=20&sort=DATE&sortType=ASC")
+            .header(
+                "Authorization",
+                "Basic " + Base64.getEncoder()
+                    .encodeToString(("ccsdkapps" + ":" + "ccsdkapps").toByteArray(UTF_8))
+            )
+            .exchange()
+            .expectStatus().isOk
+            .expectBody()
+            .jsonPath("$.content").isArray
+            .jsonPath("$.totalElements").isNumber
+            .jsonPath("$.content.length()").value<Int> {
+                assertTrue(it > 0, "paged response should have at least one element")
+            }
+    }
+
+    @Test
+    @Throws(JSONException::class)
+    fun test07b_getPagedBlueprintModelsFilteredByPublishedY() {
+        webTestClient.get()
+            .uri("/api/v1/blueprint-model/paged?offset=0&limit=20&sort=DATE&sortType=ASC&published=true")
+            .header(
+                "Authorization",
+                "Basic " + Base64.getEncoder()
+                    .encodeToString(("ccsdkapps" + ":" + "ccsdkapps").toByteArray(UTF_8))
+            )
+            .exchange()
+            .expectStatus().isOk
+            .expectBody()
+            .jsonPath("$.content").isArray
+            .jsonPath("$.content.length()").value<Int> {
+                assertTrue(it > 0, "published=true should return at least one element")
+            }
+            .jsonPath("$.content[0].published").isEqualTo("Y")
+    }
+
+    @Test
+    @Throws(JSONException::class)
+    fun test07c_getPagedBlueprintModelsFilteredByPublishedN() {
+        webTestClient.get()
+            .uri("/api/v1/blueprint-model/paged?offset=0&limit=20&sort=DATE&sortType=ASC&published=false")
+            .header(
+                "Authorization",
+                "Basic " + Base64.getEncoder()
+                    .encodeToString(("ccsdkapps" + ":" + "ccsdkapps").toByteArray(UTF_8))
+            )
+            .exchange()
+            .expectStatus().isOk
+            .expectBody()
+            .jsonPath("$.content").isArray
+            .jsonPath("$.content.length()").isEqualTo(0)
+    }
+
     @Test
     @Throws(JSONException::class)
     fun test08_searchBlueprintModels() {