├── .abapgit.xml
├── .editorconfig
├── .gitattributes
├── .github
└── workflows
│ ├── ci.yml
│ ├── downport702.yml
│ ├── playwright.yml
│ └── preview-deployment.yml
├── .gitignore
├── .npmrc
├── .vscode
└── extensions.json
├── LICENSE
├── README.md
├── abap_transpile.json
├── abaplint-app.jsonc
├── abaplint-downport.jsonc
├── abaplint-onprem.jsonc
├── abaplint.jsonc
├── cf
├── cf-build.yml
├── mta.yaml
└── sed.sh
├── package-lock.json
├── package.json
├── playwright.config.js
├── src
├── package.devc.xml
├── zcl_otm_table_maintenance.clas.abap
└── zcl_otm_table_maintenance.clas.xml
├── test
├── index.html
├── index.mjs
├── playwright.spec.mjs
├── setup.mjs
├── web.mjs
├── zcl_http_handler.clas.abap
├── zcl_otm_table_maintenance.clas.testclasses.abap
└── zopentest.tabl.xml
└── webpack.config.js
/.abapgit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | E
6 | /src/
7 | PREFIX
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: https://EditorConfig.org
2 |
3 | # top-most EditorConfig file
4 | root = true
5 |
6 | # 1 space indentation for xml files
7 | [*.xml]
8 | charset = utf-8
9 | end_of_line = lf
10 | insert_final_newline = true
11 | trim_trailing_whitespace = true
12 | indent_style = space
13 | indent_size = 1
14 |
15 | # match the format used by abapGit
16 | [*.{abap,js,json,html,css}]
17 | charset = utf-8
18 | end_of_line = lf
19 | insert_final_newline = true
20 | trim_trailing_whitespace = true
21 | indent_size = 2
22 | indent_style = space
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # newlines
2 | * text=auto eol=lf
3 |
4 | *.xml text eol=lf
5 | *.abap text eol=lf
6 | *.md text eol=lf
7 | *.txt text eol=lf
8 | *.yml text eol=lf
9 | *.yaml text eol=lf
10 | *.js text eol=lf
11 | *.json text eol=lf
12 | *.html text eol=lf
13 | *.css text eol=lf
14 | *.svg text eol=lf
15 | *.sh text eol=lf
16 |
17 | *.jpg binary
18 | *.gif binary
19 | *.png binary
20 | *.jpeg binary
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | test:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - uses: actions/checkout@v4
10 | - uses: actions/setup-node@v4
11 | - run: npm ci
12 | - run: npm test
13 |
--------------------------------------------------------------------------------
/.github/workflows/downport702.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on: [pull_request]
4 |
5 | jobs:
6 | downport702:
7 | runs-on: ubuntu-latest
8 | timeout-minutes: 5
9 | steps:
10 | - uses: actions/checkout@v4
11 | - uses: actions/setup-node@v4
12 | - run: npm ci
13 | - run: npm run lint
14 | - run: npm run downport
15 | - run: rm src/*
16 | - run: cp downport/* src/
17 | - uses: actions/upload-artifact@v4
18 | with:
19 | name: downport702
20 | path: |
21 | src/*
22 | .abapgit.xml
--------------------------------------------------------------------------------
/.github/workflows/playwright.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on: [pull_request]
4 |
5 | jobs:
6 | playwright:
7 | runs-on: ubuntu-latest
8 | timeout-minutes: 5
9 | steps:
10 | - uses: actions/checkout@v4
11 | - uses: actions/setup-node@v4
12 | - run: npm ci
13 | - run: npm run start-local &
14 | - run: sleep 30
15 | - run: npx playwright install
16 | - run: npm run playwright
17 | - run: ls
18 | - uses: actions/upload-artifact@v4
19 | with:
20 | name: screenshot
21 | path: screenshot.png
--------------------------------------------------------------------------------
/.github/workflows/preview-deployment.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | pull_request:
5 |
6 | permissions:
7 | contents: read
8 | pull-requests: write
9 |
10 | jobs:
11 | preview-deployment:
12 | runs-on: ubuntu-latest
13 | timeout-minutes: 10
14 | steps:
15 | - uses: actions/checkout@v4
16 | - uses: actions/setup-node@v4
17 | - name: npm install
18 | run: |
19 | npm install
20 | npm test
21 | npm run web:build
22 |
23 | - name: deploy-merged-build
24 | uses: peaceiris/actions-gh-pages@v3
25 | with:
26 | deploy_key: ${{ secrets.PREVIEW_DEPLOYMENTS_WRITE }}
27 | external_repository: open-abap/preview-deployments
28 | user_name: 'github-actions[bot]'
29 | user_email: 'github-actions[bot]@users.noreply.github.com'
30 | publish_branch: main
31 | publish_dir: ./build
32 | destination_dir: ${{ github.sha }}
33 |
34 | - name: Find Comment
35 | uses: peter-evans/find-comment@v2
36 | id: fc
37 | with:
38 | issue-number: ${{ github.event.pull_request.number }}
39 | comment-author: 'github-actions[bot]'
40 | body-includes: preview-deployment
41 |
42 | - name: Create or update comment
43 | uses: peter-evans/create-or-update-comment@v2
44 | with:
45 | comment-id: ${{ steps.fc.outputs.comment-id }}
46 | issue-number: ${{ github.event.pull_request.number }}
47 | body: |
48 | preview-deployment:
49 | https://open-abap.github.io/preview-deployments/${{ github.sha }}/
50 | edit-mode: replace
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | output
2 | node_modules
3 | downport
4 | screenshot.png
5 | mta_archives/
6 | build
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | ignore-scripts=false
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "larshp.vscode-abaplint"
4 | ]
5 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 open-abap
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # open-table-maintenance
2 |
3 | Works with:
4 | * [open-abap](https://github.com/open-abap/open-abap) via `npm install && npm run start-local`
5 | * [Steampunk](https://blogs.sap.com/2019/08/20/its-steampunk-now/)
6 | * [Embedded Steampunk](https://blogs.sap.com/2021/09/30/steampunk-is-going-all-in/)
7 | * On-Premise v740sp05 and up
8 | * (Code is automatically downported on build, so it should also work with 702)
9 | * SAP Business Technology Platform (BTP) Cloud Foundry environment
10 |
11 | Install with [abapGit](https://abapgit.org), or copy paste into a system
12 |
13 | Warning: only hardcode the table name, don't use with important tables, use at own risk!
14 |
15 | ## Use-case: On-prem shim
16 |
17 | ```abap
18 | METHOD if_http_extension~handle_request.
19 |
20 | DATA(result) = NEW zcl_otm_table_maintenance( 'ZOPENTEST' )->serve( VALUE #(
21 | method = server->request->get_method( )
22 | path = server->request->get_header_field( '~path' )
23 | body = server->request->get_data( ) ) ).
24 |
25 | server->response->set_data( result-body ).
26 | server->response->set_content_type( result-content_type ).
27 | server->response->set_status(
28 | code = result-status
29 | reason = CONV #( result-status ) ).
30 |
31 | ENDMETHOD.
32 | ```
33 |
34 | ## Use-case: Steampunk shim
35 |
36 | ```abap
37 | METHOD if_http_service_extension~handle_request.
38 |
39 | DATA(result) = NEW zcl_otm_table_maintenance( 'ZOPENTEST' )->serve( VALUE #(
40 | method = request->get_method( )
41 | path = request->get_header_field( '~path' )
42 | body = request->get_binary( ) ) ).
43 |
44 | response->set_binary( result-body ).
45 | response->set_header_field(
46 | i_name = 'Content-Type'
47 | i_value = result-content_type ).
48 | response->set_status(
49 | i_code = result-status
50 | i_reason = CONV #( result-status ) ).
51 |
52 | ENDMETHOD.
53 | ```
54 |
55 | ## Deploy to SAP Business Technology Platform (BTP) Cloud Foundry environment
56 |
57 | ### Prerequisites
58 |
59 | * Cloud Foundry CLI
60 | * Multi Target App build tool (mbt)
61 |
62 | ### Installation
63 |
64 | Login to your SAP BTP Cloud Foundry environment
65 |
66 | ```bash
67 | npm run cf:build
68 | npm run cf:deploy
69 | ```
70 |
71 | Then you can start the app by adding /abap/ at the end of the URL.
72 |
73 | ### Removal
74 |
75 | ```bash
76 | npm run cf:undeploy
77 | ```
78 |
--------------------------------------------------------------------------------
/abap_transpile.json:
--------------------------------------------------------------------------------
1 | {
2 | "input_folder": "downport",
3 | "input_filter": [],
4 | "output_folder": "output",
5 | "libs": [
6 | {
7 | "url": "https://github.com/open-abap/open-abap-core"
8 | },
9 | {
10 | "url": "https://github.com/open-abap/express-icf-shim"
11 | }
12 | ],
13 | "write_unit_tests": true,
14 | "write_source_map": true,
15 | "options": {
16 | "ignoreSyntaxCheck": false,
17 | "addFilenames": true,
18 | "addCommonJS": true,
19 | "extraSetup": "../test/setup.mjs"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/abaplint-app.jsonc:
--------------------------------------------------------------------------------
1 | {
2 | "configurations": {
3 | "default": {
4 | "filename": "./abaplint.jsonc"
5 | },
6 | "v740sp05": {
7 | "filename": "./abaplint-onprem.jsonc"
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/abaplint-downport.jsonc:
--------------------------------------------------------------------------------
1 | {
2 | "global": {
3 | "files": "/downport/**/*.*"
4 | },
5 | "dependencies": [
6 | {
7 | "url": "https://github.com/open-abap/open-abap-core",
8 | "files": "/src/**/*.*"
9 | }
10 | ],
11 | "syntax": {
12 | "version": "v702",
13 | "errorNamespace": "."
14 | },
15 | "rules": {
16 | "downport": true,
17 |
18 | "begin_end_names": true,
19 | "check_ddic": true,
20 | "check_include": true,
21 | "check_syntax": true,
22 | "global_class": true,
23 | "implement_methods": true,
24 | "definitions_top": true,
25 | "method_implemented_twice": true,
26 | "parser_error": true,
27 | "parser_missing_space": true,
28 | "superclass_final": true,
29 | "unknown_types": true,
30 | "xml_consistency": true
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/abaplint-onprem.jsonc:
--------------------------------------------------------------------------------
1 | {
2 | "global": {
3 | "files": "/src/**/*.*"
4 | },
5 | "dependencies": [
6 | {
7 | "url": "https://github.com/abaplint/deps",
8 | "files": "/src/**/*.*"
9 | }
10 | ],
11 | "syntax": {
12 | "version": "v740sp05",
13 | "errorNamespace": "^(Z|Y|LCL_|TY_|LIF_)"
14 | },
15 | "rules": {
16 | "begin_end_names": true,
17 | "check_ddic": true,
18 | "check_include": true,
19 | "check_syntax": true,
20 | "global_class": true,
21 | "implement_methods": true,
22 | "method_implemented_twice": true,
23 | "parser_error": true,
24 | "parser_missing_space": true,
25 | "superclass_final": true,
26 | "unknown_types": true,
27 | "xml_consistency": true
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/abaplint.jsonc:
--------------------------------------------------------------------------------
1 | {
2 | "global": {
3 | "files": "/src/**/*.*"
4 | },
5 | "dependencies": [
6 | {
7 | "url": "https://github.com/abapedia/steampunk-2302-api",
8 | "files": "/src/**/*.*"
9 | }
10 | ],
11 | "syntax": {
12 | "version": "Cloud",
13 | "errorNamespace": "."
14 | },
15 | "rules": {
16 | "cds_legacy_view": true,
17 | "cds_parser_error": true,
18 | "classic_exceptions_overlap": true,
19 | "local_testclass_consistency": true,
20 | "no_aliases": true,
21 | "no_external_form_calls": true,
22 | "no_inline_in_optional_branches": false,
23 | "nrob_consistency": true,
24 | "omit_preceding_zeros": true,
25 | "pragma_style": true,
26 | "prefer_corresponding": true,
27 | "slow_parameter_passing": true,
28 | "superfluous_value": true,
29 | "unnecessary_pragma": true,
30 | "7bit_ascii": true,
31 | "abapdoc": false,
32 | "align_parameters": true,
33 | "allowed_object_naming": true,
34 | "allowed_object_types": {
35 | "exclude": [],
36 | "severity": "Error",
37 | "allowed": []
38 | },
39 | "ambiguous_statement": true,
40 | "avoid_use": {
41 | "exclude": [],
42 | "severity": "Error",
43 | "define": true,
44 | "testSeams": true,
45 | "statics": true,
46 | "defaultKey": true,
47 | "break": true,
48 | "describeLines": true
49 | },
50 | "begin_end_names": true,
51 | "begin_single_include": true,
52 | "call_transaction_authority_check": true,
53 | "chain_mainly_declarations": {
54 | "exclude": [],
55 | "severity": "Error",
56 | "definitions": true,
57 | "write": true,
58 | "move": true,
59 | "refresh": true,
60 | "unassign": true,
61 | "clear": true,
62 | "hide": true,
63 | "free": true,
64 | "include": true,
65 | "check": true,
66 | "sort": true
67 | },
68 | "check_abstract": true,
69 | "check_comments": {
70 | "exclude": [],
71 | "severity": "Error",
72 | "allowEndOfLine": false
73 | },
74 | "check_ddic": true,
75 | "check_include": true,
76 | "check_subrc": {
77 | "exclude": [],
78 | "severity": "Error",
79 | "openDataset": true,
80 | "authorityCheck": true,
81 | "selectSingle": true,
82 | "selectTable": true,
83 | "updateDatabase": true,
84 | "insertDatabase": true,
85 | "modifyDatabase": true,
86 | "readTable": true,
87 | "assign": true,
88 | "find": true
89 | },
90 | "check_syntax": true,
91 | "check_text_elements": true,
92 | "check_transformation_exists": true,
93 | "class_attribute_names": {
94 | "patternKind": "required",
95 | "ignoreNames": [],
96 | "ignorePatterns": [],
97 | "ignoreExceptions": true,
98 | "ignoreLocal": true,
99 | "ignoreInterfaces": false,
100 | "statics": "^G._.+$",
101 | "instance": "^M._.+$",
102 | "constants": ""
103 | },
104 | "cloud_types": true,
105 | "colon_missing_space": true,
106 | "commented_code": {
107 | "allowIncludeInFugr": true
108 | },
109 | "constant_classes": true,
110 | "constructor_visibility_public": true,
111 | "contains_tab": {
112 | "exclude": [],
113 | "severity": "Error",
114 | "spaces": 1
115 | },
116 | "cyclic_oo": true,
117 | "cyclomatic_complexity": true,
118 | "dangerous_statement": {
119 | "execSQL": true,
120 | "kernelCall": true,
121 | "systemCall": true,
122 | "exportDynpro": true,
123 | "insertReport": true,
124 | "generateDynpro": true,
125 | "generateReport": true,
126 | "generateSubroutine": true,
127 | "deleteReport": true,
128 | "deleteTextpool": true,
129 | "deleteDynpro": true,
130 | "dynamicSQL": false
131 | },
132 | "db_operation_in_loop": true,
133 | "definitions_top": true,
134 | "description_empty": true,
135 | "double_space": {
136 | "exclude": [],
137 | "severity": "Error",
138 | "keywords": true,
139 | "startParen": true,
140 | "endParen": true,
141 | "afterColon": true
142 | },
143 | "downport": true,
144 | "empty_line_in_statement": {
145 | "exclude": [],
146 | "severity": "Error",
147 | "allowChained": false
148 | },
149 | "empty_statement": true,
150 | "empty_structure": {
151 | "exclude": [],
152 | "severity": "Error",
153 | "loop": true,
154 | "if": true,
155 | "while": true,
156 | "case": true,
157 | "when": true,
158 | "select": true,
159 | "do": true,
160 | "at": true,
161 | "try": true
162 | },
163 | "exit_or_check": {
164 | "exclude": [],
165 | "severity": "Error",
166 | "allowExit": false,
167 | "allowCheck": false
168 | },
169 | "exporting": true,
170 | "forbidden_identifier": {
171 | "exclude": [],
172 | "severity": "Error",
173 | "check": []
174 | },
175 | "forbidden_pseudo_and_pragma": {
176 | "exclude": [],
177 | "severity": "Error",
178 | "pseudo": [],
179 | "pragmas": [],
180 | "ignoreGlobalClassDefinition": false,
181 | "ignoreGlobalInterface": false
182 | },
183 | "forbidden_void_type": {
184 | "exclude": [],
185 | "severity": "Error",
186 | "check": []
187 | },
188 | "form_tables_obsolete": true,
189 | "fully_type_constants": {
190 | "exclude": [],
191 | "severity": "Error",
192 | "checkData": true
193 | },
194 | "function_module_recommendations": {
195 | "exclude": [],
196 | "severity": "Error",
197 | "recommendations": [
198 | {
199 | "name": "SUBST_GET_FILE_LIST",
200 | "replace": "see note 1686357"
201 | },
202 | {
203 | "name": "ROUND",
204 | "replace": "use built in function: round()"
205 | },
206 | {
207 | "name": "SO_NEW_DOCUMENT_ATT_SEND_API1",
208 | "replace": "use CL_BCS"
209 | },
210 | {
211 | "name": "ECATT_CONV_XSTRING_TO_STRING",
212 | "replace": "use CL_BINARY_CONVERT"
213 | },
214 | {
215 | "name": "SCMS_STRING_TO_XSTRING",
216 | "replace": "use CL_BINARY_CONVERT"
217 | },
218 | {
219 | "name": "JOB_CREATE",
220 | "replace": "use CL_BP_ABAP_JOB"
221 | },
222 | {
223 | "name": "JOB_SUBMIT",
224 | "replace": "use CL_BP_ABAP_JOB"
225 | },
226 | {
227 | "name": "GUI_DOWNLOAD",
228 | "replace": "use CL_GUI_FRONTEND_SERVICES"
229 | },
230 | {
231 | "name": "GUI_UPLOAD",
232 | "replace": "use CL_GUI_FRONTEND_SERVICES"
233 | },
234 | {
235 | "name": "WS_FILENAME_GET",
236 | "replace": "use CL_GUI_FRONTEND_SERVICES"
237 | },
238 | {
239 | "name": "F4_FILENAME",
240 | "replace": "use CL_GUI_FRONTEND_SERVICES"
241 | },
242 | {
243 | "name": "SAPGUI_PROGRESS_INDICATOR",
244 | "replace": "use CL_PROGRESS_INDICATOR"
245 | },
246 | {
247 | "name": "GUID_CREATE",
248 | "replace": "use CL_SYSTEM_UUID"
249 | },
250 | {
251 | "name": "SSFC_BASE64_DECODE",
252 | "replace": "use class CL_HTTP_UTILITY methods"
253 | },
254 | {
255 | "name": "SSFC_BASE64_ENCODE",
256 | "replace": "use class CL_HTTP_UTILITY methods"
257 | },
258 | {
259 | "name": "SCMS_BASE64_DECODE_STR",
260 | "replace": "use class CL_HTTP_UTILITY methods"
261 | },
262 | {
263 | "name": "POPUP_TO_DECIDE",
264 | "replace": "use POPUP_TO_CONFIRM"
265 | },
266 | {
267 | "name": "REUSE_ALV_GRID_DISPLAY",
268 | "replace": "use CL_SALV_TABLE=>FACTORY or CL_GUI_ALV_GRID"
269 | },
270 | {
271 | "name": "CALCULATE_HASH_FOR_RAW",
272 | "replace": "use CL_ABAP_HMAC"
273 | },
274 | {
275 | "name": "FUNCTION_EXISTS",
276 | "replace": "surround with try-catch CX_SY_DYN_CALL_ILLEGAL_METHOD instead"
277 | }
278 | ]
279 | },
280 | "functional_writing": {
281 | "exclude": [],
282 | "severity": "Error",
283 | "ignoreExceptions": true
284 | },
285 | "global_class": true,
286 | "identical_conditions": true,
287 | "identical_contents": true,
288 | "identical_descriptions": true,
289 | "identical_form_names": true,
290 | "if_in_if": true,
291 | "implement_methods": true,
292 | "in_statement_indentation": {
293 | "exclude": [],
294 | "severity": "Error",
295 | "blockStatements": 2,
296 | "ignoreExceptions": true
297 | },
298 | "indentation": {
299 | "exclude": [],
300 | "severity": "Error",
301 | "ignoreExceptions": true,
302 | "alignTryCatch": false,
303 | "selectionScreenBlockIndentation": false,
304 | "globalClassSkipFirst": false,
305 | "ignoreGlobalClassDefinition": false,
306 | "ignoreGlobalInterface": false
307 | },
308 | "inline_data_old_versions": true,
309 | "intf_referencing_clas": {
310 | "exclude": [],
311 | "severity": "Error",
312 | "allow": []
313 | },
314 | "keep_single_parameter_on_one_line": {
315 | "exclude": [],
316 | "severity": "Error",
317 | "length": 120
318 | },
319 | "keyword_case": {
320 | "exclude": [],
321 | "severity": "Error",
322 | "style": "upper",
323 | "ignoreExceptions": true,
324 | "ignoreLowerClassImplmentationStatement": true,
325 | "ignoreGlobalClassDefinition": false,
326 | "ignoreGlobalInterface": false,
327 | "ignoreFunctionModuleName": false,
328 | "ignoreGlobalClassBoundaries": false,
329 | "ignoreKeywords": []
330 | },
331 | "line_break_multiple_parameters": {
332 | "exclude": [],
333 | "severity": "Error",
334 | "count": 1
335 | },
336 | "line_break_style": false,
337 | "line_length": {
338 | "exclude": [],
339 | "severity": "Error",
340 | "length": 120
341 | },
342 | "line_only_punc": {
343 | "exclude": [],
344 | "severity": "Error",
345 | "ignoreExceptions": true
346 | },
347 | "local_class_naming": {
348 | "exclude": [],
349 | "severity": "Error",
350 | "patternKind": "required",
351 | "ignoreNames": [],
352 | "ignorePatterns": [],
353 | "local": "^LCL_.+$",
354 | "exception": "^LCX_.+$",
355 | "test": "^LTCL_.+$"
356 | },
357 | "local_variable_names": false,
358 | "main_file_contents": true,
359 | "many_parentheses": true,
360 | "max_one_method_parameter_per_line": true,
361 | "max_one_statement": true,
362 | "message_exists": true,
363 | "method_implemented_twice": true,
364 | "method_length": {
365 | "exclude": [],
366 | "severity": "Error",
367 | "statements": 100,
368 | "errorWhenEmpty": true,
369 | "ignoreTestClasses": false,
370 | "checkForms": true
371 | },
372 | "method_overwrites_builtin": true,
373 | "method_parameter_names": false,
374 | "mix_returning": true,
375 | "modify_only_own_db_tables": {
376 | "exclude": [],
377 | "severity": "Error",
378 | "reportDynamic": false,
379 | "ownTables": "^[yz]"
380 | },
381 | "msag_consistency": true,
382 | "names_no_dash": true,
383 | "nesting": {
384 | "exclude": [],
385 | "severity": "Error",
386 | "depth": 5
387 | },
388 | "newline_between_methods": {
389 | "exclude": [],
390 | "severity": "Error",
391 | "count": 3,
392 | "logic": "less"
393 | },
394 | "no_chained_assignment": true,
395 | "no_public_attributes": true,
396 | "no_yoda_conditions": true,
397 | "object_naming": {
398 | "exclude": [],
399 | "severity": "Error",
400 | "patternKind": "required",
401 | "ignoreNames": [],
402 | "ignorePatterns": [],
403 | "clas": "^ZC(L|X)",
404 | "intf": "^ZIF",
405 | "prog": "^Z",
406 | "fugr": "^Z",
407 | "tabl": "^Z",
408 | "ttyp": "^Z",
409 | "dtel": "^Z",
410 | "doma": "^Z",
411 | "msag": "^Z",
412 | "tran": "^Z",
413 | "enqu": "^EZ",
414 | "auth": "^Z",
415 | "pinf": "^Z",
416 | "idoc": "^Z",
417 | "xslt": "^Z",
418 | "ssfo": "^Z",
419 | "ssst": "^Z",
420 | "shlp": "^Z"
421 | },
422 | "obsolete_statement": {
423 | "refresh": true,
424 | "compute": true,
425 | "clientSpecified": true,
426 | "formDefinition": true,
427 | "formImplementation": true,
428 | "add": true,
429 | "subtract": true,
430 | "multiply": true,
431 | "divide": true,
432 | "occurences": true,
433 | "move": true,
434 | "requested": true,
435 | "occurs": true,
436 | "setExtended": true,
437 | "withHeaderLine": true,
438 | "fieldSymbolStructure": true,
439 | "typePools": true,
440 | "load": true,
441 | "parameter": true,
442 | "ranges": true,
443 | "communication": true,
444 | "pack": true,
445 | "selectWithoutInto": true,
446 | "freeMemory": true,
447 | "exitFromSQL": true,
448 | "sortByFS": true,
449 | "callTransformation": true,
450 | "regex": true
451 | },
452 | "omit_parameter_name": true,
453 | "omit_receiving": true,
454 | "parser_702_chaining": true,
455 | "parser_error": true,
456 | "parser_missing_space": true,
457 | "prefer_inline": false,
458 | "prefer_is_not": true,
459 | "prefer_raise_exception_new": true,
460 | "prefer_returning_to_exporting": true,
461 | "prefer_xsdbool": false, // no, xsdbool introduced in 740sp08
462 | "preferred_compare_operator": {
463 | "exclude": [],
464 | "severity": "Error",
465 | "badOperators": [
466 | "EQ",
467 | "><",
468 | "NE",
469 | "GE",
470 | "GT",
471 | "LT",
472 | "LE"
473 | ]
474 | },
475 | "prefix_is_current_class": {
476 | "exclude": [],
477 | "severity": "Error",
478 | "omitMeInstanceCalls": true
479 | },
480 | "reduce_string_templates": true,
481 | "release_idoc": true,
482 | "remove_descriptions": {
483 | "exclude": [],
484 | "severity": "Error",
485 | "ignoreExceptions": false,
486 | "ignoreWorkflow": true
487 | },
488 | "rfc_error_handling": true,
489 | "select_add_order_by": true,
490 | "select_performance": false,
491 | "selection_screen_naming": true,
492 | "sequential_blank": true,
493 | "short_case": {
494 | "exclude": [],
495 | "severity": "Error",
496 | "length": 1,
497 | "allow": []
498 | },
499 | "sicf_consistency": true,
500 | "space_before_colon": true,
501 | "space_before_dot": {
502 | "exclude": [],
503 | "severity": "Error",
504 | "ignoreGlobalDefinition": true,
505 | "ignoreExceptions": true
506 | },
507 | "sql_escape_host_variables": true,
508 | "start_at_tab": true,
509 | "static_call_via_instance": true,
510 | "superclass_final": true,
511 | "sy_modification": true,
512 | "tabl_enhancement_category": true,
513 | "try_without_catch": true,
514 | "type_form_parameters": true,
515 | "types_naming": {
516 | "exclude": [],
517 | "severity": "Error",
518 | "pattern": "^TY_.+$"
519 | },
520 | "uncaught_exception": true,
521 | "unknown_types": true,
522 | "unnecessary_chaining": false,
523 | "unreachable_code": true,
524 | "unsecure_fae": true,
525 | "unused_ddic": true,
526 | "unused_methods": true,
527 | "unused_types": {
528 | "exclude": [],
529 | "severity": "Error",
530 | "skipNames": []
531 | },
532 | "unused_variables": {
533 | "exclude": [],
534 | "severity": "Error",
535 | "skipNames": []
536 | },
537 | "use_bool_expression": true,
538 | "use_class_based_exceptions": true,
539 | "use_line_exists": false, // downport missing some scenarios
540 | "use_new": true,
541 | "when_others_last": true,
542 | "whitespace_end": true,
543 | "xml_consistency": true
544 | }
545 | }
546 |
--------------------------------------------------------------------------------
/cf/cf-build.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on: [pull_request]
4 |
5 | jobs:
6 | cf-build:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - uses: actions/checkout@v2
10 | - uses: actions/setup-node@v2
11 | with:
12 | node-version: '14'
13 | - run: npm ci
14 | - name: Install MTA Build Tool
15 | run: npm install --no-save mbt
16 | - name: Build MTA
17 | run: npm run cf:build
18 | - name: Upload Artifact
19 | uses: actions/upload-artifact@master
20 | with:
21 | name: mta
22 | path: ./cf/mta_archives/open-table-maintenance.mtar
23 | cf-test-run:
24 | needs: cf-build
25 | runs-on: ubuntu-latest
26 | steps:
27 | - uses: actions/setup-node@v2
28 | with:
29 | node-version: '14'
30 | - name: Download Artifact
31 | uses: actions/download-artifact@master
32 | with:
33 | name: mta
34 | path: ./
35 | - name: Extract MTAR
36 | run: unzip open-table-maintenance.mtar
37 | - name: Start application
38 | run: |
39 | cd open-table-maintenance
40 | unzip -q data.zip
41 | npm start &
42 | sleep 3
43 | - run: curl http://localhost:3000/abap
--------------------------------------------------------------------------------
/cf/mta.yaml:
--------------------------------------------------------------------------------
1 | ID: open-table-maintenance
2 | _schema-version: 3.2.0
3 | description: ABAP table maintenance
4 | version: 0.0.1
5 | parameters:
6 | enable-parallel-deployments: true
7 | build-parameters:
8 | before-all:
9 | - builder: custom
10 | commands:
11 | - npm run lint
12 | - npm run downport
13 | - npm run transpile
14 | - cp ../package.json ../output/
15 | - cp ../test/index.mjs ../output/start.mjs
16 | - mkdir ../output/test/
17 | - cp ../test/setup.mjs ../output/test/
18 | - ./sed.sh 's/..\/output\//.\//g' -i ../output/start.mjs
19 | modules:
20 | - name: open-table-maintenance
21 | type: nodejs
22 | path: ../output
23 | parameters:
24 | disk-quota: 512M
25 | memory: 128M
26 |
--------------------------------------------------------------------------------
/cf/sed.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | if [[ "$OSTYPE" == "darwin"* ]]; then
3 | exec "gsed" "$@"
4 | else
5 | exec "sed" "$@"
6 | fi
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "open-table-maintenance",
3 | "private": true,
4 | "version": "1.0.0",
5 | "description": "open-table-maintenance",
6 | "scripts": {
7 | "lint": "abaplint",
8 | "transpile": "rm -rf output && abap_transpile",
9 | "unit": "npm run transpile && echo RUNNING && node output/index.mjs",
10 | "test": "npm run lint && npm run downport && npm run unit",
11 | "downport": "rm -rf downport && cp -r src downport && cp test/*.abap downport && cp test/*.xml downport && abaplint --fix abaplint-downport.jsonc",
12 | "playwright": "npx playwright test",
13 | "start-local": "npm run lint && npm run downport && npm run transpile && node test/index.mjs",
14 | "cf:build": "mbt build -s cf/ --mtar open-table-maintenance.mtar",
15 | "cf:deploy": "cf deploy cf/mta_archives/open-table-maintenance.mtar",
16 | "web": "webpack serve --mode development --env development",
17 | "web:build": "webpack --mode development --env development",
18 | "cf:undeploy": "cf undeploy open-table-maintenance"
19 | },
20 | "repository": {
21 | "type": "git",
22 | "url": "git+https://github.com/open-abap/open-table-maintenance.git"
23 | },
24 | "author": "",
25 | "license": "ISC",
26 | "bugs": {
27 | "url": "https://github.com/open-abap/open-table-maintenance/issues"
28 | },
29 | "homepage": "https://github.com/open-abap/open-table-maintenance#readme",
30 | "dependencies": {
31 | "@abaplint/cli": "^2.113.110",
32 | "@abaplint/database-sqlite": "^2.10.24",
33 | "@abaplint/runtime": "^2.10.49",
34 | "@abaplint/transpiler-cli": "^2.10.49",
35 | "@playwright/test": "^1.52.0",
36 | "buffer": "^6.0.3",
37 | "web-encoding": "^1.1.5",
38 | "copy-webpack-plugin": "^13.0.0",
39 | "crypto-browserify": "^3.12.1",
40 | "express": "^4.21.2",
41 | "html-webpack-plugin": "^5.6.3",
42 | "path-browserify": "^1.0.1",
43 | "webpack-cli": "^6.0.1",
44 | "webpack-dev-server": "^5.2.1"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/playwright.config.js:
--------------------------------------------------------------------------------
1 | // playwright.config.js
2 | // @ts-check
3 |
4 | /** @type {import('@playwright/test').PlaywrightTestConfig} */
5 | const config = {
6 | testDir: 'test',
7 | timeout: 30000,
8 | forbidOnly: !!process.env.CI,
9 | retries: 0,
10 | // Limit the number of workers on CI, use default locally
11 | workers: process.env.CI ? 2 : undefined,
12 | use: {
13 | // Configure browser and context here
14 | },
15 | };
16 |
17 | module.exports = config;
18 |
--------------------------------------------------------------------------------
/src/package.devc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | open-table-maintenance
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/zcl_otm_table_maintenance.clas.abap:
--------------------------------------------------------------------------------
1 | CLASS zcl_otm_table_maintenance DEFINITION
2 | PUBLIC
3 | CREATE PUBLIC .
4 |
5 | PUBLIC SECTION.
6 | * MIT License, Copyright (c) 2021 open-abap
7 | * https://github.com/open-abap/open-table-maintenance
8 |
9 | TYPES:
10 | BEGIN OF ty_request,
11 | method TYPE string,
12 | path TYPE string,
13 | body TYPE xstring,
14 | END OF ty_request.
15 | TYPES:
16 | BEGIN OF ty_http,
17 | status TYPE i,
18 | content_type TYPE string,
19 | body TYPE xstring,
20 | END OF ty_http .
21 |
22 | METHODS constructor
23 | IMPORTING
24 | !iv_table TYPE tabname .
25 | METHODS serve
26 | IMPORTING
27 | !is_request TYPE ty_request
28 | RETURNING
29 | VALUE(rs_http) TYPE ty_http .
30 | PROTECTED SECTION.
31 | PRIVATE SECTION.
32 |
33 | TYPES:
34 | ty_names TYPE STANDARD TABLE OF abap_compname WITH EMPTY KEY .
35 | TYPES:
36 | * there is no common released type for both steampunk and on-prem, workaround:
37 | BEGIN OF ty_fixvalue,
38 | low TYPE c LENGTH 10,
39 | high TYPE c LENGTH 10,
40 | option TYPE c LENGTH 2,
41 | ddlanguage TYPE c LENGTH 1,
42 | ddtext TYPE c LENGTH 60,
43 | END OF ty_fixvalue .
44 | TYPES:
45 | ty_fixvalues TYPE STANDARD TABLE OF ty_fixvalue WITH EMPTY KEY .
46 | TYPES:
47 | BEGIN OF ty_fielddata,
48 | name TYPE abap_compname,
49 | key TYPE abap_bool,
50 | type_kind TYPE abap_typekind,
51 | length TYPE i,
52 | fixvalues TYPE ty_fixvalues,
53 | END OF ty_fielddata .
54 | TYPES:
55 | ty_metadata TYPE STANDARD TABLE OF ty_fielddata WITH EMPTY KEY .
56 |
57 | DATA mv_table TYPE tabname .
58 |
59 | METHODS from_xstring
60 | IMPORTING
61 | !xstring TYPE xstring
62 | RETURNING
63 | VALUE(string) TYPE string .
64 | METHODS get_html
65 | RETURNING
66 | VALUE(rv_html) TYPE string .
67 | METHODS read_table
68 | RETURNING
69 | VALUE(rv_json) TYPE string .
70 | METHODS save_table
71 | IMPORTING
72 | !iv_json TYPE string
73 | RAISING
74 | cx_sy_conversion_data_loss .
75 | METHODS to_json
76 | IMPORTING
77 | !ref TYPE REF TO data
78 | RETURNING
79 | VALUE(rv_json) TYPE string .
80 | METHODS to_xstring
81 | IMPORTING
82 | !string TYPE string
83 | RETURNING
84 | VALUE(xstring) TYPE xstring .
85 | METHODS list_key_fields
86 | RETURNING
87 | VALUE(names) TYPE ty_names .
88 | METHODS build_metadata
89 | RETURNING
90 | VALUE(rt_metadata) TYPE ty_metadata .
91 | ENDCLASS.
92 |
93 |
94 |
95 | CLASS zcl_otm_table_maintenance IMPLEMENTATION.
96 |
97 |
98 | METHOD build_metadata.
99 | DATA lv_key TYPE abap_bool.
100 | DATA lo_element TYPE REF TO cl_abap_elemdescr.
101 | DATA lt_values TYPE ty_fixvalues.
102 |
103 | DATA(lt_key_fields) = list_key_fields( ).
104 | DATA(lt_components) = CAST cl_abap_structdescr( cl_abap_typedescr=>describe_by_name(
105 | mv_table ) )->get_components( ).
106 |
107 | LOOP AT lt_components INTO DATA(ls_component).
108 |
109 | lo_element ?= ls_component-type.
110 | lt_values = lo_element->get_ddic_fixed_values( ).
111 |
112 | READ TABLE lt_key_fields WITH KEY table_line = ls_component-name TRANSPORTING NO FIELDS.
113 | lv_key = boolc( sy-subrc = 0 ).
114 | APPEND VALUE #(
115 | name = ls_component-name
116 | key = lv_key
117 | type_kind = ls_component-type->type_kind
118 | length = ls_component-type->length
119 | fixvalues = lt_values
120 | ) TO rt_metadata.
121 | ENDLOOP.
122 |
123 | ENDMETHOD.
124 |
125 |
126 | METHOD constructor.
127 | ASSERT iv_table IS NOT INITIAL.
128 | mv_table = iv_table.
129 | ENDMETHOD.
130 |
131 |
132 | METHOD from_xstring.
133 |
134 | DATA conv TYPE REF TO object.
135 |
136 | TRY.
137 | CALL METHOD ('CL_ABAP_CONV_CODEPAGE')=>create_in
138 | RECEIVING
139 | instance = conv.
140 |
141 | CALL METHOD conv->('IF_ABAP_CONV_IN~CONVERT')
142 | EXPORTING
143 | source = xstring
144 | RECEIVING
145 | result = string.
146 | CATCH cx_sy_dyn_call_illegal_class.
147 | DATA(conv_in_class) = 'CL_ABAP_CONV_IN_CE'.
148 | CALL METHOD (conv_in_class)=>create
149 | EXPORTING
150 | encoding = 'UTF-8'
151 | RECEIVING
152 | conv = conv.
153 |
154 | CALL METHOD conv->('CONVERT')
155 | EXPORTING
156 | input = xstring
157 | IMPORTING
158 | data = string.
159 | ENDTRY.
160 |
161 | ENDMETHOD.
162 |
163 |
164 | METHOD get_html.
165 | rv_html = |\n| &&
166 | |\n| &&
167 | |
\n| &&
168 | |open-table-maintenance\n| &&
169 | |\n| &&
170 | |\n| &&
171 | |\n| &&
172 | |\n| &&
173 | |\n| &&
229 | |\n| &&
230 | |\n| &&
231 | |open-table-maintenance
\n| &&
232 | |
\n| &&
233 | |loading
\n| &&
234 | |\n| &&
235 | ||.
236 | ENDMETHOD.
237 |
238 |
239 | METHOD list_key_fields.
240 | DATA obj TYPE REF TO object.
241 | DATA lv_tabname TYPE c LENGTH 16.
242 | DATA lr_ddfields TYPE REF TO data.
243 | FIELD-SYMBOLS TYPE any.
244 | FIELD-SYMBOLS TYPE simple.
245 | FIELD-SYMBOLS TYPE ANY TABLE.
246 |
247 | * convert to correct type,
248 | lv_tabname = mv_table.
249 |
250 | TRY.
251 | CALL METHOD ('XCO_CP_ABAP_DICTIONARY')=>database_table
252 | EXPORTING
253 | iv_name = lv_tabname
254 | RECEIVING
255 | ro_database_table = obj.
256 | ASSIGN obj->('IF_XCO_DATABASE_TABLE~FIELDS->IF_XCO_DBT_FIELDS_FACTORY~KEY') TO .
257 | ASSERT sy-subrc = 0.
258 | obj = .
259 | CALL METHOD obj->('IF_XCO_DBT_FIELDS~GET_NAMES')
260 | RECEIVING
261 | rt_names = names.
262 | CATCH cx_sy_dyn_call_illegal_class.
263 | DATA(workaround) = 'DDFIELDS'.
264 | CREATE DATA lr_ddfields TYPE (workaround).
265 | ASSIGN lr_ddfields->* TO .
266 | ASSERT sy-subrc = 0.
267 | = CAST cl_abap_structdescr( cl_abap_typedescr=>describe_by_name(
268 | lv_tabname ) )->get_ddic_field_list( ).
269 | LOOP AT ASSIGNING .
270 | ASSIGN COMPONENT 'KEYFLAG' OF STRUCTURE TO .
271 | IF sy-subrc <> 0 OR <> abap_true.
272 | CONTINUE.
273 | ENDIF.
274 | ASSIGN COMPONENT 'FIELDNAME' OF STRUCTURE TO .
275 | ASSERT sy-subrc = 0.
276 | APPEND TO names.
277 | ENDLOOP.
278 | ENDTRY.
279 |
280 | ENDMETHOD.
281 |
282 |
283 | METHOD read_table.
284 |
285 | FIELD-SYMBOLS TYPE STANDARD TABLE.
286 | DATA dref TYPE REF TO data.
287 | CREATE DATA dref TYPE STANDARD TABLE OF (mv_table) WITH DEFAULT KEY.
288 | ASSIGN dref->* TO .
289 | ASSERT sy-subrc = 0.
290 |
291 | " dont check SUBRC, the table might be empty
292 | SELECT * FROM (mv_table) ORDER BY PRIMARY KEY INTO TABLE @ ##SUBRC_OK.
293 |
294 | rv_json = to_json( dref ).
295 |
296 | ENDMETHOD.
297 |
298 |
299 | METHOD save_table.
300 |
301 | FIELD-SYMBOLS TYPE STANDARD TABLE.
302 | DATA dref TYPE REF TO data.
303 | CREATE DATA dref TYPE STANDARD TABLE OF (mv_table) WITH DEFAULT KEY.
304 | ASSIGN dref->* TO .
305 | ASSERT sy-subrc = 0.
306 |
307 | CALL TRANSFORMATION id SOURCE XML iv_json RESULT data = .
308 |
309 | MODIFY (mv_table) FROM TABLE @ ##SUBRC_OK.
310 |
311 | ENDMETHOD.
312 |
313 |
314 | METHOD serve.
315 |
316 | TRY.
317 | rs_http-status = 200.
318 | IF is_request-path CP '*/rest'.
319 | IF is_request-method = 'GET'.
320 | DATA(lv_body) = read_table( ).
321 | rs_http-content_type = 'application/json'.
322 | ELSEIF is_request-method = 'POST'.
323 | save_table( from_xstring( is_request-body ) ).
324 | ELSE.
325 | ASSERT 1 = 2.
326 | ENDIF.
327 | ELSE.
328 | lv_body = get_html( ).
329 | rs_http-content_type = 'text/html'.
330 | ENDIF.
331 | rs_http-body = to_xstring( lv_body ).
332 | CATCH cx_root.
333 | rs_http-status = 500.
334 | rs_http-body = to_xstring( |Exception occurred| ).
335 | ENDTRY.
336 |
337 | ENDMETHOD.
338 |
339 |
340 | METHOD to_json.
341 |
342 | FIELD-SYMBOLS TYPE STANDARD TABLE.
343 | ASSIGN ref->* TO .
344 | ASSERT sy-subrc = 0.
345 |
346 | DATA(meta) = build_metadata( ).
347 | DATA(writer) = cl_sxml_string_writer=>create( if_sxml=>co_xt_json ).
348 | CALL TRANSFORMATION id
349 | SOURCE
350 | data =
351 | meta = meta
352 | tablename = mv_table
353 | sy = sy
354 | RESULT XML writer.
355 | rv_json = from_xstring( writer->get_output( ) ).
356 |
357 | ENDMETHOD.
358 |
359 |
360 | METHOD to_xstring.
361 |
362 | DATA conv TYPE REF TO object.
363 |
364 | TRY.
365 | CALL METHOD ('CL_ABAP_CONV_CODEPAGE')=>create_out
366 | RECEIVING
367 | instance = conv.
368 |
369 | CALL METHOD conv->('IF_ABAP_CONV_OUT~CONVERT')
370 | EXPORTING
371 | source = string
372 | RECEIVING
373 | result = xstring.
374 | CATCH cx_sy_dyn_call_illegal_class.
375 | DATA(conv_out_class) = 'CL_ABAP_CONV_OUT_CE'.
376 | CALL METHOD (conv_out_class)=>create
377 | EXPORTING
378 | encoding = 'UTF-8'
379 | RECEIVING
380 | conv = conv.
381 |
382 | CALL METHOD conv->('CONVERT')
383 | EXPORTING
384 | data = string
385 | IMPORTING
386 | buffer = xstring.
387 | ENDTRY.
388 |
389 | ENDMETHOD.
390 | ENDCLASS.
391 |
--------------------------------------------------------------------------------
/src/zcl_otm_table_maintenance.clas.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ZCL_OTM_TABLE_MAINTENANCE
7 | E
8 | open-table-maintenance
9 | 1
10 | X
11 | X
12 | X
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/test/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | web
5 |
6 |
7 | loading
8 |
9 |
10 |
--------------------------------------------------------------------------------
/test/index.mjs:
--------------------------------------------------------------------------------
1 | import express from 'express';
2 | import {initializeABAP} from "../output/init.mjs";
3 | import {cl_express_icf_shim} from "../output/cl_express_icf_shim.clas.mjs";
4 | await initializeABAP();
5 |
6 | const PORT = process.env.PORT || 3000;
7 |
8 | const app = express();
9 | app.disable('x-powered-by');
10 | app.set('etag', false);
11 | app.use(express.raw({type: "*/*"}));
12 |
13 | // ------------------
14 |
15 | app.get('/', function (req, res) {
16 | res.send('path: /');
17 | });
18 |
19 | // ------------------
20 |
21 | app.all(["/abap", "/abap*"], async function (req, res) {
22 | await cl_express_icf_shim.run({req, res, class: "ZCL_HTTP_HANDLER"});
23 | });
24 |
25 | app.listen(PORT);
26 | console.log("Listening on port http://localhost:" + PORT + "/abap");
27 |
--------------------------------------------------------------------------------
/test/playwright.spec.mjs:
--------------------------------------------------------------------------------
1 | import { test, expect } from '@playwright/test';
2 |
3 | test('test', async ({ page }) => {
4 | await page.goto('http://localhost:3000/abap');
5 |
6 | await page.dblclick('text=1_ >> :nth-match(td, 3)');
7 | await page.fill('input', 'foo');
8 | await page.press('input', 'Enter', {delay: 100});
9 |
10 | await page.dblclick('tr:nth-child(2) td:nth-child(3)');
11 | await page.fill('input', 'bar');
12 |
13 | await page.dblclick('tr:nth-child(2) td:nth-child(2)');
14 | await page.fill('input', 'sdf');
15 |
16 | await page.click('text=Save', {delay: 100});
17 | page.once('dialog', dialog => {
18 | console.log(`Dialog message: ${dialog.message()}`);
19 | dialog.dismiss().catch(() => {});
20 | });
21 |
22 | await page.goto('http://localhost:3000/abap', {waitUntil: 'domcontentloaded'});
23 | await page.waitForTimeout(100);
24 | expect(page.locator('tr:nth-child(2) td:nth-child(3)')).toHaveText('bar');
25 |
26 | await page.screenshot({ path: 'screenshot.png' });
27 | });
28 |
--------------------------------------------------------------------------------
/test/setup.mjs:
--------------------------------------------------------------------------------
1 | import {SQLiteDatabaseClient} from "@abaplint/database-sqlite";
2 |
3 | export async function setup(abap, schemas, insert) {
4 | abap.context.databaseConnections["DEFAULT"] = new SQLiteDatabaseClient();
5 | await abap.context.databaseConnections["DEFAULT"].connect();
6 | await abap.context.databaseConnections["DEFAULT"].execute(schemas.sqlite);
7 | await abap.context.databaseConnections["DEFAULT"].execute(insert);
8 | }
--------------------------------------------------------------------------------
/test/web.mjs:
--------------------------------------------------------------------------------
1 | import {initializeABAP} from "../output/_init.mjs";
2 | await initializeABAP();
3 |
4 | async function redirectFetch(url, options) {
5 | let data = "";
6 |
7 | let res = {
8 | append: (d) => {
9 | console.dir("append2: " + d); },
10 | send: (d) => {
11 | console.dir("send2");
12 | data = Buffer.from(d).toString();
13 | },
14 | status: (status) => {
15 | console.dir("status2: " + status);
16 | return res; },
17 | }
18 |
19 | const method = options?.method || "GET";
20 | const body = options?.body || "";
21 |
22 | const req = {
23 | body: Buffer.from(body).toString("hex"),
24 | method: method,
25 | path: url,
26 | url: url,
27 | };
28 | console.dir(req);
29 | await abap.Classes["CL_EXPRESS_ICF_SHIM"].run({req: req, res, class: "ZCL_HTTP_HANDLER"})
30 | console.log("redirectFetch RESPONSE,");
31 | console.dir(data);
32 | return { json: async () => JSON.parse(data)};
33 | }
34 |
35 | async function run() {
36 | let res = {
37 | append: (data) => {
38 | console.dir("append: " + data); },
39 | send: (data) => {
40 | console.dir("send");
41 | let r = Buffer.from(data).toString();
42 |
43 | // document.write() doesnt work when loaded from async script
44 | document.documentElement.innerHTML = r;
45 |
46 | // and setting innerHTML does not automatically load/initialize the scripts
47 | const scripts = Array.from(document.getElementsByTagName("script"));
48 | {
49 | var myScript = document.createElement('script');
50 | myScript.src = scripts[0].src;
51 | document.head.appendChild(myScript);
52 | }
53 | {
54 | var myScript = document.createElement('script');
55 | myScript.src = scripts[1].src;
56 | document.head.appendChild(myScript);
57 | }
58 | {
59 | var myScript = document.createElement('script');
60 | myScript.textContent = scripts[2].textContent;
61 | document.head.appendChild(myScript);
62 | }
63 |
64 | globalThis.fetch = redirectFetch;
65 |
66 | setTimeout(() => {
67 | console.dir("dispatch load");
68 | window.dispatchEvent(new Event("load"));
69 | }, 1000);
70 | },
71 | status: (status) => {
72 | console.dir("status: " + status);
73 | return res; },
74 | }
75 |
76 | await abap.Classes["CL_EXPRESS_ICF_SHIM"].run({req: {body: "", method: "GET", path: "", url: ""}, res, class: "ZCL_HTTP_HANDLER"});
77 | }
78 |
79 | run();
80 |
--------------------------------------------------------------------------------
/test/zcl_http_handler.clas.abap:
--------------------------------------------------------------------------------
1 | CLASS zcl_http_handler DEFINITION PUBLIC.
2 | PUBLIC SECTION.
3 | INTERFACES if_http_extension.
4 | ENDCLASS.
5 |
6 | CLASS zcl_http_handler IMPLEMENTATION.
7 |
8 | METHOD if_http_extension~handle_request.
9 |
10 | DATA(result) = NEW zcl_otm_table_maintenance( 'ZOPENTEST' )->serve( VALUE #(
11 | method = server->request->get_method( )
12 | path = server->request->get_header_field( '~path' )
13 | body = server->request->get_data( ) ) ).
14 |
15 | server->response->set_data( result-body ).
16 | server->response->set_content_type( result-content_type ).
17 | server->response->set_status(
18 | code = result-status
19 | reason = CONV #( result-status ) ).
20 |
21 | ENDMETHOD.
22 |
23 | ENDCLASS.
24 |
--------------------------------------------------------------------------------
/test/zcl_otm_table_maintenance.clas.testclasses.abap:
--------------------------------------------------------------------------------
1 | CLASS ltcl_test DEFINITION FOR TESTING RISK LEVEL HARMLESS DURATION SHORT FINAL.
2 |
3 | PRIVATE SECTION.
4 | METHODS list_key_fields FOR TESTING RAISING cx_static_check.
5 | METHODS build_metadata FOR TESTING RAISING cx_static_check.
6 |
7 | ENDCLASS.
8 |
9 | CLASS ltcl_test IMPLEMENTATION.
10 |
11 | METHOD list_key_fields.
12 |
13 | DATA(fields) = NEW zcl_otm_table_maintenance( 'ZOPENTEST' )->list_key_fields( ).
14 |
15 | cl_abap_unit_assert=>assert_equals(
16 | act = lines( fields )
17 | exp = 1 ).
18 |
19 | ENDMETHOD.
20 |
21 | METHOD build_metadata.
22 |
23 | DATA(fields) = NEW zcl_otm_table_maintenance( 'ZOPENTEST' )->build_metadata( ).
24 |
25 | cl_abap_unit_assert=>assert_equals(
26 | act = lines( fields )
27 | exp = 3 ).
28 |
29 | ENDMETHOD.
30 |
31 | ENDCLASS.
32 |
--------------------------------------------------------------------------------
/test/zopentest.tabl.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ZOPENTEST
7 | E
8 | TRANSP
9 | ZOPENTEST
10 | X
11 | A
12 | 1
13 |
14 |
15 | ZOPENTEST
16 | A
17 | 0
18 | APPL1
19 | N
20 |
21 |
22 |
23 | KEYFIELD
24 | X
25 | 0
26 | C
27 | 000008
28 | X
29 | CHAR
30 | 000004
31 | CHAR
32 |
33 |
34 | VALUEFIELD
35 | 0
36 | C
37 | 000020
38 | CHAR
39 | 000010
40 | CHAR
41 |
42 |
43 | BOOLEAN
44 | ABAP_BOOLEAN
45 | 0
46 | X
47 | F
48 | E
49 |
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-require-imports */
2 | /* eslint-disable @typescript-eslint/no-var-requires */
3 | const path = require("path");
4 | const HtmlWebpackPlugin = require("html-webpack-plugin");
5 | const CopyPlugin = require("copy-webpack-plugin");
6 | const webpack = require("webpack");
7 |
8 | module.exports = ({mode} = {mode: "development"}) => ({
9 | entry: {
10 | "app": "./test/web.mjs",
11 | },
12 | mode,
13 | devtool: "nosources-source-map",
14 | experiments: {
15 | topLevelAwait: true
16 | },
17 | output: {
18 | path: path.join(__dirname, "build"),
19 | filename: "[name].bundle.js",
20 | globalObject: "self",
21 | clean: true,
22 | },
23 | devServer: {
24 | open: true,
25 | hot: true,
26 | },
27 | resolve: {
28 | fallback: {
29 | "./%23ui2%23cl_json.clas.mjs": false,
30 | "crypto": require.resolve("crypto-browserify"),
31 | "path": require.resolve("path-browserify"),
32 | "buffer": require.resolve("buffer/"),
33 | "util/types": false,
34 | "util": require.resolve("web-encoding"),
35 | "zlib": false,
36 | "stream": false,
37 | "process": false,
38 | "http": false,
39 | "url": false,
40 | "fs": false,
41 | "tls": false,
42 | "https": false,
43 | "net": false,
44 | },
45 | extensions: [".mjs", ".js"],
46 | },
47 | module: {
48 | rules: [
49 | ]
50 | },
51 | plugins: [
52 | new HtmlWebpackPlugin({
53 | template: "test/index.html",
54 | }),
55 | new CopyPlugin({
56 | patterns: [
57 | { from: './node_modules/sql.js/dist/sql-wasm.wasm', to: "./" },
58 | { from: './node_modules/sql.js/dist/sql-wasm-debug.wasm', to: "./" },
59 | { from: './node_modules/sql.js/dist/sql-wasm-debug.js', to: "./" },
60 | ],
61 | }),
62 | new webpack.ProvidePlugin({
63 | process: 'process/browser',
64 | Buffer: ['buffer', 'Buffer'],
65 | }),
66 | ],
67 | });
68 |
--------------------------------------------------------------------------------