Code Monkey home page Code Monkey logo

xquery-versioning-module's Introduction

Versioning Module for eXist-db XQuery

Build Status Java 8 License

This repository holds the source code for the eXist-db XQuery Versioning extension module.

Compiling

Requirements: Java 8, Maven 3.

  1. git clone https://github.com/eXist-db/xquery-versioning-module.git

  2. cd xquery-versioning-module

  3. mvn package

You will then find a file named similar to target/xquery-versioning-module-1.1.4.xar.

Installation into eXist-db

You can install the module into eXist-db in either one of two ways:

  1. As an EXPath Package (.xar file)
  2. Directly as a XQuery Java Extension Module (.jar file)

EXPath Package Installation into eXist-db (.xar)

  1. If you have compiled yourself (see above), you can take the target/xquery-versioning-module-1.1.4.xar file and upload it via eXist's EXPath Package Manager app in its Dashboard

  2. Otherwise, the latest release version will also be available from the eXist's EXPath Package Manager app in its Dashboard

Direct Installation into eXist-db (.jar)

  1. If you have compiled yourself (see above), copy target/xquery-versioning-module-1.1.4-exist.jar to $EXIST_HOME/lib/user.

  2. Edit $EXIST_HOME/conf.xml and add the following to the <builtin-modules>:

    <module uri="http://exist-db.org/xquery/versioning" class="org.exist.versioning.xquery.VersioningModule"/>
  3. Restart eXist-db

Example collection.xconf Trigger Configuration

<collection xmlns="http://exist-db.org/collection-config/1.0">
    <triggers>
        <trigger event="create,update,delete"
            class="org.exist.versioning.VersioningTrigger">
            <parameter name="overwrite" value="no"/>
        </trigger>
    </triggers>
</collection>

API Overview

Namespace URI: http://exist-db.org/xquery/versioning

Namespace Prefix: versioning

Class: org.exist.versioning.xquery.VersioningModule

  1. To find the differences between two nodes:

    versioning:diff($a as node(), $b as node()) as node()
  2. To apply a patch to a node:

    versioning:patch($node as node(), $patch as node()) as item()
  3. To annotate a node:

    versioning:annotate($node as node(), $patch as node()) as item()

Utility API Overview

Namespace URI: http://exist-db.org/versioning

Namespace Prefix: v

file: versioning.xqm

  1. Return all revisions of the specified document as a sequence of xs:integer revision numbers in ascending order:

    v:revisions($doc as node()) as xs:integer*
  2. Return all version docs, including the full diff, for the specified document:

    v:versions($doc as node()) as element(v:version)*
  3. Restore a certain revision of a document by applying a sequence of diffs and return it as an in-memory node:

    v:doc($doc as node(), $rev as xs:integer?) as node()*
  4. Apply a given patch on a document:

    v:apply-patch($doc as node(), $diffs as element(v:version)*)
  5. For the document passed as first argument, retrieve the revision specified in the second argument. Generate a diff between both version, i.e. HEAD and the given revision:

    v:diff($doc as node(), $rev as xs:integer) as element(v:version)?
  6. Return an XML document in which all changes between $rev` and $rev - 1` are annotated.

    v:annotate($doc as node(), $rev as xs:integer)
  7. Check if there are any revisions in the database which are newer than the version identified by the specified base revision and key:

    v:find-newer-revision($doc as node(), $base as xs:integer, $key as xs:string) as element(v:version)?
  8. Returns an XML fragment showing the version history of the document to which the specified node belongs. All revisions are listed with date and user, but without the detailed diff:

    v:history($doc as node()) as element(v:history)

xquery-versioning-module's People

Contributors

adamretter avatar chaeron avatar dependabot-preview[bot] avatar dependabot-support avatar dependabot[bot] avatar dizzzz avatar duncdrum avatar gev avatar joewiz avatar ljo avatar reinhapa avatar shabanovd avatar tgraham-antenna avatar wolfgangmm avatar xristy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

xquery-versioning-module's Issues

classcast BrokerPool.PROPERTY_DATA_DIR

In exist 3.1 this property is a Path.

on line 463 of VersioningTrigger this fails:

final String dataDir = (String) pool.getConfiguration().getProperty(BrokerPool.PROPERTY_DATA_DIR);

Function v:diff() is not defined in namespace 'http://exist-db.org/versioning'

Hello,

I have eXist 4.0.0 running on CentOS 7 and I'm using the versioning module offered in the package manager. I can successfully use v.history or v:revisions. Nevertheless, if I type something like

xquery version "3.1";

import module namespace v="http://exist-db.org/versioning";
v:diff(doc("/db/apps/app-ct/data/indices/pedb.xml"))

into eXide, it complains that Function v:diff() is not defined in namespace 'http://exist-db.org/versioning. Do I need to install something additional?

"VersioningFilter" is not applicable

What is the problem

The VersioningFilter is not applicable.

What did you expect

With the serialization filter enabled, I expect to see the added metadata attributes (like v:revision, v:key) when accessing a document, as described here.

