Code Monkey home page Code Monkey logo

auto-record's Issues

Immutable Collections Extension

CompactConstructorExtension replaces collection-type components with their immutable copy. By collection-type, Set, List, Queue and Map implementations need to be understand.

[BUG] Annotation parameters are not passed to generated record

Describe the bug

Annotation parameters are not passed to generated record. Only the annotation itself.

Details

Details needed to reproduce bug:

  1. Library version: '3.1.1'
  2. Interface being processed:
@AutoRecord
public interface MyCustomClass {

    @JsonProperty("myIds")
    Collection<String> getMyIds();

    @JsonCreator
    static MyCustomClass create(Collection<String> getMyIds) {
        return new MyCustomClassRecord(getMyIds);
    }
}

  1. Generated record:
@Generated("pl.com.labaj.autorecord.AutoRecord")
@GeneratedWithAutoRecord
public record MyCustomClass(
    @JsonProperty Collection<String> getMyIds) implements MyIdsResponse {
  public MyIdsResponseRecord {
    requireNonNull(getMyIds, "getMyIds must not be null");
  }
}
  1. Error during compilation:
none

Expected behavior

In generated record: @JsonProperty("myIds") Collection<String> getMyIds

Additional context

Add any other context about the problem here.
none

[BUG] @JsonProperty annotation is not propagated to record field and builder method

Describe the bug

Create an interface with method annotated with @JsonProperty, after generation annotation is not propagated

Details

Details needed to reproduce bug:

  1. Library version: '3.0.0'
  2. Interface being processed:
@DefaultAutoRecordApi
public interface TestRecord {
    @JsonProperty
    String name();
}

Annotation

@Retention(SOURCE)
@Target(TYPE)
@AutoRecord.Template(
        recordOptions = @AutoRecord.Options(withBuilder = true),
        builderOptions = @RecordBuilder.Options(enableWither = false, useImmutableCollections = false, useUnmodifiableCollections = false),
        extensions = @AutoRecord.Extension(extensionClass = "pl.com.labaj.autorecord.extension.arice.ImmutableCollectionsExtension")
)
public @interface DefaultAutoRecordApi {}
  1. Generated record:
@Generated("pl.com.labaj.autorecord.AutoRecord")
@GeneratedWithAutoRecord
@RecordBuilder
@RecordBuilder.Options(
    addClassRetainedGenerated = true,
    enableWither = false
)
public record TestRecordRecord(String name) implements TestRecord {
  public TestRecordRecord {
    // pl.com.labaj.autorecord.processor.AutoRecordProcessor
    requireNonNull(name, "name must not be null");
  }

  public static TestRecordRecordBuilder builder() {
    return TestRecordRecordBuilder.builder();
  }

  public TestRecordRecordBuilder toBuilder() {
    return TestRecordRecordBuilder.builder(this);
  }
}

Builder

@Generated("io.soabase.recordbuilder.core.RecordBuilder")
@RecordBuilderGenerated
public class TestRecordRecordBuilder {
    private String name;

    @Generated("io.soabase.recordbuilder.core.RecordBuilder")
    private TestRecordRecordBuilder() {
    }

    @Generated("io.soabase.recordbuilder.core.RecordBuilder")
    private TestRecordRecordBuilder(String name) {
        this.name = name;
    }

    /**
     * Static constructor/builder. Can be used instead of new TestRecordRecord(...)
     */
    @Generated("io.soabase.recordbuilder.core.RecordBuilder")
    public static TestRecordRecord TestRecordRecord(String name) {
        return new TestRecordRecord(name);
    }

    /**
     * Return a new builder with all fields set to default Java values
     */
    @Generated("io.soabase.recordbuilder.core.RecordBuilder")
    public static TestRecordRecordBuilder builder() {
        return new TestRecordRecordBuilder();
    }

    /**
     * Return a new builder with all fields set to the values taken from the given record instance
     */
    @Generated("io.soabase.recordbuilder.core.RecordBuilder")
    public static TestRecordRecordBuilder builder(TestRecordRecord from) {
        return new TestRecordRecordBuilder(from.name());
    }

