silentbalanceyh/vertx-zero

View on GitHub
vertx-pin/zero-ui/src/main/java/cn/vertxup/ui/service/FieldService.java

Summary

Maintainability
A
2 hrs
Test Coverage
package cn.vertxup.ui.service;

import cn.vertxup.ui.domain.tables.daos.UiFieldDao;
import cn.vertxup.ui.domain.tables.pojos.UiField;
import io.horizon.uca.log.Annal;
import io.vertx.core.Future;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.mod.ui.cv.em.RowType;
import io.vertx.up.eon.KName;
import io.vertx.up.fn.Fn;
import io.vertx.up.unity.Ux;
import io.vertx.up.util.Ut;

import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Collectors;

import static io.vertx.mod.ui.refine.Ui.LOG;

public class FieldService implements FieldStub {
    private static final Annal LOGGER = Annal.get(FieldService.class);

    @Override
    public Future<JsonArray> fetchUi(final String formId) {
        return Ux.Jooq.on(UiFieldDao.class)
            .<UiField>fetchAsync(KName.Ui.CONTROL_ID, formId)
            .compose(ui -> {
                if (Objects.isNull(ui) || ui.isEmpty()) {
                    LOG.Ui.warn(LOGGER, " Field not configured.");
                    return Ux.future(new JsonArray());
                } else {
                    final JsonArray uiJson = Ut.serializeJson(ui);
                    return this.attachConfig(uiJson);
                }
            });
    }

    @Override
    public Future<JsonArray> updateA(final String controlId, final JsonArray data) {
        final ConcurrentMap<Object, Boolean> seen = new ConcurrentHashMap<>();
        // 1. mountIn fields, convert those into object from string
        final List<UiField> fields = Ut.itJArray(data)
            // filter(deduplicate) by name
            .filter(item -> (!item.containsKey(KName.NAME)) ||
                (Ut.isNotNil(item.getString(KName.NAME)) && null == seen.putIfAbsent(item.getString(KName.NAME), Boolean.TRUE)))
            .map(item -> Ut.valueToString(item,
                FieldStub.OPTION_JSX,
                FieldStub.OPTION_CONFIG,
                FieldStub.OPTION_ITEM,
                FieldStub.RULES,
                KName.METADATA
            ))
            .map(field -> field.put(KName.Ui.CONTROL_ID, Optional.ofNullable(field.getString(KName.Ui.CONTROL_ID)).orElse(controlId)))
            .map(field -> Ux.fromJson(field, UiField.class))
            .collect(Collectors.toList());
        // 2. delete old ones and insert new ones
        return this.deleteByControlId(controlId)
            .compose(result -> Ux.Jooq.on(UiFieldDao.class)
                .insertAsync(fields)
                .compose(Ux::futureA)
                // 3. mountOut
                .compose(Fn.ofJArray(
                    FieldStub.OPTION_JSX,
                    FieldStub.OPTION_CONFIG,
                    FieldStub.OPTION_ITEM,
                    FieldStub.RULES,
                    KName.METADATA
                )));
    }

    @Override
    public Future<Boolean> deleteByControlId(final String controlId) {
        return Ux.Jooq.on(UiFieldDao.class)
            .deleteByAsync(new JsonObject().put(KName.Ui.CONTROL_ID, controlId));
    }