Describe how to reproduce or add a test

  • Compile the xquery-versioning-module project as explained here (with the Java 8 and Maven 3 requirement).

  • Copy the built target/xquery-versioning-module-1.1.2-exist.jar into $EXIST_HOME/lib/user.

  • In $EXIST_HOME/conf.xml, add the following line in the <builtin-modules> element as explained here (paragraph Direct Installation into eXist-db (.jar)):
    <module uri="http://exist-db.org/xquery/versioning" class="org.exist.versioning.xquery.VersioningModule"/>.

  • In $EXIST_HOME/conf.xml, enable the VersioningFilter by adding the following line in the <serializer> element as explained here:
    <custom-filter class="org.exist.versioning.VersioningFilter"/>.

  • Restart eXist-db.

  • Configure a collection with the versioning trigger registered (see here).

  • Access to document in this collection to see that versioning metadata attributes (v:revision, v:key, v:path) are not added as they are supposed to be.

Context information

  • eXist-db version + Git Revision hash: eXist-db 3.1 / c679cd8
  • xquery-versioning-module version + Git Revision hash: 1.1.2 / 1c1bfba
  • Java version: Java8u121
  • Operating system: Windows 7
  • 64 bit
  • Custom changes: in conf.xml, I added the following line in the <builtin-modules> element as explained here:
    <module uri="http://exist-db.org/xquery/versioning" class="org.exist.versioning.xquery.VersioningModule"/>
    I also enabled the serialized filter VersioningFilter as explained here.

Annotate Differences from Versions

copied from eXist-db/exist#626 // @StephanMa


Java Version 1.6.0_35
eXist-db Version 2.0
Ubuntu 15.04 x64

also testet with
Java Version 1.8.0_40
eXist-db Version 2.2
Ubuntu 15.04 x64

There are two bugs inside the version:annotate function:

Document Origin

<cei:diplomaticAnalysis>
                        <cei:listBiblEdition>
                            <cei:bibl>Wichner I, 8 </cei:bibl>
                            <cei:bibl>StUB I, 96 </cei:bibl>
                            <cei:bibl>GP I/1, S. 90 (Regest)</cei:bibl>
                        </cei:listBiblEdition>
                    </cei:diplomaticAnalysis>

Document Modified

<cei:diplomaticAnalysis>
                        <cei:listBibl>
                            <cei:bibl/>
                        </cei:listBibl>
                        <cei:listBiblEdition>
                            <cei:bibl/>
                        </cei:listBiblEdition>
                        <cei:listBiblRegest>
                            <cei:bibl/>
                        </cei:listBiblRegest>
                        <cei:listBiblFaksimile>
                            <cei:bibl/>
                        </cei:listBiblFaksimile>
                        <cei:listBiblErw>
                            <cei:bibl/>
                        </cei:listBiblErw>
                    </cei:diplomaticAnalysis>

The version:diff shows the Information about the deleted and added Entries.

<v:delete XXX:ref="1.7.2.3.2.5.1.1.1" XXX:name="" XXX:parent="cei:bibl"/>
<v:delete XXX:ref="1.7.2.3.2.5.1.2.1" XXX:name="" XXX:parent="cei:bibl"/>
<v:delete XXX:ref="1.7.2.3.2.5.1.3.1" XXX:name="" XXX:parent="cei:bibl"/>
...
<v:insert XXX:ref="1.7.2.3.2.5.1" XXX:name="cei:listBiblEdition" XXX:parent="cei:diplomaticAnalysis">
<cei:listBibl xmlns:cei="http://www.monasterium.net/NS/cei">
<cei:bibl/>
</cei:listBibl>
</v:insert>
<v:insert XXX:ref="1.7.2.3.2.5.1.2" XXX:name="cei:bibl" XXX:parent="cei:listBiblEdition">
<v:end XXX:namespace="http://www.monasterium.net/NS/cei" XXX:name="cei:listBiblEdition"/>
<v:start XXX:namespace="http://www.monasterium.net/NS/cei" XXX:name="cei:listBiblRegest"/>
</v:insert>
<v:insert XXX:ref="1.7.2.3.2.5.1.3" XXX:name="cei:bibl" XXX:parent="cei:listBiblEdition">
<v:end XXX:namespace="http://www.monasterium.net/NS/cei" XXX:name="cei:listBiblRegest"/>
<v:start XXX:namespace="http://www.monasterium.net/NS/cei" XXX:name="cei:listBiblFaksimile"/>
</v:insert>

But when i want to have an Annotation of the Origin file, i get

<cei:diplomaticAnalysis v:change="changed">
<cei:listBibl v:change="added">
<cei:bibl/>
</cei:listBibl>
<cei:listBiblEdition v:change="changed">
<cei:bibl v:change="changed">
<v:wrapper v:change="deleted">Wichner I, 8 </v:wrapper>
</cei:bibl>
</cei:listBiblEdition>
<cei:listBiblRegest v:change="tag-added">
<cei:bibl v:change="changed">
<v:wrapper v:change="deleted">StUB I, 96 </v:wrapper>
</cei:bibl>
</cei:listBiblRegest>
<cei:listBiblFaksimile v:change="tag-added">
<cei:bibl v:change="changed">
<v:wrapper v:change="deleted">GP I/1, S. 90 (Regest)</v:wrapper>
</cei:bibl>
</cei:listBiblFaksimile>
</cei:diplomaticAnalysis>

The Deletion of the bibl-Elements are linked to different Parent-Nodes.

