Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new attribute type JsonProperty #1545

Merged
merged 6 commits into from
Feb 22, 2024
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
4 changes: 4 additions & 0 deletions CHANGES_NEXT_RELEASE
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@
* Support for attributes of type VocabularyProperty
* Support for attributes of type JsonProperty
* Support for the new URL parameter "format" for output formats (normalized, concise, simplified)
* Support for JsonProperty attribute type
* Fixed a possible crash for TRoE and attributes of type Vocab/Json/Language

## Notes
* TRoE is still not prepared for attributes of type Vocab/Json/Language, so, attributes of those types are not stored in the historical database
* Modified the @context hosting feature to be according to API spec
* Support for URI param 'kind' for GET Contexts
* Improved counters for GET Context/Contexts
Expand Down
4 changes: 2 additions & 2 deletions src/lib/orionld/dbModel/dbModelFromApiAttribute.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,13 +149,13 @@ bool dbModelFromApiAttribute(KjNode* attrP, KjNode* dbAttrsP, KjNode* attrAddedV
//

// Move special fields back to "attrP"
const char* specialV[] = { "type", "value", "object", "languageMap", "vocab", "datasetId" }; // observedAt+unitCode are mds (db-model)
const char* specialV[] = { "type", "value", "object", "languageMap", "vocab", "json", "datasetId" }; // observedAt+unitCode are mds (db-model)
for (unsigned int ix = 0; ix < K_VEC_SIZE(specialV); ix++)
{
KjNode* nodeP = kjLookup(mdP, specialV[ix]);
if (nodeP != NULL)
{
if ((ix == 2) || (ix == 3) || (ix == 4)) // "object", "languageMap", "vocab": change name to "value" (Orion's DB model)
if ((ix >= 2) && (ix <= 5)) // "object", "languageMap", "vocab", "json": change name to "value" (Orion's DB model)
nodeP->name = (char*) "value";

kjChildRemove(mdP, nodeP);
Expand Down
14 changes: 14 additions & 0 deletions src/lib/orionld/dbModel/dbModelToApiAttribute.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ void dbModelToApiAttribute(KjNode* dbAttrP, bool sysAttrs, bool eqsForDots)
{
if (strcmp(typeP->value.s, "Relationship") == 0) valueP->name = (char*) "object";
else if (strcmp(typeP->value.s, "LanguageProperty") == 0) valueP->name = (char*) "languageMap";
else if (strcmp(typeP->value.s, "JsonProperty") == 0) valueP->name = (char*) "json";
else if (strcmp(typeP->value.s, "VocabularyProperty") == 0)
{
valueP->name = (char*) "vocab";
Expand Down Expand Up @@ -398,6 +399,17 @@ KjNode* dbModelToApiAttribute2(KjNode* dbAttrP, KjNode* datasetP, bool sysAttrs,
valueP->name = (char*) "vocab";
attrP = dbAttrP;
}
else if (strcmp(attrTypeNodeP->value.s, "JsonProperty") == 0)
{
KjNode* valueP = kjLookup(dbAttrP, "value");

// Remove everything except the value, and change its name to "vocab"
dbAttrP->value.firstChildP = valueP;
dbAttrP->lastChild = valueP;
valueP->next = NULL;
valueP->name = (char*) "json";
attrP = dbAttrP;
}
else
{
// "Steal" the value node and rename it to have the attribute's name instead - that's all that's needed for SIMPLIFIED FORMAT
Expand Down Expand Up @@ -435,6 +447,8 @@ KjNode* dbModelToApiAttribute2(KjNode* dbAttrP, KjNode* datasetP, bool sysAttrs,
{
if (attrType == Relationship)
nodeP->name = (char*) "object";
else if (attrType == JsonProperty)
nodeP->name = (char*) "json";
else if (attrType == VocabularyProperty)
{
nodeP->name = (char*) "vocab";
Expand Down
2 changes: 2 additions & 0 deletions src/lib/orionld/dbModel/dbModelToApiSubAttribute.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ void dbModelToApiSubAttribute(KjNode* dbSubAttrP)
if (strcmp(typeP->value.s, "Relationship") == 0) valueP->name = (char*) "object";
else if (strcmp(typeP->value.s, "LanguageProperty") == 0) valueP->name = (char*) "languageMap";
else if (strcmp(typeP->value.s, "VocabularyProperty") == 0) valueP->name = (char*) "vocab";
else if (strcmp(typeP->value.s, "JsonProperty") == 0) valueP->name = (char*) "json";
}
}

Expand Down Expand Up @@ -144,6 +145,7 @@ KjNode* dbModelToApiSubAttribute2(KjNode* dbSubAttributeP, bool sysAttrs, Orionl
if (subAttrType == Relationship) nodeP->name = (char*) "object";
else if (subAttrType == LanguageProperty) nodeP->name = (char*) "languageMap";
else if (subAttrType == VocabularyProperty) nodeP->name = (char*) "vocab";
else if (subAttrType == JsonProperty) nodeP->name = (char*) "json";

kjChildAdd(subAttrP, nodeP);
}
Expand Down
108 changes: 104 additions & 4 deletions src/lib/orionld/payloadCheck/pCheckAttribute.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,34 +71,47 @@ static const char* attrTypeChangeTitle(OrionldAttributeType oldType, OrionldAttr
if (oldType == GeoProperty) return "Attempt to transform a GeoProperty into a Property";
if (oldType == LanguageProperty) return "Attempt to transform a LanguageProperty into a Property";
if (oldType == VocabularyProperty) return "Attempt to transform a VocabularyProperty into a Property";
if (oldType == JsonProperty) return "Attempt to transform a JsonProperty into a Property";
}
else if (newType == Relationship)
{
if (oldType == Property) return "Attempt to transform a Property into a Relationship";
if (oldType == GeoProperty) return "Attempt to transform a GeoProperty into a Relationship";
if (oldType == LanguageProperty) return "Attempt to transform a LanguageProperty into a Relationship";
if (oldType == VocabularyProperty) return "Attempt to transform a VocabularyProperty into a Relationship";
if (oldType == JsonProperty) return "Attempt to transform a JsonProperty into a Relationship";
}
else if (newType == GeoProperty)
{
if (oldType == Property) return "Attempt to transform a Property into a GeoProperty";
if (oldType == Relationship) return "Attempt to transform a Relationship into a GeoProperty";
if (oldType == LanguageProperty) return "Attempt to transform a LanguageProperty into a GeoProperty";
if (oldType == VocabularyProperty) return "Attempt to transform a VocabularyProperty into a GeoProperty";
if (oldType == JsonProperty) return "Attempt to transform a JsonProperty into a GeoProperty";
}
else if (newType == LanguageProperty)
{
if (oldType == Property) return "Attempt to transform a Property into a LanguageProperty";
if (oldType == Relationship) return "Attempt to transform a Relationship into a LanguageProperty";
if (oldType == GeoProperty) return "Attempt to transform a GeoProperty into a LanguageProperty";
if (oldType == VocabularyProperty) return "Attempt to transform a VocabularyProperty into a LanguageProperty";
if (oldType == JsonProperty) return "Attempt to transform a JsonProperty into a LanguageProperty";
}
else if (newType == VocabularyProperty)
{
if (oldType == Property) return "Attempt to transform a Property into a VocabularyProperty";
if (oldType == Relationship) return "Attempt to transform a Relationship into a VocabularyProperty";
if (oldType == GeoProperty) return "Attempt to transform a GeoProperty into a VocabularyProperty";
if (oldType == LanguageProperty) return "Attempt to transform a LanguageProperty into a GeoProperty";
if (oldType == LanguageProperty) return "Attempt to transform a LanguageProperty into a VocabularyProperty";
if (oldType == JsonProperty) return "Attempt to transform a JsonProperty into a VocabularyProperty";
}
else if (newType == JsonProperty)
{
if (oldType == Property) return "Attempt to transform a Property into a JsonProperty";
if (oldType == Relationship) return "Attempt to transform a Relationship into a JsonProperty";
if (oldType == GeoProperty) return "Attempt to transform a GeoProperty into a JsonProperty";
if (oldType == LanguageProperty) return "Attempt to transform a LanguageProperty into a JsonProperty";
if (oldType == VocabularyProperty) return "Attempt to transform a VocabularyProperty into a JsonProperty";
}

return "Attribute type inconsistency";
Expand Down Expand Up @@ -413,6 +426,7 @@ bool valueAndTypeCheck(KjNode* attrP, OrionldAttributeType attributeType, bool a
KjNode* objectP = kjLookup(attrP, "object");
KjNode* languageMapP = kjLookup(attrP, "languageMap");
KjNode* vocabP = kjLookup(attrP, "vocab");
KjNode* jsonP = kjLookup(attrP, "json");

if (attributeType == Property)
{
Expand All @@ -431,6 +445,11 @@ bool valueAndTypeCheck(KjNode* attrP, OrionldAttributeType attributeType, bool a
orionldError(OrionldBadRequestData, "Forbidden field for a Property: vocab", attrP->name, 400);
return false;
}
else if (jsonP != NULL)
{
orionldError(OrionldBadRequestData, "Forbidden field for a Property: json", attrP->name, 400);
return false;
}
else if ((valueP == NULL) && (attributeExisted == false)) // Attribute is new but the value is missing
{
orionldError(OrionldBadRequestData, "Missing /value/ field for Property at creation time", attrP->name, 400);
Expand All @@ -454,6 +473,11 @@ bool valueAndTypeCheck(KjNode* attrP, OrionldAttributeType attributeType, bool a
orionldError(OrionldBadRequestData, "Forbidden field for a GeoProperty: vocab", attrP->name, 400);
return false;
}
else if (jsonP != NULL)
{
orionldError(OrionldBadRequestData, "Forbidden field for a GeoProperty: json", attrP->name, 400);
return false;
}
else if ((valueP == NULL) && (attributeExisted == false)) // Attribute is new but the value is missing
{
orionldError(OrionldBadRequestData, "Missing /value/ field for GeoProperty at creation time", attrP->name, 400);
Expand All @@ -477,6 +501,11 @@ bool valueAndTypeCheck(KjNode* attrP, OrionldAttributeType attributeType, bool a
orionldError(OrionldBadRequestData, "Forbidden field for a Relationship: vocab", attrP->name, 400);
return false;
}
else if (jsonP != NULL)
{
orionldError(OrionldBadRequestData, "Forbidden field for a Relationship: json", attrP->name, 400);
return false;
}
else if ((objectP == NULL) && (attributeExisted == false)) // Attribute is new but the value is missing
{
orionldError(OrionldBadRequestData, "Missing /object/ field for Relationship at creation time", attrP->name, 400);
Expand All @@ -500,6 +529,11 @@ bool valueAndTypeCheck(KjNode* attrP, OrionldAttributeType attributeType, bool a
orionldError(OrionldBadRequestData, "Forbidden field for a LanguageProperty: vocab", attrP->name, 400);
return false;
}
else if (jsonP != NULL)
{
orionldError(OrionldBadRequestData, "Forbidden field for a LanguageProperty: json", attrP->name, 400);
return false;
}
else if ((languageMapP == NULL) && (attributeExisted == false)) // Attribute is new but the value is missing
{
orionldError(OrionldBadRequestData, "Missing /languageMap/ field for LanguageProperty at creation time", attrP->name, 400);
Expand All @@ -523,12 +557,45 @@ bool valueAndTypeCheck(KjNode* attrP, OrionldAttributeType attributeType, bool a
orionldError(OrionldBadRequestData, "Forbidden field for a VocabularyProperty: languageMap", attrP->name, 400);
return false;
}
else if (jsonP != NULL)
{
orionldError(OrionldBadRequestData, "Forbidden field for a VocabularyProperty: json", attrP->name, 400);
return false;
}
else if ((vocabP == NULL) && (attributeExisted == false)) // Attribute is new but the value is missing
{
orionldError(OrionldBadRequestData, "Missing /vocab/ field for VocabularyProperty at creation time", attrP->name, 400);
return false;
}
}
else if (attributeType == JsonProperty)
{
if (valueP != NULL)
{
orionldError(OrionldBadRequestData, "Forbidden field for a JsonProperty: value", attrP->name, 400);
return false;
}
else if (objectP != NULL)
{
orionldError(OrionldBadRequestData, "Forbidden field for a JsonProperty: object", attrP->name, 400);
return false;
}
else if (languageMapP != NULL)
{
orionldError(OrionldBadRequestData, "Forbidden field for a JsonProperty: languageMap", attrP->name, 400);
return false;
}
else if (vocabP != NULL)
{
orionldError(OrionldBadRequestData, "Forbidden field for a JsonProperty: vocab", attrP->name, 400);
return false;
}
else if ((jsonP == NULL) && (attributeExisted == false)) // Attribute is new but the value is missing
{
orionldError(OrionldBadRequestData, "Missing /json/ field for JsonProperty at creation time", attrP->name, 400);
return false;
}
}

return true;
}
Expand Down Expand Up @@ -837,6 +904,15 @@ bool deletionWithTypePresent(KjNode* attrP, KjNode* typeP)
}
}
}
else if (strcmp(typeP->value.s, "JsonProperty") == 0)
{
valueP = kjLookup(attrP, "json");
if ((valueP != NULL) && (valueP->type == KjString) && (strcmp(valueP->value.s, "urn:ngsi-ld:null") == 0))
{
attrP->type = KjNull;
return true;
}
}

return false;
}
Expand All @@ -854,7 +930,8 @@ static bool deletionWithoutTypePresent
KjNode* valueP,
KjNode* objectP,
KjNode* languageMapP,
KjNode* vocabP
KjNode* vocabP,
KjNode* jsonP
)
{
if ((attributeType == Property) || (attributeType == GeoProperty))
Expand Down Expand Up @@ -893,6 +970,14 @@ static bool deletionWithoutTypePresent
}
}
}
else if (attributeType == JsonProperty)
{
if ((jsonP != NULL) && (jsonP->type == KjString) && (strcmp(jsonP->value.s, "urn:ngsi-ld:null") == 0))
{
attrP->type = KjNull;
return true;
}
}

return false;
}
Expand Down Expand Up @@ -1057,6 +1142,7 @@ static bool pCheckAttributeObject
KjNode* objectP = kjLookup(attrP, "object");
KjNode* languageMapP = kjLookup(attrP, "languageMap");
KjNode* vocabP = kjLookup(attrP, "vocab");
KjNode* jsonP = kjLookup(attrP, "json");

if (valueP != NULL)
{
Expand All @@ -1080,6 +1166,11 @@ static bool pCheckAttributeObject
attributeType = VocabularyProperty;
arrayReduce(vocabP);
}
else if (jsonP != NULL)
{
attributeType = JsonProperty;
arrayReduce(jsonP);
}
else
{
// If new attribute and no value field at all - error
Expand Down Expand Up @@ -1110,7 +1201,7 @@ static bool pCheckAttributeObject
//
if ((orionldState.serviceP->options & ORIONLD_SERVICE_OPTION_ACCEPT_JSONLD_NULL) != 0)
{
if (deletionWithoutTypePresent(attrP, attributeType, valueP, objectP, languageMapP, vocabP) == true)
if (deletionWithoutTypePresent(attrP, attributeType, valueP, objectP, languageMapP, vocabP, jsonP) == true)
return true;
}
}
Expand Down Expand Up @@ -1157,7 +1248,7 @@ static bool pCheckAttributeObject
fieldP->value = fieldP->value.firstChildP->value;
}
}
if ((attributeType == Relationship) || (attributeType == LanguageProperty) || (attributeType == VocabularyProperty))
if ((attributeType == Relationship) || (attributeType == LanguageProperty) || (attributeType == VocabularyProperty) || (attributeType == JsonProperty))
{
orionldError(OrionldBadRequestData, "Invalid member /value/", "valid for Property/GeoProperty attributes only", 400);
return false;
Expand Down Expand Up @@ -1193,6 +1284,9 @@ static bool pCheckAttributeObject
if (pCheckVocabulary(fieldP, attrP->name) == false)
return false;
}
else if ((attributeType == JsonProperty) && (strcmp(fieldP->name, "json") == 0))
{
}
else if (strcmp(fieldP->name, "observedAt") == 0)
{
//
Expand Down Expand Up @@ -1395,6 +1489,12 @@ static bool validAttrName(const char* attrName, bool isAttribute)
// - observedAt
// - datasetId (sub-attributes don't have datasetId)
//
// - Attributes of type JsonProperty can have the following special attributes:
// - type
// - json
// - observedAt
// - datasetId (sub-attributes don't have datasetId)
//
bool pCheckAttribute
(
const char* entityId,
Expand Down
8 changes: 7 additions & 1 deletion src/lib/orionld/troe/pgAttributeAppend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ void pgAttributeAppend
comma, instanceId, attributeName, opMode, entityId, observedAt, hasSubProperties, unitCode, datasetId, coordsString, orionldState.requestTimeString);
}
}
else // Property
else // Property OR JsonProperty
{
if (valueNodeP->type == KjString)
{
Expand All @@ -242,6 +242,12 @@ void pgAttributeAppend
}
else if ((valueNodeP->type == KjArray) || (valueNodeP->type == KjObject))
{
if (strcmp(type, "JsonProperty") == 0)
{
LM_W(("TRoE for Compound JsonProperty still to be implemented"));
return;
}

// WARNING: If an attribute is HUGE, it may not have room enough in a buffer allocated by kaAlloc (there's a max-size)
int renderedValueSize = kjFastRenderSize(valueNodeP);
char* renderedValue = kaAlloc(&orionldState.kalloc, renderedValueSize);
Expand Down
1 change: 1 addition & 0 deletions src/lib/orionld/troe/pgAttributeBuild.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ bool pgAttributeBuild
else if (strcmp(nodeP->name, "type") == 0) type = nodeP->value.s;
else if (strcmp(nodeP->name, "datasetId") == 0) datasetId = nodeP->value.s;
else if (strcmp(nodeP->name, "value") == 0) valueNodeP = nodeP;
else if (strcmp(nodeP->name, "json") == 0) valueNodeP = nodeP;
else if (strcmp(nodeP->name, "object") == 0)
{
if (nodeP->type == KjString)
Expand Down
2 changes: 2 additions & 0 deletions src/lib/orionld/types/OrionldAttributeType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const char* orionldAttributeTypeName(OrionldAttributeType attributeType)
case GeoProperty: return "GeoProperty";
case LanguageProperty: return "LanguageProperty";
case VocabularyProperty: return "VocabularyProperty";
case JsonProperty: return "JsonProperty";
}

return "InvalidAttributeType";
Expand All @@ -62,6 +63,7 @@ OrionldAttributeType orionldAttributeType(const char* typeString)
else if (strcmp(typeString, "GeoProperty") == 0) return GeoProperty;
else if (strcmp(typeString, "LanguageProperty") == 0) return LanguageProperty;
else if (strcmp(typeString, "VocabularyProperty") == 0) return VocabularyProperty;
else if (strcmp(typeString, "JsonProperty") == 0) return JsonProperty;

return NoAttributeType;
}
3 changes: 2 additions & 1 deletion src/lib/orionld/types/OrionldAttributeType.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ typedef enum OrionldAttributeType
Relationship,
GeoProperty,
LanguageProperty,
VocabularyProperty
VocabularyProperty,
JsonProperty
} OrionldAttributeType;


Expand Down
Loading
Loading