@linkup-ai/abap-ai 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/README.md +384 -0
  2. package/dist/adt-client.js +364 -0
  3. package/dist/cli/activate.js +113 -0
  4. package/dist/cli/init.js +333 -0
  5. package/dist/cli/remove.js +80 -0
  6. package/dist/cli/status.js +229 -0
  7. package/dist/cli/systems.js +68 -0
  8. package/dist/cli.js +81 -0
  9. package/dist/index.js +1318 -0
  10. package/dist/knowledge/abap/abap-dictionary.md +199 -0
  11. package/dist/knowledge/abap/abap-sql.md +296 -0
  12. package/dist/knowledge/abap/amdp.md +273 -0
  13. package/dist/knowledge/abap/clean-code.md +293 -0
  14. package/dist/knowledge/abap/cloud-background-processing.md +250 -0
  15. package/dist/knowledge/abap/cloud-communication.md +265 -0
  16. package/dist/knowledge/abap/cloud-development.md +176 -0
  17. package/dist/knowledge/abap/cloud-extensibility.md +252 -0
  18. package/dist/knowledge/abap/cloud-released-apis.md +261 -0
  19. package/dist/knowledge/abap/constructor-expressions.md +289 -0
  20. package/dist/knowledge/abap/enhancements.md +232 -0
  21. package/dist/knowledge/abap/exceptions.md +271 -0
  22. package/dist/knowledge/abap/internal-tables.md +205 -0
  23. package/dist/knowledge/abap/object-orientation.md +298 -0
  24. package/dist/knowledge/abap/performance.md +216 -0
  25. package/dist/knowledge/abap/rap-abstract-entities.md +206 -0
  26. package/dist/knowledge/abap/rap-business-events.md +216 -0
  27. package/dist/knowledge/abap/rap-draft.md +191 -0
  28. package/dist/knowledge/abap/rap-eml.md +453 -0
  29. package/dist/knowledge/abap/rap-end-to-end.md +486 -0
  30. package/dist/knowledge/abap/rap-feature-control.md +185 -0
  31. package/dist/knowledge/abap/rap-numbering.md +280 -0
  32. package/dist/knowledge/abap/rap-service-exposure.md +163 -0
  33. package/dist/knowledge/abap/rap-unmanaged.md +468 -0
  34. package/dist/knowledge/abap/string-processing.md +180 -0
  35. package/dist/knowledge/abap/unit-testing.md +303 -0
  36. package/dist/knowledge/abap-cds/access-control.md +241 -0
  37. package/dist/knowledge/abap-cds/annotations.md +331 -0
  38. package/dist/knowledge/abap-cds/associations.md +254 -0
  39. package/dist/knowledge/abap-cds/expressions.md +230 -0
  40. package/dist/knowledge/abap-cds/functions.md +245 -0
  41. package/dist/knowledge/abap-cds/metadata-extensions.md +294 -0
  42. package/dist/knowledge/cap/authentication.md +278 -0
  43. package/dist/knowledge/cap/cdl-syntax.md +247 -0
  44. package/dist/knowledge/cap/cql-queries.md +266 -0
  45. package/dist/knowledge/cap/deployment.md +343 -0
  46. package/dist/knowledge/cap/event-handlers.md +287 -0
  47. package/dist/knowledge/cap/fiori-integration.md +303 -0
  48. package/dist/knowledge/cap/service-definitions.md +287 -0
  49. package/dist/knowledge/fiori/annotations.md +347 -0
  50. package/dist/knowledge/fiori/deployment.md +340 -0
  51. package/dist/knowledge/fiori/fiori-elements.md +332 -0
  52. package/dist/knowledge/fiori/fiori-side-effects.md +107 -0
  53. package/dist/knowledge/fiori/fiori-valuelist.md +144 -0
  54. package/dist/knowledge/fiori/ui5-controllers.md +358 -0
  55. package/dist/knowledge/fiori/ui5-data-binding.md +311 -0
  56. package/dist/knowledge/fiori/ui5-fragments-dialogs.md +330 -0
  57. package/dist/knowledge/fiori/ui5-manifest.md +411 -0
  58. package/dist/knowledge/fiori/ui5-routing.md +303 -0
  59. package/dist/knowledge/fiori/ui5-xml-views.md +294 -0
  60. package/dist/logger.js +114 -0
  61. package/dist/system-profile.js +207 -0
  62. package/dist/tools/abap-doc.js +72 -0
  63. package/dist/tools/abapgit.js +161 -0
  64. package/dist/tools/activate.js +68 -0
  65. package/dist/tools/atc-check.js +117 -0
  66. package/dist/tools/auth-object.js +56 -0
  67. package/dist/tools/breakpoints.js +76 -0
  68. package/dist/tools/call-hierarchy.js +84 -0
  69. package/dist/tools/cds-annotations.js +98 -0
  70. package/dist/tools/cds-dependencies.js +65 -0
  71. package/dist/tools/check.js +47 -0
  72. package/dist/tools/code-completion.js +70 -0
  73. package/dist/tools/code-coverage.js +111 -0
  74. package/dist/tools/create-amdp.js +111 -0
  75. package/dist/tools/create-dcl.js +81 -0
  76. package/dist/tools/create-transport.js +38 -0
  77. package/dist/tools/create.js +285 -0
  78. package/dist/tools/data-preview.js +37 -0
  79. package/dist/tools/delete.js +45 -0
  80. package/dist/tools/deploy-bsp.js +298 -0
  81. package/dist/tools/discovery.js +59 -0
  82. package/dist/tools/element-info.js +93 -0
  83. package/dist/tools/enhancements.js +186 -0
  84. package/dist/tools/extract-method.js +44 -0
  85. package/dist/tools/function-group.js +59 -0
  86. package/dist/tools/knowledge.js +275 -0
  87. package/dist/tools/lock-object.js +75 -0
  88. package/dist/tools/message-class.js +67 -0
  89. package/dist/tools/navigate.js +80 -0
  90. package/dist/tools/number-range.js +57 -0
  91. package/dist/tools/object-documentation.js +43 -0
  92. package/dist/tools/object-structure.js +78 -0
  93. package/dist/tools/object-versions.js +57 -0
  94. package/dist/tools/package-contents.js +60 -0
  95. package/dist/tools/pretty-printer.js +35 -0
  96. package/dist/tools/publish-binding.js +49 -0
  97. package/dist/tools/quick-fix.js +69 -0
  98. package/dist/tools/read.js +167 -0
  99. package/dist/tools/refactor-rename.js +60 -0
  100. package/dist/tools/release-transport.js +24 -0
  101. package/dist/tools/released-apis.js +51 -0
  102. package/dist/tools/repository-tree.js +90 -0
  103. package/dist/tools/scaffold-rap.js +642 -0
  104. package/dist/tools/search.js +73 -0
  105. package/dist/tools/shared/data-format.js +101 -0
  106. package/dist/tools/sql-console.js +17 -0
  107. package/dist/tools/system-info.js +270 -0
  108. package/dist/tools/traces.js +66 -0
  109. package/dist/tools/transport-contents.js +83 -0
  110. package/dist/tools/transports.js +67 -0
  111. package/dist/tools/unit-test.js +135 -0
  112. package/dist/tools/where-used.js +59 -0
  113. package/dist/tools/write.js +101 -0
  114. package/package.json +49 -0
