public Boolean publishConfig(ConfigForm configForm, ConfigRequestInfo configRequestInfo, String encryptedDataKey)
            throws NacosException {
        
        Map<String, Object> configAdvanceInfo = getConfigAdvanceInfo(configForm);
        ParamUtils.checkParam(configAdvanceInfo);
        
        if (AggrWhitelist.isAggrDataId(configForm.getDataId())) {
            LOGGER.warn("[aggr-conflict] {} attempt to publish single data, {}, {}", configRequestInfo.getSrcIp(),
                    configForm.getDataId(), configForm.getGroup());
            throw new NacosApiException(HttpStatus.FORBIDDEN.value(), ErrorCode.INVALID_DATA_ID,
                    "dataId:" + configForm.getDataId() + " is aggr");
        }
        
        ConfigInfo configInfo = new ConfigInfo(configForm.getDataId(), configForm.getGroup(),
                configForm.getNamespaceId(), configForm.getAppName(), configForm.getContent());
        //set old md5
        if (StringUtils.isNotBlank(configRequestInfo.getCasMd5())) {
            configInfo.setMd5(configRequestInfo.getCasMd5());
        }
        configInfo.setType(configForm.getType());
        configInfo.setEncryptedDataKey(encryptedDataKey);
        ConfigOperateResult configOperateResult;
        
        String persistEvent = ConfigTraceService.PERSISTENCE_EVENT;
        
        if (StringUtils.isBlank(configRequestInfo.getBetaIps())) {
            if (StringUtils.isBlank(configForm.getTag())) {
                if (StringUtils.isNotBlank(configRequestInfo.getCasMd5())) {
                    configOperateResult = configInfoPersistService.insertOrUpdateCas(configRequestInfo.getSrcIp(),
                            configForm.getSrcUser(), configInfo, configAdvanceInfo);
                    if (!configOperateResult.isSuccess()) {
                        LOGGER.warn(
                                "[cas-publish-config-fail] srcIp = {}, dataId= {}, casMd5 = {}, msg = server md5 may have changed.",
                                configRequestInfo.getSrcIp(), configForm.getDataId(), configRequestInfo.getCasMd5());
                        throw new NacosApiException(HttpStatus.INTERNAL_SERVER_ERROR.value(),
                                ErrorCode.RESOURCE_CONFLICT, "Cas publish fail, server md5 may have changed.");
                    }
                } else {
                    configOperateResult = configInfoPersistService.insertOrUpdate(configRequestInfo.getSrcIp(),
                            configForm.getSrcUser(), configInfo, configAdvanceInfo);
                }
                ConfigChangePublisher.notifyConfigChange(
                        new ConfigDataChangeEvent(false, configForm.getDataId(), configForm.getGroup(),
                                configForm.getNamespaceId(), configOperateResult.getLastModified()));
            } else {
                if (StringUtils.isNotBlank(configRequestInfo.getCasMd5())) {
                    configOperateResult = configInfoTagPersistService.insertOrUpdateTagCas(configInfo,
                            configForm.getTag(), configRequestInfo.getSrcIp(), configForm.getSrcUser());
                    if (!configOperateResult.isSuccess()) {
                        LOGGER.warn(
                                "[cas-publish-tag-config-fail] srcIp = {}, dataId= {}, casMd5 = {}, msg = server md5 may have changed.",
                                configRequestInfo.getSrcIp(), configForm.getDataId(), configRequestInfo.getCasMd5());
                        throw new NacosApiException(HttpStatus.INTERNAL_SERVER_ERROR.value(),
                                ErrorCode.RESOURCE_CONFLICT,
                                "Cas publish tag config fail, server md5 may have changed.");
                    }
                } else {
                    configOperateResult = configInfoTagPersistService.insertOrUpdateTag(configInfo, configForm.getTag(),
                            configRequestInfo.getSrcIp(), configForm.getSrcUser());
                }
                persistEvent = ConfigTraceService.PERSISTENCE_EVENT_TAG + "-" + configForm.getTag();
                ConfigChangePublisher.notifyConfigChange(
                        new ConfigDataChangeEvent(false, configForm.getDataId(), configForm.getGroup(),
                                configForm.getNamespaceId(), configForm.getTag(),
                                configOperateResult.getLastModified()));
            }
        } else {
            // beta publish
            if (StringUtils.isNotBlank(configRequestInfo.getCasMd5())) {
                configOperateResult = configInfoBetaPersistService.insertOrUpdateBetaCas(configInfo,
                        configRequestInfo.getBetaIps(), configRequestInfo.getSrcIp(), configForm.getSrcUser());
                if (!configOperateResult.isSuccess()) {
                    LOGGER.warn(
                            "[cas-publish-beta-config-fail] srcIp = {}, dataId= {}, casMd5 = {}, msg = server md5 may have changed.",
                            configRequestInfo.getSrcIp(), configForm.getDataId(), configRequestInfo.getCasMd5());
                    throw new NacosApiException(HttpStatus.INTERNAL_SERVER_ERROR.value(), ErrorCode.RESOURCE_CONFLICT,
                            "Cas publish beta config fail, server md5 may have changed.");
                }
            } else {
                configOperateResult = configInfoBetaPersistService.insertOrUpdateBeta(configInfo,
                        configRequestInfo.getBetaIps(), configRequestInfo.getSrcIp(), configForm.getSrcUser());
            }
            persistEvent = ConfigTraceService.PERSISTENCE_EVENT_BETA;
            ConfigChangePublisher.notifyConfigChange(
                    new ConfigDataChangeEvent(true, configForm.getDataId(), configForm.getGroup(),
                            configForm.getNamespaceId(), configOperateResult.getLastModified()));
        }
        ConfigTraceService.logPersistenceEvent(configForm.getDataId(), configForm.getGroup(),
                configForm.getNamespaceId(), configRequestInfo.getRequestIpApp(), configOperateResult.getLastModified(),
                InetUtils.getSelfIP(), persistEvent, ConfigTraceService.PERSISTENCE_TYPE_PUB, configForm.getContent());
        return true;
    }
