diff --git a/README.markdown b/README.markdown
index 8af4a02b..0ecdfec0 100644
--- a/README.markdown
+++ b/README.markdown
@@ -1,6 +1,8 @@
Gnieh Diffson [](https://travis-ci.org/gnieh/diffson) [](https://www.codacy.com/app/satabin/diffson?utm_source=github.com&utm_medium=referral&utm_content=gnieh/diffson&utm_campaign=Badge_Grade) [](https://codecov.io/github/gnieh/diffson?branch=master) [](https://maven-badges.herokuapp.com/maven-central/org.gnieh/diffson-core_2.13) [](https://javadoc.io/doc/org.gnieh/diffson-core_2.13)
=============
+Extended version of library to be able to use `*` operation on array so it is possible execute operations on all array elements.
+
[](https://gitter.im/gnieh/diffson?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
A [scala][6] implementation of the [RFC-6901][1], [RFC-6902][2], and [RFC-7396][11].
diff --git a/build.sbt b/build.sbt
index f74fde5f..9ab379c7 100644
--- a/build.sbt
+++ b/build.sbt
@@ -4,12 +4,24 @@ import sbtcrossproject.CrossPlugin.autoImport.{crossProject, CrossType}
val scala212 = "2.12.10"
val scala213 = "2.13.1"
+lazy val publishSettings: Seq[Setting[_]] = Seq(
+ publishTo := Some(Resolver.url("upstartcommerce", url("https://upstartcommerce.jfrog.io/artifactory/generic"))(Resolver.ivyStylePatterns)),
+ credentials += Credentials(Path.userHome / ".sbt" / ".credentials"),
+ publishMavenStyle := false)
+
+lazy val notPublishSettings = Seq(
+ publishArtifact := false,
+ skip in publish := true,
+ publishLocal := {},
+ publish := {}
+)
+
lazy val commonSettings = Seq(
- organization := "org.gnieh",
- scalaVersion := scala213,
- version := "4.0.2",
+ organization := "com.upstartcommerce",
+ scalaVersion := scala212,
+ version := "4.0.3",
description := "Json diff/patch library",
- licenses += ("The Apache Software License, Version 2.0" -> url("http://www.apache.org/licenses/LICENSE-2.0.txt")),
+ licenses ++= Seq("The Apache Software License, Version 2.0" -> url("http://www.apache.org/licenses/LICENSE-2.0.txt")),
homepage := Some(url("https://github.com/gnieh/diffson")),
parallelExecution := false,
scalacOptions ++= PartialFunction.condOpt(CrossVersion.partialVersion(scalaVersion.value)) {
@@ -46,20 +58,23 @@ lazy val commonSettings = Seq(
.setPreference(DoubleIndentConstructorArguments, true)
.setPreference(MultilineScaladocCommentsStartOnFirstLine, true)
.setPreference(DanglingCloseParenthesis, Prevent)
- }) ++ publishSettings
+ })
lazy val diffson = project.in(file("."))
.enablePlugins(ScoverageSbtPlugin)
.settings(commonSettings: _*)
+ .settings(notPublishSettings: _*)
.settings(
name := "diffson",
- packagedArtifacts := Map())
+ packagedArtifacts := Map(),
+ )
.aggregate(core.jvm, core.js, sprayJson, circe.jvm, circe.js, playJson.jvm, playJson.js, testkit.jvm, testkit.js)
lazy val core = crossProject(JSPlatform, JVMPlatform)
.crossType(CrossType.Pure).in(file("core"))
.enablePlugins(ScoverageSbtPlugin, ScalaUnidocPlugin)
.settings(commonSettings: _*)
+ .settings(publishSettings)
.settings(
name := "diffson-core",
crossScalaVersions := Seq(scala212, scala213),
@@ -69,25 +84,29 @@ lazy val core = crossProject(JSPlatform, JVMPlatform)
"io.estatico" %%% "newtype" % "0.4.3",
"org.scalatest" %%% "scalatest" % "3.1.1" % Test,
"org.scalacheck" %%% "scalacheck" % "1.14.3" % Test
- ))
+ ),
+ )
.jsSettings(coverageEnabled := false)
lazy val testkit = crossProject(JSPlatform, JVMPlatform)
.crossType(CrossType.Full).in(file("testkit"))
.enablePlugins(ScoverageSbtPlugin)
.settings(commonSettings: _*)
+ .settings(notPublishSettings: _*)
.settings(
name := "diffson-testkit",
crossScalaVersions := Seq(scala212, scala213),
libraryDependencies ++= Seq(
"org.scalatest" %%% "scalatest" % "3.1.1",
- "org.scalacheck" %%% "scalacheck" % "1.14.3"))
+ "org.scalacheck" %%% "scalacheck" % "1.14.3")
+ )
.jsSettings(coverageEnabled := false)
.dependsOn(core)
lazy val sprayJson = project.in(file("sprayJson"))
.enablePlugins(ScoverageSbtPlugin)
.settings(commonSettings: _*)
+ .settings(notPublishSettings: _*)
.settings(
name := "diffson-spray-json",
crossScalaVersions := Seq(scala212, scala213),
@@ -98,10 +117,12 @@ lazy val playJson = crossProject(JSPlatform, JVMPlatform)
.crossType(CrossType.Full).in(file("playJson"))
.enablePlugins(ScoverageSbtPlugin)
.settings(commonSettings: _*)
+ .settings(publishSettings)
.settings(
name := "diffson-play-json",
libraryDependencies += "com.typesafe.play" %%% "play-json" % "2.8.1",
- crossScalaVersions := Seq(scala212, scala213))
+ crossScalaVersions := Seq(scala212, scala213),
+ )
.jsSettings(coverageEnabled := false)
.dependsOn(core, testkit % Test)
@@ -110,6 +131,7 @@ lazy val circe = crossProject(JSPlatform, JVMPlatform)
.crossType(CrossType.Full).in(file("circe"))
.enablePlugins(ScoverageSbtPlugin)
.settings(commonSettings: _*)
+ .settings(notPublishSettings: _*)
.settings(
name := "diffson-circe",
libraryDependencies ++= Seq(
@@ -123,39 +145,3 @@ lazy val circe = crossProject(JSPlatform, JVMPlatform)
libraryDependencies += "io.circe" %%% "not-java-time" % "0.2.0"
)
.dependsOn(core, testkit % Test)
-
-lazy val publishSettings = Seq(
- publishMavenStyle := true,
- publishArtifact in Test := false,
- // The Nexus repo we're publishing to.
- publishTo := Some(
- if (isSnapshot.value)
- Opts.resolver.sonatypeSnapshots
- else
- Opts.resolver.sonatypeStaging
- ),
- pomIncludeRepository := { x => false },
- pomExtra := (
-
- https://github.com/gnieh/diffson
- scm:git:git://github.com/gnieh/diffson.git
- scm:git:git@github.com:gnieh/diffson.git
- HEAD
-
-
-
- satabin
- Lucas Satabin
- lucas.satabin@gnieh.org
-
-
-
- travis
- https://travis-ci.org/#!/gnieh/diffson
-
-
- github
- https://github.com/gnieh/diffson/issues
-
- )
-)
diff --git a/core/src/main/scala/diffson/jsonpatch/JsonPatch.scala b/core/src/main/scala/diffson/jsonpatch/JsonPatch.scala
index 9a853df0..d695ef25 100644
--- a/core/src/main/scala/diffson/jsonpatch/JsonPatch.scala
+++ b/core/src/main/scala/diffson/jsonpatch/JsonPatch.scala
@@ -49,6 +49,8 @@ sealed abstract class Operation[Json: Jsony] {
JsArray(before ++ (updated +: after.tail))
}
}
+ case (JsArray(elems), Inner(Left("*"), tl)) =>
+ elems.map(action[F](_, tl, parent)).traverse(identity).map(JsArray(_))
case (_, Inner(elem, _)) =>
F.raiseError(new PatchException(show"element ${elem.fold(identity[String], _.toString)} does not exist at path $parent"))
}
@@ -97,6 +99,8 @@ case class Remove[Json: Jsony](path: Pointer, old: Option[Json] = None) extends
val (before, after) = arr.splitAt(idx)
F.pure(JsArray(before ++ after.tail))
}
+ case (JsArray(_), Leaf(Left("*"))) =>
+ F.pure(JsArray(Vector()))
case (JsArray(_), Leaf(Left("-"))) =>
// how could we possibly remove an element that appears after the last one?
F.raiseError(new PatchException(show"element - does not exist at path $parent"))
@@ -124,6 +128,8 @@ case class Replace[Json: Jsony](path: Pointer, value: Json, old: Option[Json] =
F.raiseError(new PatchException(show"element $idx does not exist at path $parent"))
else
F.pure(JsArray(arr.updated(idx, value)))
+ case (JsArray(arr), Leaf(Left("*"))) =>
+ F.pure(JsArray(arr.map(_ => value)))
case (JsArray(_), Leaf(Left("-"))) =>
F.raiseError(new PatchException(show"element - does not exist at path $parent"))
case (JsObject(obj), Leaf(ObjectField(lbl))) =>
diff --git a/testkit/jvm/src/main/resources/conformance/tests.json b/testkit/jvm/src/main/resources/conformance/tests.json
index dd060da0..63654679 100644
--- a/testkit/jvm/src/main/resources/conformance/tests.json
+++ b/testkit/jvm/src/main/resources/conformance/tests.json
@@ -69,6 +69,11 @@
"patch": [{"op": "add", "path": "/baz/0/foo", "value": "world"}],
"expected": {"foo": 1, "baz": [{"qux": "hello", "foo": "world"}]} },
+ { "comment": "Add into all array elements",
+ "doc": {"foo": 1, "baz": [{"qux": "hello"}, {"qux": "bye"}]},
+ "patch": [{"op": "add", "path": "/baz/*/foo", "value": "world"}],
+ "expected": {"foo": 1, "baz": [{"qux": "hello", "foo": "world"}, {"qux": "bye", "foo": "world"}]} },
+
{ "doc": {"bar": [1, 2]},
"patch": [{"op": "add", "path": "/bar/8", "value": "5"}],
"error": "element 8 does not exist at path /bar" },
@@ -145,6 +150,11 @@
"patch": [{"op": "replace", "path": "/baz/0/qux", "value": "world"}],
"expected": {"foo": [1, 2, 3, 4], "baz": [{"qux": "world"}]} },
+ { "comment": "test handle asterisk correctly on replace array",
+ "doc": {"foo": [1, 2, 3, 4], "baz": [{"qux": "hello"}, {"qux": "hi"}]},
+ "patch": [{"op": "replace", "path": "/baz/*/qux", "value": "newWorld"}],
+ "expected": {"foo": [1, 2, 3, 4], "baz": [{"qux": "newWorld"}, {"qux": "newWorld"}]} },
+
{ "doc": ["foo"],
"patch": [{"op": "replace", "path": "/0", "value": "bar"}],
"expected": ["bar"] },
@@ -264,6 +274,11 @@
"patch": [ { "op": "add", "path": "/2/1/-", "value": { "foo": [ "bar", "baz" ] } } ],
"expected": [ 1, 2, [ 3, [ 4, 5, { "foo": [ "bar", "baz" ] } ] ] ]},
+ { "comment": "test remove with asterisk should remove all items",
+ "doc": {"foo": 1, "baz": [{"qux": "hello"}, {"qux": "hello1"}]},
+ "patch": [{"op": "remove", "path": "/baz/*"}],
+ "expected": {"foo": 1, "baz": []}},
+
{ "comment": "test remove with bad number should fail",
"doc": {"foo": 1, "baz": [{"qux": "hello"}]},
"patch": [{"op": "remove", "path": "/baz/1e0/qux"}],
@@ -346,4 +361,4 @@
"doc": {"foo": 1},
"patch": [{"op": "spam", "path": "/foo", "value": 1}],
"error": "Unknown operation \"spam\"" }
-]
+]
\ No newline at end of file