    /**
     * Return a stream of the record components as map entries keyed with the component name and the value as the component value
     */
    @Generated("io.soabase.recordbuilder.core.RecordBuilder")
    public static Stream<Map.Entry<String, Object>> stream(TestRecordRecord record) {
        return Stream.of(new AbstractMap.SimpleImmutableEntry<>("name", record.name()));
    }

    /**
     * Return a new record instance with all fields set to the current values in this builder
     */
    @Generated("io.soabase.recordbuilder.core.RecordBuilder")
    public TestRecordRecord build() {
        return new TestRecordRecord(name);
    }

    @Generated("io.soabase.recordbuilder.core.RecordBuilder")
    @Override
    public String toString() {
        return "TestRecordRecordBuilder[name=" + name + "]";
    }

    @Generated("io.soabase.recordbuilder.core.RecordBuilder")
    @Override
    public int hashCode() {
        return Objects.hash(name);
    }

    @Generated("io.soabase.recordbuilder.core.RecordBuilder")
    @Override
    public boolean equals(Object o) {
        return (this == o) || ((o instanceof TestRecordRecordBuilder r)
                && Objects.equals(name, r.name));
    }

    /**
     * Set a new value for the {@code name} record component in the builder
     */
    @Generated("io.soabase.recordbuilder.core.RecordBuilder")
    public TestRecordRecordBuilder name(String name) {
        this.name = name;
        return this;
    }

