diff --git a/docsite/.gitignore b/docsite/.gitignore new file mode 100644 index 0000000..b2d6de3 --- /dev/null +++ b/docsite/.gitignore @@ -0,0 +1,20 @@ +# Dependencies +/node_modules + +# Production +/build + +# Generated files +.docusaurus +.cache-loader + +# Misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/docsite/README.md b/docsite/README.md new file mode 100644 index 0000000..728fade --- /dev/null +++ b/docsite/README.md @@ -0,0 +1,52 @@ +# Website + +This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator. + +## Installation + +```sh +npm install +``` + +## Local Development + +```sh +npm start +``` + +This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. + +## Build + +```sh +npm build +``` + +This command generates static content into the `build` directory and can be served using any static contents hosting service. + +## Deployment + +Using SSH: + +```sh +USE_SSH=true npm deploy +``` + +Not using SSH: + +```sh +GIT_USER= npm deploy +``` + +If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. + +## Notes + +- `npm install --save @docusaurus/plugin-client-redirects` to support redirect from `/docs/` to `/docs/intro` +- redirect gh-actions to master +- logo works in wip +- prism `additionalLanguages: ['ABAP']` +- add google analytics +- if blog link is above `truncate` is must be complete (start with `/`), otherwise will be broken on tags page +- change Repo/Settings/Pages/Source to Github actions (otherwise does not deploy from GA) - after that the pages are based on the GA artifact not the `gh-pages` branch! (TODO: maybe check how to deploy to a branch ... but maybe not) +- Algolia alternatives: https://typesense.org/docs/overview/comparison-with-alternatives.html#typesense-vs-algolia diff --git a/docsite/blog/2022-07-23 - json alternative to maintenance views in abap/index.md b/docsite/blog/2022-07-23 - json alternative to maintenance views in abap/index.md new file mode 100644 index 0000000..47b6804 --- /dev/null +++ b/docsite/blog/2022-07-23 - json alternative to maintenance views in abap/index.md @@ -0,0 +1,69 @@ +--- +slug: json-alternative-to-maintenance-views-in-abap +title: JSON alternative to maintenance views in Abap +authors: sbcgua +tags: [old] +--- + +What do you do if you need to add configuration possibilities to your program? In most cases, you will probably create a DB table for those settings and a maintenance view for it. Maintenance views is a quite powerful and relatively simple tool. Yet they are not without downsides and complexity when your configuration has a hierarchical structure or/and when the structure is not yet final and changes frequently e.g. when you're developing a new product. How to achieve the flexibility you need in such a case? + + + +One of the modern development patterns is to use JSON for configuration. Indeed, it is very flexible, not constrained. Any part of JSON can be easily addressed. Why not try using JSON in ABAP? I and my team are developing a tool for electronic document exchange services integration (like Docusign, SharePoint, and some others), the product is in active development and the configuration must stay flexible. Maybe it will be converted to maintenance views in the later stages, but for now, we need the flexibility. We don't want constant modifications of data dictionary and maintenance view regeneration to stop our pace. Here is what we came up with. + +As storage for settings, we have a flat table with 3 fields. + +![structure sample](./structure-sample.png) + +Table structure + +- 2 char keys - we used 2 for potential substructuring, but ... maybe it is an over-engineering. For now, we don't use the 2nd key, and probably 1 level is enough since the underlying JSON has a tree-like nature. +- The last table field is a string for JSON content. + +That's all you need for a config of any complexity. + +From the user perspective, we wrote a simple program, that shows the key-list in ALV and then opens the underlying JSON in a `cl_gui_textedit`. Nothing complex, I'm considering to open-source it, if there is an interest in it (though really no huge development behind this). + +![settings sample](./settings-sample.png) + +List of settings + +![settings sample detailed](./settings-sample-detailed.png) + +Example of json config + +As you can see the content is freely editable JSON text. + +Finally, accessing the config parameters is done with a help of [ajson package](https://github.com/sbcgua/ajson). I wrote about it before - [AJSON – yet another abap json parser and serializer](/blog/ajson-yet-another-abap-json-parser-and-serializer/). Since the original blog post, the library has matured and we use it in several productive tools which exchange the data with external APIs and web services. The tool is designed to be convenient for a developer, so the accessing code looks, for example, as follows: + +```abap +data lo_json_settings type ref to zif_ajson. +data lv_printer_name type string. + +lo_json_settings = zcl_ede_settings_factory=>get_json( 'VCHASNO' ). +lv_printer_name = lo_json_settings->get( + |/overridePrinterDefaults/{ cs_nast-kappl }/{ cs_nast-kschl }| ). +... +``` + +Where `zcl_ede_settings_factory` is a simple abstraction wrapper to read JSON blob from the table and parse it with ajson, the rest is purely ajson functionality. + +Ajson is not just capable of reading simple values of course but also can read structures, tables, filter notes, etc. I won't go into details here, just mention that it was designed with the developer's convenience and typical JSON access patterns in mind. Please refer to the mentioned blog post above or to the library [documentation at Github](https://github.com/sbcgua/ajson). + +Thus we've got a very flexible and easily maintainable configuration. Adding a new parameter takes minimal time and effort until the configuration shape is fully stable. No DDIC modifications, no broken maintenance views. Isn't it a paradise? Well, nothing perfect in the world ... 🙂 + +Pitfalls + +... it is not without downsides to be aware of. Here is the list from the top of my head: + +1) Validation. Maintenance views imply typical checks like record duplicates, domain values validation, and all this discipline stuff out of the box. In JSON, you have to write extra code for validation. Or implement something like JSON Schema (which I have in plans, though a bit lack of time recently). At the same time, the necessity of validation depends on the task. In our tools, we didn't face serious issues (unexpectedly to me), because of good code structure and later implicit checks of the selected and processed data. Well, anyway, validation is important. + +2) Editability. `cl_gui_textedit` doesn't know anything about JSON and has no idea about syntax highlighting, tabulation, and stuff like that. Either it doesn't show domain values or search helps. Personally, I don't think this is a huge issue, configuration is done by technical people, it is their job to know, properly configure and test programs. Yet ... certainly far from ideal. + +3) Self Documentation. In my opinion, this is perhaps the biggest drawback. When you open a structured interface like a maintenance view or clustered view, you kind of see the full picture, you won't forget to fill a tiny setting in the corner, you can F1 to read the doc about a particular parameter. Not sure how to solve this one in the long run, except with good external documentation. Maybe some fiory-based editor? + +Anyway, the benefits of JSON are heavier than the drawbacks for our particular case, so I consider this approach a success for our current project. + +I hope this was interesting for you. Tell me in the comments what do you think about it? Is it applicable to your projects? Are there any undescribed advantages and disadvantages? Should SAP built-in something like this in standard? 😉 + +P.S. _Originally posted at [SAP Community platform](https://community.sap.com/t5/application-development-and-automation-blog-posts/json-alternative-to-maintenance-views-in-abap/ba-p/13528908) on 2022-Jul-23._ diff --git a/docsite/blog/2022-07-23 - json alternative to maintenance views in abap/settings-sample-detailed.png b/docsite/blog/2022-07-23 - json alternative to maintenance views in abap/settings-sample-detailed.png new file mode 100644 index 0000000..8813a5a Binary files /dev/null and b/docsite/blog/2022-07-23 - json alternative to maintenance views in abap/settings-sample-detailed.png differ diff --git a/docsite/blog/2022-07-23 - json alternative to maintenance views in abap/settings-sample.png b/docsite/blog/2022-07-23 - json alternative to maintenance views in abap/settings-sample.png new file mode 100644 index 0000000..10e31b9 Binary files /dev/null and b/docsite/blog/2022-07-23 - json alternative to maintenance views in abap/settings-sample.png differ diff --git a/docsite/blog/2022-07-23 - json alternative to maintenance views in abap/structure-sample.png b/docsite/blog/2022-07-23 - json alternative to maintenance views in abap/structure-sample.png new file mode 100644 index 0000000..cde45be Binary files /dev/null and b/docsite/blog/2022-07-23 - json alternative to maintenance views in abap/structure-sample.png differ diff --git a/docsite/blog/authors.yml b/docsite/blog/authors.yml new file mode 100644 index 0000000..2051301 --- /dev/null +++ b/docsite/blog/authors.yml @@ -0,0 +1,8 @@ +sbcgua: + name: Alexander Tsybulsky + title: SAP Consultant, developer, sbcg.com.ua CEO + url: https://github.com/sbcgua + image_url: https://github.com/sbcgua.png + socials: + linkedin: atsybulsky + github: sbcgua diff --git a/docsite/blog/tags.yml b/docsite/blog/tags.yml new file mode 100644 index 0000000..e87bbfa --- /dev/null +++ b/docsite/blog/tags.yml @@ -0,0 +1,4 @@ +old: + label: old + permalink: /old + description: Old posts (SAP Community) diff --git a/docsite/docs/10-intro.md b/docsite/docs/10-intro.md new file mode 100644 index 0000000..287e4b5 --- /dev/null +++ b/docsite/docs/10-intro.md @@ -0,0 +1,49 @@ +--- +sidebar_position: 10 +--- + +# Introduction + +AJson is a JSON parser/serializer for ABAP designed with the convenience for developer in mind. It works with ABAP release 7.02 or higher. + +## Features + +- Parse JSON content into a flexible form, not fixed to any predefined data structure, allowing to modify the parsed data, selectively access its parts and slice subsections of it + - slicing can be particularly useful for REST header separation e.g. `{ "success": 1, "error": "", "payload": {...} }` where 1st level attrs are processed in one layer of your application and payload in another (and can differ from request to request) +- Allows conversion to fixed ABAP structures/tables (`to_abap`) +- Convenient interface to manipulate the data - `set( value )`, `set( structure )`, `set( table )`, `set( another_instance_of_ajson )`, also typed e.g. `set_date` + - also `setx` for text-based value setting like `setx( '/a/b:123' )` (useful e.g. for constants in APIs or in unit-tests) +- Seralization to string +- Freezing (read only) instance content +- Filtering - create a JSON skipping empty values, predefined paths, or your custom filter. +- Mapping - rule-based changing node names (e.g. snake case to camel case, upper/lower case) +- Iterating (conveniently) through the array items or object members +- Utility to calculate difference between 2 JSONs +- Supports: timestamps +- Supports: data reference initialization + +## Example of usage + +```abap +data r type ref to zif_ajson. +data fragment type ref to zif_ajson. + +r = zcl_ajson=>parse( '{"success": 1, "error": "", "payload": {"text": "hello"}}' ). + +r->get( '/success' ). " returns "1" +r->get_integer( '/success' ). " returns 1 (number) +r->get_boolean( '/success' ). " returns "X" (abap_true - because not empty) +r->get( '/payload/text' ). " returns "hello" + +r->members( '/' ). " returns table of "success", "error", "payload" + +fragment = r->slice( '/payload' ). +fragment->get( '/text' ). " returns "hello" (the root has changed) + +fragment->set( + iv_path = '/text' + iv_val = 'new text' ). +fragment->stringify( ). " {"text":"new text"} +``` + +See more examples and usages in the further docs. diff --git a/docsite/docusaurus.config.js b/docsite/docusaurus.config.js new file mode 100644 index 0000000..a2d0ddb --- /dev/null +++ b/docsite/docusaurus.config.js @@ -0,0 +1,178 @@ +// @ts-check +// See: https://docusaurus.io/docs/api/docusaurus-config + +import {themes as prismThemes} from 'prism-react-renderer'; + +// This runs in Node.js - Don't use client-side code here (browser APIs, JSX...) + +const githubUrl = 'https://github.com'; +const organizationName = 'sbcgua'; +const projectName = 'mockup_loader'; +const repoUrl = `${githubUrl}/${organizationName}/${projectName}` + +const linkedinHref = (/** @type {string} */ name) => ` + + + + + `; // reconsider this hardcoded HTML, see also @docusaurus\theme-classic\lib\theme\Icon\Socials\LinkedIn\index.js +const myLinkedin = linkedinHref('https://www.linkedin.com/in/atsybulsky/'); + +/** @type {import('@docusaurus/types').Config} */ +const config = { + + future: { + v4: true, + experimental_faster: true, + }, + + title: 'Mockup Loader', + tagline: 'Mockup loader - the right tool to simplify data preparation for SAP ABAP unit tests', + favicon: 'img/favicon.ico', + + // Site URLs + url: githubUrl, // Set the production url of the site here + baseUrl: `/${projectName}/`, // Set the // pathname under which your site is served, @github usually '//' + + // GitHub pages deployment config + organizationName, // GitHub org/user name + projectName, // Repo name + + onBrokenLinks: 'throw', + + markdown: { + hooks: { + onBrokenMarkdownLinks: 'warn', + }, + }, + + i18n: { + defaultLocale: 'en', + locales: ['en'], + }, + + presets: [ + [ + 'classic', + /** @type {import('@docusaurus/preset-classic').Options} */ + { + docs: { + sidebarPath: './sidebars.js', + }, + blog: { + showReadingTime: true, + feedOptions: { + type: ['rss', 'atom'], + xslt: true, + }, + // Useful options to enforce blogging best practices + onInlineTags: 'warn', + onInlineAuthors: 'warn', + onUntruncatedBlogPosts: 'warn', + }, + theme: { + customCss: './src/css/custom.css', + }, + gtag: { + trackingID: 'G-0QEBYGR127', + anonymizeIP: true, + }, + }, + ], + ], + + themeConfig: + /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ + { + image: 'img/logo-128.png', + navbar: { + title: 'Mockup loader', + logo: { + alt: 'Mockup loader Logo', + src: 'img/logo.svg', + }, + items: [ + { + type: 'docSidebar', + sidebarId: 'tutorialSidebar', + position: 'left', + label: 'Documentation', + }, + { + to: '/blog', + label: 'Blog', + position: 'left' + }, + { + href: repoUrl, + label: 'GitHub', + position: 'right', + }, + ], + }, + footer: { + style: 'dark', + links: [ + { + label: 'Documentation', + to: '/docs/intro', + }, + { + label: 'Blog', + to: '/blog', + }, + { + label: 'GitHub', + href: repoUrl, + }, + ], + copyright: `Copyright © ${new Date().getFullYear()} Alexander Tsybulsky ${myLinkedin} (aka sbcgua), Built with Docusaurus.`, + }, + prism: { + theme: prismThemes.github, + darkTheme: prismThemes.dracula, + additionalLanguages: ['abap'], + }, + algolia: { + appId: 'JYRGWOZND5', + apiKey: '7f7a2954f42dc2446a9c7bf3c7f305af', + indexName: 'sbcgua-mockup-loader', + contextualSearch: false, // No versions and languages so far + + // Optional: Specify domains where the navigation should occur through window.location instead on history.push. Useful when our Algolia config crawls multiple documentation sites and we want to navigate with window.location.href to them. + // externalUrlRegex: 'external\\.com|domain\\.com', + // Optional: Replace parts of the item URLs from Algolia. Useful when using the same search index for multiple deployments using a different baseUrl. You can use regexp or string in the `from` param. For example: localhost:3000 vs myCompany.com/docs + // replaceSearchResultPathname: { + // from: '/docs/', // or as RegExp: /\/docs\// + // to: '/', + // }, + + // Optional: Algolia search parameters + // searchParameters: {}, + + // Optional: path for search page that enabled by default (`false` to disable it) + searchPagePath: 'search', + // Optional: whether the insights feature is enabled or not on Docsearch (`false` by default) + insights: false, + }, + }, + + plugins: [ + [ + '@docusaurus/plugin-client-redirects', + { + redirects: [ + { + from: ['/docs'], + to: '/docs/intro', + }, + ], + }, + ], + ], +}; + +export default config; diff --git a/docsite/package.json b/docsite/package.json new file mode 100644 index 0000000..2037267 --- /dev/null +++ b/docsite/package.json @@ -0,0 +1,47 @@ +{ + "name": "docsite-mockup-loader", + "title": "Mockup Loader Documentation", + "version": "0.0.1", + "private": true, + "scripts": { + "docusaurus": "docusaurus", + "start": "docusaurus start", + "build": "docusaurus build", + "swizzle": "docusaurus swizzle", + "deploy": "docusaurus deploy", + "clear": "docusaurus clear", + "serve": "docusaurus serve", + "write-translations": "docusaurus write-translations", + "write-heading-ids": "docusaurus write-heading-ids" + }, + "dependencies": { + "@docusaurus/core": "3.9.1", + "@docusaurus/faster": "^3.9.1", + "@docusaurus/plugin-client-redirects": "^3.9.1", + "@docusaurus/preset-classic": "3.9.1", + "@mdx-js/react": "^3.1.1", + "clsx": "^2.1.1", + "prism-react-renderer": "^2.4.1", + "react": "^19.1.1", + "react-dom": "^19.1.1" + }, + "devDependencies": { + "@docusaurus/module-type-aliases": "3.9.1", + "@docusaurus/types": "3.9.1" + }, + "browserslist": { + "production": [ + ">0.5%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 3 chrome version", + "last 3 firefox version", + "last 5 safari version" + ] + }, + "engines": { + "node": ">=18.0" + } +} diff --git a/docsite/sidebars.js b/docsite/sidebars.js new file mode 100644 index 0000000..ff66d5e --- /dev/null +++ b/docsite/sidebars.js @@ -0,0 +1,12 @@ +// @ts-check + +// This runs in Node.js - Don't use client-side code here (browser APIs, JSX...) + +/** + @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} + */ +const sidebars = { + tutorialSidebar: [{type: 'autogenerated', dirName: '.'}], +}; + +export default sidebars; diff --git a/docsite/src/components/feature-list.md b/docsite/src/components/feature-list.md new file mode 100644 index 0000000..12c2c62 --- /dev/null +++ b/docsite/src/components/feature-list.md @@ -0,0 +1,23 @@ +# Features + +- Parse JSON content into a flexible form, not fixed to any predefined data structure, allowing to modify the parsed data, selectively access its parts and slice subsections of it. +- Allows conversion to fixed ABAP structures/tables (`to_abap`) - strict or "corresponding". +- Supports timestamps +- Supports reference instantiation +- Supports checking node types +- Convenient interface to manipulate the data: + - `set( value )` + - `set( structure )` + control over item order (sorted or historical) + - `set( table )` + - `push` to an array item-per-item + - `set( another_instance_of_ajson )` + - also typed e.g. `set_date`, `set_number` + - also `setx` for text-based value setting e.g. `setx( '/a/b:123' )` (useful e.g. for constants in APIs or in unit-tests) + - node deletion +- Seralization to string (indented and not). +- Freezing (read only) instance content. +- Filtering - create a JSON skipping empty values, predefined paths, or your custom filter. +- Mapping - rule-based changing node names (e.g. snake case to camel case, upper/lower case) +- Iterating (conveniently) through the array items or object members +- Utility to calculate difference between 2 JSONs +- See **[documentation](./docs/intro)** for more details ... diff --git a/docsite/src/css/custom.css b/docsite/src/css/custom.css new file mode 100644 index 0000000..2bc6a4c --- /dev/null +++ b/docsite/src/css/custom.css @@ -0,0 +1,30 @@ +/** + * Any CSS included here will be global. The classic template + * bundles Infima by default. Infima is a CSS framework designed to + * work well for content-centric websites. + */ + +/* You can override the default Infima variables here. */ +:root { + --ifm-color-primary: #2e8555; + --ifm-color-primary-dark: #29784c; + --ifm-color-primary-darker: #277148; + --ifm-color-primary-darkest: #205d3b; + --ifm-color-primary-light: #33925d; + --ifm-color-primary-lighter: #359962; + --ifm-color-primary-lightest: #3cad6e; + --ifm-code-font-size: 95%; + --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); +} + +/* For readability concerns, you should choose a lighter palette in dark mode. */ +[data-theme='dark'] { + --ifm-color-primary: #25c2a0; + --ifm-color-primary-dark: #21af90; + --ifm-color-primary-darker: #1fa588; + --ifm-color-primary-darkest: #1a8870; + --ifm-color-primary-light: #29d5b0; + --ifm-color-primary-lighter: #32d8b4; + --ifm-color-primary-lightest: #4fddbf; + --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); +} diff --git a/docsite/src/pages/index.js b/docsite/src/pages/index.js new file mode 100644 index 0000000..28fb903 --- /dev/null +++ b/docsite/src/pages/index.js @@ -0,0 +1,47 @@ +import clsx from 'clsx'; +import Link from '@docusaurus/Link'; +import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; +import Layout from '@theme/Layout'; + +import Heading from '@theme/Heading'; +import styles from './index.module.css'; +import Stuff from '@site/src/components/feature-list.md'; + +function HomepageHeader() { + const {siteConfig} = useDocusaurusContext(); + return ( +
+
+ + {siteConfig.title} + +

