Giter VIP home page Giter VIP logo

dsentric's Introduction

Build Status Coverage Status License

dsentric

Data contract patterns, validation, lenses and query

dsentric is an explorative library designed to provided accessor to dynamic data which is independent of the underlying data type.

There is a monocle implementation which can be extended to customised data types.

There is also a very light-weight, performance focussed Map[String, Any], Vector[Any] based data structure implementation as well. This also supports further functionality such as querying and projections.

dsentric works by describing a singleton contract which represents data we might wish to extract from the data structure. By doing so, we get easy validation, lenses and even a type safe mongo db query generator.

  /*define a contract,
    \  \?  \! expected, optional, default properties
    \: \:?  \:!  expected, optional, default array properties
    \\ \\? expected, option object properties
  */
  object OrderLine extends Contract {
    val product = \[String]
    val quanity = \[Int]
  }
  
  object Order extends Contract {
    val firstName = \[String](nonEmptyOrWhiteSpace)
    val lastName = \[String](nonEmptyOrWhiteSpace)
    val orderId = \?[Int](reserved && immutable)

    val email = new \\ {
      val friendlyName = \?[String]
      val address = \[String]
    }
    val status = \?[String](in("pending", "processing", "sent") && reserved)
    val notes = \?[String](internal)

    val orderLines = \:(OrderLines)

    import ApplicationLens._
    //Combine properties to make a composite pattern matcher
    lazy val fullName = firstName @: lastName
  }

  //Create a new data object
  val newOrder = Order.$create{o =>
    o.firstName.$set("John") ~
    o.lastName.$set("Smith") ~
    o.email.address.$set("[email protected]") ~
    o.orderLines.$set(Vector(OrderLine.$create(l => l.product.$set("LightBulb") ~ l.quantity.$set(3))))
  }

  //validate a new data object
  val validated = Order.$validate(newOrder)

  //pattern match property values
  newOrder match {
    case Order.email.address(email) && Order.email.friendlyName(Some(name)) =>
      println(s"$email <$name>")
    case Order.email.address(email) && Order.fullName(firstName, lastName) =>
      println(s"$email <$firstName $lastName>")
  }

  //make changes to the data object.
  val pending =
    Order{o =>
      o.orderId.$set(123) ~
      o.status.$set("pending") ~
      o.notes.$modify(maybe => maybe.foldLeft("Order now pending.")(_ + _))
    }(newOrder)

  //strip out any properties marked internal
  val sendToClient = Order.$sanitize(pending)

  //generate query data
  val relatedOrdersQuery = Order.orderId.$gt(56) && Order.status.$in("processing", "sent")
  
  relatedOrdersQuery.isMatch(pending)
  
  //This produces a mongo query data structure
  
  //experimental convert to postgres jsonb clause
  val postgresQuery = QueryJsonb("data", relatedOrdersQuery)

  val statusDelta = Order.$create(_.status.$set("processing"))
  //validate against current state
  Order.$validate(statusDelta, pending)
  //apply delta to current state
  val processing = pending.applyDelta(statusDelta)

  //Define subcontract for reusable or recursive structures
  trait UserTimestamp extends SubContract {
    val account = \[String]
    val timestamp = \[Long]("timestamp")
  }
  object Element extends Contract {
    val created = new \\(immutable) with UserTimestamp
    val modified = new \\ with UserTimestamp
  }
  
  trait Recursive extends SubContract {
    lazy val rec = new \\? with Recursive
  }
  
  //Produce a mongo style projection which extracts only those fields
  val nameProjection = Order.firstName.$ & Order.lastName.$
  nameProjection.select(pending)
  
  //Create your own domain specific DObject
  case class Custom(value:Map[String, Any]) extends AnyVal with DObject with DObjectLike[Custom] {
    protected def wrap(value: Map[String, Any]): Custom =
      Custom(value)
  }
  //With its own contract type
  object CustomContract extends ContractFor[Custom]

*mongo query is not a full feature set.

#Verify

#Delta Verify

#Additional Properties object

  • Value object with Contract
  • Returns a D <: DObject
  • Number of dedicated lens operation for addional properties, like add etc
  • Get Additional Properties as a Map method?