Document Origin

                    <cei:issued>
                        <cei:placeName>Lateran</cei:placeName>
                        <cei:date value="11041025">11041025</cei:date>
                    </cei:issued>

Document Modified

                    <cei:issued>
                        <cei:placeName>Tokyo</cei:placeName>
                        <cei:date value="19830205">19830205</cei:date>
                        <cei:dateRange from="99999999" to="99999999">21.01.1234</cei:dateRange>
                    </cei:issued>

Diff

<v:delete XXX:ref="1.7.2.3.2.2.2.2" XXX:name="" XXX:parent="cei:date"/>
...
<v:insert XXX:ref="1.7.2.3.2.2.2.1" XXX:name="value" XXX:parent="cei:date">
<v:attribute XXX:value="19830205"/>
19830205</v:insert>

Annotation

<cei:date v:change="changed" value="19830205">
<v:wrapper v:change="deleted">11041025</v:wrapper>
</cei:date>

For me, there should be another wrapper-entry with the attribute "added" and the correct value.

Thank you and have a nice day
Stephan

Not all details and versions seem to be stored after xQuery update operations

I have enabled the versioning by installing the module available in package manager and adding

<collection xmlns="http://exist-db.org/collection-config/1.0">
    <index>
        <fulltext default="none" attributes="no">
        </fulltext>
    </index>
    <triggers>
        <trigger event="create,delete,update"
            class="org.exist.versioning.VersioningTrigger">
            <parameter name="overwrite" value="no"/>
        </trigger>
    </triggers>
</collection>

to /db/system/config/db/collection.xconf.

Issue 1

When a user calls a update insert xquery (via an html form) a new version file is created under the system/versions folder with information about added items. For update delete, though, it creates a version file with something like:

<v:version xmlns:v="http://exist-db.org/versioning">
    <v:properties>
        <v:document>file.xml</v:document>
        <v:user>user</v:user>
        <v:date>2018-02-27T11:05:52.999Z</v:date>
        <v:revision>44</v:revision>
    </v:properties>
    <v:diff>
        <v:delete ref="3.5.2.2.666"/>
    </v:diff>
</v:version>

Is it possible to have details about what has been deleted?

Issue 2

Also I noticed that version 43 hasn't been created. I only have version 42 and 44. I have several of these gaps. Why is it so?

Issue 3

Finally, this query in eXide:

xquery version "3.1";

import module namespace v="http://exist-db.org/versioning";
v:history(doc("/db/apps/app-ct/data/indices/pedb.xml"))

Returns all the versions (with the above-mentioned gaps) I see in my system folder. Nevertheless, something like:

xquery version "3.1";

import module namespace v="http://exist-db.org/versioning";
v:versions(doc("/db/apps/app-ct/data/indices/pedb.xml"))

only returns the first 10 versions. This is a weird behaviour. I've cleared the browser's cache several times. What is happening?

Compatibility with exist 5.2.0

Describe the bug
xquery-versioning-module maybe is not compatible with Exist 5.2.0

Expected behavior
Get the module working with Exist 5.2.0

To Reproduce
Sorry not beeing able to automate a test, but to reproduce:

1 Install exist 5.2.0 downloading exist-installer-5.2.0.jar
2 Install xquery-versioning-module 1.1.5 with Package Manager
(then copy manually the jars in existhome/lib, the install procedure also need some fix)
3 create a sample collection with exide, in /db/apps/sample containing an xml
4 save the simplest collection.conf as per documentation
5 restart existdb
6 update the file with exide: success, the .base file is copied into the /db/system/versions/db/apps/sample
7 update the file again: fail , need a restart of existdb to continue
8 after restart, every time you try to save even another file into the sample collection you get also an error from exide, not easy to read in the screenshot, can't shrink the window.

Screenshot from 2020-08-08 18-09-47

Anyway the error is reported in exist.log:
Caused by: java.lang.NoSuchMethodError: org.exist.collections.Collection.getLock()Lorg/exist/storage/lock/Lock; at org.exist.versioning.VersioningTrigger.getVersionsCollection(VersioningTrigger.java:454) ~[xquery-versioning-module-1.1.5.jar:1.1.5] at org.exist.versioning.VersioningTrigger.before(VersioningTrigger.java:259) ~[xquery-versioning-module-1.1.5.jar:1.1.5]

Looking at the org.exist.collections.Collection code, it seems that getLock is no more defined, but still called at line 454 in VersioningTrigger.java, after the versions collection creation.

Context

  • OS: ubuntu 18.0.4
  • eXist-db Version: [5.2.0]
  • Java Version: [openjdk version "1.8.0_265"]
  • App Version: [1.1.5]

Additional context

  • How is eXist-db installed? exist-installer-5.2.0.jar
  • Custom changes in conf.xml

<custom-filter class="org.exist.versioning.VersioningFilter"/>

<module uri="http://exist-db.org/xquery/versioning" class="org.exist.versioning.xquery.VersioningModule"/>

  • collection.conf
    <collection xmlns="http://exist-db.org/collection-config/1.0"> <triggers> <trigger event="create,update,delete" class="org.exist.versioning.VersioningTrigger"> <parameter name="overwrite" value="yes"/> </trigger> </triggers> </collection>

Failed to compile due to "kuberam-expath-plugin" execution

What is the problem

Failed to compile the project due to kuberam-expath-plugin.

What did you expect