{siteConfig.tagline}

+
+ + See documentation + +
+
+
+ ); +} + +export default function Home() { + const {siteConfig} = useDocusaurusContext(); + return ( + + +
+
+
+ +
+
+
+
+ ); +} diff --git a/docsite/src/pages/index.module.css b/docsite/src/pages/index.module.css new file mode 100644 index 0000000..9f71a5d --- /dev/null +++ b/docsite/src/pages/index.module.css @@ -0,0 +1,23 @@ +/** + * CSS files with the .module.css suffix will be treated as CSS modules + * and scoped locally. + */ + +.heroBanner { + padding: 4rem 0; + text-align: center; + position: relative; + overflow: hidden; +} + +@media screen and (max-width: 996px) { + .heroBanner { + padding: 2rem; + } +} + +.buttons { + display: flex; + align-items: center; + justify-content: center; +} diff --git a/docsite/static/.nojekyll b/docsite/static/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/docsite/static/img/favicon.ico b/docsite/static/img/favicon.ico new file mode 100644 index 0000000..a1448e2 Binary files /dev/null and b/docsite/static/img/favicon.ico differ diff --git a/docsite/static/img/logo-128.png b/docsite/static/img/logo-128.png new file mode 100644 index 0000000..96cacda Binary files /dev/null and b/docsite/static/img/logo-128.png differ diff --git a/docsite/static/img/logo.svg b/docsite/static/img/logo.svg new file mode 100644 index 0000000..0c228a9 --- /dev/null +++ b/docsite/static/img/logo.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/docsite/wip/icons.svg b/docsite/wip/icons.svg new file mode 100644 index 0000000..f2226c2 --- /dev/null +++ b/docsite/wip/icons.svg @@ -0,0 +1,147 @@ + + + + + + + + + + + + AJson + Abap Json + AJaj + + + + + + + + + + + + + + diff --git a/docsite/wip/logo-128.png b/docsite/wip/logo-128.png new file mode 100644 index 0000000..96cacda Binary files /dev/null and b/docsite/wip/logo-128.png differ diff --git a/docsite/wip/logo-16.png b/docsite/wip/logo-16.png new file mode 100644 index 0000000..a0a96e7 Binary files /dev/null and b/docsite/wip/logo-16.png differ diff --git a/docsite/wip/logo-32.png b/docsite/wip/logo-32.png new file mode 100644 index 0000000..b2f95a2 Binary files /dev/null and b/docsite/wip/logo-32.png differ diff --git a/docsite/wip/logo-64.png b/docsite/wip/logo-64.png new file mode 100644 index 0000000..8ad5ae7 Binary files /dev/null and b/docsite/wip/logo-64.png differ diff --git a/docsite/wip/logo-opt.svg b/docsite/wip/logo-opt.svg new file mode 100644 index 0000000..0c228a9 --- /dev/null +++ b/docsite/wip/logo-opt.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/docsite/wip/logo.svg b/docsite/wip/logo.svg new file mode 100644 index 0000000..6823c69 --- /dev/null +++ b/docsite/wip/logo.svg @@ -0,0 +1,54 @@ + + + + + + + + + + + + +