    /**
     * Return the current value for the {@code name} record component in the builder
     */
    @Generated("io.soabase.recordbuilder.core.RecordBuilder")
    public String name() {
        return name;
    }

Expected behavior

@JsonProperty annotation being present on record field declaration and builder's name(...) method.

Additional context

Add any other context about the problem here.

[BUG]Reproducible build is not working

I've created jvm-repo-rebuild/reproducible-central#130 according to the documentation.

I'm facing unexpected errors during build:

[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  16.946 s
[INFO] Finished at: 2023-10-27T21:58:16Z
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.3.1:resources (default-resources) on project auto-record-api: Execution default-resources of goal org.apache.maven.plugins:maven-resources-plugin:3.3.1:resources failed: A required class was missing while executing org.apache.maven.plugins:maven-resources-plugin:3.3.1:resources: org/apache/commons/lang3/StringUtils
[ERROR] -----------------------------------------------------
[ERROR] realm =    plugin>org.apache.maven.plugins:maven-resources-plugin:3.3.1
[ERROR] strategy = org.codehaus.plexus.classworlds.strategy.SelfFirstStrategy
[ERROR] urls[0] = file:/var/maven/.m2/repository-022/org/apache/maven/plugins/maven-resources-plugin/3.3.1/maven-resources-plugin-3.3.1.jar
[ERROR] urls[1] = file:/var/maven/.m2/repository-022/org/codehaus/plexus/plexus-interpolation/1.26/plexus-interpolation-1.26.jar
[ERROR] urls[2] = file:/var/maven/.m2/repository-022/org/codehaus/plexus/plexus-utils/3.5.1/plexus-utils-3.5.1.jar
[ERROR] urls[3] = file:/var/maven/.m2/repository-022/org/apache/maven/shared/maven-filtering/3.3.1/maven-filtering-3.3.1.jar
[ERROR] urls[4] = file:/var/maven/.m2/repository-022/org/sonatype/plexus/plexus-build-api/0.0.7/plexus-build-api-0.0.7.jar
[ERROR] urls[5] = file:/var/maven/.m2/repository-022/commons-io/commons-io/2.11.0/commons-io-2.11.0.jar
[ERROR] urls[6] = file:/var/maven/.m2/repository-022/org/apache/commons/commons-lang3/3.12.0/commons-lang3-3.12.0.jar
[ERROR] Number of foreign imports: 1
[ERROR] import: Entry[import  from realm ClassRealm[project>pl.com.labaj.autorecord:auto-record-project:3.1.0, parent: ClassRealm[maven.api, parent: null]]]
[ERROR]
[ERROR] -----------------------------------------------------
[ERROR] : org.apache.commons.lang3.StringUtils
[ERROR] -> [Help 1]
org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.3.1:resources (default-resources) on project auto-record-api: Execution default-resources of goal org.apache.maven.plugins:maven-resources-plugin:3.3.1:resources failed: A required class was missing while executing org.apache.maven.plugins:maven-resources-plugin:3.3.1:resources: org/apache/commons/lang3/StringUtils
-----------------------------------------------------
realm =    plugin>org.apache.maven.plugins:maven-resources-plugin:3.3.1
strategy = org.codehaus.plexus.classworlds.strategy.SelfFirstStrategy
urls[0] = file:/var/maven/.m2/repository-022/org/apache/maven/plugins/maven-resources-plugin/3.3.1/maven-resources-plugin-3.3.1.jar
urls[1] = file:/var/maven/.m2/repository-022/org/codehaus/plexus/plexus-interpolation/1.26/plexus-interpolation-1.26.jar
urls[2] = file:/var/maven/.m2/repository-022/org/codehaus/plexus/plexus-utils/3.5.1/plexus-utils-3.5.1.jar
urls[3] = file:/var/maven/.m2/repository-022/org/apache/maven/shared/maven-filtering/3.3.1/maven-filtering-3.3.1.jar
urls[4] = file:/var/maven/.m2/repository-022/org/sonatype/plexus/plexus-build-api/0.0.7/plexus-build-api-0.0.7.jar
urls[5] = file:/var/maven/.m2/repository-022/commons-io/commons-io/2.11.0/commons-io-2.11.0.jar
urls[6] = file:/var/maven/.m2/repository-022/org/apache/commons/commons-lang3/3.12.0/commons-lang3-3.12.0.jar
Number of foreign imports: 1
import: Entry[import  from realm ClassRealm[project>pl.com.labaj.autorecord:auto-record-project:3.1.0, parent: ClassRealm[maven.api, parent: null]]]

-----------------------------------------------------

    at org.apache.maven.lifecycle.internal.MojoExecutor.doExecute2 (MojoExecutor.java:347)
    at org.apache.maven.lifecycle.internal.MojoExecutor.doExecute (MojoExecutor.java:330)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:213)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:175)
    at org.apache.maven.lifecycle.internal.MojoExecutor.access$000 (MojoExecutor.java:76)
    at org.apache.maven.lifecycle.internal.MojoExecutor$1.run (MojoExecutor.java:163)
    at org.apache.maven.plugin.DefaultMojosExecutionStrategy.execute (DefaultMojosExecutionStrategy.java:39)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:160)
    at org.apache.maven.lifecycle.internal.MojoExecutor.executeForkedExecutions (MojoExecutor.java:462)
    at org.apache.maven.lifecycle.internal.MojoExecutor.doExecute (MojoExecutor.java:325)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:213)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:175)
    at org.apache.maven.lifecycle.internal.MojoExecutor.access$000 (MojoExecutor.java:76)
    at org.apache.maven.lifecycle.internal.MojoExecutor$1.run (MojoExecutor.java:163)
    at org.apache.maven.plugin.DefaultMojosExecutionStrategy.execute (DefaultMojosExecutionStrategy.java:39)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:160)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:105)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:73)
    at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build (SingleThreadedBuilder.java:53)
    at org.apache.maven.lifecycle.internal.LifecycleStarter.execute (LifecycleStarter.java:118)
    at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:261)
    at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:173)
    at org.apache.maven.DefaultMaven.execute (DefaultMaven.java:101)
    at org.apache.maven.cli.MavenCli.execute (MavenCli.java:827)
    at org.apache.maven.cli.MavenCli.doMain (MavenCli.java:272)
    at org.apache.maven.cli.MavenCli.main (MavenCli.java:195)
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0 (Native Method)
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:77)
    at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke (Method.java:568)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced (Launcher.java:282)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launch (Launcher.java:225)
    at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode (Launcher.java:406)
    at org.codehaus.plexus.classworlds.launcher.Launcher.main (Launcher.java:347)