    private Future<JsonArray> attachConfig(final JsonArray fieldJson) {
        /*
         * metadata mode for parsing processor
         */
        final JsonArray ui = new JsonArray();
        /*
         * Calculate row
         */
        final int rowIndex = Ut.itJArray(fieldJson)
            .map(each -> each.getInteger("yPoint"))
            .max(Comparator.naturalOrder())
            .orElse(0);
        for (int idx = 0; idx <= rowIndex; idx++) {
            final Integer current = idx;
            final List<JsonObject> row = Ut.itJArray(fieldJson)
                .filter(item -> current.equals(item.getInteger("yPoint")))
                .sorted(Comparator.comparing(item -> item.getInteger("xPoint")))
                .toList();
            /*
             * Calculate columns
             */
            final JsonArray rowArr = new JsonArray();
            row.forEach(cell -> {
                /*
                 * Title row is special here
                 */
                final RowType rowType = Ut.toEnum(() -> cell.getString("rowType"),
                    RowType.class, RowType.FIELD);
                final JsonObject dataCell = new JsonObject();
                if (RowType.TITLE == rowType) {
                    dataCell.put("title", cell.getValue("label"));
                    dataCell.put("field", cell.getValue(KName.KEY));    // Specific field for title.
                } else if (RowType.CONTAINER == rowType) {
                    dataCell.put("complex", Boolean.TRUE);
                    // Container type will be mapped to name field here
                    dataCell.put(KName.NAME, cell.getValue("container"));
                    // optionJsx -> config
                    Ut.valueToJObject(cell, FieldStub.OPTION_JSX);
                    if (Objects.nonNull(cell.getValue(FieldStub.OPTION_JSX))) {
                        dataCell.put(KName.Ui.CONFIG, cell.getValue(FieldStub.OPTION_JSX));
                    }
                } else {
                    Ut.valueToJObject(cell,
                        FieldStub.OPTION_JSX,
                        FieldStub.OPTION_CONFIG,
                        FieldStub.OPTION_ITEM,
                        "rules"
                    );
                    final String render = Objects.isNull(cell.getString("render")) ? "" :
                        cell.getString("render");
                    final String label = Objects.isNull(cell.getString("label")) ? "" :
                        cell.getString("label");
                    final String metadata = cell.getString("name")
                        + "," + label + "," + cell.getInteger("span")
                        + ",," + render;

                    dataCell.put("metadata", metadata);
                    /*
                     * hidden
                     */
                    final Boolean hidden = cell.getBoolean("hidden");
                    if (hidden) {
                        dataCell.put("hidden", Boolean.TRUE);
                    }
                    /*
                     * Rules
                     */
                    final JsonArray rules = cell.getJsonArray("rules");
                    if (Objects.nonNull(rules) && !rules.isEmpty()) {
                        dataCell.put("optionConfig.rules", rules);
                    }
                    /*
                     * Three core configuration
                     */
                    if (Objects.nonNull(cell.getValue(FieldStub.OPTION_JSX))) {
                        dataCell.put(FieldStub.OPTION_JSX, cell.getValue(FieldStub.OPTION_JSX));
                    }
                    if (Objects.nonNull(cell.getValue(FieldStub.OPTION_CONFIG))) {
                        dataCell.put(FieldStub.OPTION_CONFIG, cell.getValue(FieldStub.OPTION_CONFIG));
                    }
                    if (Objects.nonNull(cell.getValue(FieldStub.OPTION_ITEM))) {
                        dataCell.put(FieldStub.OPTION_ITEM, cell.getValue(FieldStub.OPTION_ITEM));
                    }

                    /*
                     * moment
                     * 1) When `Edit/Add` status
                     * 2) When `View` status
                     * In this kind of situation, the config `optionJsx` must contains `config.format` here.
                     */
                    final JsonObject optionJsx = cell.getJsonObject(FieldStub.OPTION_JSX);

                    if (Ut.isNotNil(optionJsx)) {
                        final JsonObject config = optionJsx.getJsonObject("config");
                        if (Ut.isNotNil(config) && config.containsKey("format")) {
                            /*
                             * Date here for moment = true
                             * Here are some difference between two components
                             * 1) For `Date`, the format should be string
                             * 2) For `TableEditor`, the format should be object
                             * The table editor is added new here
                             */
                            final Object format = config.getValue("format");
                            if (String.class == format.getClass()) {
                                dataCell.put("moment", true);
                            }
                        }
                    }
                }
                rowArr.add(dataCell);
            });
            ui.add(rowArr);
        }
        return Ux.future(ui);
    }
}