Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,4 @@ The load-bearing few. **Full detail for every relocated item → [tutorials-ims-
- **User-facing UI changes want a committed e2e spec** — advisory PR nudge on `app/**`/`hugo/**` changes; real coverage runs in the post-DEV-deploy `e2e` job. Ref: [e2e-coverage-pattern.md](docs/developers/reference/e2e-coverage-pattern.md).
- **`test:e2e` is post-deploy only, not on PRs** — self-skips without `SMOKE_BASE_URL`. Served tutorials render `<main>`+`<h1>`, NOT `<article>`. Runbook: `test/e2e/README.md`.
- **Freshness detector grounding needs the corpus-embedding backfill** — until `srv/jobs/freshness-corpus-embedding-job.js` runs, every API-obsolescence claim degrades to `confidence: Low`. Tutorial source from `ContentFiles.sourceContent` via `getTutorialSource(slug)`, NOT `Steps.description`. → gotchas.md "Freshness detector".
- **External channels subsystem** — `Channels` entity (`db/channels.cds`) is the source of truth; re-ingest via `npm run seed-channels`; directory at `/channels` (baked by `fetch-channels` in `build:all`); verb-lane fill via `npm run promote-channels`; community items never land in `START_HERE`. → [channels.md](docs/developers/reference/channels.md).
6 changes: 4 additions & 2 deletions app/admin-shell/scripts/admin-shell-overrides.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ module.exports = {
'video-rotation',
'pats',
'featureFlags',
'petoberfest'
'petoberfest',
'channels'
],

//
Expand Down Expand Up @@ -161,7 +162,8 @@ module.exports = {
'content-moderation': 'cm',
pats: 'pt',
featureFlags: 'ffl',
petoberfest: 'pb'
petoberfest: 'pb',
channels: 'ch'
},

//
Expand Down
6 changes: 4 additions & 2 deletions app/admin-shell/webapp/controller/Shell.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ sap.ui.define([
pats: "pats",
petoberfest: "petoberfest",
petoberfestContests: "petoberfestContests",
topicClusters: "topicClusters"
topicClusters: "topicClusters",
channels: "channels"
};

var NAV_KEY_TO_TITLE = {
Expand Down Expand Up @@ -117,7 +118,8 @@ sap.ui.define([
pats: "Personal Access Tokens",
petoberfest: "Pet Photo Moderation",
petoberfestContests: "Petoberfest Contests",
topicClusters: "Topic Clusters"
topicClusters: "Topic Clusters",
channels: "Channels"
};

return Controller.extend("sap.tutorials.admin.shell.controller.Shell", {
Expand Down
26 changes: 25 additions & 1 deletion app/admin-shell/webapp/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@
"sap.tutorials.admin.videoRotation": "./components/video-rotation",
"sap.tutorials.admin.pats": "./components/pats",
"sap.tutorials.admin.featureFlags": "./components/featureFlags",
"sap.tutorials.admin.petoberfest": "./components/petoberfest"
"sap.tutorials.admin.petoberfest": "./components/petoberfest",
"sap.tutorials.admin.channels": "./components/channels"
},
"models": {
"admin": {
Expand Down Expand Up @@ -379,6 +380,12 @@
"settings": {},
"componentData": {},
"lazy": true
},
"channelsComponent": {
"name": "sap.tutorials.admin.channels",
"settings": {},
"componentData": {},
"lazy": true
}
},
"services": {
Expand Down Expand Up @@ -889,6 +896,16 @@
}
]
},
{
"name": "channels",
"pattern": "channels",
"target": [
{
"name": "channelsTarget",
"prefix": "ch"
}
]
},
{
"name": "feedbackDashboard",
"pattern": "feedback/dashboard",
Expand Down Expand Up @@ -1232,6 +1249,13 @@
"viewLevel": 1,
"prefix": "pb"
},
"channelsTarget": {
"type": "Component",
"usage": "channelsComponent",
"id": "channelsTarget",
"viewLevel": 1,
"prefix": "ch"
},
"feedbackDashboardTarget": {
"viewName": "TutorialFeedbackDashboard",
"viewLevel": 1
Expand Down
1 change: 1 addition & 0 deletions app/admin-shell/webapp/model/navigation.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
{ "key": "concepts", "title": "Concepts" },
{ "key": "advocates", "title": "Advocates" },
{ "key": "alerts", "title": "Alerts" },
{ "key": "channels", "title": "Channels" },
{ "key": "operations", "title": "Featured Tasks" }
]
},
Expand Down
15 changes: 15 additions & 0 deletions app/admin/channels/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "sap.tutorials.admin.channels",
"version": "0.0.1",
"private": true,
"description": "Channels - Admin Fiori Elements",
"sapux": true,
"scripts": {
"start": "fiori run --open index.html",
"build": "ui5 build --clean-dest"
},
"devDependencies": {
"@sap/ux-specification": "latest",
"@ui5/cli": "^4.0.0"
}
}
12 changes: 12 additions & 0 deletions app/admin/channels/ui5.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
specVersion: "4.0"
metadata:
name: sap.tutorials.admin.channels
type: application
framework:
name: SAPUI5
version: "1.136.0"
libraries:
- name: sap.m
- name: sap.ui.core
- name: sap.ushell
- name: sap.fe.templates
4 changes: 4 additions & 0 deletions app/admin/channels/webapp/Component.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
sap.ui.define(["sap/fe/core/AppComponent"], function (AppComponent) {
"use strict";
return AppComponent.extend("sap.tutorials.admin.channels.Component", { metadata: { manifest: "json" } });
});
2 changes: 2 additions & 0 deletions app/admin/channels/webapp/i18n/i18n.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
appTitle=Channels
appDescription=Curate external SAP developer channels
79 changes: 79 additions & 0 deletions app/admin/channels/webapp/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
{
"_version": "1.65.0",
"sap.app": {
"id": "sap.tutorials.admin.channels",
"type": "application",
"title": "{{appTitle}}",
"description": "{{appDescription}}",
"applicationVersion": { "version": "0.0.1" },
"i18n": "i18n/i18n.properties",
"dataSources": {
"mainService": {
"uri": "/admin/",
"type": "OData",
"settings": { "odataVersion": "4.0" }
}
},
"crossNavigation": {
"inbounds": {
"Channels-manage": {
"semanticObject": "Channels",
"action": "manage",
"title": "{{appTitle}}",
"signature": { "parameters": {}, "additionalParameters": "allowed" }
}
}
}
},
"sap.ui5": {
"dependencies": {
"minUI5Version": "1.136.0",
"libs": { "sap.fe.templates": {} }
},
"models": {
"": {
"dataSource": "mainService",
"preload": true,
"settings": {
"synchronizationMode": "None",
"operationMode": "Server",
"autoExpandSelect": true,
"earlyRequests": true
}
},
"i18n": {
"type": "sap.ui.model.resource.ResourceModel",
"settings": { "bundleName": "sap.tutorials.admin.channels.i18n.i18n" }
}
},
"routing": {
"routes": [
{ "name": "ChannelsList", "pattern": ":?query:", "target": "ChannelsList" },
{ "name": "ChannelsObject", "pattern": "Channels({key}):?query:", "target": "ChannelsObject" }
],
"targets": {
"ChannelsList": {
"type": "Component",
"id": "ChannelsList",
"name": "sap.fe.templates.ListReport",
"options": {
"settings": {
"contextPath": "/Channels",
"initialLoad": "Enabled"
}
}
},
"ChannelsObject": {
"type": "Component",
"id": "ChannelsObject",
"name": "sap.fe.templates.ObjectPage",
"options": {
"settings": {
"contextPath": "/Channels"
}
}
}
}
}
}
}
43 changes: 43 additions & 0 deletions db/channels.cds
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
namespace com.sap.developers.ims;

using { managed, cuid } from '@sap/cds/common';

type ChannelOwnerType : String enum {
SAP_Official; SAP_Developer_Advocate; SAP_Executive;
Community_Member; Community_Organization; User_Group;
Third_party_Training; Third_party_Media; Third_party_Platform;
}
type ChannelStatus : String enum { Active; Archived; Closed; Discontinued; EOL; }

@assert.unique.sourceId: [sourceId]
entity Channels : cuid, managed {
sourceId : String(40) @mandatory; // "portal-001" — dedup / re-ingest key
name : String(200) @mandatory;
url : String(500) @mandatory;
relatedUrls : array of String(500);
aliases : array of String(120);
purpose : String(1000); // cleaned of [cite:] markers at ingest
notes : String(1000);
ownerName : String(120);
ownerType : ChannelOwnerType @assert.range;
isSapOwned : Boolean default false;
category : String(60);
subcategory : String(80);
platform : String(40);
status : ChannelStatus default 'Active' @assert.range;
focusAreas : array of String(60);
tags : array of String(40);
updateFrequency: String(40);
githubStars : Integer;
subscribers : Integer;

// ── curation / lifecycle (admin-editable; absent from ingest so re-seed never wipes) ──
isPublished : Boolean default true;
isFeatured : Boolean default false;
editorialNote : String(800);
contentHash : String(64);
ingestBatch : String(40);
linkStatus : String(20) default 'UNKNOWN';
linkStatusOverride : String(20);
lastChecked : Timestamp;
}
Loading
Loading