Caused by: org.apache.maven.plugin.PluginExecutionException: Execution default-resources of goal org.apache.maven.plugins:maven-resources-plugin:3.3.1:resources failed: A required class was missing while executing org.apache.maven.plugins:maven-resources-plugin:3.3.1:resources: org/apache/commons/lang3/StringUtils
-----------------------------------------------------
realm =    plugin>org.apache.maven.plugins:maven-resources-plugin:3.3.1
strategy = org.codehaus.plexus.classworlds.strategy.SelfFirstStrategy
urls[0] = file:/var/maven/.m2/repository-022/org/apache/maven/plugins/maven-resources-plugin/3.3.1/maven-resources-plugin-3.3.1.jar
urls[1] = file:/var/maven/.m2/repository-022/org/codehaus/plexus/plexus-interpolation/1.26/plexus-interpolation-1.26.jar
urls[2] = file:/var/maven/.m2/repository-022/org/codehaus/plexus/plexus-utils/3.5.1/plexus-utils-3.5.1.jar
urls[3] = file:/var/maven/.m2/repository-022/org/apache/maven/shared/maven-filtering/3.3.1/maven-filtering-3.3.1.jar
urls[4] = file:/var/maven/.m2/repository-022/org/sonatype/plexus/plexus-build-api/0.0.7/plexus-build-api-0.0.7.jar
urls[5] = file:/var/maven/.m2/repository-022/commons-io/commons-io/2.11.0/commons-io-2.11.0.jar
urls[6] = file:/var/maven/.m2/repository-022/org/apache/commons/commons-lang3/3.12.0/commons-lang3-3.12.0.jar
Number of foreign imports: 1
import: Entry[import  from realm ClassRealm[project>pl.com.labaj.autorecord:auto-record-project:3.1.0, parent: ClassRealm[maven.api, parent: null]]]

-----------------------------------------------------

    at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo (DefaultBuildPluginManager.java:152)
    at org.apache.maven.lifecycle.internal.MojoExecutor.doExecute2 (MojoExecutor.java:342)
    at org.apache.maven.lifecycle.internal.MojoExecutor.doExecute (MojoExecutor.java:330)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:213)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:175)
    at org.apache.maven.lifecycle.internal.MojoExecutor.access$000 (MojoExecutor.java:76)
    at org.apache.maven.lifecycle.internal.MojoExecutor$1.run (MojoExecutor.java:163)
    at org.apache.maven.plugin.DefaultMojosExecutionStrategy.execute (DefaultMojosExecutionStrategy.java:39)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:160)
    at org.apache.maven.lifecycle.internal.MojoExecutor.executeForkedExecutions (MojoExecutor.java:462)
    at org.apache.maven.lifecycle.internal.MojoExecutor.doExecute (MojoExecutor.java:325)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:213)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:175)
    at org.apache.maven.lifecycle.internal.MojoExecutor.access$000 (MojoExecutor.java:76)
    at org.apache.maven.lifecycle.internal.MojoExecutor$1.run (MojoExecutor.java:163)
    at org.apache.maven.plugin.DefaultMojosExecutionStrategy.execute (DefaultMojosExecutionStrategy.java:39)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:160)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:105)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:73)
    at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build (SingleThreadedBuilder.java:53)
    at org.apache.maven.lifecycle.internal.LifecycleStarter.execute (LifecycleStarter.java:118)
    at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:261)
    at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:173)
    at org.apache.maven.DefaultMaven.execute (DefaultMaven.java:101)
    at org.apache.maven.cli.MavenCli.execute (MavenCli.java:827)
    at org.apache.maven.cli.MavenCli.doMain (MavenCli.java:272)
    at org.apache.maven.cli.MavenCli.main (MavenCli.java:195)
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0 (Native Method)
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:77)
    at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke (Method.java:568)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced (Launcher.java:282)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launch (Launcher.java:225)
    at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode (Launcher.java:406)
    at org.codehaus.plexus.classworlds.launcher.Launcher.main (Launcher.java:347)