#Map Object

  • Value object with Contract
  • Returns a Map of correct types
  • How do we resolve a map of a map of a contract? ie Map[String, Map[String, D]] Contract generates an internal DCodec for D? No not enough, unless we have a contract Decodec type?

##Needs a specific Delta object

(I think validate only one the delta application result not the delta value itself, IE pass through currentState and previousState, could be slow on nested objects so need to optimise)

  • Delta context - IE full replace of an nested object rather than merge (Thinking Fixtures)

  • Perhaps the difference here is A DObject or \ will delta merge, however a Case Class Type will replace?

  • $get should work on Deltas/Dobjects as well as just D ie so can use contract to get delta value. OR you could provide a property object to a dobject to extract the value obj.apply(pProperty[T, D]):Option[T]

Notion of emptiable objects, IE if they are empty we remove them, thinking of say a Range object ( ), or the Object object Perhaps the notion of emptiness can be confgurable?

?Notion of Healing support, when merging two objects,

  • could ignore bad merged value
  • In a Map[Key, Object] it could drop bad objects...

How to handle a keyed object map Ie Map(Key, Element(Key)(values)) - might be solved by above validation :)

function to extract paths. Validation Immutable on optional, Ie must leave blank if blank initially. Maybe immutable and immutableOnSet? Query Tree Needs more robust tests Include boolean identities Ignore if not create How to handle an update on a removed nested object? Immutable required properties help... DObject sub classing

PathSetter supports sub class transform, what about super class, Dobject => Dobject applied to .a D for example. Needs a consistent elegant approach

WARNING

when adding a Contract element, ie as an Additional Property or element in a vector, the process doesnt validate correctness as being of the correct Type is sufficient under current definition. We know this is fine for types, but for Contract DOBjects, we do allow adding manually adding elements that dont support the contract, where as say a delta operation, this would be checked. Corollary For Modify it will validate the entity coming out of the Object but not the value going in

This may need to change in the future.

Maps are tricky, empty maps will reduce to nothing

Terminology

set - It is not the responsibility of Dsentric to verify the values being set, beyond standard property type Setting an empty object will remove the value, assuming its type is valid.

unapply - extract the value, type match return None if invalid type. If Contract match, also return None if contract is not valid.

traversal - We dont need to validate contracts in the traversal path, Just need to make sure it can be traversed

its possible to get a default value for an expected object that is not present
As the Expected object will return an empty object in the traversal if its not found

Traversal paths go through both the contract and the codecs, as codecs can reference contracts.

reduce - Removes extraneous values. This would remove empty objects Would clear out null values If a codec returns NotFound on raw data

get - extract the value, return any type or other structural failures

Delta - if an expected property is empty, delta is Not required to fix it.
Delta does not need to repair, just cannot make things worse - If delta value is sent, but is the same as current value, but current value is invalid, it will fail validation.

---Expected -> unapply Some(t) or None [failure or empty] ---Maybe -> unapply Some(Some(t)) or Some(None)[empty] or None [failure]

---Maybe (Empty) ---Expected -> unapply Some(None)[path empty] ---Expected -> unapply Some(None)[path empty] ---Expected -> unapply Some(None)[empty]

---Maybe (Valid) ---Expected -> unapply Some(Some(t)) or None[failure or empty] ---Expected -> unapply Some(None) [pathEmpty] Some(t) or None [failure] Some(Empty) [empty] ---Expected -> unapply Some(None)

---Expected ---Expected -> unapply Option[T]

---Maybe (empty) ---Expected ---Expected == PathMaybeEmpty

---Maybe ---Expected (empty) ---Expected == NotFound

TODO:

Delta, $get or unapply on a map type wont handle nulls in the value part.

Projections PAthSetters Delta Function

Review Projections - simply properties and projections, so can do use a property as a project Projections Projections Should support WildCard key match, or regex key match maybe "$/*/" Projection building ie DProjection("nested" -> {​​​​​​​DProjection}​​​​​​​)

ADD: Contract.property.$contains()

Operation, Can we have PathSetters that apply to all additional properties or values in a map or array... good for masking. Conditional mask remove flags?

dsentric's People

Contributors

concentralukecollier avatar jamiepullar avatar jbwheatley avatar jpullar-orgvue avatar nahuaque avatar sakis-orgvue avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.