Skip to content

Commit

Permalink
Merge pull request #45 from openepcis/GEN-41_user_extension_from_web_…
Browse files Browse the repository at this point in the history
…vocabulary

Enhancement to User Extension and support GS1 Web Vocabulary
  • Loading branch information
sboeckelmann authored May 22, 2024
2 parents c063a1b + 019e565 commit b92e7f5
Show file tree
Hide file tree
Showing 9 changed files with 474 additions and 188 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,20 @@
"userExtensions": [],
"ilmd": [
{
"element": "extensionElement",
"namespace": "https://loyalty-card.example.com/",
"localName": "redeemDate",
"dataType": "string",
"ID": 1,
"text": "16-07-2022"
"extensionID": 0,
"prefix": "customExtension",
"contextURL": "",
"children": [
{
"property": "redeemDate",
"dataType": "simple",
"prefix": "loyalty-card",
"contextURL": "https://loyalty-card.example.com/",
"key": 0,
"children": [],
"data": "16-07-2022"
}
]
}
],
"errorDeclaration": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,20 @@
"sensorElementList": [],
"userExtensions": [
{
"element": "extensionElement",
"namespace": "http://myvoc.example.org/",
"localName": "inspector_badge_nr",
"dataType": "string",
"ID": 1,
"text": "244301128"
"extensionID": 0,
"prefix": "customExtension",
"contextURL": "",
"children": [
{
"property": "inspector_badge_nr",
"dataType": "simple",
"prefix": "myvoc",
"contextURL": "http://myvoc.example.org/",
"key": 0,
"children": [],
"data": "244301128"
}
]
}
],
"ilmd": [],
Expand Down Expand Up @@ -72,14 +80,14 @@
{
"identifiersId": 1,
"identifierType": "Identifiers",
"objectIdentifierSyntax": "WebURI",
"instanceType": "sgtin",
"instanceData": {
"identifierType": "sgtin",
"serialType": "range",
"sgtin": "09521141174736",
"rangeFrom": 101
}
},
"objectIdentifierSyntax": "WebURI"
}
],
"connectorsInfo": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,43 @@
"userExtensions": [],
"ilmd": [
{
"element": "extensionElement",
"namespace": "https://gs1-ilmd.example.com",
"localName": "expirationDate",
"dataType": "string",
"ID": 1,
"text": "2024-04-15"
},
{
"element": "extensionElement",
"namespace": "https://gs1-ilmd.example.com",
"localName": "hasBatchLotNumber",
"dataType": "string",
"ID": 2,
"text": "100"
"extensionID": 0,
"prefix": "gs1",
"contextURL": "https://gs1.org/voc/",
"children": [
{
"label": "gs1:Product",
"key": 1,
"dataType": "complex",
"type": "class",
"data": "",
"contextURL": "https://gs1.org/voc/",
"children": [
{
"label": "gs1:expirationDate",
"rangeType": "xsd:date",
"dataType": "simple",
"type": "simple",
"data": "2024-04-15",
"key": 0,
"additionType": "property",
"contextURL": "https://gs1.org/voc/",
"parentKey": 1
},
{
"label": "gs1:hasBatchLotNumber",
"rangeType": "xsd:string",
"dataType": "simple",
"type": "simple",
"data": "100",
"key": 1,
"additionType": "property",
"contextURL": "https://gs1.org/voc/",
"parentKey": 1
}
]
}
]
}
],
"errorDeclaration": {
Expand Down Expand Up @@ -199,14 +222,14 @@
{
"identifiersId": 1,
"identifierType": "Identifiers",
"objectIdentifierSyntax": "WebURI",
"instanceType": "sgtin",
"instanceData": {
"identifierType": "sgtin",
"serialType": "range",
"sgtin": "09521568251228",
"rangeFrom": 100
}
},
"objectIdentifierSyntax": "WebURI"
}
],
"connectorsInfo": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,116 +15,132 @@
*/
package io.openepcis.testdata.generator.format;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.openepcis.testdata.generator.constants.TestDataGeneratorException;
import io.openepcis.testdata.generator.reactivestreams.StreamingEPCISDocument;
import io.quarkus.runtime.annotations.RegisterForReflection;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.microprofile.openapi.annotations.enums.SchemaType;
import org.eclipse.microprofile.openapi.annotations.media.Schema;

import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

import static io.openepcis.constants.EPCIS.EPCIS_DEFAULT_NAMESPACES;

@Getter
@Setter
@ToString
@RegisterForReflection
@Data
public class UserExtensionSyntax implements Serializable {
@Schema(
type = SchemaType.STRING,
description = "User extension namespace eg. https://example.com",
required = true)
private String namespace;
type = SchemaType.STRING,
description = "Label associated with the web vocabulary (Ex: gs1:appDownload, gs1:Country, etc.)"
)
private String label;

@Schema(type = SchemaType.STRING,
description = "Prefix associated with the custom user extension (Ex: cbvmda, example, ex1, ex1:lotNumber, etc.)"
)
private String prefix;

@Schema(type = SchemaType.STRING,
description = "Property associated with the custom user extension (Ex: lotNumber, distance, ex1:lotNumber, etc.)"
)
private String property;

@Schema(type = SchemaType.STRING,
description = "Context URL associated with the user extension (Ex: https://example.com, https://ex1.com, etc.)"
)
private String contextURL;

@Schema(type = SchemaType.STRING,
description = "Data associated with the custom user extension (Ex: 1, value-1, 1234, ex1:lotNumber=1234 etc.)"
)
private String data;

