/** * Expands a possible CURIE string into a full IRI. If the string is not * recognized as a CURIE that can be expanded into an IRI, then false is * returned. * * @param ctx the context to use. * @param str the string to expand (no <>). * @param iri the string to store the IRI in. * @param usedCtx a context to update if a value was used from "ctx". * * @return true if the string was expanded, false if not. */ static bool _expandCurie( DynamicObject& ctx, const char* str, string& iri, DynamicObject* usedCtx = NULL) { bool rval = false; if(!ctx.isNull()) { // try to find a colon const char* ptr = strchr(str, ':'); if(ptr != NULL) { // get the potential CURIE prefix size_t len = ptr - str + 1; char prefix[len]; snprintf(prefix, len, "%s", str); // see if the prefix is in the context if(ctx->hasMember(prefix)) { // prefix found, normalize string DynamicObject& uri = ctx[prefix]; len = strlen(uri->getString()) + strlen(ptr + 1) + 3; iri = StringTools::format("%s%s", uri->getString(), ptr + 1); if(usedCtx != NULL) { (*usedCtx)[prefix] = uri.clone(); } rval = true; } } } return rval; }
/* Check if a JSON-LD object is a specific type. */ static bool _isType(DynamicObject& in, const char* type) { bool rval = false; if(in->hasMember("a")) { DynamicObject& a = in["a"]; switch(a->getType()) { case String: { rval = (a == type); break; } case Array: { DynamicObjectIterator i = a.getIterator(); while(!rval && i->hasNext()) { DynamicObject& next = i->next(); rval = (next == type); } break; } default: break; } } return rval; }
// FIXME: should this be using new JSON-LD frame stuff rather than this? bool JsonLd::filter( DynamicObject& context, DynamicObject& filter, DynamicObject& in, DynamicObject& out, bool simplify) { bool rval; DynamicObject normFilter; DynamicObject normIn; DynamicObject normOut; normOut->setType(Map); // remove contexts rval = removeContext(filter, normFilter) && removeContext(in, normIn); // filter to the output if(rval) { _filter(normFilter, normIn, normOut); // FIXME: fixup graph // Search normOut for unknown references that are in normIn and add them. // Futher optimize by checking reference count and embedding data as // needed. This will result in a graph that is as complete as the input // with regard to references. // flatten in the case of one result if(simplify && normOut->hasMember("@") && normOut["@"]->length() == 1) { normOut = normOut["@"][0]; } } // add context to output rval = rval && addContext(context, normOut, out); return rval; }
/** * Sets a subject's predicate to the given object value. If a value already * exists, it will be appended to an array. If the object value is NULL, then * the subject's predicate will be converted to an array. * * @param s the subject. * @param p the predicate. * @param o the object, NULL to only convert s[p] to an array. */ static void _setPredicate(DynamicObject& s, const char* p, const char* o) { // FIXME: must sort objects or keep array order? if(s->hasMember(p)) { if(s[p]->getType() != Array) { DynamicObject tmp = s[p]; s[p] = DynamicObject(); s[p]->append(tmp); } if(o != NULL) { s[p]->append(o); } } else if(o == NULL) { s[p]->setType(Array); } else { s[p] = o; } }
bool SampleService::getFileInfos( BtpAction* action, DynamicObject& in, DynamicObject& out, DynamicObject& fileInfos) { bool rval; // do not send dyno as response out.setNull(); // get catalog interface CatalogInterface* ci = dynamic_cast<CatalogInterface*>( mNode->getModuleApiByType("bitmunk.catalog")); // get targeted node user DynamicObject vars; action->getResourceQuery(vars); if(!vars->hasMember("nodeuser")) { BM_ID_SET(vars["nodeuser"], mNode->getDefaultUserId()); } // get resource parameters DynamicObject params; action->getResourceParams(params); // populate ware bundle Ware ware; BM_ID_SET(ware["mediaId"], BM_MEDIA_ID(params[0])); MediaPreferenceList mpl; mpl[0]["preferences"][0]["contentType"] = vars["contentType"]; mpl[0]["preferences"][0]["minBitrate"] = 1; // FIXME: Make sure the node user is checked against logged in user? if((rval = ci->populateWareBundle( BM_USER_ID(vars["nodeuser"]), ware, mpl))) { FileInfoIterator i = ware["fileInfos"].getIterator(); while(i->hasNext()) { FileInfo& fi = i->next(); // FIXME: get content type for file info instead of "extension" const char* extension = fi["extension"]->getString(); // FIXME: only support for mp3 audio at this time if(strcmp(extension, "mp3") == 0) { // get sample range for file info Media media; BM_ID_SET(media["id"], BM_MEDIA_ID(fi["mediaId"])); if(getSampleRange(media)) { fi["media"] = media; fileInfos->append() = fi; } } } } return rval; }
inline static bool _isBlankNode(DynamicObject& v) { // look for no subject or "<_:" or "_:" at the beginning of the subject return ( !v->hasMember("@") || strstr(v["@"], "<_:") == v["@"]->getString() || strstr(v["@"], "_:") == v["@"]->getString()); }
ExceptionRef Exception::convertToException(DynamicObject& dyno) { ExceptionRef rval = new Exception( dyno["message"]->getString(), dyno["type"]->getString()); if(dyno->hasMember("cause")) { ExceptionRef cause = convertToException(dyno["cause"]); rval->setCause(cause); } if(dyno->hasMember("details")) { *(*rval).mDetails = dyno["details"].clone(); } return rval; }
static void _setPredicate( DynamicObject& s, const char* predicate, DynamicObject& object) { if(s->hasMember(predicate)) { s[predicate].push(object); } else { s[predicate] = object; } }
bool Map::isValid( DynamicObject& obj, ValidatorContext* context) { bool rval = true; if(!obj.isNull() && obj->getType() == monarch::rt::Map) { Validators::iterator i; for(i = mValidators.begin(); i != mValidators.end(); ++i) { // only add a "." if this is not a root map if(context->getDepth() != 0) { context->pushPath("."); } context->pushPath(i->first); if(obj->hasMember(i->first)) { // do not short-circuit to ensure all keys tested if(!i->second.validator->isValid(obj[i->first], context)) { rval = false; } } else if(!i->second.validator->isOptional(context)) { rval = false; DynamicObject detail = context->addError("monarch.validation.MissingField", &obj); detail["validator"] = "monarch.validator.Map"; detail["message"] = "A required field has not been specified."; detail["key"] = i->first; } context->popPath(); if(context->getDepth() > 0) { context->popPath(); } } } else { rval = false; DynamicObject detail = context->addError("monarch.validation.TypeError", &obj); detail["validator"] = "monarch.validator.Map"; detail["message"] = "The given object type must a mapping (Map) type"; } return rval; }
bool Messenger::exchange( UserId peerId, Url* url, BtpMessage* out, BtpMessage* in, UserId userId, UserId agentId, uint32_t timeout) { bool rval = true; if(userId != 0) { // get user profile ProfileRef p; if(!mNode->getLoginData(agentId == 0 ? userId : agentId, NULL, &p)) { ExceptionRef e = new Exception( "Could not do BTP exchange. Not logged in.", "bitmunk.node.Messenger.NotLoggedIn"); BM_ID_SET(e->getDetails()["userId"], userId); BM_ID_SET(e->getDetails()["agentId"], agentId); Exception::set(e); rval = false; } else if(!p.isNull()) { // set user ID and profile for outgoing secure message out->setUserId(userId); out->setAgentProfile(p); // set public key source for incoming secure message in->setPublicKeySource(mNode->getPublicKeyCache()); } } // do btp exchange if(rval) { // if nodeuser is set, override peer ID with it DynamicObject vars; url->getQueryVariables(vars); if(vars->hasMember("nodeuser")) { peerId = BM_USER_ID(vars["nodeuser"]); } rval = mClient.exchange(peerId, url, out, in, timeout); } return rval; }
static bool _setDecodedParam( Statement* s, unsigned int index, DynamicObject& param, DynamicObject& value) { bool rval = true; if(param->hasMember("encode")) { // FIXME: could use streams here and handle types other than string, // but the DatabaseClient API might be abandoned before this actually // ever really gets used to that extent // fill byte buffer with initial data ByteBuffer b; b.put(value->getString(), value->length(), true); // apply each decoding // FIXME: optimize this by doing it once and storing it when // defining the schema DynamicObject decode = param["encode"].clone(); decode->reverse(); DynamicObjectIterator i = decode.getIterator(); while(rval && i->hasNext()) { const char* type = i->next()->getString(); // convert hex to binary if(strcmp(type, "hex") == 0) { const char* hex = b.bytes(); unsigned int len = b.length(); unsigned int binLen = (len >> 1) + 1; char bin[binLen]; rval = Convert::hexToBytes(hex, len, bin, binLen); if(rval) { b.clear(); b.put(bin, binLen, true); } } } // only blobs are supported at the moment rval = rval && s->setBlob(index, b.bytes(), b.length()); }
static void _setEmbed(DynamicObject& s, const char* p, DynamicObject& o) { // FIXME: must sort objects or keep array order? if(s->hasMember(p)) { if(s[p]->getType() != Array) { DynamicObject tmp = s[p]; s[p] = DynamicObject(); s[p]->append(tmp); } s[p]->append(o); } else { s[p] = o; } }
void DirectiveService::insertDirective( DynamicObject& cache, DynamicObject& directive) { // Note: Assume cache lock is engaged. // get random number for ID char tmp[22]; do { // get a random number between 1 and 1000000000 uint64_t n = Random::next(1, 1000000000); snprintf(tmp, 22, "%" PRIu64, n); } while(cache->hasMember(tmp)); // set directive ID and insert into cache directive["id"] = tmp; cache[tmp] = directive; }
// Currently just handles top level and @:[...] constructs. static bool _findId(DynamicObject& in, const char* id, DynamicObject& out) { out.setNull(); if(in->hasMember("@")) { DynamicObject& _id = in["@"]; switch(_id->getType()) { // top level object case String: { if(_id == id) { out = in; } break; } // top level is blank node case Array: { bool found = false; DynamicObjectIterator i = _id.getIterator(); while(!found && i->hasNext()) { DynamicObject& next = i->next(); if(next["@"] == id) { out = next; found = true; } } break; } default: break; } } return true; }
/** * Recursively removes context from the given input object. * * @param ctx the context to use (changes during recursion as necessary). * @param in the input object. * @param out the normalized output object. * @param bnodeId the last blank node ID used. * * @return true on success, false on failure with exception set. */ static bool _removeContext( DynamicObject& ctx, DynamicObject& in, DynamicObject& out, int& bnodeId) { bool rval = true; if(in.isNull()) { out.setNull(); } else { // initialize output DynamicObjectType inType = in->getType(); out->setType(inType); // update context if(inType == Map && in->hasMember("#")) { ctx = in["#"]; } if(inType == Map) { rval = _normalize(ctx, in, NULL, &out, bnodeId); } else if(inType == Array) { // strip context from each object in the array DynamicObjectIterator i = in.getIterator(); while(i->hasNext()) { DynamicObject& next = i->next(); rval = _removeContext(ctx, next, out->append(), bnodeId); } } } return rval; }
// Currently just handles top level and @:[...] constructs. static bool _findType(DynamicObject& in, const char* type, DynamicObject& out) { out->clear(); if(in->hasMember("@")) { DynamicObject& id = in["@"]; switch(id->getType()) { // top level object case String: { if(_isType(in, type)) { out->append(in); } break; } // top level is blank node case Array: { DynamicObjectIterator i = id.getIterator(); while(i->hasNext()) { DynamicObject& next = i->next(); if(_isType(next, type)) { out->append(next); } } break; } default: break; } } return true; }
static bool _filterOne(DynamicObject& filter, DynamicObject& object) { bool rval = true; // loop over all filter properties DynamicObjectIterator i; i = filter.getIterator(); while(rval && i->hasNext()) { DynamicObject& next = i->next(); const char* name = i->getName(); // check if object has property rval = object->hasMember(name); if(rval) { // loop over all filter values DynamicObjectIterator fpi; fpi = next.getIterator(); while(rval && fpi->hasNext()) { DynamicObject& fpnext = fpi->next(); // make sure value appears in object property DynamicObjectIterator opi; opi = object[name].getIterator(); rval = false; while(!rval && opi->hasNext()) { DynamicObject& opnext = opi->next(); rval = (fpnext == opnext); } } } } return rval; }
void DownloadStateInitializer::runSellerPoolUpdater() { // create list of all seller pools SellerPoolList pools; pools->setType(Array); FileInfoIterator fii = mDownloadState["ware"]["fileInfos"].getIterator(); while(fii->hasNext()) { FileInfo& fi = fii->next(); // create a seller pool to populate, try to retrieve up // to 100 sellers SellerPool sp; BM_ID_SET(sp["fileInfo"]["id"], BM_FILE_ID(fi["id"])); BM_ID_SET(sp["fileInfo"]["mediaId"], BM_MEDIA_ID(fi["mediaId"])); sp["sellerDataSet"]["start"] = 0; sp["sellerDataSet"]["num"] = 100; // add seller pool to list pools->append(sp); yield(); } // start up SellerPoolUpdater fiber SellerPoolUpdater* spu = new SellerPoolUpdater(getNode(), getId()); spu->setUserId(getUserId()); spu->setDownloadState(mDownloadState); spu->setSellerPools(pools); spu->initializeFileProgress(true); mNode->getFiberScheduler()->addFiber(spu); // sleep until spu is done bool keepSleeping = true; while(keepSleeping) { sleep(); // get messages FiberMessageQueue* msgs = getMessages(); while(!msgs->empty()) { DynamicObject msg = msgs->front(); msgs->pop_front(); if(msg->hasMember("interrupt")) { logDownloadStateMessage("interrupted"); // send interrupted event Event e; e["type"] = EVENT_DOWNLOAD_STATE ".initializationInterrupted"; sendDownloadStateEvent(e); keepSleeping = false; } // FIXME: is this intentionally not an elseif? if(msg->hasMember("sellerPoolUpdateComplete")) { // stop processing download state mPurchaseDatabase->stopProcessingDownloadState( mDownloadState, getId()); // send success event Event e; e["type"] = EVENT_DOWNLOAD_STATE ".initialized"; sendDownloadStateEvent(e); keepSleeping = false; } else if(msg->hasMember("sellerPoolUpdateError")) { // send error event Event e; e["type"] = EVENT_DOWNLOAD_STATE ".exception"; e["details"]["exception"] = msg["exception"]; sendDownloadStateEvent(e); keepSleeping = false; } } } }
/** * Recursively normalizes the given input object. * * Input: A subject with predicates and possibly embedded other subjects. * Output: Either a map of normalized subjects OR a tree of normalized subjects. * * Normalization Algorithm: * * Replace the existing context if the input has '#'. * * For each key-value: * 1. Split the key on a colon and look for prefix in the context. If found, * expand the key to an IRI, else it is already an IRI, add <>, save the * new predicate to add to the output. * 2. If value is a Map, then it is a subject, set the predicate to the * subject's '@' IRI value and recurse into it. Else goto #3. * 3. Look up the key in the context to find type coercion info. If not found, * goto #4, else goto #5. * 4. Check the value for an integer, double, or boolean. If matched, set * type according to xsd types. If not matched, look for <>, if found, * do CURIE vs. IRI check like #1 and create appropriate value. * 5. If type coercion entry is a string, encode the value using the specific * type. If it is an array, check the type in order of preference. If an * unrecognized type (non-xsd, non-IRI) is provided, throw an exception. * * @param ctx the context to use (changes during recursion as necessary). * @param in the input object. * @param subjects a map of normalized subjects. * @param out a tree normalized objects. * @param bnodeId the last blank node ID used. * * @return true on success, false on failure with exception set. */ static bool _normalize( DynamicObject ctx, DynamicObject& in, DynamicObject* subjects, DynamicObject* out, int& bnodeId) { bool rval = true; // FIXME: validate context (check for non-xsd types in type coercion arrays) if(!in.isNull()) { // update context if(in->hasMember("#")) { ctx = in["#"]; } // vars for normalization state DynamicObject subject(NULL); if(subjects != NULL) { subject = DynamicObject(); subject->setType(Map); // assign blank node ID as needed if(!in->hasMember("@")) { string bnodeKey = _createBlankNodeId(++bnodeId); subject["@"] = bnodeKey.c_str(); } } string nKey; // iterate over key-values DynamicObjectIterator i = in.getIterator(); while(rval && i->hasNext()) { DynamicObject& value = i->next(); const char* key = i->getName(); // skip context keys if(key[0] == '#') { continue; } // get normalized key nKey = _normalizeValue(ctx, key, RDF_TYPE_IRI, NULL, NULL); // put values in an array for single code path DynamicObject values; values->setType(Array); if(value->getType() == Array) { values.merge(value, true); // preserve array structure when not using subjects map if(out != NULL) { (*out)[nKey.c_str()]->setType(Array); } } else { values->append(value); } // normalize all values DynamicObjectIterator vi = values.getIterator(); while(rval && vi->hasNext()) { DynamicObject v = vi->next(); if(v->getType() == Map) { if(subjects != NULL) { // get a normalized subject for the value string vSubject; if(v->hasMember("@")) { // normalize existing subject vSubject = _normalizeValue( ctx, v["@"], RDF_TYPE_IRI, NULL, NULL); } else { // generate the next blank node ID in order to preserve // the blank node embed in the code below vSubject = _createBlankNodeId(bnodeId + 1); } // determine if value is a blank node bool isBNode = _isBlankNode(v); // update non-blank node subject (use value's subject IRI) if(!isBNode) { _setPredicate(subject, nKey.c_str(), vSubject.c_str()); } // recurse rval = _normalize(ctx, v, subjects, out, bnodeId); // preserve embedded blank node if(rval && isBNode) { // remove embed from top-level subjects DynamicObject embed = (*subjects)[vSubject.c_str()]; (*subjects)->removeMember(vSubject.c_str()); embed->removeMember("@"); _setEmbed(subject, nKey.c_str(), embed); } } else { // update out and recurse DynamicObject next = (*out)[nKey.c_str()]; if(value->getType() == Array) { next = next->append(); } else { next->setType(Map); } rval = _normalize(ctx, v, subjects, &next, bnodeId); } } else { _setPredicate( (subjects != NULL) ? subject : *out, nKey.c_str(), _normalizeValue(ctx, v, RDF_TYPE_UNKNOWN, key, NULL).c_str()); } } } // add subject to map if(subjects != NULL) { (*subjects)[subject["@"]->getString()] = subject; } } return rval; }
/* Check if a JSON-LD object has an id. */ static bool _hasId(DynamicObject& in) { return in->hasMember("@"); }
bool SampleService::sendm3u(BtpAction* action, DynamicObject& fileInfos) { bool rval = true; // build m3u file string content; content.append("#EXTM3U\n"); // create play list char temp[22]; FileInfoIterator i = fileInfos.getIterator(); while(i->hasNext()) { FileInfo& fi = i->next(); Media& media = fi["media"]; int sampleLength = media["sampleRange"][1]->getUInt32() - media["sampleRange"][0]->getUInt32(); snprintf(temp, 22, "%u", (sampleLength < 0 ? 0 : sampleLength)); string artist; if(media["contributors"]->hasMember("Performer")) { artist = media["contributors"]["Performer"][0]["name"]->getString(); } else if(media["contributors"]->hasMember("Artist")) { artist = media["contributors"]["Artist"][0]["name"]->getString(); } content.append("#EXTINF:"); content.append(temp); content.push_back(','); content.append(artist); content.append(" - "); content.append(media["title"]->getString()); content.push_back('\n'); // FIXME: get host from configuration or something // needs to be public IP that remote side can access DynamicObject vars; action->getResourceQuery(vars); if(!vars->hasMember("nodeuser")) { BM_ID_SET(vars["nodeuser"], mNode->getDefaultUserId()); } HttpRequestHeader* header = action->getRequest()->getHeader(); string host = header->getFieldValue("X-Forwarded-Host"); if(host.length() == 0) { host = header->getFieldValue("Host"); } content.append("http://"); content.append(host); content.append("/api/3.0/sales/samples/file/"); content.append(fi["mediaId"]->getString()); content.push_back('/'); content.append(fi["id"]->getString()); content.push_back('\n'); } // set up response header HttpResponseHeader* header = action->getResponse()->getHeader(); header->setField("Content-Type", "audio/x-mpegurl"); header->setField( "Content-Disposition", "attachment; filename=bitmunk-sample.m3u"); // create content input stream ByteBuffer b(content.length()); b.put(content.c_str(), content.length(), false); ByteArrayInputStream bais(&b); // send sample action->getResponse()->getHeader()->setStatus(200, "OK"); action->sendResult(&bais); return rval; }
bool DomWriter::writeWithNamespaceSupport( Element& e, OutputStream* os, int level, DynamicObject& nsPrefixMap) { bool rval = true; if(level < 0) { level = mIndentLevel; } // add any entries to the current namespace prefix map if(e->hasMember("attributes") && e["attributes"]->getType() == Map) { AttributeIterator attrs = e["attributes"].getIterator(); while(attrs->hasNext()) { Attribute& attr = attrs->next(); // check for an xml namespace prefix definition const char* attrName = attrs->getName(); if(strncmp(attrName, "xmlns:", 6) == 0) { const char* uri = attr["value"]->getString(); nsPrefixMap[uri] = attrName + 6; } } } // create element name, which may require using the namespace prefix map string elementName; if(e->hasMember("namespace")) { const char* ns = e["namespace"]->getString(); if(nsPrefixMap->hasMember(ns)) { // prepend prefix for given namespace elementName.append(nsPrefixMap[ns]->getString()); elementName.push_back(':'); } } elementName.append(e["name"]->getString()); // open start element rval = ((mCompact || level == 0) ? true : os->write("\n", 1)) && writeIndentation(os, level) && os->write("<", 1) && os->write(elementName.c_str(), elementName.length()); // write attributes e["attributes"]->setType(Map); AttributeIterator attrs = e["attributes"].getIterator(); while(rval && attrs->hasNext()) { Attribute& attr = attrs->next(); // create attribute name, which may require using the namespace prefix map string attrName; if(attr->hasMember("namespace")) { const char* ns = attr["namespace"]->getString(); if(nsPrefixMap->hasMember(ns)) { // prepend prefix for given namespace attrName.append(nsPrefixMap[ns]->getString()); attrName.push_back(':'); } } attrName.append(attr["name"]->getString()); rval = os->write(" ", 1) && os->write(attrName.c_str(), attrName.length()) && os->write("=\"", 2) && os->write(attr["value"]->getString(), attr["value"]->length()) && os->write("\"", 1); } // close start element, if element is empty, use compact element e["data"]->setType(String); e["children"]->setType(Map); int dataLength = e["data"]->length(); bool empty = (dataLength == 0 && e["children"]->length() == 0); rval = rval && (empty ? os->write("/>", 2) : os->write(">", 1)); // write element data and children if(rval && !empty) { // write element children if(rval && e["children"]->length() > 0) { // serialize each child DynamicObjectIterator lists = e["children"].getIterator(); while(rval && lists->hasNext()) { DynamicObject& list = lists->next(); list->setType(Array); ElementIterator children = list.getIterator(); while(rval && children->hasNext()) { // serialize child Element& child = children->next(); rval = writeWithNamespaceSupport( child, os, level + 1, nsPrefixMap); } } } // write element data if(dataLength > 0) { // xml-encode data string encoded = encode(e["data"]->getString()); rval = os->write(encoded.c_str(), encoded.length()); } // write end element // (only write indentation when data is blank and children are present) if(dataLength == 0 && !empty) { rval = rval && (mCompact ? true : os->write("\n", 1)) && writeIndentation(os, level); } rval = rval && os->write("</", 2) && os->write(elementName.c_str(), elementName.length()) && os->write(">", 1); } return rval; }
bool SampleService::getSampleFileByIds( BtpAction* action, DynamicObject& in, DynamicObject& out, MediaId mediaId, FileId fileId) { bool rval; // do not send dyno as response out.setNull(); // determine if too busy or not bool permit = false; mSampleSemaphoreLock.lock(); { int current = mSampleSemaphore["current"]->getInt32(); if(current < mSampleSemaphore["max"]->getInt32()) { mSampleSemaphore["current"] = current + 1; permit = true; } } mSampleSemaphoreLock.unlock(); // ensure permit was granted to stream sample if(!permit) { // too many samples streaming action->getResponse()->getHeader()->setStatus(503, "Service Unavailable"); action->sendResult(); rval = true; } else { // get catalog interface CatalogInterface* ci = dynamic_cast<CatalogInterface*>( mNode->getModuleApiByType("bitmunk.catalog")); // get targeted node user DynamicObject vars; action->getResourceQuery(vars); if(!vars->hasMember("nodeuser")) { BM_ID_SET(vars["nodeuser"], mNode->getDefaultUserId()); } // get resource parameters DynamicObject params; action->getResourceParams(params); FileInfo fi; BM_ID_SET(fi["mediaId"], mediaId); BM_ID_SET(fi["id"], fileId); if((rval = ci->populateFileInfo(BM_USER_ID(vars["nodeuser"]), fi))) { // send the sample rval = sendFile(action, fi); } // release permit mSampleSemaphoreLock.lock(); { int current = mSampleSemaphore["current"]->getInt32(); mSampleSemaphore["current"] = current - 1; } mSampleSemaphoreLock.unlock(); if(!rval) { // sample not found action->getResponse()->getHeader()->setStatus(404, "Not Found"); action->sendResult(); rval = true; } } return rval; }
static DynamicObject _compactIri( DynamicObject& ctx, DynamicObject& usedCtx, const char* predicate, const char* encoded, const char* value, char** curie) { DynamicObject rval(NULL); // used to store if brackets should be added bool addBrackets = true; // predicates themselves have no brackets if(predicate == NULL) { addBrackets = false; } // no brackets for "@" or "a" else if(strcmp(predicate, "@") == 0 || strcmp(predicate, "a") == 0) { addBrackets = false; } // check type coercion for IRI else if(ctx->hasMember("#types") && ctx["#types"]->hasMember(predicate)) { DynamicObject& type = ctx["#types"][predicate]; usedCtx["#types"][predicate] = type.clone(); // single type if(type->getType() == String) { string t = _normalizeValue(ctx, type, RDF_TYPE_IRI, NULL, &usedCtx); if(strcmp(t.c_str(), XSD_ANY_URI_NORM) == 0) { addBrackets = false; } } // type coercion info is an ordered list of possible types else { // do not add brackets if URI is a valid type DynamicObjectIterator ti = type.getIterator(); while(ti->hasNext()) { DynamicObject& next = ti->next(); string t = _normalizeValue(ctx, next, RDF_TYPE_IRI, NULL, &usedCtx); if(strcmp(t.c_str(), XSD_ANY_URI_NORM) == 0) { addBrackets = false; } } } } // check the context for a prefix that could shorten the IRI to a CURIE DynamicObjectIterator i = ctx.getIterator(); while(rval.isNull() && i->hasNext()) { DynamicObject& next = i->next(); // skip special context keys (start with '#') const char* name = i->getName(); if(name[0] != '#') { const char* uri = next; const char* ptr = strstr(value, uri); if(ptr != NULL && ptr == value) { size_t ulen = strlen(uri); size_t vlen = strlen(value); if(vlen > ulen) { // add 2 to make room for null-terminator and colon size_t total = strlen(name) + (vlen - ulen) + 2; if(addBrackets) { total += 2; } _realloc(curie, total); const char* format = addBrackets ? "<%s:%s>" : "%s:%s"; snprintf(*curie, total, format, name, ptr + ulen); rval = DynamicObject(); rval = *curie; usedCtx[name] = uri; } else if(vlen == ulen && strcmp(name, "a") == 0) { rval = DynamicObject(); rval = "a"; } } } } // if rval still not set, use encoded or value based on addBrackets if(rval.isNull()) { rval = DynamicObject(); rval = addBrackets ? encoded : value; } return rval; }
static DynamicObject _compactTypedLiteral( DynamicObject& ctx, DynamicObject& usedCtx, const char* predicate, const char* encoded, const char* value, const char* datatype, char** tmp) { DynamicObject rval(NULL); // check type coercion if(predicate != NULL) { // prefer type coercion map first, then use default basic types bool inTypesMap = false; string t; if(ctx->hasMember("#types") && ctx["#types"]->hasMember(predicate)) { DynamicObject& type = ctx["#types"][predicate]; usedCtx["#types"][predicate] = type.clone(); inTypesMap = true; // single type if(type->getType() == String) { // get fully-qualified datatype t = _normalizeValue(ctx, type, RDF_TYPE_IRI, NULL, &usedCtx); } // type coercion info is an ordered list of possible types else { // FIXME: need to check if datatype matches type coercion type // FIXME: determine whether to make int,bool,double,or IRI } } else { // use given datatype (try to coerce basic built in types) DynamicObject nullCtx(NULL); DynamicObject type; type = datatype; t = _normalizeValue(nullCtx, type, RDF_TYPE_IRI, NULL, NULL); } rval = DynamicObject(); if(strcmp(t.c_str(), XSD_ANY_URI_NORM) == 0) { rval = value; } else if(strcmp(t.c_str(), XSD_BOOLEAN_NORM) == 0) { rval = value; rval->setType(Boolean); } else if(strcmp(t.c_str(), XSD_INTEGER_NORM) == 0) { rval = value; rval->setType(Int64); } else if(strcmp(t.c_str(), XSD_DOUBLE_NORM) == 0) { rval = value; rval->setType(Double); } // unrecognized type else { // if the type was explicitly specified in the type coercion map, // then use the decoded value, otherwise use the encoded one rval = inTypesMap ? value : encoded; } } // use full encoded value, nothing in context to compact IRI if(rval.isNull()) { rval = DynamicObject(); rval = encoded; } return rval; }
void DatabaseClient::buildParams( SchemaObject& schema, DynamicObject& members, DynamicObject& params, const char* tableAlias) { // ensure params is an array params->setType(Array); // create shared table alias object DynamicObject taObj(NULL); if(tableAlias != NULL) { taObj = DynamicObject(); taObj = tableAlias; } // map the given members object into a list of parameters that can // be used to generate sql and set parameter values DynamicObjectIterator i = schema["columns"].getIterator(); while(i->hasNext()) { DynamicObject& column = i->next(); const char* memberName = column["memberName"]->getString(); // if the members map contains the given member name, create a param // for it and append it to the params array if(members->hasMember(memberName)) { /* Note: The member value is either a value, map, or an array. If it's * an array, then each entry in the array is either a value or a map. * * Create a single parameter for all values. Create an individual * parameter for every map entry. This algorithm will store all maps * in "p" and all values in "values". Then "values" will be added to * "p". Then a new parameter will be created for each entry in "p". */ DynamicObject& mv = members[memberName]; DynamicObject p(Array); DynamicObject values(Array); // group maps and values if(mv->getType() == Map) { p->append(mv); } else if(mv->getType() == Array) { DynamicObjectIterator mvi = mv.getIterator(); while(mvi->hasNext()) { DynamicObject& next = mvi->next(); if(next->getType() == Map) { p->append(next); } else { values->append(next); } } } else { values->append(mv); } // treat values as a single param if(values->length() > 0) { p->append(values); } // create params DynamicObjectIterator pi = p.getIterator(); while(pi->hasNext()) { DynamicObject& next = pi->next(); // add param DynamicObject& param = params->append(); param["name"] = column["name"]; param["type"] = column["columnType"]; param["op"] = "="; param["boolOp"] = "AND"; if(column->hasMember("encode")) { param["encode"] = column["encode"]; } if(tableAlias != NULL) { param["tableAlias"] = taObj; } // if next param is a map, get operator and value if(next->getType() == Map) { if(next->hasMember("op")) { param["op"] = next["op"].clone(); } if(next->hasMember("boolOp")) { param["boolOp"] = next["boolOp"].clone(); } param["value"] = next["value"].clone(); } else { param["value"] = next.clone(); } } } } }
bool JsonLd::normalize(DynamicObject& in, DynamicObject& out) { bool rval = true; // initialize output out->setType(Map); out->clear(); // create map to store subjects DynamicObject subjects; subjects->setType(Map); // initialize context DynamicObject ctx(NULL); if(in->hasMember("#")) { ctx = in["#"]; } // put all subjects in an array for single code path DynamicObject input; input->setType(Array); if(in->hasMember("@") && in["@"]->getType() == Array) { input.merge(in["@"], true); } else { input->append(in); } // do normalization int bnodeId = 0; DynamicObjectIterator i = input.getIterator(); while(rval && i->hasNext()) { rval = _normalize(ctx, i->next(), &subjects, NULL, bnodeId); } // build output if(rval) { // single subject if(subjects->length() == 1) { DynamicObject subject = subjects.first(); out.merge(subject, false); // FIXME: will need to check predicates for blank nodes as well... // and fail if they aren't embeds? // strip blank node '@' if(_isBlankNode(out)) { out->removeMember("@"); } } // multiple subjects else { DynamicObject& array = out["@"]; array->setType(Array); i = subjects.getIterator(); while(i->hasNext()) { DynamicObject& next = i->next(); // FIXME: will need to check predicates for blank nodes as well... // and fail if they aren't embeds? // strip blank node '@' if(_isBlankNode(next)) { next->removeMember("@"); } array->append(next); } } } return rval; }
static void customCatalogTests(Node& node, TestRunner& tr) { resetTestEnvironment(node); Messenger* messenger = node.getMessenger(); // generate the ware URLs that will be used throughout the test. Url waresUrl; waresUrl.format( "%s/api/3.0/catalog/wares?nodeuser=%" PRIu64, messenger->getSelfUrl(true).c_str(), TEST_USER_ID); Url waresNonDefaultListUrl; waresNonDefaultListUrl.format( "%s/api/3.0/catalog/wares?nodeuser=%" PRIu64 "&id=%s&default=false", messenger->getSelfUrl(true).c_str(), TEST_USER_ID, TEST_WARE_ID_2); Url wareUrl; wareUrl.format( "%s/api/3.0/catalog/wares/%s?nodeuser=%" PRIu64, messenger->getSelfUrl(true).c_str(), TEST_WARE_ID_2, TEST_USER_ID); // generate the files URL that will be used to prime the medialibrary Url filesUrl; filesUrl.format("%s/api/3.2/medialibrary/files?nodeuser=%" PRIu64, messenger->getSelfUrl(true).c_str(), TEST_USER_ID); Url removeUrl; removeUrl.format( "%s/api/3.2/medialibrary/files/%s?nodeuser=%" PRIu64, messenger->getSelfUrl(true).c_str(), TEST_FILE_ID_2, TEST_USER_ID); // Create basic and detailed test ware with id=2 Ware basicWare2; Ware detailedWare2; { basicWare2["id"] = TEST_WARE_ID_2; basicWare2["description"] = "This ware was added by test-services-customcatalog"; detailedWare2 = basicWare2.clone(); detailedWare2["mediaId"] = 2; detailedWare2["fileInfos"]->setType(Array); FileInfo fi = detailedWare2["fileInfos"]->append(); fi["id"] = TEST_FILE_ID_2; fi["mediaId"] = 2; File file((sTestDataDir + TEST_FILENAME_2).c_str()); fi["extension"] = file->getExtension() + 1; fi["contentType"] = "audio/mpeg"; fi["contentSize"] = TEST_CONTENT_SIZE_2; fi["size"] = (uint64_t)file->getLength(); fi["path"] = file->getAbsolutePath(); detailedWare2["payees"]->setType(Array); Payee p1 = detailedWare2["payees"]->append(); Payee p2 = detailedWare2["payees"]->append(); p1["id"] = 900; p1["amountType"] = PAYEE_AMOUNT_TYPE_FLATFEE; p1["amount"] = "0.10"; p1["description"] = "This payee is for media ID 2"; p2["id"] = 900; p2["amountType"] = PAYEE_AMOUNT_TYPE_PTOTAL; p2["percentage"] = "0.10"; p2["description"] = "This payee is for media ID 2"; } // remove any previous files from the media library messenger->deleteResource(&removeUrl, NULL, node.getDefaultUserId()); // pass even if there is an exception - this is meant to just clear any // previous entries in the medialibrary database if they existed. Exception::clear(); tr.group("customcatalog"); tr.test("add file to medialibrary (valid)"); { DynamicObject in; DynamicObject out; // create a FileInfo object string normalizedPath; File::normalizePath( (sTestDataDir + TEST_FILENAME_2).c_str(), normalizedPath); out["path"] = normalizedPath.c_str(); out["mediaId"] = 2; // prepare event waiter EventWaiter ew(node.getEventController()); ew.start("bitmunk.medialibrary.File.updated"); ew.start("bitmunk.medialibrary.File.exception"); // add the file to the media library assertNoException( messenger->post(&filesUrl, &out, &in, node.getDefaultUserId())); // wait for file ID set event assert(ew.waitForEvent(5*1000)); // ensure it has an exception Event e = ew.popEvent(); //dumpDynamicObject(e); if(e["details"]->hasMember("exception")) { ExceptionRef ex = Exception::convertToException( e["details"]["exception"]); Exception::set(ex); } else if(strcmp( e["type"]->getString(), "bitmunk.medialibrary.File.updated") == 0) { // add new mediaLibraryId to the basic and detailed wares basicWare2["mediaLibraryId"] = e["details"]["mediaLibraryId"]; detailedWare2["mediaLibraryId"] = e["details"]["mediaLibraryId"]; } } tr.passIfNoException(); tr.test("add ware without payee-scheme (valid)"); { DynamicObject in; DynamicObject out; // create the outgoing ware object FileInfo fi; fi["id"] = TEST_FILE_ID_2; out["id"] = TEST_WARE_ID_2; out["mediaId"] = 2; out["description"] = "This ware was added by test-services-customcatalog"; out["fileInfo"] = fi; out["payees"]->setType(Array); Payee p1 = out["payees"]->append(); Payee p2 = out["payees"]->append(); p1["id"] = 900; p1["amountType"] = PAYEE_AMOUNT_TYPE_FLATFEE; p1["amount"] = "0.10"; p1["description"] = "This payee is for media ID 2"; p2["id"] = 900; p2["amountType"] = PAYEE_AMOUNT_TYPE_PTOTAL; p2["percentage"] = "0.10"; p2["description"] = "This payee is for media ID 2"; // add the ware to the custom catalog messenger->post(&waresUrl, &out, &in, node.getDefaultUserId()); // set ware payeeSchemeIds basicWare2["payeeSchemeId"] = in["payeeSchemeId"]; detailedWare2["payeeSchemeId"] = in["payeeSchemeId"]; } tr.passIfNoException(); tr.test("get ware (valid)"); { // get a specific ware from the custom catalog Ware receivedWare; assertNoException( messenger->get(&wareUrl, receivedWare, node.getDefaultUserId())); // remove format details for comparison if(receivedWare->hasMember("fileInfos") && receivedWare["fileInfos"]->getType() == Array && receivedWare["fileInfos"]->length() == 1) { receivedWare["fileInfos"][0]->removeMember("formatDetails"); } // check to make sure that the wares match assertNamedDynoCmp( "Expected ware ResourceSet", detailedWare2, "Received ware ResourceSet", receivedWare); } tr.passIfNoException(); tr.test("get wares"); { // get a specific ware from the custom catalog Ware receivedWareSet; Url url; url.format( "%s/api/3.0/catalog/wares?nodeuser=%" PRIu64 "&id=%s", messenger->getSelfUrl(true).c_str(), TEST_USER_ID, TEST_WARE_ID_2); assertNoException( messenger->get(&url, receivedWareSet, node.getDefaultUserId())); // check ware set Ware expectedWareSet; expectedWareSet["resources"][0] = basicWare2.clone(); expectedWareSet["total"] = 1; expectedWareSet["num"] = 1; expectedWareSet["start"] = 0; assertNamedDynoCmp( "Expected ware ResourceSet", expectedWareSet, "Received ware ResourceSet", receivedWareSet); } tr.passIfNoException(); tr.test("get wares by fileId"); { // get a specific ware from the custom catalog Ware receivedWareSet; Url url; url.format( "%s/api/3.0/catalog/wares?nodeuser=%" PRIu64 "&fileId=%s", messenger->getSelfUrl(true).c_str(), TEST_USER_ID, TEST_FILE_ID_2); assertNoException( messenger->get(&url, receivedWareSet, node.getDefaultUserId())); // check ware set Ware expectedWareSet; expectedWareSet["resources"][0] = basicWare2.clone(); expectedWareSet["total"] = 1; expectedWareSet["num"] = 1; expectedWareSet["start"] = 0; assertNamedDynoCmp( "Expected ware ResourceSet", expectedWareSet, "Received ware ResourceSet", receivedWareSet); } tr.passIfNoException(); tr.test("get specific unknown wares"); { // get a specific ware from the custom catalog Ware receivedWareSet; Url url; url.format( "%s/api/3.0/catalog/wares?nodeuser=%" PRIu64 "&id=%s&id=INVALID", messenger->getSelfUrl(true).c_str(), TEST_USER_ID, TEST_WARE_ID_2); messenger->get(&url, receivedWareSet, node.getDefaultUserId()); // check ware set Ware expectedWareSet; expectedWareSet["resources"][0] = basicWare2.clone(); expectedWareSet["total"] = 1; expectedWareSet["num"] = 2; expectedWareSet["start"] = 0; assertNamedDynoCmp( "Expected ware ResourceSet", expectedWareSet, "Received ware ResourceSet", receivedWareSet); } tr.passIfNoException(); tr.test("get wares by dup ware+file ids"); { // This test gets the same ware by both ware id and file id and returns // duplicate results. // get a specific ware from the custom catalog Ware receivedWareSet; Url url; url.format( "%s/api/3.0/catalog/wares?nodeuser=%" PRIu64 "&id=%s&fileId=%s", messenger->getSelfUrl(true).c_str(), TEST_USER_ID, TEST_WARE_ID_2, TEST_FILE_ID_2); messenger->get(&url, receivedWareSet, node.getDefaultUserId()); // check ware set Ware expectedWareSet; expectedWareSet["resources"][0] = basicWare2.clone(); expectedWareSet["total"] = 1; expectedWareSet["num"] = 2; expectedWareSet["start"] = 0; assertNamedDynoCmp( "Expected ware ResourceSet", expectedWareSet, "Received ware ResourceSet", receivedWareSet); } tr.passIfNoException(); tr.test("remove ware (valid)"); { // remove the ware messenger->deleteResource(&wareUrl, NULL, node.getDefaultUserId()); } tr.passIfNoException(); /*************************** Payee Scheme tests **************************/ // generate the payee schemes URL Url payeeSchemesUrl; payeeSchemesUrl.format("%s/api/3.0/catalog/payees/schemes?nodeuser=%" PRIu64, messenger->getSelfUrl(true).c_str(), TEST_USER_ID); PayeeSchemeId psId = 0; tr.test("add payee scheme (valid)"); { DynamicObject in; DynamicObject out; // create the outgoing list of payees to organize into a payee scheme out["description"] = "Payee scheme description 1"; out["payees"]->setType(Array); PayeeList payees = out["payees"]; Payee p1 = payees->append(); Payee p2 = payees->append(); p1["id"] = 900; p1["amountType"] = PAYEE_AMOUNT_TYPE_FLATFEE; p1["amount"] = "0.10"; p1["description"] = "test-services-customcatalog test payee 1"; p2["id"] = 900; p2["amountType"] = PAYEE_AMOUNT_TYPE_PTOTAL; p2["percentage"] = "0.10"; p2["description"] = "test-services-customcatalog test payee 2"; // add the ware to the custom catalog messenger->post(&payeeSchemesUrl, &out, &in, node.getDefaultUserId()); if(in->hasMember("payeeSchemeId")) { psId = in["payeeSchemeId"]->getUInt32(); } } tr.passIfNoException(); // generate the payee scheme URL Url payeeSchemeIdUrl; payeeSchemeIdUrl.format("%s/api/3.0/catalog/payees/schemes/%u?nodeuser=%" PRIu64, messenger->getSelfUrl(true).c_str(), psId, TEST_USER_ID); tr.test("get payee scheme (valid)"); { // Create the expected payee scheme PayeeScheme expectedPayeeScheme; expectedPayeeScheme["id"] = psId; expectedPayeeScheme["userId"] = node.getDefaultUserId(); expectedPayeeScheme["description"] = "Payee scheme description 1"; expectedPayeeScheme["payees"]->setType(Array); Payee p1 = expectedPayeeScheme["payees"]->append(); Payee p2 = expectedPayeeScheme["payees"]->append(); p1["id"] = 900; p1["amountType"] = PAYEE_AMOUNT_TYPE_FLATFEE; p1["amount"] = "0.10"; p1["description"] = "test-services-customcatalog test payee 1"; p2["id"] = 900; p2["amountType"] = PAYEE_AMOUNT_TYPE_PTOTAL; p2["percentage"] = "0.10"; p2["description"] = "test-services-customcatalog test payee 2"; // get a specific payee scheme from the custom catalog PayeeScheme receivedPayeeScheme; messenger->get( &payeeSchemeIdUrl, receivedPayeeScheme, node.getDefaultUserId()); // check payee schemes assertNamedDynoCmp( "Expected payee scheme", expectedPayeeScheme, "Received payee scheme", receivedPayeeScheme); } tr.passIfNoException(); tr.test("get all payee schemes (valid)"); { // Create the result set ResourceSet expectedResults; expectedResults["total"] = 2; expectedResults["start"] = 0; expectedResults["num"] = 2; PayeeScheme ps = expectedResults["resources"]->append(); ps["id"] = 1; ps["userId"] = node.getDefaultUserId(); ps["description"] = "This ware was added by test-services-customcatalog"; ps["payees"]->setType(Array); Payee p1 = ps["payees"]->append(); Payee p2 = ps["payees"]->append(); p1["id"] = 900; p1["amountType"] = PAYEE_AMOUNT_TYPE_FLATFEE; p1["amount"] = "0.10"; p1["description"] = "This payee is for media ID 2"; p2["id"] = 900; p2["amountType"] = PAYEE_AMOUNT_TYPE_PTOTAL; p2["percentage"] = "0.10"; p2["description"] = "This payee is for media ID 2"; ps = expectedResults["resources"]->append(); ps["id"] = 2; ps["userId"] = node.getDefaultUserId(); ps["description"] = "Payee scheme description 1"; ps["payees"]->setType(Array); Payee p3 = ps["payees"]->append(); Payee p4 = ps["payees"]->append(); p3["id"] = 900; p3["amountType"] = PAYEE_AMOUNT_TYPE_FLATFEE; p3["amount"] = "0.10"; p3["description"] = "test-services-customcatalog test payee 1"; p4["id"] = 900; p4["amountType"] = PAYEE_AMOUNT_TYPE_PTOTAL; p4["percentage"] = "0.10"; p4["description"] = "test-services-customcatalog test payee 2"; // get all payee schemes from the custom catalog ResourceSet receivedResults; messenger->get( &payeeSchemesUrl, receivedResults, node.getDefaultUserId()); // check payee schemes assertNamedDynoCmp( "Expected payee scheme ResourceSet", expectedResults, "Received payee scheme ResourceSet", receivedResults); } tr.passIfNoException(); tr.test("update payee scheme (valid)"); { DynamicObject in; DynamicObject out; // create the outgoing list of payees to organize into a payee scheme out["description"] = "Payee scheme description 2"; Payee p1 = out["payees"]->append(); Payee p2 = out["payees"]->append(); p1["id"] = 900; p1["amountType"] = PAYEE_AMOUNT_TYPE_FLATFEE; p1["amount"] = "0.15"; p1["description"] = "test-services-customcatalog test payee 1 (updated)"; p2["id"] = 900; p2["amountType"] = PAYEE_AMOUNT_TYPE_PTOTAL; p2["percentage"] = "0.14"; p2["description"] = "test-services-customcatalog test payee 2 (updated)"; // update a pre-existing payee scheme assertNoException( messenger->post( &payeeSchemeIdUrl, &out, &in, node.getDefaultUserId())); // ensure that the payee scheme was updated assert(in["payeeSchemeId"]->getUInt32() == 2); if(in->hasMember("payeeSchemeId")) { psId = in["payeeSchemeId"]->getUInt32(); } } tr.passIfNoException(); tr.test("add ware with payee scheme (valid)"); { DynamicObject in; DynamicObject out; // create the outgoing ware object FileInfo fi; fi["id"] = TEST_FILE_ID_2; out["id"] = TEST_WARE_ID_2; out["mediaId"] = 2; out["description"] = "This ware was added by test-services-customcatalog"; out["fileInfo"] = fi; out["payeeSchemeId"] = psId; // add the ware to the custom catalog messenger->post(&waresUrl, &out, &in, node.getDefaultUserId()); } tr.passIfNoException(); tr.test("remove associated payee scheme (invalid)"); { messenger->deleteResource( &payeeSchemeIdUrl, NULL, node.getDefaultUserId()); } tr.passIfException(); tr.test("remove ware associated w/ payee scheme (valid)"); { messenger->deleteResource( &wareUrl, NULL, node.getDefaultUserId()); } tr.passIfNoException(); tr.test("remove payee scheme (valid)"); { messenger->deleteResource( &payeeSchemeIdUrl, NULL, node.getDefaultUserId()); } tr.passIfNoException(); tr.test("create ware w/ invalid payee scheme (invalid)"); { DynamicObject in; DynamicObject out; // create the outgoing ware object FileInfo fi; fi["id"] = TEST_FILE_ID_2; PayeeScheme ps; ps["id"] = psId; out["id"] = TEST_WARE_ID_2; out["mediaId"] = 2; out["description"] = "This ware was added by test-services-customcatalog"; out["fileInfo"] = fi; out["payeeScheme"] = ps; // add the ware to the custom catalog messenger->post(&waresUrl, &out, &in, node.getDefaultUserId()); } tr.passIfException(); tr.test("remove ware (invalid)"); { // remove a ware that doesn't exist messenger->deleteResource(&wareUrl, NULL, node.getDefaultUserId()); } tr.passIfException(); tr.ungroup(); }
bool DatabaseClient::mapInstance( const char* objType, DynamicObject& obj, DynamicObject& mapping, DynamicObject* userData) { bool rval = false; /* Algorithm to create a column mapping to the values in the given object: 1. Get the OR mapping for the object type. 2. Iterate over the members in the mapping, assigning each one's associated information to a matching member in the instance object. 3. Include any values from the instance object in the instance mapping validating and coercing types as needed. */ /* The instance mapping format: * * mapping: { * "tables": {} of table name to table entry * "entries": [ * "table": the database table name * "columns": [ * (cloned info from the OR mapping) + * "member": the object member name * "value": the value for the column (to apply via an operator, * coerced to columnType) * "userData": as given to this call * ], * "fkeys": [ * (cloned info from the OR mapping) + * "member": the object member name * "value": the value for "fcolumn" (coerced to columnType) * "userData": as given to this call * ] * ] * } */ // initialize mapping mapping["tables"]->setType(Map); mapping["entries"]->setType(Array); // get OR map for the given object type ObjRelMap orMap = getObjRelMap(objType); if(!orMap.isNull()) { rval = true; DynamicObjectIterator i = orMap["members"].getIterator(); while(rval && i->hasNext()) { // get OR member info DynamicObject& info = i->next(); // if object is NULL, then we want to get ALL members if(obj.isNull() || obj->hasMember(i->getName())) { // start building an instance mapping entry DynamicObject entry(NULL); // add/update entry based on table const char* table = info["table"]->getString(); if(mapping["tables"]->hasMember(table)) { // update existing entry entry = mapping["tables"][table]; } else { // add a new entry entry = mapping["entries"]->append(); entry["table"] = info["table"]; entry["columns"]->setType(Array); entry["fkeys"]->setType(Array); if(orMap->hasMember("autoIncrement") && orMap["autoIncrement"]->hasMember(table)) { const char* id = orMap["autoIncrement"][table]->getString(); entry["autoIncrement"]["id"] = id; entry["autoIncrement"]["type"]->setType( orMap["members"][id]["columnType"]->getType()); } mapping["tables"][table] = entry; } // clone info, add member name and user-data DynamicObject d = info.clone(); d["member"] = i->getName(); if(userData != NULL) { d["userData"] = *userData; } // set value if(obj.isNull()) { d["value"]; } else { // FIXME: validate data type d["value"] = obj[i->getName()].clone(); } // coerce data type to match column type d["value"]->setType(d["columnType"]->getType()); // add to entry based on group entry[info["group"]->getString()]->append(d); } } } return rval; }
/** * Normalizes a value using the given context. * * @param ctx the context. * @param value the value to normalize. * @param type the expected RDF type, use RDF_TYPE_UNKNOWN if not known. * @param predicate an optional predicate for the value (used to look up * type coercion info). * @param usedCtx a context to update if a value was used from "ctx". * * @return the normalized string. */ static string _normalizeValue( DynamicObject& ctx, DynamicObject value, RdfType type, const char* predicate, DynamicObject* usedCtx) { string rval; // "@" or "a"/RDF_TYPE predicates have values that are IRIs or CURIEs if(predicate != NULL && (strcmp(predicate, "@") == 0 || strcmp(predicate, "a") == 0 || strcmp(predicate, RDF_TYPE_NORM) == 0)) { type = RDF_TYPE_IRI; } // IRI "@" is already normalized if(type == RDF_TYPE_IRI && strcmp(value, "@") == 0) { rval.assign(value); } // IRI "a" is special rdf type else if(type == RDF_TYPE_IRI && strcmp(value, "a") == 0) { rval.assign(RDF_TYPE_NORM); } else { string datatype; // look for type coercion info if(type == RDF_TYPE_UNKNOWN && predicate != NULL && !ctx.isNull() && ctx->hasMember("#types") && ctx["#types"]->hasMember(predicate)) { DynamicObject& tci = ctx["#types"][predicate]; if(usedCtx != NULL) { (*usedCtx)["#types"][predicate] = tci.clone(); } // handle specific type if(tci->getType() == String) { rval = value->getString(); datatype = _normalizeValue(ctx, tci, RDF_TYPE_IRI, NULL, usedCtx); type = (strcmp(datatype.c_str(), XSD_ANY_URI_NORM) == 0) ? RDF_TYPE_IRI : RDF_TYPE_TYPED_LITERAL; } // handle type preferences in order else { DynamicObjectIterator ti = tci.getIterator(); while(type == RDF_TYPE_UNKNOWN && ti->hasNext()) { // FIXME: if value works w/type, set value, datatype, type //type = RDF_TYPE_TYPED_LITERAL; } } } // determine type from DynamicObjectType else if(type == RDF_TYPE_UNKNOWN && value->getType() != String) { type = RDF_TYPE_TYPED_LITERAL; rval = value->getString(); // handle native xsd types if(value->isNumber()) { datatype = value->isInteger() ? XSD_INTEGER : XSD_DOUBLE; } else if(value->getType() == Boolean) { datatype = XSD_BOOLEAN; } else { // FIXME: this should never happen? datatype = XSD_ANY_TYPE; } } else { // JSON-LD decode RdfType t = type; _decode(value, type, rval, datatype); if(type == RDF_TYPE_TYPED_LITERAL) { _expandCurie(ctx, datatype.c_str(), datatype, usedCtx); } // preserve expected type if(t != RDF_TYPE_UNKNOWN) { type = t; } } // expand CURIE if(type == RDF_TYPE_IRI) { _expandCurie(ctx, rval.c_str(), rval, usedCtx); } // JSON-LD encode rval = _encode(type, rval.c_str(), datatype.c_str()); } return rval; }