I expected the project to compile without failure, and finally get both target/xquery-versioning-module-1.1.2.xar and target/xquery-versioning-module-1.1.2-exist.jar (as explained here).

Describe how to reproduce or add a test

Compile the project as explained here (with the Java 8 and Maven 3 requirement).
The build fails during kuberam-expath-plugin execution. It seems that path to the built target/xquery-versioning-module-1.1.2-exist.jar is incorrect
(\\C\Users\[some path]\xquery-versioning-module\target\xquery-versioning-module-1.1.2.jar insteaf of C:\Users\[some path]\xquery-versioning-module\target\xquery-versioning-module-1.1.2.jar).
Here is the stacktrace (I put in bold relevant informations to me):

[INFO] --- kuberam-expath-plugin:0.4.8:make-xar (create-xar) @ xquery-versioning-module ---
[INFO] descriptorsDirectoryPath: C:\Users\[some path]\xquery-versioning-module\target\expath-descriptors-7a87d469-cd5d-4b21-ae88-ca8f9de89d0a
mavenResourcesExecution: org.apache.maven.shared.filtering.MavenResourcesExecution@4a9860
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 1 resource to C:\Users\[some path]\xquery-versioning-module\target\make-xar-tmp
[INFO] Resolved artifact: org.exist-db.xquery.extensions:xquery-versioning-module:jar:1.1.2
java.io.FileNotFoundException: \\C\Users\[some path]\xquery-versioning-module\target\xquery-versioning-module-1.1.2.jar (Le chemin réseau n'a pas été trouvé)
	at java.util.zip.ZipFile.open(Native Method)
	at java.util.zip.ZipFile.(ZipFile.java:219)
	at java.util.zip.ZipFile.(ZipFile.java:149)
	at java.util.jar.JarFile.(JarFile.java:166)
	at java.util.jar.JarFile.(JarFile.java:103)
	at sun.net.www.protocol.jar.URLJarFile.(URLJarFile.java:93)
	at sun.net.www.protocol.jar.URLJarFile.getJarFile(URLJarFile.java:69)
	at sun.net.www.protocol.jar.JarFileFactory.get(JarFileFactory.java:94)
	at sun.net.www.protocol.jar.JarURLConnection.connect(JarURLConnection.java:122)
	at sun.net.www.protocol.jar.JarURLConnection.getJarFile(JarURLConnection.java:89)
	at java.net.JarURLConnection.getManifest(JarURLConnection.java:231)
	at java.net.JarURLConnection.getMainAttributes(JarURLConnection.java:283)
	at ro.kuberam.maven.plugins.expath.mojos.MakeXarMojo.getMainClass(MakeXarMojo.java:273)
	at ro.kuberam.maven.plugins.expath.mojos.MakeXarMojo.execute(MakeXarMojo.java:177)
	at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:134)
	at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:207)
	at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
	at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
	at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:116)
	at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:80)
	at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:51)
	at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:128)
	at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:307)
	at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:193)
	at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:106)
	at org.apache.maven.cli.MavenCli.execute(MavenCli.java:863)
	at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:288)
	at org.apache.maven.cli.MavenCli.main(MavenCli.java:199)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:289)
	at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:229)
	at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:415)
	at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:356)
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 5.027 s
[INFO] Finished at: 2017-03-23T08:54:03+01:00
[INFO] Final Memory: 21M/231M
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal ro.kuberam.maven.plugins:kuberam-expath-plugin:0.4.8:make-xar (create-xar) on project xquery-versioning-module: Execution create-xar of goal ro.kuberam.maven.plugins:kuberam-expath-plugin:0.4.8:make-xar failed. NullPointerException -> [Help 1]
org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal ro.kuberam.maven.plugins:kuberam-expath-plugin:0.4.8:make-xar (create-xar) on project xquery-versioning-module: Execution create-xar of goal ro.kuberam.maven.plugins:kuberam-expath-plugin:0.4.8:make-xar failed.
	at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:212)
	at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
	at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
	at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:116)
	at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:80)
	at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:51)
	at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:128)
	at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:307)
	at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:193)
	at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:106)
	at org.apache.maven.cli.MavenCli.execute(MavenCli.java:863)
	at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:288)
	at org.apache.maven.cli.MavenCli.main(MavenCli.java:199)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:289)
	at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:229)
	at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:415)
	at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:356)
Caused by: org.apache.maven.plugin.PluginExecutionException: Execution create-xar of goal ro.kuberam.maven.plugins:kuberam-expath-plugin:0.4.8:make-xar failed.
	at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:145)
	at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:207)
	... 20 more
Caused by: java.lang.NullPointerException
	at ro.kuberam.maven.plugins.expath.mojos.MakeXarMojo.getMainClass(MakeXarMojo.java:278)
	at ro.kuberam.maven.plugins.expath.mojos.MakeXarMojo.execute(MakeXarMojo.java:177)
	at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:134)
	... 21 more

Note that the project build succeeds if I remove the kuberam-expath-plugin from pom.xml: at least the target/xquery-versioning-module-1.1.2-exist.jar is built.

Context information

  • xquery-versioning-module version + Git Revision hash: 1.1.2 / 1c1bfba
  • Java version: Java8u121
  • Operating system: Windows 7
  • 64 bit
  • No custom changes

can't return version history correctly