@NotNull(message = "Local name cannot be Null for extension")
@Schema(
type = SchemaType.STRING,
description = "User extension local name eg. example",
required = true)
private String localName;

private String text;
private List<UserExtensionSyntax> complex;
private String namespacePrefix;

@JsonCreator
public static UserExtensionSyntax createUserExtensionSyntax( //
@JsonProperty("namespace") String namespace, //
@JsonProperty("localName") String localName, //
@JsonProperty("text") String text,
@JsonProperty("complex") List<UserExtensionSyntax> complex) {
if (complex != null && !complex.isEmpty()) {
return new UserExtensionSyntax(namespace, localName, complex);
}
return new UserExtensionSyntax(namespace, localName, text);
}
type = SchemaType.STRING,
enumeration = {
"simple",
"complex"
},
description =
"Type of user extension simple type with direct value or complex type with children elements")
private List<UserExtensionSyntax> children;

@Schema(type = SchemaType.OBJECT,
description = "Raw JSON-LD formatted extension with @context which can be directly appended as extensions."
)
private transient Object rawJsonld;


//Method to format the UserExtensions based on the Web Vocabulary or Custom extensions by recursively reading them
public Map<String, Object> toMap() {
try {
final Map<String, Object> map = new HashMap<>();
final Map<String, Object> map = new HashMap<>();

// Check if the namespace already exist within the context if not then only add.
if (StreamingEPCISDocument.getContext() != null
&& !StreamingEPCISDocument.getContext().containsKey(this.namespacePrefix)) {
StreamingEPCISDocument.getContext().put(this.namespacePrefix, this.namespace);
}
if (this.rawJsonld == null) {
//Add the context prefix and url to the document context
buildContextInfo(this.prefix, this.contextURL);

//Based on the Web Vocabulary or Custom Extensions get the respective key and add information
final String key = StringUtils.isNotBlank(this.label) ? this.label : (this.prefix + ":" + this.property);

if (complex != null && !complex.isEmpty()) {
final Map<String, Object> complexMap =
complex.stream()
//for complex children is not empty then recursively loop over children and format the elements
if (CollectionUtils.isNotEmpty(children)) {
final Map<String, Object> complexMap = children.stream()
.flatMap(c -> c.toMap().entrySet().stream())
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
map.put(namespacePrefix + ":" + localName, complexMap);
map.put(key, complexMap);
} else {
map.put(namespacePrefix + ":" + localName, text);
//for simple string directly add the information
map.put(key, data);
}
return map;
} catch (Exception ex) {
throw new TestDataGeneratorException(
"Exception occurred during formatting of the Extensions, Please check the values provided values for User Extensions/ILMD/Error Extension : "
+ ex.getMessage(), ex);
}
}
} else if (this.rawJsonld instanceof Map<?, ?>) {
// Check if rawJsonld is a Map if so extract context info and append others to map
@SuppressWarnings("unchecked")
final Map<String, Object> genExtMap = (Map<String, Object>) this.rawJsonld;

// Constructor for String DataType
public UserExtensionSyntax(String namespace) {
try {
if (namespace.toLowerCase().contains("https://")
|| namespace.toLowerCase().contains("http://")) {
this.namespacePrefix =
namespace.substring(
namespace.indexOf("/", namespace.indexOf("/") + 1) + 1, namespace.indexOf("."));
} else {
this.namespacePrefix = namespace;
}
} catch (Exception ex) {
throw new TestDataGeneratorException(
"Exception occurred during formatting of the Extensions Namespace prefix, Please check the values provided values for User Extensions/ILMD/Error Extension Namespace : "
+ ex.getMessage(), ex);
// Extract @context if present and append it to the StreamingEPCISDocument @context of EPCIS Document
Optional.ofNullable(genExtMap.get("@context"))
.filter(obj -> obj instanceof List)
.map(obj -> (List<Map<String, String>>) obj)
.ifPresent(contextList -> contextList.stream()
.flatMap(contextMap -> contextMap.entrySet().stream())
.forEach(entry -> buildContextInfo(entry.getKey(), entry.getValue())));


genExtMap.remove("@context"); // exclude the @context from the rawJsonld
map.putAll(genExtMap); // add remaining entries to the map
}
}

// Constructor for String DataType
public UserExtensionSyntax(String namespace, String localName, String text) {
this(namespace);
this.namespace = namespace;
this.localName = localName;
this.text = text;
return map;
}

// Constructor for Complex DataType
public UserExtensionSyntax(
String namespace, String localName, List<UserExtensionSyntax> complex) {
this(namespace);
this.namespace = namespace;
this.localName = localName;
this.complex = complex;

//Function to add the context url and the prefix to the StreamingEPCISDocument context to generate the @context
private void buildContextInfo(final String prefix, final String contextURL) {
// Check if the namespace already exist within the context if not then only add.
if (StreamingEPCISDocument.getContext() != null
&& !StreamingEPCISDocument.getContext().containsKey(prefix)
&& StringUtils.isNotBlank(prefix)
&& StringUtils.isNotBlank(contextURL)) {

//Check if the namespace matches any of the default namespaces if so omit them
boolean isDefaultContext = EPCIS_DEFAULT_NAMESPACES.entrySet().stream().anyMatch(entry -> entry.getKey().equals(prefix) && entry.getValue().equals(contextURL));

//if no matches to default namespace then add
if (!isDefaultContext) {
StreamingEPCISDocument.getContext().put(prefix, contextURL);
}
}
}
}
Loading

0 comments on commit b92e7f5

Please sign in to comment.