Caused by: org.apache.maven.plugin.PluginContainerException: A required class was missing while executing org.apache.maven.plugins:maven-resources-plugin:3.3.1:resources: org/apache/commons/lang3/StringUtils
-----------------------------------------------------
realm =    plugin>org.apache.maven.plugins:maven-resources-plugin:3.3.1
strategy = org.codehaus.plexus.classworlds.strategy.SelfFirstStrategy
urls[0] = file:/var/maven/.m2/repository-022/org/apache/maven/plugins/maven-resources-plugin/3.3.1/maven-resources-plugin-3.3.1.jar
urls[1] = file:/var/maven/.m2/repository-022/org/codehaus/plexus/plexus-interpolation/1.26/plexus-interpolation-1.26.jar
urls[2] = file:/var/maven/.m2/repository-022/org/codehaus/plexus/plexus-utils/3.5.1/plexus-utils-3.5.1.jar
urls[3] = file:/var/maven/.m2/repository-022/org/apache/maven/shared/maven-filtering/3.3.1/maven-filtering-3.3.1.jar
urls[4] = file:/var/maven/.m2/repository-022/org/sonatype/plexus/plexus-build-api/0.0.7/plexus-build-api-0.0.7.jar
urls[5] = file:/var/maven/.m2/repository-022/commons-io/commons-io/2.11.0/commons-io-2.11.0.jar
urls[6] = file:/var/maven/.m2/repository-022/org/apache/commons/commons-lang3/3.12.0/commons-lang3-3.12.0.jar
Number of foreign imports: 1
import: Entry[import  from realm ClassRealm[project>pl.com.labaj.autorecord:auto-record-project:3.1.0, parent: ClassRealm[maven.api, parent: null]]]

-----------------------------------------------------

    at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo (DefaultBuildPluginManager.java:150)
    at org.apache.maven.lifecycle.internal.MojoExecutor.doExecute2 (MojoExecutor.java:342)
    at org.apache.maven.lifecycle.internal.MojoExecutor.doExecute (MojoExecutor.java:330)
(...)  
Caused by: java.lang.ClassNotFoundException: org.apache.commons.lang3.StringUtils
    at org.codehaus.plexus.classworlds.strategy.SelfFirstStrategy.loadClass (SelfFirstStrategy.java:50)
    at org.codehaus.plexus.classworlds.realm.ClassRealm.unsynchronizedLoadClass (ClassRealm.java:271)
    at org.codehaus.plexus.classworlds.realm.ClassRealm.loadClass (ClassRealm.java:247)
    at org.codehaus.plexus.classworlds.realm.ClassRealm.loadClass (ClassRealm.java:239)

Archive /var/maven/.m2/repository-022/org/apache/commons/commons-lang3/3.12.0/commons-lang3-3.12.0.jar exists and org.apache.commons.lang3.StringUtils class int it.

Memoizer and builders issue

  1. New instance of memoizers should be created
  2. Creating memoizers in compact constructor if needed should be added

[BUG] Generic type with concrete type parameters generate non-compiling code

Describe the bug

Given following set of interfaces:

interface BaseType {
}
public interface Common<T extends BaseType> {
    T getParam();
}
public interface ConcreteType extends BaseType {
}
@DefaultAutoRecord
public interface MyRecord extends Common<ConcreteType> {
}

I would expect generated MyRecordRecord constructor to have argument of type ConcreteType, however this is not the case, Type is set to T and record cannot be compiled.


**Details**

See above

3. Generated record:

```java
@Generated("pl.com.labaj.autorecord.AutoRecord")
@GeneratedWithAutoRecord
@RecordBuilder
@RecordBuilder.Options(
    addClassRetainedGenerated = true,
    enableWither = false
)
public record MyRecordRecord(T getParam) implements MyRecord {
  public MyRecordRecord {
    // pl.com.labaj.autorecord.processor.AutoRecordProcessor
    requireNonNull(getParam, "getParam must not be null");
  }

  public static MyRecordRecordBuilder builder() {
    return MyRecordRecordBuilder.builder();
  }

  public MyRecordRecordBuilder toBuilder() {
    return MyRecordRecordBuilder.builder(this);
  }
}

5. Error during compilation:

java: cannot find symbol
symbol: class T
location: class MyRecordRecord


**Expected behavior**

```java
public record MyRecordRecord(ConcreteType getParam) implements MyRecord

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.