hi,all,
I have installed the versioning module, as:
add in xconf file :

    <triggers>
        <trigger event="create,delete,update"
            class="org.exist.versioning.VersioningTrigger">
            <parameter name="overwrite" value="no"/>
        </trigger>
    </triggers>

a file :

<DataSet code="type.01" >
        <HDSN00.01.013 dataelementName="文档编号" value="817c2e7d-7223-5f4c-a1ab-d41f446a24c2"/>
        <HDSN00.01.014 dataelementName="文档生成日期" valueaa="20140106000208"/>
        <HDSN00.01.015 dataelementName="参与人电话" value="13458979160"/>
        <HDSN00.01.016 dataelementName="参与人姓名" value="test"/>
</DataSet>

change to

<DataSet code="type.01" >
        <HDSN00.01.013 dataelementName="文档编号" value="817c2e7d-7223-5f4c-a1ab-d41f446a24c2"/>
        <HDSN00.01.014 dataelementName="文档生成日期" valueaa="20140106000208"/> <!--  node name changed-->
        <HDSN00.01.015 dataelementName="参与人电话" value="13458979160"/>
        <HDSN00.01.016 dataelementName="参与人姓名" value="test"/>
</DataSet>

so:

v:doc(doc("/db/ehr/v2/t4.xml"),9) 

got:

<DataSet code="type.01">
<HDSN00.01.013 dataelementName="文档编号" value="817c2e7d-7223-5f4c-a1ab-d41f446a24c2"/>
<HDSN00.01.014aa/>
<HDSN00.01.015 dataelementName="参与人电话" value="13458979160"/>
<HDSN00.01.016 dataelementName="参与人姓名" value="test"/>
</DataSet>

VersioningTrigger doesn't avoid write conflicts

What is the problem

The VersioningTrigger doesn't avoir conflicts.

What did you expect

Following document about parameter overwrite as described here, I expect that a user can't overwrite modifications already committed by another one, when they're editing the same document at the same time.

Describe how to reproduce or add a test

  • Add xquery-versioning-module in your eXist-db instance.

  • In $EXIST_HOME/conf.xml, add the following line in the <builtin-modules> element as explained here (paragraph Direct Installation into eXist-db (.jar)):
    <module uri="http://exist-db.org/xquery/versioning" class="org.exist.versioning.xquery.VersioningModule"/>.

  • In $EXIST_HOME/conf.xml, enable the VersioningFilter by adding the following line in the <serializer> element as explained here:
    <custom-filter class="org.exist.versioning.VersioningFilter"/>.

  • Restart eXist-db.

  • Configure a collection with the versioning trigger registered (see here) with parameter overwrite="no".

  • With different accounts, try to overwrite changes of a user and see that one may overwrite the other's changes.

Context information

  • eXist-db version + Git Revision hash: eXist-db 3.2.0-SNAPSHOT / c7f6e81
  • xquery-versioning-module version + Git Revision hash: 1.1.4 / fbdecdb
  • Java version: Java8u121
  • Operating system: Windows 7
  • 64 bit
  • Custom changes: in conf.xml, I added the following line in the <builtin-modules> element as explained here:
    <module uri="http://exist-db.org/xquery/versioning" class="org.exist.versioning.xquery.VersioningModule"/>
    I also enabled the serialized filter VersioningFilter as explained here.
    I configured versioning trigger with parameter <parameter name="overwrite" value="no"/> on my collection, as explained here.

[BUG] Errors managing xml versions

Describe the bug
The plugin does not work as expected managing updates of xml data in collection

Expected behavior
The plugin should manage revisions of xml data, recording differences and giving old revisions data when using api functions

To Reproduce
Compile
git clone https://github.com/eXist-db/xquery-versioning-module.git && cd xquery-versioning-module && mvn package
Then install the plugin in a fresh existdb database install, i.e. using a Docker image.
docker run -it -d -p 8080:8080 -p 8443:8443 --name exist existdb/existdb:latest

Add the line <custom-filter class="org.exist.versioning.VersioningFilter"/> in the serializer section of conf.xml, copy into the docker container /exist/etc path, then restart the database.

Create a new collection in existdb, called /db/resources, containing the two attached files.:

