Skip to content
Merged
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ If you have any questions, please feel free to contact us or share with our comm
## 💻 Deployment Options
You can access Openblocks from [cloud-hosted version](https://cloud.openblocks.dev/) at any time, or use the following resources for deploying Openblocks on different platforms:
- [Docker](https://docs.openblocks.dev/self-hosting)
- [DigitalOcean](https://docs.openblocks.dev/self-hosting/digitalocean)

## 💪 Contributing
- Language support: If you have experience with a language that isn't currently supported by our product, send us a pull request.
Expand Down
2 changes: 1 addition & 1 deletion client/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.1.1
1.1.2
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const ColorContainer = styled.div`
align-items: flex-start;
justify-content: space-between;
float: right;
gap: 8px;
gap: 6px;
margin-left: -24px;
> div:nth-of-type(1) {
margin: 3px 0;
Expand Down
4 changes: 2 additions & 2 deletions client/packages/openblocks/src/i18n/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1448,7 +1448,7 @@ export const en = {
},
pluginSetting: {
title: "Plugins",
npmPluginTitle: "NPM plugins",
npmPluginTitle: "npm plugins",
npmPluginDesc: "Set up npm plugins for all applications in the current workspace.",
npmPluginEmpty: "No npm plugins were added.",
npmPluginAddButton: "Add a npm plugin",
Expand Down Expand Up @@ -1830,7 +1830,7 @@ export const en = {
pluginExisted: "This npm plugin already existed",
compNotFound: "Component {compName} not found.",
addPluginModalTitle: "Add npm plugin",
pluginNameLabel: "NPM package's URL or name",
pluginNameLabel: "npm package's URL or name",
noCompText: "No components.",
compsLoading: "Loading...",
removePluginBtnText: "Remove",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { trans } from "i18n";
import { i18nObjs } from "../../i18n/index";
import { DatasourceInfo, HttpConfig } from "api/datasourceApi";
import { enObj } from "i18n/locales";
import { QUICK_REST_API_ID } from "constants/datasourceConstants";

const tourSteps: Step[] = [
{
Expand Down Expand Up @@ -172,11 +173,6 @@ function addQuery(editorState: EditorState, datasourceInfos: DatasourceInfo[]) {
);
} else {
// there's no sample data source, fall back to api source
const apiDataSource = datasourceInfos.find(
(info) =>
info.datasource.type === "restApi" &&
(info.datasource.datasourceConfig as HttpConfig).url === ""
);
queriesComp.dispatch(
queriesComp.pushAction({
id: id,
Expand All @@ -186,7 +182,7 @@ function addQuery(editorState: EditorState, datasourceInfos: DatasourceInfo[]) {
path: i18nObjs.editorTutorials.mockDataUrl || enObj.editorTutorials.mockDataUrl,
bodyType: "application/json",
},
datasourceId: apiDataSource?.datasource.id,
datasourceId: QUICK_REST_API_ID,
triggerType: "manual",
})
);
Expand Down Expand Up @@ -227,11 +223,11 @@ export default function EditorTutorials() {

const openTableData = () => {
const ele = document.getElementsByClassName(leftCompListClassName)[0];
const table = ele?.getElementsByClassName("ant-tree-title")[1];
const table = ele?.getElementsByClassName("ant-tree-title")[0];
if (table) {
(table as HTMLDivElement)?.click();
}
}
};

const handleJoyrideCallback = (data: CallBackProps) => {
const { status, index, action, type } = data;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public class MongoConfig {

@PostConstruct
public void init() {
mappingMongoConverter.setMapKeyDotReplacement("_");
mappingMongoConverter.setMapKeyDotReplacement("##OB_REPLACE##");
}

@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

Expand All @@ -21,6 +22,9 @@
import com.openblocks.infra.annotation.PossibleEmptyMono;
import com.openblocks.infra.birelation.BiRelation;
import com.openblocks.infra.birelation.BiRelationService;
import com.openblocks.sdk.config.CommonConfig;
import com.openblocks.sdk.config.CommonConfig.Workspace;
import com.openblocks.sdk.constants.WorkspaceMode;

import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Flux;
Expand All @@ -42,6 +46,9 @@ public class OrgMemberServiceImpl implements OrgMemberService {
@Autowired
private OrganizationService organizationService;

@Autowired
private CommonConfig commonConfig;

@Override
public Mono<List<OrgMember>> getOrganizationMembers(String orgId, int page, int count) {
return emptyMonoIfEmptyList(biRelationService.getBySourceId(ORG_MEMBER, orgId)
Expand All @@ -66,6 +73,23 @@ public Flux<OrgMember> getAllActiveOrgs(String userId) {
.filter(organization -> organization.getState() != OrganizationState.DELETED)
.map(Organization::getId)
.collectList()
// enterprise mode
.flatMap(orgIds -> {
Workspace workspace = commonConfig.getWorkspace();
if (workspace.getMode() == WorkspaceMode.ENTERPRISE) {
if (StringUtils.isNotBlank(workspace.getEnterpriseOrgId())) {
if (orgIds.contains(workspace.getEnterpriseOrgId())) {
return Mono.just(List.of(workspace.getEnterpriseOrgId()));
}
return addMember(workspace.getEnterpriseOrgId(), userId, MemberRole.MEMBER)
.thenReturn(List.of(workspace.getEnterpriseOrgId()));
}
if (orgIds.size() > 1) {
return Mono.just(orgIds.subList(0, 1));
}
}
return Mono.just(orgIds);
})
.map(HashSet::new)
.cache();

Expand Down
2 changes: 1 addition & 1 deletion server/openblocks-plugins/graphqlPlugin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<version>2.5.12</version>
<version>2.5.5</version>
<scope>test</scope>
</dependency>
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import static com.google.common.base.MoreObjects.firstNonNull;
import static com.openblocks.plugin.graphql.GraphQLError.GRAPHQL_EXECUTION_ERROR;
import static com.openblocks.plugin.graphql.utils.GraphQLBodyUtils.convertToGraphQLPOSTBodyFormat;
import static com.openblocks.plugin.graphql.utils.GraphQLBodyUtils.convertToGraphQLBody;
import static com.openblocks.sdk.exception.PluginCommonError.JSON_PARSE_ERROR;
import static com.openblocks.sdk.exception.PluginCommonError.QUERY_ARGUMENT_ERROR;
import static com.openblocks.sdk.exception.PluginCommonError.QUERY_EXECUTION_ERROR;
Expand Down Expand Up @@ -264,11 +264,7 @@ public Mono<QueryExecutionResult> executeQuery(Object o, GraphQLQueryExecutionCo
.exchangeStrategies(EXCHANGE_STRATEGIES)
.build();
if (!GRAPHQL_TYPE.equalsIgnoreCase(contentType)) {
try {
context.setQueryBody(convertToGraphQLPOSTBodyFormat(context));
} catch (PluginException e) {
return Mono.error(e);
}
context.setQueryBody(convertToGraphQLBody(context));
}
BodyInserter<?, ? super ClientHttpRequest> bodyInserter = buildBodyInserter(
context.isEncodeParams(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
package com.openblocks.plugin.graphql.utils;

import org.json.JSONObject;
import java.util.HashMap;
import java.util.Map;

import com.fasterxml.jackson.databind.JsonNode;
import com.openblocks.plugin.graphql.model.GraphQLQueryExecutionContext;
import com.openblocks.sdk.exception.PluginException;
import com.openblocks.sdk.util.JsonUtils;


public class GraphQLBodyUtils {

public static final String QUERY_KEY = "query";
public static final String VARIABLES_KEY = "variables";

public static String convertToGraphQLPOSTBodyFormat(GraphQLQueryExecutionContext graphQLQueryExecutionContext) throws PluginException {
JSONObject query = new JSONObject();
query.put(QUERY_KEY, graphQLQueryExecutionContext.getQueryBody());
JsonNode variables = graphQLQueryExecutionContext.getVariablesParams();
query.put(VARIABLES_KEY, variables);
return query.toString();
public static String convertToGraphQLBody(GraphQLQueryExecutionContext graphQLQueryExecutionContext) {
Map<String, Object> map = new HashMap<>();
map.put(QUERY_KEY, graphQLQueryExecutionContext.getQueryBody());
map.put(VARIABLES_KEY, graphQLQueryExecutionContext.getVariablesParams());
return JsonUtils.toJson(map);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,17 @@
import static com.openblocks.sdk.util.MustacheHelper.renderMustacheJsonString;
import static com.openblocks.sdk.util.MustacheHelper.renderMustacheString;
import static com.openblocks.sdk.util.StreamUtils.collectList;
import static com.openblocks.sdk.util.StreamUtils.distinctByKey;
import static org.apache.commons.collections4.MapUtils.emptyIfNull;
import static org.apache.commons.lang3.StringUtils.firstNonBlank;
import static org.apache.commons.lang3.StringUtils.trimToEmpty;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
Expand Down Expand Up @@ -96,6 +96,7 @@
import com.openblocks.sdk.plugin.restapi.auth.BasicAuthConfig;
import com.openblocks.sdk.plugin.restapi.auth.RestApiAuthType;
import com.openblocks.sdk.query.QueryVisitorContext;
import com.openblocks.sdk.util.JsonUtils;
import com.openblocks.sdk.webclient.WebClients;

import lombok.Builder;
Expand Down Expand Up @@ -125,7 +126,6 @@ public RestApiQueryExecutionContext buildQueryExecutionContext(RestApiDatasource

// from datasource config
String urlDomain = datasourceConfig.getUrl();
String datasourceBody = datasourceConfig.getBody();
List<Property> datasourceHeaders = datasourceConfig.getHeaders();
List<Property> datasourceUrlParams = datasourceConfig.getParams();
List<Property> datasourceBodyFormData = datasourceConfig.getBodyFormData();
Expand Down Expand Up @@ -162,7 +162,7 @@ public RestApiQueryExecutionContext buildQueryExecutionContext(RestApiDatasource
}

Map<String, String> urlParams = buildUrlParams(datasourceUrlParams, updatedQueryParams);
List<Property> bodyParams = buildBodyParams(datasourceBodyFormData, updatedQueryBodyParams);
List<Property> bodyParams = mergeBody(datasourceBodyFormData, updatedQueryBodyParams);

URI uri = RestApiUriBuilder.buildUri(urlDomain, updatedQueryPath, requestParams, urlParams, encodeParams);

Expand All @@ -174,7 +174,7 @@ public RestApiQueryExecutionContext buildQueryExecutionContext(RestApiDatasource
.urlParams(urlParams)
.bodyParams(bodyParams)
.encodeParams(encodeParams)
.queryBody(firstNonBlank(updatedQueryBody, datasourceBody))
.queryBody(mergeBody(updatedQueryBody, datasourceBodyFormData, contentType))
.forwardCookies(forwardCookies)
.forwardAllCookies(forwardAllCookies)
.requestCookies(queryVisitorContext.getCookies())
Expand All @@ -183,6 +183,23 @@ public RestApiQueryExecutionContext buildQueryExecutionContext(RestApiDatasource
.build();
}

private String mergeBody(String queryBody, List<Property> datasourceBody, String contentType) {
if (CollectionUtils.isEmpty(datasourceBody)) {
return queryBody;
}
if (!isJsonContentType(contentType)) {
return queryBody;
}
Map<String, Object> map = JsonUtils.fromJsonMap(queryBody);
if (map == null) {
return queryBody;
}
for (Property property : datasourceBody) {
map.putIfAbsent(property.getKey(), property.getValue());
}
return JsonUtils.toJson(map);
}

@Override
public Mono<QueryExecutionResult> executeQuery(Object webClientFilter, RestApiQueryExecutionContext context) {

Expand Down Expand Up @@ -395,12 +412,18 @@ private JsonNode parseExecuteResultHeaders(HttpHeaders headers) {
}
}

private List<Property> buildBodyParams(List<Property> datasourceBodyFormData, List<Property> updatedQueryBodyParams) {
return Stream.concat(datasourceBodyFormData.stream(),
updatedQueryBodyParams.stream())
.filter(it -> it.getKey() != null)
.filter(distinctByKey(Property::getKey))
.toList();
private List<Property> mergeBody(List<Property> datasourceBodyFormData, List<Property> updatedQueryBodyParams) {
Set<String> keySet = updatedQueryBodyParams.stream()
.map(Property::getKey)
.collect(Collectors.toCollection(HashSet::new));
List<Property> merge = new ArrayList<>(updatedQueryBodyParams);
for (Property property : datasourceBodyFormData) {
if (!keySet.contains(property.getKey())) {
merge.add(property);
keySet.add(property.getKey());
}
}
return merge;
}

private Map<String, String> buildUrlParams(List<Property> datasourceUrlParams, List<Property> updatedQueryParams) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ public static boolean isPicture(MediaType contentType) {
}

public static boolean isJson(MediaType contentType) {
return contentType.includes(MediaType.APPLICATION_JSON);
return StringUtils.equalsIgnoreCase("application", contentType.getType())
&& (StringUtils.equalsIgnoreCase(contentType.getSubtype(), "json") ||
StringUtils.contains(contentType.getSubtype(), "+json"));
}

public static String parseContentType(Map<String, String> allHeaders) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,32 @@ public void testDigestAuth() {
.verifyComplete();
}

@Test
public void responseJsonTypeTest() {
RestApiDatasourceConfig datasourceConfig = RestApiDatasourceConfig.builder()
.url("https://postman-echo.com/response-headers?key=value")
.build();
for (String contentType : List.of("application/hal+json",
"application/problem+json",
"application/json",
"application/json;charset=UTF-8",
"application/graphql+json",
"application/stream+json"
)) {
Map<String, Object> queryConfig = ImmutableMap.of("httpMethod", "GET",
"params", List.of(new Property("Content-Type", contentType))
);
StepVerifier.create(execute(datasourceConfig, queryConfig, emptyMap()))
.assertNext(result -> {
assertTrue(result.isSuccess());
assertNotNull(result.getData());
JsonNode value = ((ObjectNode) result.getData()).get("key");
assertEquals("value", value.asText());
})
.verifyComplete();
}
}

private Mono<QueryExecutionResult> execute(RestApiDatasourceConfig datasourceConfig, Map<String, Object> queryConfig,
Map<String, Object> params) {
QueryExecutionContext context = executor.doBuildQueryExecutionContext(datasourceConfig,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import com.openblocks.domain.user.model.User;
import com.openblocks.domain.user.service.UserService;
import com.openblocks.sdk.config.CommonConfig;
import com.openblocks.sdk.config.CommonConfig.Workspace;
import com.openblocks.sdk.constants.WorkspaceMode;
import com.openblocks.sdk.exception.BizError;
import com.openblocks.sdk.exception.BizException;
Expand Down Expand Up @@ -228,7 +229,13 @@ public Mono<Boolean> removeUserFromOrg(String orgId, String userId) {
@Override
public Mono<Boolean> removeOrg(String orgId) {
return checkVisitorAdminRole(orgId)
.then(checkIfSaasMode())
.then(Mono.defer(() -> {
Workspace workspace = commonConfig.getWorkspace();
if (workspace.getMode() == WorkspaceMode.ENTERPRISE && orgId.equals(workspace.getEnterpriseOrgId())) {
return Mono.error(new BizException(UNSUPPORTED_OPERATION, "BAD_REQUEST"));
}
return Mono.empty();
}))
.then(organizationService.delete(orgId));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ common:
cors-allowed-domains: ${CORS_ALLOWED_DOMAINS:*}
max-query-request-size-in-mb: 20
max-query-response-size-in-mb: 20
workspace:
mode: ENTERPRISE

spring:
data:
Expand Down