@Test
    void testPublishConfig() throws NacosException {
        ConfigForm configForm = new ConfigForm();
        configForm.setDataId("test");
        configForm.setGroup("test");
        configForm.setContent("test content");
        
        ConfigRequestInfo configRequestInfo = new ConfigRequestInfo();
        
        // if betaIps is blank, tag is blank and casMd5 is blank
        when(configInfoPersistService.insertOrUpdate(any(), any(), any(ConfigInfo.class), any())).thenReturn(new ConfigOperateResult());
        Boolean aResult = configOperationService.publishConfig(configForm, configRequestInfo, "");
        verify(configInfoPersistService).insertOrUpdate(any(), any(), any(ConfigInfo.class), any());
        assertTrue(aResult);
        
        // if betaIps is blank, tag is blank and casMd5 is not blank
        configRequestInfo.setCasMd5("test casMd5");
        when(configInfoPersistService.insertOrUpdateCas(any(), any(), any(ConfigInfo.class), any())).thenReturn(new ConfigOperateResult());
        Boolean bResult = configOperationService.publishConfig(configForm, configRequestInfo, "");
        verify(configInfoPersistService).insertOrUpdateCas(any(), any(), any(ConfigInfo.class), any());
        assertTrue(bResult);
        configRequestInfo.setCasMd5("");
        
        // if betaIps is blank, tag is not blank and casMd5 is blank
        configForm.setTag("test tag");
        when(configInfoTagPersistService.insertOrUpdateTag(any(ConfigInfo.class), eq("test tag"), any(), any())).thenReturn(
                new ConfigOperateResult());
        Boolean cResult = configOperationService.publishConfig(configForm, configRequestInfo, "");
        verify(configInfoTagPersistService).insertOrUpdateTag(any(ConfigInfo.class), eq("test tag"), any(), any());
        assertTrue(cResult);
        
        // if betaIps is blank, tag is not blank and casMd5 is not blank
        configForm.setTag("test tag");
        configRequestInfo.setCasMd5("test casMd5");
        when(configInfoTagPersistService.insertOrUpdateTagCas(any(ConfigInfo.class), eq("test tag"), any(), any())).thenReturn(
                new ConfigOperateResult());
        Boolean dResult = configOperationService.publishConfig(configForm, configRequestInfo, "");
        verify(configInfoTagPersistService).insertOrUpdateTagCas(any(ConfigInfo.class), eq("test tag"), any(), any());
        assertTrue(dResult);
        configRequestInfo.setCasMd5("");
        configForm.setTag("");
        
        // if betaIps is not blank and casMd5 is blank
        configRequestInfo.setBetaIps("test-betaIps");
        when(configInfoBetaPersistService.insertOrUpdateBeta(any(ConfigInfo.class), eq("test-betaIps"), any(), any())).thenReturn(
                new ConfigOperateResult());
        Boolean eResult = configOperationService.publishConfig(configForm, configRequestInfo, "");
        verify(configInfoBetaPersistService).insertOrUpdateBeta(any(ConfigInfo.class), eq("test-betaIps"), any(), any());
        assertTrue(eResult);
        
        // if betaIps is not blank and casMd5 is not blank
        configRequestInfo.setBetaIps("test-betaIps");
        configRequestInfo.setCasMd5("test casMd5");
        when(configInfoBetaPersistService.insertOrUpdateBetaCas(any(ConfigInfo.class), eq("test-betaIps"), any(), any())).thenReturn(
                new ConfigOperateResult());
        Boolean fResult = configOperationService.publishConfig(configForm, configRequestInfo, "");
        verify(configInfoBetaPersistService).insertOrUpdateBetaCas(any(ConfigInfo.class), eq("test-betaIps"), any(), any());
        assertTrue(fResult);
    }