test.xq is a modified version of the original in (https://github.com/eXist-db/xquery-versioning-module/blob/master/src/test/xquery/test.xq)
test.xml is exactly (https://github.com/eXist-db/xquery-versioning-module/blob/master/src/test/resources/test.xml)

Execute test.xq visiting http://127.0.0.1:8080/exist/rest/db/resources/test.xq

Instead of an XML containing info on the passed tests, you will get a page with an error.

Screenshots

Error

The test creates a collection /db/test containing only the first document, creation of new versions fails.

Context

  • OS: ubuntu 20.0.4
  • eXist-db Version: [6.1.0-SNAPSHOT]

Additional context

  • eXist-db installed as Docker exist:latest
  • Changes in configuration of database already descripted.

Attached resources:

resources.zip

Problem with utility versioning API (v:doc)

Hello guys,
I'm no sure, but I think there's a problem with the utility versioning API (versioning.xqm, namespace=http://exist-db.org/versioning).

I found, to my sense, errors with the v:doc function, and therefore v:annotate and v:diff.
Let say I have a base document, and then I:

  1. insert an element in a first revision (rev=1),
  2. then another in a second revision (rev=2),
  3. then another in the last revision (rev=3).

When I check the result of my document at revision 1 with v:doc, I see my first inserted element; that's OK.
When I check the result of my document at revision 2 with v:doc, I see my second element inserted, but the first element inserted in revision 1 appears twice = KO.
When I do the same at revision 3, I have three times my first insert element, twice the second one and then the once the third one = KO.

From my point of view, v:doc is applying too many patches and doesn't reflect the document at a point.

The other consequence is that my document (after modifications) and the result with v:doc at the last revision applied to this document are different!

Thanks for reading
Dovikos

Exception in VersioningTrigger

What is the problem

The xquery-versioning-module failed to store diffs.
I think the problem is the same as #2.

What did you expect

When I update a file (which collection is configured for versioning) , I expected that the versioning module:

  • would create a base file in the /db/system/versions collection if not exists = success;
  • would create a diff between current and base file = this failed;

Describe how to reproduce or add a test

Build xquery-versioning-module-1.1.jar.
Copy it to $EXIST_HOME/lib/user of your eXist 3.1 installation.
Register the versioning module in conf.xml, as explained here.
Start eXist.
Configure a collection {your-collection} with the versioning trigger registered (see here).
Update a file in this collection, and see that no diff was created in /db/system/versions/{your-collection} (only the .base file is created).

Here is the found stacktrace when I configured logs on the org.exist.versioning package. (I put in bold relevant informations to me) :

017-03-18 14:48:08,546 [qtp665565246-28] ERROR (VersioningTrigger.java [after]:410) - Caught exception in VersioningTrigger: sun.nio.fs.WindowsPath cannot be cast to java.lang.String
java.lang.ClassCastException: sun.nio.fs.WindowsPath cannot be cast to java.lang.String
at org.exist.versioning.VersioningTrigger.newRevision(VersioningTrigger.java:463) ~[xquery-versioning-module-1.1.jar:?]
at org.exist.versioning.VersioningTrigger.after(VersioningTrigger.java:341) ~[xquery-versioning-module-1.1.jar:?]
at org.exist.versioning.VersioningTrigger.afterUpdateDocument(VersioningTrigger.java:577) ~[xquery-versioning-module-1.1.jar:?]
at org.exist.collections.triggers.DocumentTriggers.afterUpdateDocument(DocumentTriggers.java:254) ~[exist.jar:3.1.0]
at org.exist.collections.MutableCollection.storeXMLInternal(MutableCollection.java:1264) ~[exist.jar:3.1.0]
at org.exist.collections.MutableCollection.store(MutableCollection.java:1150) ~[exist.jar:3.1.0]
at org.exist.xmldb.LocalCollection.lambda$storeXMLResource$13(LocalCollection.java:587) ~[exist.jar:3.1.0]
at org.exist.xmldb.function.LocalXmldbCollectionFunction.apply(LocalXmldbCollectionFunction.java:48) ~[exist.jar:3.1.0]
at org.exist.xmldb.LocalCollection.lambda$null$22(LocalCollection.java:729) ~[exist.jar:3.1.0]
at org.exist.xmldb.function.LocalXmldbCollectionFunction.apply(LocalXmldbCollectionFunction.java:48) ~[exist.jar:3.1.0]
at org.exist.xmldb.AbstractLocal.lambda$with$6(AbstractLocal.java:172) ~[exist.jar:3.1.0]
at org.exist.xmldb.AbstractLocal.lambda$null$4(AbstractLocal.java:118) ~[exist.jar:3.1.0]
at org.exist.xmldb.function.LocalXmldbFunction.apply(LocalXmldbFunction.java:46) ~[exist.jar:3.1.0]
at org.exist.xmldb.AbstractLocal.withDb(AbstractLocal.java:195) ~[exist.jar:3.1.0]
at org.exist.xmldb.AbstractLocal.lambda$modify$5(AbstractLocal.java:118) ~[exist.jar:3.1.0]
at org.exist.xmldb.LocalCollection.lambda$modify$23(LocalCollection.java:727) ~[exist.jar:3.1.0]
at org.exist.xmldb.LocalCollection.storeXMLResource(LocalCollection.java:552) [exist.jar:3.1.0]
at org.exist.xmldb.LocalCollection.storeResource(LocalCollection.java:502) [exist.jar:3.1.0]
at org.exist.xmldb.LocalCollection.storeResource(LocalCollection.java:491) [exist.jar:3.1.0]
at org.exist.xquery.functions.xmldb.XMLDBStore.evalWithCollection(XMLDBStore.java:243) [exist.jar:3.1.0]
at org.exist.xquery.functions.xmldb.XMLDBAbstractCollectionManipulator.eval(XMLDBAbstractCollectionManipulator.java:174) [exist.jar:3.1.0]
at org.exist.xquery.BasicFunction.eval(BasicFunction.java:74) [exist.jar:3.1.0]
at org.exist.xquery.InternalFunctionCall.eval(InternalFunctionCall.java:41) [exist.jar:3.1.0]
at org.exist.xquery.ConditionalExpression.eval(ConditionalExpression.java:100) [exist.jar:3.1.0]
at org.exist.xquery.DebuggableExpression.eval(DebuggableExpression.java:58) [exist.jar:3.1.0]
at org.exist.xquery.ConditionalExpression.eval(ConditionalExpression.java:102) [exist.jar:3.1.0]
at org.exist.xquery.LetExpr.eval(LetExpr.java:99) [exist.jar:3.1.0]
at org.exist.xquery.LetExpr.eval(LetExpr.java:111) [exist.jar:3.1.0]
at org.exist.xquery.AbstractExpression.eval(AbstractExpression.java:71) [exist.jar:3.1.0]
at org.exist.xquery.PathExpr.eval(PathExpr.java:276) [exist.jar:3.1.0]
at org.exist.xquery.TryCatchExpression.eval(TryCatchExpression.java:142) [exist.jar:3.1.0]
at org.exist.xquery.DebuggableExpression.eval(DebuggableExpression.java:58) [exist.jar:3.1.0]
at org.exist.xquery.LetExpr.eval(LetExpr.java:111) [exist.jar:3.1.0]
at org.exist.xquery.LetExpr.eval(LetExpr.java:111) [exist.jar:3.1.0]
at org.exist.xquery.LetExpr.eval(LetExpr.java:111) [exist.jar:3.1.0]
at org.exist.xquery.LetExpr.eval(LetExpr.java:111) [exist.jar:3.1.0]
at org.exist.xquery.LetExpr.eval(LetExpr.java:111) [exist.jar:3.1.0]
at org.exist.xquery.LetExpr.eval(LetExpr.java:111) [exist.jar:3.1.0]
at org.exist.xquery.LetExpr.eval(LetExpr.java:111) [exist.jar:3.1.0]
at org.exist.xquery.AbstractExpression.eval(AbstractExpression.java:71) [exist.jar:3.1.0]
at org.exist.xquery.PathExpr.eval(PathExpr.java:276) [exist.jar:3.1.0]
at org.exist.xquery.AbstractExpression.eval(AbstractExpression.java:71) [exist.jar:3.1.0]
at org.exist.xquery.XQuery.execute(XQuery.java:253) [exist.jar:3.1.0]
at org.exist.xquery.XQuery.execute(XQuery.java:185) [exist.jar:3.1.0]
at org.exist.http.RESTServer.executeXQuery(RESTServer.java:1506) [exist-optional.jar:3.1.0]
at org.exist.http.RESTServer.checkForXQueryTarget(RESTServer.java:1203) [exist-optional.jar:3.1.0]
at org.exist.http.RESTServer.doPut(RESTServer.java:1004) [exist-optional.jar:3.1.0]
at org.exist.http.servlets.EXistServlet.doPut(EXistServlet.java:128) [exist-optional.jar:3.1.0]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:710) [servlet-api-3.1.jar:3.1.0]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:790) [servlet-api-3.1.jar:3.1.0]
at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:845) [jetty-servlet-9.3.9.v20160517.jar:9.3.9.v20160517]
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:583) [jetty-servlet-9.3.9.v20160517.jar:9.3.9.v20160517]
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143) [jetty-server-9.3.9.v20160517.jar:9.3.9.v20160517]
at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:566) [jetty-security-9.3.9.v20160517.jar:9.3.9.v20160517]
at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:226) [jetty-server-9.3.9.v20160517.jar:9.3.9.v20160517]
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1174) [jetty-server-9.3.9.v20160517.jar:9.3.9.v20160517]
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:511) [jetty-servlet-9.3.9.v20160517.jar:9.3.9.v20160517]
at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:185) [jetty-server-9.3.9.v20160517.jar:9.3.9.v20160517]
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1106) [jetty-server-9.3.9.v20160517.jar:9.3.9.v20160517]
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141) [jetty-server-9.3.9.v20160517.jar:9.3.9.v20160517]
at org.eclipse.jetty.server.Dispatcher.forward(Dispatcher.java:159) [jetty-server-9.3.9.v20160517.jar:9.3.9.v20160517]
at org.eclipse.jetty.server.Dispatcher.forward(Dispatcher.java:74) [jetty-server-9.3.9.v20160517.jar:9.3.9.v20160517]
at org.exist.http.urlrewrite.Forward.doRewrite(Forward.java:50) [exist-optional.jar:3.1.0]
at org.exist.http.urlrewrite.XQueryURLRewrite.doRewrite(XQueryURLRewrite.java:545) [exist-optional.jar:3.1.0]
at org.exist.http.urlrewrite.XQueryURLRewrite.service(XQueryURLRewrite.java:349) [exist-optional.jar:3.1.0]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:790) [servlet-api-3.1.jar:3.1.0]
at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:845) [jetty-servlet-9.3.9.v20160517.jar:9.3.9.v20160517]
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1689) [jetty-servlet-9.3.9.v20160517.jar:9.3.9.v20160517]
at de.betterform.agent.web.filter.XFormsFilter.doFilter(XFormsFilter.java:171) [betterform-exist-5.1-SNAPSHOT-20160615.jar:?]
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1668) [jetty-servlet-9.3.9.v20160517.jar:9.3.9.v20160517]
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:581) [jetty-servlet-9.3.9.v20160517.jar:9.3.9.v20160517]
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143) [jetty-server-9.3.9.v20160517.jar:9.3.9.v20160517]
at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:524) [jetty-security-9.3.9.v20160517.jar:9.3.9.v20160517]
at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:226) [jetty-server-9.3.9.v20160517.jar:9.3.9.v20160517]
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1174) [jetty-server-9.3.9.v20160517.jar:9.3.9.v20160517]
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:511) [jetty-servlet-9.3.9.v20160517.jar:9.3.9.v20160517]
at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:185) [jetty-server-9.3.9.v20160517.jar:9.3.9.v20160517]
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1106) [jetty-server-9.3.9.v20160517.jar:9.3.9.v20160517]
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141) [jetty-server-9.3.9.v20160517.jar:9.3.9.v20160517]
at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:213) [jetty-server-9.3.9.v20160517.jar:9.3.9.v20160517]
at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:119) [jetty-server-9.3.9.v20160517.jar:9.3.9.v20160517]
at org.eclipse.jetty.server.handler.gzip.GzipHandler.handle(GzipHandler.java:396) [jetty-server-9.3.9.v20160517.jar:9.3.9.v20160517]
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:134) [jetty-server-9.3.9.v20160517.jar:9.3.9.v20160517]
at org.eclipse.jetty.server.Server.handle(Server.java:524) [jetty-server-9.3.9.v20160517.jar:9.3.9.v20160517]
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:319) [jetty-server-9.3.9.v20160517.jar:9.3.9.v20160517]
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:253) [jetty-server-9.3.9.v20160517.jar:9.3.9.v20160517]
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:273) [jetty-io-9.3.9.v20160517.jar:9.3.9.v20160517]
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:95) [jetty-io-9.3.9.v20160517.jar:9.3.9.v20160517]
at org.eclipse.jetty.io.SelectChannelEndPoint$2.run(SelectChannelEndPoint.java:93) [jetty-io-9.3.9.v20160517.jar:9.3.9.v20160517]
at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.executeProduceConsume(ExecuteProduceConsume.java:303) [jetty-util-9.3.9.v20160517.jar:9.3.9.v20160517]
at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.produceConsume(ExecuteProduceConsume.java:148) [jetty-util-9.3.9.v20160517.jar:9.3.9.v20160517]
at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.run(ExecuteProduceConsume.java:136) [jetty-util-9.3.9.v20160517.jar:9.3.9.v20160517]
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:671) [jetty-util-9.3.9.v20160517.jar:9.3.9.v20160517]
at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:589) [jetty-util-9.3.9.v20160517.jar:9.3.9.v20160517]
at java.lang.Thread.run(Thread.java:745) [?:1.8.0_121]