@@ -0,0 +1,294 @@
1
+ # CDS Metadata Extensions — DDLX for UI annotations
2
+
3
+ ## Purpose
4
+
5
+ Separate UI annotations from CDS data model. Requires `@Metadata.allowExtensions: true` on the CDS view.
6
+
7
+ ## Basic Metadata Extension
8
+
9
+ ```abap
10
+ @Metadata.layer: #CORE
11
+ annotate entity ZC_Travel with
12
+ {
13
+ @UI.facet: [
14
+ { id: 'Travel', type: #IDENTIFICATION_REFERENCE, label: 'Travel', position: 10 },
15
+ { id: 'Booking', type: #LINEITEM_REFERENCE, label: 'Bookings', position: 20,
16
+ targetElement: '_Booking' }
17
+ ]
18
+
19
+ @UI.hidden: true
20
+ TravelID;
21
+
22
+ @UI: {
23
+ lineItem: [{ position: 10 }],
24
+ selectionField: [{ position: 10 }],
25
+ identification: [{ position: 10 }]
26
+ }
27
+ AgencyID;
28
+
29
+ @UI: {
30
+ lineItem: [{ position: 20 }],
31
+ selectionField: [{ position: 20 }],
32
+ identification: [{ position: 20 }]
33
+ }
34
+ CustomerID;
35
+
36
+ @UI: {
37
+ lineItem: [{ position: 30 }],
38
+ identification: [{ position: 30 }]
39
+ }
40
+ BeginDate;
41
+
42
+ @UI.identification: [{ position: 40 }]
43
+ BookingFee;
44
+
45
+ @UI: {
46
+ lineItem: [{ position: 40 }],
47
+ identification: [{ position: 50 }]
48
+ }
49
+ TotalPrice;
50
+
51
+ @UI: {
52
+ lineItem: [{ position: 50 }],
53
+ selectionField: [{ position: 30 }],
54
+ identification: [{ position: 60 }],
55
+ textArrangement: #TEXT_ONLY
56
+ }
57
+ OverallStatus;
58
+ }
59
+ ```
60
+
61
+ ## @Metadata.layer
62
+
63
+ | Layer | Priority | Use |
64
+ |-------|:--------:|-----|
65
+ | `#CORE` | Lowest | Base annotations (application developer) |
66
+ | `#LOCALIZATION` | Low | Language-specific overrides |
67
+ | `#INDUSTRY` | Medium | Industry solution overrides |
68
+ | `#PARTNER` | High | Partner customizations |
69
+ | `#CUSTOMER` | Highest | Customer overrides (wins over all) |
70
+
71
+ Higher layer annotations override lower layer for the same field + annotation.
72
+
73
+ ## UI Facets — Object Page Sections
74
+
75
+ ```abap
76
+ @UI.facet: [
77
+ " Simple identification section
78
+ { id: 'General',
79
+ type: #IDENTIFICATION_REFERENCE,
80
+ label: 'General Information',
81
+ position: 10 },
82
+
83
+ " Child entity table
84
+ { id: 'Items',
85
+ type: #LINEITEM_REFERENCE,
86
+ label: 'Order Items',
87
+ position: 20,
88
+ targetElement: '_Item' },
89
+
90
+ " Field group section
91
+ { id: 'Pricing',
92
+ type: #FIELDGROUP_REFERENCE,
93
+ label: 'Pricing',
94
+ position: 30,
95
+ targetQualifier: 'PricingGroup' },
96
+
97
+ " Collection (groups sub-facets)
98
+ { id: 'Admin',
99
+ type: #COLLECTION,
100
+ label: 'Administration',
101
+ position: 40 },
102
+ { id: 'AdminDates',
103
+ type: #FIELDGROUP_REFERENCE,
104
+ label: 'Dates',
105
+ position: 10,
106
+ parentId: 'Admin',
107
+ targetQualifier: 'DatesGroup' },
108
+ { id: 'AdminUsers',
109
+ type: #FIELDGROUP_REFERENCE,
110
+ label: 'Users',
111
+ position: 20,
112
+ parentId: 'Admin',
113
+ targetQualifier: 'UsersGroup' }
114
+ ]
115
+ ```
116
+
117
+ ## Facet Types
118
+
119
+ | Type | Target | Use |
120
+ |------|--------|-----|
121
+ | `#IDENTIFICATION_REFERENCE` | Fields with `@UI.identification` | Main details form |
122
+ | `#LINEITEM_REFERENCE` | Child entity `targetElement` | Child table |
123
+ | `#FIELDGROUP_REFERENCE` | Fields with `@UI.fieldGroup` matching `targetQualifier` | Grouped form |
124
+ | `#COLLECTION` | Sub-facets via `parentId` | Tab/section container |
125
+ | `#DATAPOINT_REFERENCE` | `@UI.dataPoint` matching qualifier | KPI in header |
126
+ | `#STATUS_INFO_REFERENCE` | Status field | Status in header |
127
+
128
+ ## UI Header Info
129
+
130
+ ```abap
131
+ @UI.headerInfo: {
132
+ typeName: 'Travel',
133
+ typeNamePlural: 'Travels',
134
+ title: { type: #STANDARD, value: 'Description' },
135
+ description: { type: #STANDARD, value: 'TravelID' },
136
+ imageUrl: 'ImageURL'
137
+ }
138
+ ```
139
+
140
+ ## Actions in Metadata Extension
141
+
142
+ ```abap
143
+ @Metadata.layer: #CORE
144
+ annotate entity ZC_Travel with
145
+ {
146
+ " Action buttons in list table
147
+ @UI.lineItem: [
148
+ { position: 10 },
149
+ { type: #FOR_ACTION, dataAction: 'acceptTravel', label: 'Accept' },
150
+ { type: #FOR_ACTION, dataAction: 'rejectTravel', label: 'Reject' }
151
+ ]
152
+ TravelID;
153
+
154
+ " Action buttons on object page
155
+ @UI.identification: [
156
+ { position: 10 },
157
+ { type: #FOR_ACTION, dataAction: 'acceptTravel', label: 'Accept', position: 1 },
158
+ { type: #FOR_ACTION, dataAction: 'rejectTravel', label: 'Reject', position: 2 }
159
+ ]
160
+ TravelID;
161
+ }
162
+ ```
163
+
164
+ ## Field Group
165
+
166
+ ```abap
167
+ @Metadata.layer: #CORE
168
+ annotate entity ZC_Order with
169
+ {
170
+ @UI.fieldGroup: [{ qualifier: 'PricingGroup', position: 10 }]
171
+ NetAmount;
172
+
173
+ @UI.fieldGroup: [{ qualifier: 'PricingGroup', position: 20 }]
174
+ TaxAmount;
175
+
176
+ @UI.fieldGroup: [{ qualifier: 'PricingGroup', position: 30 }]
177
+ TotalAmount;
178
+
179
+ @UI.fieldGroup: [{ qualifier: 'DatesGroup', position: 10 }]
180
+ CreatedAt;
181
+
182
+ @UI.fieldGroup: [{ qualifier: 'DatesGroup', position: 20 }]
183
+ LastChangedAt;
184
+ }
185
+ ```
186
+
187
+ ## DataPoint (Header KPIs)
188
+
189
+ ```abap
190
+ @UI.dataPoint: {
191
+ qualifier: 'StatusDP',
192
+ title: 'Status',
193
+ criticality: 'StatusCriticality'
194
+ }
195
+ OverallStatus;
196
+
197
+ @UI.dataPoint: {
198
+ qualifier: 'PriceDP',
199
+ title: 'Total Price'
200
+ }
201
+ TotalPrice;
202
+
203
+ " Reference in header facets
204
+ @UI.facet: [
205
+ { id: 'HeaderStatus',
206
+ type: #DATAPOINT_REFERENCE,
207
+ purpose: #HEADER,
208
+ targetQualifier: 'StatusDP',
209
+ position: 10 },
210
+ { id: 'HeaderPrice',
211
+ type: #DATAPOINT_REFERENCE,
212
+ purpose: #HEADER,
213
+ targetQualifier: 'PriceDP',
214
+ position: 20 }
215
+ ]
216
+ ```
217
+
218
+ ## Criticality
219
+
220
+ ```abap
221
+ " Computed field in CDS (or virtual field set in handler)
222
+ case overall_status
223
+ when 'O' then 2 " Open = yellow (Critical)
224
+ when 'A' then 3 " Accepted = green (Positive)
225
+ when 'X' then 1 " Rejected = red (Negative)
226
+ else 0 " grey (Neutral)
227
+ end as StatusCriticality,
228
+
229
+ " Referenced in annotation
230
+ @UI.lineItem: [{ position: 50, criticality: 'StatusCriticality' }]
231
+ OverallStatus;
232
+ ```
233
+
234
+ | Value | Color | Meaning |
235
+ |:-----:|-------|---------|
236
+ | 0 | Grey | Neutral |
237
+ | 1 | Red | Negative/Error |
238
+ | 2 | Yellow | Critical/Warning |
239
+ | 3 | Green | Positive/Success |
240
+ | 5 | Blue | Information |
241
+
242
+ ## Complete Child Metadata Extension
243
+
244
+ ```abap
245
+ @Metadata.layer: #CORE
246
+ annotate entity ZC_Booking with
247
+ {
248
+ @UI.facet: [
249
+ { id: 'Booking', type: #IDENTIFICATION_REFERENCE, label: 'Booking Details', position: 10 }
250
+ ]
251
+
252
+ @UI.hidden: true
253
+ TravelID;
254
+
255
+ @UI.hidden: true
256
+ BookingID;
257
+
258
+ @UI: { lineItem: [{ position: 10 }], identification: [{ position: 10 }] }
259
+ BookingDate;
260
+
261
+ @UI: { lineItem: [{ position: 20 }], identification: [{ position: 20 }] }
262
+ CarrierID;
263
+
264
+ @UI: { lineItem: [{ position: 30 }], identification: [{ position: 30 }] }
265
+ ConnectionID;
266
+
267
+ @UI: { lineItem: [{ position: 40 }], identification: [{ position: 40 }] }
268
+ FlightDate;
269
+
270
+ @UI: { lineItem: [{ position: 50 }], identification: [{ position: 50 }] }
271
+ FlightPrice;
272
+ }
273
+ ```
274
+
275
+ ## Rules
276
+ - CDS view MUST have `@Metadata.allowExtensions: true`
277
+ - Metadata Extension annotates **projection CDS** (ZC_*), not interface (ZI_*)
278
+ - `@Metadata.layer: #CORE` is the standard for application development
279
+ - Number positions by 10s for easy insertion
280
+ - `@UI.facet` goes on the FIRST field (or any field — it's entity-level)
281
+ - `parentId` links sub-facets to a `#COLLECTION` facet
282
+ - `targetElement` for child tables matches the association name (e.g., `_Booking`)
283
+ - `targetQualifier` matches the `qualifier` in `@UI.fieldGroup`
284
+
285
+ ## Anti-Patterns
286
+ | Anti-Pattern | Correct |
287
+ |---|---|
288
+ | UI annotations in interface CDS (ZI_*) | Metadata Extension on projection (ZC_*) |
289
+ | Missing `@Metadata.allowExtensions` on CDS | Extension silently ignored |
290
+ | `@UI.facet` with `targetElement` that doesn't match association name | Use exact association name |
291
+ | Positions 1, 2, 3 | Use 10, 20, 30 for insertion gaps |
292
+ | Actions in `@UI.fieldGroup` | Actions in `@UI.lineItem` or `@UI.identification` with `type: #FOR_ACTION` |
293
+ | `#FIELDGROUP_REFERENCE` without matching `targetQualifier` | Qualifier must match `@UI.fieldGroup` qualifier |
294
+ | Multiple `@UI.facet` on different fields | Put all facets in ONE `@UI.facet` array on one field |
@@ -0,0 +1,278 @@
1
+ # CAP Authentication — XSUAA, roles, scopes, instance-level auth
2
+
3
+ ## Enable Authentication
4
+
5
+ ```json
6
+ // package.json
7
+ {
8
+ "cds": {
9
+ "requires": {
10
+ "auth": {
11
+ "kind": "xsuaa",
12
+ "[development]": {
13
+ "kind": "mocked",
14
+ "users": {
15
+ "admin": { "password": "admin", "roles": ["admin", "viewer"] },
16
+ "viewer": { "password": "viewer", "roles": ["viewer"] },
17
+ "carol": { "password": "carol", "roles": [], "attr": { "Country": "DE" } }
18
+ }
19
+ }
20
+ }
21
+ }
22
+ }
23
+ }
24
+ ```
25
+
26
+ ## Service-Level Access Control
27
+
28
+ ```cds
29
+ // Entire service requires authentication
30
+ service AdminService @(requires: 'admin') {
31
+ entity Books as projection on my.Books;
32
+ entity Authors as projection on my.Authors;
33
+ }
34
+
35
+ // Public read, auth for write
36
+ service CatalogService @(requires: 'authenticated-user') {
37
+ @readonly
38
+ entity Books as projection on my.Books;
39
+ }
40
+
41
+ // No auth (public API)
42
+ service PublicService {
43
+ @readonly
44
+ entity Products as projection on my.Products;
45
+ }
46
+ ```
47
+
48
+ ## Entity-Level Access Control
49
+
50
+ ```cds
51
+ service OrderService {
52
+ // Only admin can manage orders
53
+ @requires: 'admin'
54
+ entity Orders as projection on my.Orders;
55
+
56
+ // Multiple roles (any matches)
57
+ @requires: ['admin', 'support']
58
+ action cancelAllOrders();
59
+
60
+ // Read-only for authenticated users
61
+ @readonly @requires: 'authenticated-user'
62
+ entity Products as projection on my.Products;
63
+ }
64
+ ```
65
+
66
+ ## Instance-Level Access (Row-Based)
67
+
68
+ ```cds
69
+ // Restrict based on user role + attributes
70
+ annotate OrderService.Orders with @(restrict: [
71
+ // Admin: full access
72
+ { grant: '*', to: 'admin' },
73
+
74
+ // Viewer: read only
75
+ { grant: 'READ', to: 'viewer' },
76
+
77
+ // Support: read + update
78
+ { grant: ['READ', 'UPDATE'], to: 'support' },
79
+
80
+ // Country-based: only see orders from user's country
81
+ { grant: 'READ', where: 'country = $user.Country' },
82
+
83
+ // Own records: user sees only what they created
84
+ { grant: 'READ', where: 'createdBy = $user' },
85
+
86
+ // Combined: role + attribute
87
+ { grant: ['READ', 'UPDATE'], to: 'regional_admin',
88
+ where: 'country = $user.Country' }
89
+ ]);
90
+ ```
91
+
92
+ ## xs-security.json — Scopes and Roles
93
+
94
+ ```json
95
+ {
96
+ "xsappname": "myapp",
97
+ "tenant-mode": "dedicated",
98
+ "scopes": [
99
+ { "name": "$XSAPPNAME.admin", "description": "Admin" },
100
+ { "name": "$XSAPPNAME.viewer", "description": "Viewer" },
101
+ { "name": "$XSAPPNAME.support", "description": "Support" }
102
+ ],
103
+ "attributes": [
104
+ { "name": "Country", "description": "Country", "valueType": "string" },
105
+ { "name": "CostCenter", "description": "Cost Center", "valueType": "string" }
106
+ ],
107
+ "role-templates": [
108
+ {
109
+ "name": "Admin",
110
+ "description": "Full access",
111
+ "scope-references": ["$XSAPPNAME.admin", "$XSAPPNAME.viewer"]
112
+ },
113
+ {
114
+ "name": "Viewer",
115
+ "description": "Read-only",
116
+ "scope-references": ["$XSAPPNAME.viewer"]
117
+ },
118
+ {
119
+ "name": "RegionalAdmin",
120
+ "description": "Admin for specific country",
121
+ "scope-references": ["$XSAPPNAME.admin"],
122
+ "attribute-references": ["Country"]
123
+ }
124
+ ],
125
+ "role-collections": [
126
+ {
127
+ "name": "MyApp_Admin",
128
+ "role-template-references": ["$XSAPPNAME.Admin"]
129
+ },
130
+ {
131
+ "name": "MyApp_Viewer",
132
+ "role-template-references": ["$XSAPPNAME.Viewer"]
133
+ }
134
+ ]
135
+ }
136
+ ```
137
+
138
+ ## Accessing User Info in Handlers
139
+
140
+ ```js
141
+ this.before('*', (req) => {
142
+ const { user } = req;
143
+
144
+ // User ID
145
+ console.log(user.id); // 'admin@company.com'
146
+
147
+ // Role check
148
+ if (user.is('admin')) { /* full access */ }
149
+ if (user.is('viewer')) { /* read only */ }
150
+
151
+ // User attributes
152
+ const country = user.attr.Country; // 'DE'
153
+ const costCenter = user.attr.CostCenter;
154
+
155
+ // Tenant
156
+ console.log(req.tenant); // 't1' (multi-tenant)
157
+
158
+ // Locale
159
+ console.log(req.locale); // 'en'
160
+ });
161
+ ```
162
+
163
+ ## Custom Authorization in Handler
164
+
165
+ ```js
166
+ this.before('UPDATE', 'Orders', async (req) => {
167
+ // Only order owner or admin can update
168
+ const order = await SELECT.one.from('Orders', req.data.ID);
169
+ if (!order) return req.reject(404, 'Order not found');
170
+
171
+ if (order.createdBy !== req.user.id && !req.user.is('admin')) {
172
+ return req.reject(403, 'Only the order creator or admin can modify this order');
173
+ }
174
+ });
175
+
176
+ this.before('DELETE', 'Orders', async (req) => {
177
+ // Admin only
178
+ if (!req.user.is('admin')) {
179
+ return req.reject(403, 'Only administrators can delete orders');
180
+ }
181
+ });
182
+
183
+ // Attribute-based filtering
184
+ this.before('READ', 'Orders', (req) => {
185
+ if (!req.user.is('admin')) {
186
+ // Non-admin users only see their country's orders
187
+ const country = req.user.attr.Country;
188
+ if (country) {
189
+ req.query.where({ country });
190
+ }
191
+ }
192
+ });
193
+ ```
194
+
195
+ ## Mock Users for Development
196
+
197
+ ```json
198
+ // package.json — mocked auth
199
+ {
200
+ "cds": {
201
+ "requires": {
202
+ "auth": {
203
+ "[development]": {
204
+ "kind": "mocked",
205
+ "users": {
206
+ "alice": {
207
+ "password": "alice",
208
+ "roles": ["admin", "viewer"],
209
+ "attr": { "Country": "DE" }
210
+ },
211
+ "bob": {
212
+ "password": "bob",
213
+ "roles": ["viewer"],
214
+ "attr": { "Country": "US" }
215
+ }
216
+ }
217
+ },
218
+ "[production]": {
219
+ "kind": "xsuaa"
220
+ }
221
+ }
222
+ }
223
+ }
224
+ }
225
+ ```
226
+
227
+ ```bash
228
+ # Test with mock user
229
+ curl -u alice:alice http://localhost:4004/admin/Books
230
+
231
+ # Or in browser: login prompt with alice/alice
232
+ ```
233
+
234
+ ## Mapping CDS Roles to XSUAA Scopes
235
+
236
+ | CDS `@requires` | xs-security.json scope |
237
+ |---|---|
238
+ | `'admin'` | `$XSAPPNAME.admin` |
239
+ | `'viewer'` | `$XSAPPNAME.viewer` |
240
+ | `'authenticated-user'` | Any valid JWT token (built-in) |
241
+ | `'system-user'` | Technical user (built-in) |
242
+
243
+ CAP auto-maps: `@requires: 'admin'` → checks for scope `$XSAPPNAME.admin` in JWT.
244
+
245
+ ## BTP Role Collection Assignment
246
+
247
+ ```bash
248
+ # After deployment, assign users to role collections in BTP Cockpit:
249
+ # 1. BTP Cockpit → Subaccount → Security → Role Collections
250
+ # 2. Select "MyApp_Admin"
251
+ # 3. Add user: alice@company.com
252
+ # 4. Save
253
+
254
+ # Or via CLI:
255
+ cf create-service-key myapp-auth myapp-auth-key
256
+ ```
257
+
258
+ ## Rules
259
+ - `@requires: 'authenticated-user'` for any logged-in user
260
+ - `@requires: 'admin'` maps to scope `$XSAPPNAME.admin` in xs-security.json
261
+ - `@restrict` for instance-level (row-based) authorization
262
+ - `$user` in `where` clause resolves to `req.user.id`
263
+ - `$user.Country` resolves to user attribute `Country`
264
+ - Mock users in `[development]` profile — XSUAA in `[production]`
265
+ - Role collections assigned in BTP Cockpit after deployment
266
+ - Always test with mock users before deploying to BTP
267
+
268
+ ## Anti-Patterns
269
+ | Anti-Pattern | Correct |
270
+ |---|---|
271
+ | Hardcoded user checks in handler | Use `@requires` + `@restrict` declaratively |
272
+ | `@requires` on service but sensitive entities open | Add `@requires` on entity level too |
273
+ | Missing `[production]` XSUAA config | Mocked auth in production = no security |
274
+ | `$XSAPPNAME.admin` hardcoded in handler | Use `req.user.is('admin')` |
275
+ | No mock users in development | Define mock users for local testing |
276
+ | Role collection without role template | Role collections reference role templates |
277
+ | Checking `req.user` without auth middleware | Ensure `"auth"` is in `cds.requires` |
278
+ | Missing `forwardAuthToken` in App Router | Auth token not propagated to CAP server |