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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions docsite/.gitignore
Original file line number Diff line number Diff line change
@@ -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*
52 changes: 52 additions & 0 deletions docsite/README.md
Original file line number Diff line number Diff line change
@@ -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=<Your GitHub username> 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
Original file line number Diff line number Diff line change
@@ -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?

<!-- truncate -->

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._
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions docsite/blog/authors.yml
Original file line number Diff line number Diff line change
@@ -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
4 changes: 4 additions & 0 deletions docsite/blog/tags.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
old:
label: old
permalink: /old
description: Old posts (SAP Community)
49 changes: 49 additions & 0 deletions docsite/docs/10-intro.md
Original file line number Diff line number Diff line change
@@ -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.
178 changes: 178 additions & 0 deletions docsite/docusaurus.config.js
Original file line number Diff line number Diff line change
@@ -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) => `<a href="${name}" target="_blank" rel="noopener noreferrer" title="LinkedIn" style="vertical-align: middle; text-decoration: none;">
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" preserveAspectRatio="xMidYMid" viewBox="0 0 256 256" class="linkedinSvg_FCgI" style="--dark: #0a66c2; --light: #ffffffe6;">
<path d="M218.123 218.127h-37.931v-59.403c0-14.165-.253-32.4-19.728-32.4-19.756 0-22.779 15.434-22.779 31.369v60.43h-37.93V95.967h36.413v16.694h.51a39.907
39.907 0 0 1 35.928-19.733c38.445 0 45.533 25.288 45.533 58.186l-.016 67.013ZM56.955 79.27c-12.157.002-22.014-9.852-22.016-22.009-.002-12.157 9.851-22.014
22.008-22.016 12.157-.003 22.014 9.851 22.016 22.008A22.013 22.013 0 0 1 56.955 79.27m18.966 138.858H37.95V95.967h37.97v122.16ZM237.033.018H18.89C8.58-.098.125
8.161-.001 18.471v219.053c.122 10.315 8.576 18.582 18.89 18.474h218.144c10.336.128 18.823-8.139 18.966-18.474V18.454c-.147-10.33-8.635-18.588-18.966-18.453">
</path>
</svg>
</a>`; // 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 /<baseUrl>/ pathname under which your site is served, @github usually '/<projectName>/'

// 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;
Loading
Loading