Context information

  • eXist-db version + Git Revision hash: eXist-db 3.1 / c679cd8
  • xquery-versioning-module version + Git Revision hash: 1.1 / 1920f55
  • Java version: Java8u121
  • Operating system: Windows 7
  • 64 bit
  • Custom changes: in conf.xml, I added the following line in the <builtin-modules> element as explained here:
    <module uri="http://exist-db.org/xquery/versioning" class="org.exist.versioning.xquery.VersioningModule"/>
    I also added the utility API module: <module uri="http://exist-db.org/versioning" src="resource:org/exist/versioning/xquery/versioning.xqm"/>
    I also enabled the serialized filter as explained here.

Missing module "versioning.xqm"

What is the problem

The versioning.xqm module is missing from target/xquery-versioning-module-1.1.2-exist.jar, when you built the project from the sources.

What did you expect

I expect to have the versioning.xqm in the built target/xquery-versioning-module-1.1.2-exist.jar, in order to use it in eXist.

Describe how to reproduce or add a test

  • Compile the project as explained here (with the Java 8 and Maven 3 requirement).

  • Copy the built target/xquery-versioning-module-1.1.2-exist.jar into $EXIST_HOME/lib/user.

  • In $EXIST_HOME/conf.xml, add the following line in the <builtin-modules> element as explained here:
    <module uri="http://exist-db.org/xquery/versioning" class="org.exist.versioning.xquery.VersioningModule"/>.
    Add also the utility API module: <module uri="http://exist-db.org/versioning" src="resource:org/exist/versioning/xquery/versioning.xqm"/> (paragraph Direct Installation into eXist-db (.jar)).
    Note: In previous eXist 2.2, the module was located at this path). I think the README miss some explanations about how to cleanly add this module configuration into eXist-db, paragraph Direct Installation into eXist-db (.jar).

  • Restart eXist-db.

  • Try to use any functions of the utility API module (example: v:revisions) and see that the module is not found:

Cannot compile xquery: err:XQST0059 error found while loading module v: Source for module 'http://exist-db.org/versioning' not found module location hint URI 'resource:org/exist/versioning/xquery/versioning.xqm'.

Context information

  • eXist-db version + Git Revision hash: eXist-db 3.1 / c679cd8
  • xquery-versioning-module version + Git Revision hash: 1.1.2 / 1c1bfba
  • Java version: Java8u121
  • Operating system: Windows 7
  • 64 bit
  • Custom changes: in conf.xml, I added the following line in the <builtin-modules> element as explained here:
    <module uri="http://exist-db.org/xquery/versioning" class="org.exist.versioning.xquery.VersioningModule"/>
    I also added the utility API module: <module uri="http://exist-db.org/versioning" src="resource:org/exist/versioning/xquery/versioning.xqm"/>
    I also enabled the serialized filter as explained here.

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.