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
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package org.apache.ofbiz.graphql.product.services;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.ofbiz.base.util.UtilMisc;
import org.apache.ofbiz.base.util.UtilValidate;
import org.apache.ofbiz.entity.Delegator;
import org.apache.ofbiz.entity.GenericEntityException;
import org.apache.ofbiz.entity.GenericValue;
import org.apache.ofbiz.entity.condition.EntityCondition;
import org.apache.ofbiz.entity.condition.EntityExpr;
import org.apache.ofbiz.entity.condition.EntityFunction;
import org.apache.ofbiz.entity.condition.EntityOperator;
import org.apache.ofbiz.entity.util.EntityFindOptions;
import org.apache.ofbiz.entity.util.EntityQuery;
import org.apache.ofbiz.graphql.schema.PaginationInputType;
import org.apache.ofbiz.service.DispatchContext;
import org.apache.ofbiz.service.GenericServiceException;
import org.apache.ofbiz.service.LocalDispatcher;
import org.apache.ofbiz.service.ModelService;
import org.apache.ofbiz.service.ServiceUtil;

/**
*
* @author grv
*
*/
public class GQLProductServices {

public static final String MODULE = GQLProductServices.class.getName();

/**
*
* @param dctx
* @param context
* @return
*/
public static Map<String, Object> searchProductsByGoodIdentificationValue(DispatchContext dctx,
Map<String, Object> context) {

Map<String, Object> serviceResult = ServiceUtil.returnSuccess();
LocalDispatcher dispatcher = dctx.getDispatcher();
Delegator delegator = dctx.getDelegator();
String idFragment = (String) context.get("idFragment");
List<EntityExpr> exprs = UtilMisc.toList(EntityCondition.makeCondition(
EntityCondition.makeCondition("goodIdentificationTypeId", EntityOperator.IN, UtilMisc.toList("SKU", "UPC", "ISBN")),EntityOperator.AND,
EntityCondition.makeCondition(EntityFunction.UPPER_FIELD("idValue"),
EntityOperator.LIKE, EntityFunction.UPPER(((String) "%" + idFragment + "%").toUpperCase()))));

EntityFindOptions options = new EntityFindOptions();
String orderBy = null;
if(context.containsKey("pagination")) {
PaginationInputType pagination = (PaginationInputType) context.get("pagination");
options.setLimit(pagination.pageSize);
options.setMaxRows(pagination.pageSize);
options.setOffset(pagination.pageIndex);
orderBy = pagination.orderByField;
}

int totalCount = 0;
try {
totalCount = (int)delegator.findCountByCondition("ProductAndGoodIdentification", EntityCondition.makeCondition(exprs), null, options);
System.out.println("That's count: "+totalCount);
List<GenericValue> productGIViewList = delegator.findList("ProductAndGoodIdentification", EntityCondition.makeCondition(exprs), null, UtilValidate.isNotEmpty(orderBy) ? Arrays.asList(orderBy.split(",")) : null, options, false);
System.out.println("productGIViewList: "+productGIViewList);
Map<String, Object> buildConnectionCtx = null;
buildConnectionCtx = dctx.makeValidContext("buildConnection", ModelService.IN_PARAM, context);
buildConnectionCtx.put("el", productGIViewList);
buildConnectionCtx.put("totalCount", totalCount);
if(context.containsKey("pagination")) {
PaginationInputType pagination = (PaginationInputType) context.get("pagination");
buildConnectionCtx.put("pageIndex", pagination.pageIndex);
buildConnectionCtx.put("pageSize", pagination.pageSize);
}
serviceResult = dispatcher.runSync("buildConnection", buildConnectionCtx);
} catch (GenericEntityException | GenericServiceException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return ServiceUtil.returnError(e.getMessage());
}
return serviceResult;
}

/**
*
* @param dctx
* @param context
* @return
*/
public static Map<String, Object> searchProductsByName(DispatchContext dctx, Map<String, Object> context) {
Map<String, Object> serviceResult = ServiceUtil.returnSuccess();
Delegator delegator = dctx.getDelegator();
String nameFragment = (String) context.get("nameFragment");
List<EntityExpr> exprs = UtilMisc.toList(EntityCondition.makeCondition(
EntityCondition.makeCondition("goodIdentificationTypeId", EntityOperator.IN,
UtilMisc.toList("SKU", "UPC", "ISBN")),
EntityOperator.AND, EntityCondition.makeCondition(EntityFunction.UPPER_FIELD("productName"),
EntityOperator.LIKE, EntityFunction.UPPER(((String) "%" + nameFragment + "%").toUpperCase()))));
try {
List<GenericValue> productGIViewList = EntityQuery.use(delegator).from("ProductAndGoodIdentification")
.where(exprs).queryList();
serviceResult.put("products", productGIViewList);
} catch (GenericEntityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return ServiceUtil.returnError(e.getMessage());
}
return serviceResult;
}

public static Map<String, Object> createProduct(DispatchContext dctx, Map<String, Object> context){
Map<String, Object> serviceResult = ServiceUtil.returnSuccess();
Delegator delegator = dctx.getDelegator();
LocalDispatcher dispather = dctx.getDispatcher();
try {
Map<String, Object> newContext = dctx.makeValidContext("createProduct", ModelService.IN_PARAM, context);
serviceResult = dispather.runSync("createProduct", newContext);
} catch (GenericServiceException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

if(ServiceUtil.isSuccess(serviceResult)) {
GenericValue product = null;
try {
product = EntityQuery.use(delegator).from("Product").where("productId", serviceResult.get("productId")).cache().queryOne();
serviceResult.put("_graphql_result_", product);
} catch (GenericEntityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return serviceResult;
}

public static Map<String, Object> createOrder(DispatchContext dctx, Map<String, Object> context){
System.out.println("context for createOrder"+context);
return ServiceUtil.returnSuccess();
}

public static Map<String, Object> getProductDetail(DispatchContext dctx, Map<String, Object> context){
System.out.println("context for getProductDetail"+context);
Map<String, Object> serviceResult = ServiceUtil.returnSuccess();
String productId = (String)context.get("id");
Delegator delegator = dctx.getDelegator();
GenericValue product = null;
try {
product = EntityQuery.use(delegator).from("Product").where("productId", productId).cache().queryOne();
}catch(GenericEntityException e) {
e.printStackTrace();
}

Map<String, Object> productDetail = new HashMap<String, Object>(product);
productDetail.put("availablePublicationCount", 3);
productDetail.putAll(product);
serviceResult.put("_graphql_result_", productDetail);
return serviceResult;
}
}
106 changes: 106 additions & 0 deletions graphql/README.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
////
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
////
= Apache OFBiz® GraphQL plug-in


:imagesdir: ./webapp/graphql/images
image::OFBiz-Logo.svg[100,100][float="left"]
image::graphql-logo.svg[100,100][float="right"]
:uri-demo: https://github.com/girishvasmatkar/graphql-product-api

This plug-in a GraphQL plugin that lets OFBiz to define GraphQL API.

== What's included
* GraphiQL (https://github.com/graphql/graphiql)
* Playground (https://github.com/prisma-labs/graphql-playground)
* OFBiz GraphQL endpoint

== OFBiz GraphQL API based on graphql-java suite of libraries
.The plug-in uses the following dependencies
* com.graphql-java:graphql-java:13.0
* com.graphql-java-kickstart:graphql-java-servlet:9.0.1
* com.graphql-java-kickstart:graphql-java-tools:5.7.1
* io.github.graphql-java:graphql-java-annotations:7.2.1

The endpoint is configured by subclassing SimpleGraphQLHttpServlet from graphql-java-servlet dependency.
----
public class GraphQLEndpointServletImpl extends SimpleGraphQLHttpServlet {
----

== Important URLs
* GraphQL endpint (https://localhost:8443/graphql/api)
* GraphQL schema (https://localhost:8443/graphql/api/schema.json)
* GraphiQL (https://localhost:8443/graphql/control/graphiql)
* GraphQL Playground (https://localhost:8443/graphql/control/playground)

== Usage
It comes with a demo query operation outlined below. The schema part is something that needs to be refined further. Both ways of creating the schema viz. GraphQL SDL and Programmatically using Java API, are demonstrated. This part will be further enhanced to include other operations that can be defined easily.

Queries can be executed like below:
----
'/graphql/?query={graphQLQueryString}' or '/graphql/?query={graphQLQueryString}&&variables={graphQLVariables}&&operationName={operationName}'
----
The bundled up schema does not define any mutation, but executing mutations is supported via POST only as per GraphQL specifications.

=== Demo Query operation
In order to test this plug-in, you need to have a demo plug-in that defines a {uri-demo}[demo] schema with some graphql queries. After deploying the demo, go to https://localhost:8443/graphql/control/graphiql (after authentication) and type in the following in the left papel of graphiql
Input:
----
query {
productApi{
product(productId:"WG-9943-B3"){
productId
productName
createdStamp
}
}
}
----
Output:
----
{
"data": {
"productApi": {
"product": {
"productId": "WG-9943-B3",
"productName": "Giant Widget B3",
"createdStamp": "2020-08-27T11:10:23Z"
}
}
}
}
----


== Authentication
The GraphQL endpoint is secured. Authentication scheme is "Bearer <JWT>". Every query/muation/subscription operation must have an Authorization header associated with it.
Example Request:

----
GET /graphql/api/?query={
productApi{
product(productId:"WG-9943-B3"){
productId
productName
createdStamp
}
}
} HTTP/1.1
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJBcGFjaGVPRkJpeiIsImlhdCI6MTU0NzczOTM0OCwiZXhwIjoxNjc5Mjc1MzQ4LCJhdWQiOiJ3d3cuZXhhbXBsZS5jb20iLCJzdWIiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiR2l2ZW5OYW1lIjoiSm9obm55IiwiU3VybmFtZSI6IlJvY2tldCIsIkVtYWlsIjoianJvY2tldEBleGFtcGxlLmNvbSIsInVzZXJMb2dpbklkIjoiYWRtaW4iLCJSb2xlIjpbIk1hbmFnZXIiLCJQcm9qZWN0IEFkbWluaXN0cmF0b3IiXX0.fwafgrgpodBJcXxNTQdZknKeWKb3sDOsQrcR2vcRw97FznD6mkE79p10Tu7cqpUx7LiXuROUAnXEgqDice-BSg
----
39 changes: 39 additions & 0 deletions graphql/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

dependencies {
//Examples of compile-time and runtime dependencies

pluginLibsCompile 'com.graphql-java:graphql-java:13.0'
pluginLibsCompile 'com.graphql-java-kickstart:graphql-java-servlet:9.0.1'
pluginLibsCompile 'com.graphql-java-kickstart:graphql-java-tools:5.7.1'
pluginLibsCompile 'io.github.graphql-java:graphql-java-annotations:7.2.1'
}

task install {
doLast {
// Install logic for this plugin
}
}

task uninstall {
doLast {
// uninstall logic for this plugin
}
}
42 changes: 42 additions & 0 deletions graphql/config/GraphqlUiLabels.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->

<resource xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ofbiz.apache.org/dtds/ofbiz-properties.xsd">
<property key="GraphqlApplication">
<value xml:lang="en">Graphql Application</value>
<value xml:lang="zh">Graphql应用程序</value>
<value xml:lang="zh-TW">Graphql應用程式</value>
</property>
<property key="GraphqlCompanyName">
<value xml:lang="en">OFBiz: Graphql</value>
<value xml:lang="zh-TW">OFBiz: Graphql</value>
</property>
<property key="GraphqlCompanySubtitle">
<value xml:lang="en">Part of the Apache OFBiz Family of Open Source Software</value>
<value xml:lang="it">Un modulo della famiglia di software open source Apache OFBiz</value>
<value xml:lang="zh">开源软件OFBiz的组成部分</value>
<value xml:lang="zh-TW">開源軟體OFBiz的組成部分</value>
</property>
<property key="GraphqlViewPermissionError">
<value xml:lang="en">You are not allowed to view this page.</value>
<value xml:lang="zh">不允许你浏览这个页面。</value>
<value xml:lang="zh-TW">不允許您檢視這個頁面.</value>
</property>
</resource>
23 changes: 23 additions & 0 deletions graphql/data/GraphqlDemoData.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->

<entity-engine-xml>

</entity-engine-xml>
Loading